browser.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. #include "../util.h"
  2. #include "../cache.h"
  3. #include "../../perf.h"
  4. #include "libslang.h"
  5. #include "ui.h"
  6. #include "util.h"
  7. #include <linux/compiler.h>
  8. #include <linux/list.h>
  9. #include <linux/rbtree.h>
  10. #include <stdlib.h>
  11. #include <sys/ttydefaults.h>
  12. #include "browser.h"
  13. #include "helpline.h"
  14. #include "keysyms.h"
  15. #include "../color.h"
  16. static int ui_browser__percent_color(struct ui_browser *browser,
  17. double percent, bool current)
  18. {
  19. if (current && (!browser->use_navkeypressed || browser->navkeypressed))
  20. return HE_COLORSET_SELECTED;
  21. if (percent >= MIN_RED)
  22. return HE_COLORSET_TOP;
  23. if (percent >= MIN_GREEN)
  24. return HE_COLORSET_MEDIUM;
  25. return HE_COLORSET_NORMAL;
  26. }
  27. int ui_browser__set_color(struct ui_browser *browser, int color)
  28. {
  29. int ret = browser->current_color;
  30. browser->current_color = color;
  31. SLsmg_set_color(color);
  32. return ret;
  33. }
  34. void ui_browser__set_percent_color(struct ui_browser *browser,
  35. double percent, bool current)
  36. {
  37. int color = ui_browser__percent_color(browser, percent, current);
  38. ui_browser__set_color(browser, color);
  39. }
  40. void ui_browser__gotorc(struct ui_browser *browser, int y, int x)
  41. {
  42. SLsmg_gotorc(browser->y + y, browser->x + x);
  43. }
  44. static struct list_head *
  45. ui_browser__list_head_filter_entries(struct ui_browser *browser,
  46. struct list_head *pos)
  47. {
  48. do {
  49. if (!browser->filter || !browser->filter(browser, pos))
  50. return pos;
  51. pos = pos->next;
  52. } while (pos != browser->entries);
  53. return NULL;
  54. }
  55. static struct list_head *
  56. ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
  57. struct list_head *pos)
  58. {
  59. do {
  60. if (!browser->filter || !browser->filter(browser, pos))
  61. return pos;
  62. pos = pos->prev;
  63. } while (pos != browser->entries);
  64. return NULL;
  65. }
  66. void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence)
  67. {
  68. struct list_head *head = browser->entries;
  69. struct list_head *pos;
  70. if (browser->nr_entries == 0)
  71. return;
  72. switch (whence) {
  73. case SEEK_SET:
  74. pos = ui_browser__list_head_filter_entries(browser, head->next);
  75. break;
  76. case SEEK_CUR:
  77. pos = browser->top;
  78. break;
  79. case SEEK_END:
  80. pos = ui_browser__list_head_filter_prev_entries(browser, head->prev);
  81. break;
  82. default:
  83. return;
  84. }
  85. assert(pos != NULL);
  86. if (offset > 0) {
  87. while (offset-- != 0)
  88. pos = ui_browser__list_head_filter_entries(browser, pos->next);
  89. } else {
  90. while (offset++ != 0)
  91. pos = ui_browser__list_head_filter_prev_entries(browser, pos->prev);
  92. }
  93. browser->top = pos;
  94. }
  95. void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence)
  96. {
  97. struct rb_root *root = browser->entries;
  98. struct rb_node *nd;
  99. switch (whence) {
  100. case SEEK_SET:
  101. nd = rb_first(root);
  102. break;
  103. case SEEK_CUR:
  104. nd = browser->top;
  105. break;
  106. case SEEK_END:
  107. nd = rb_last(root);
  108. break;
  109. default:
  110. return;
  111. }
  112. if (offset > 0) {
  113. while (offset-- != 0)
  114. nd = rb_next(nd);
  115. } else {
  116. while (offset++ != 0)
  117. nd = rb_prev(nd);
  118. }
  119. browser->top = nd;
  120. }
  121. unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser)
  122. {
  123. struct rb_node *nd;
  124. int row = 0;
  125. if (browser->top == NULL)
  126. browser->top = rb_first(browser->entries);
  127. nd = browser->top;
  128. while (nd != NULL) {
  129. ui_browser__gotorc(browser, row, 0);
  130. browser->write(browser, nd, row);
  131. if (++row == browser->height)
  132. break;
  133. nd = rb_next(nd);
  134. }
  135. return row;
  136. }
  137. bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row)
  138. {
  139. return browser->top_idx + row == browser->index;
  140. }
  141. void ui_browser__refresh_dimensions(struct ui_browser *browser)
  142. {
  143. browser->width = SLtt_Screen_Cols - 1;
  144. browser->height = SLtt_Screen_Rows - 2;
  145. browser->y = 1;
  146. browser->x = 0;
  147. }
  148. void ui_browser__handle_resize(struct ui_browser *browser)
  149. {
  150. ui__refresh_dimensions(false);
  151. ui_browser__show(browser, browser->title, ui_helpline__current);
  152. ui_browser__refresh(browser);
  153. }
  154. int ui_browser__warning(struct ui_browser *browser, int timeout,
  155. const char *format, ...)
  156. {
  157. va_list args;
  158. char *text;
  159. int key = 0, err;
  160. va_start(args, format);
  161. err = vasprintf(&text, format, args);
  162. va_end(args);
  163. if (err < 0) {
  164. va_start(args, format);
  165. ui_helpline__vpush(format, args);
  166. va_end(args);
  167. } else {
  168. while ((key == ui__question_window("Warning!", text,
  169. "Press any key...",
  170. timeout)) == K_RESIZE)
  171. ui_browser__handle_resize(browser);
  172. free(text);
  173. }
  174. return key;
  175. }
  176. int ui_browser__help_window(struct ui_browser *browser, const char *text)
  177. {
  178. int key;
  179. while ((key = ui__help_window(text)) == K_RESIZE)
  180. ui_browser__handle_resize(browser);
  181. return key;
  182. }
  183. bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text)
  184. {
  185. int key;
  186. while ((key = ui__dialog_yesno(text)) == K_RESIZE)
  187. ui_browser__handle_resize(browser);
  188. return key == K_ENTER || toupper(key) == 'Y';
  189. }
  190. void ui_browser__reset_index(struct ui_browser *browser)
  191. {
  192. browser->index = browser->top_idx = 0;
  193. browser->seek(browser, 0, SEEK_SET);
  194. }
  195. void __ui_browser__show_title(struct ui_browser *browser, const char *title)
  196. {
  197. SLsmg_gotorc(0, 0);
  198. ui_browser__set_color(browser, HE_COLORSET_ROOT);
  199. slsmg_write_nstring(title, browser->width + 1);
  200. }
  201. void ui_browser__show_title(struct ui_browser *browser, const char *title)
  202. {
  203. pthread_mutex_lock(&ui__lock);
  204. __ui_browser__show_title(browser, title);
  205. pthread_mutex_unlock(&ui__lock);
  206. }
  207. int ui_browser__show(struct ui_browser *browser, const char *title,
  208. const char *helpline, ...)
  209. {
  210. int err;
  211. va_list ap;
  212. ui_browser__refresh_dimensions(browser);
  213. pthread_mutex_lock(&ui__lock);
  214. __ui_browser__show_title(browser, title);
  215. browser->title = title;
  216. free(browser->helpline);
  217. browser->helpline = NULL;
  218. va_start(ap, helpline);
  219. err = vasprintf(&browser->helpline, helpline, ap);
  220. va_end(ap);
  221. if (err > 0)
  222. ui_helpline__push(browser->helpline);
  223. pthread_mutex_unlock(&ui__lock);
  224. return err ? 0 : -1;
  225. }
  226. void ui_browser__hide(struct ui_browser *browser __maybe_unused)
  227. {
  228. pthread_mutex_lock(&ui__lock);
  229. ui_helpline__pop();
  230. free(browser->helpline);
  231. browser->helpline = NULL;
  232. pthread_mutex_unlock(&ui__lock);
  233. }
  234. static void ui_browser__scrollbar_set(struct ui_browser *browser)
  235. {
  236. int height = browser->height, h = 0, pct = 0,
  237. col = browser->width,
  238. row = browser->y - 1;
  239. if (browser->nr_entries > 1) {
  240. pct = ((browser->index * (browser->height - 1)) /
  241. (browser->nr_entries - 1));
  242. }
  243. SLsmg_set_char_set(1);
  244. while (h < height) {
  245. ui_browser__gotorc(browser, row++, col);
  246. SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
  247. ++h;
  248. }
  249. SLsmg_set_char_set(0);
  250. }
  251. static int __ui_browser__refresh(struct ui_browser *browser)
  252. {
  253. int row;
  254. int width = browser->width;
  255. row = browser->refresh(browser);
  256. ui_browser__set_color(browser, HE_COLORSET_NORMAL);
  257. if (!browser->use_navkeypressed || browser->navkeypressed)
  258. ui_browser__scrollbar_set(browser);
  259. else
  260. width += 1;
  261. SLsmg_fill_region(browser->y + row, browser->x,
  262. browser->height - row, width, ' ');
  263. return 0;
  264. }
  265. int ui_browser__refresh(struct ui_browser *browser)
  266. {
  267. pthread_mutex_lock(&ui__lock);
  268. __ui_browser__refresh(browser);
  269. pthread_mutex_unlock(&ui__lock);
  270. return 0;
  271. }
  272. /*
  273. * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
  274. * forget about any reference to any entry in the underlying data structure,
  275. * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
  276. * after an output_resort and hist decay.
  277. */
  278. void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
  279. {
  280. off_t offset = nr_entries - browser->nr_entries;
  281. browser->nr_entries = nr_entries;
  282. if (offset < 0) {
  283. if (browser->top_idx < (u64)-offset)
  284. offset = -browser->top_idx;
  285. browser->index += offset;
  286. browser->top_idx += offset;
  287. }
  288. browser->top = NULL;
  289. browser->seek(browser, browser->top_idx, SEEK_SET);
  290. }
  291. int ui_browser__run(struct ui_browser *browser, int delay_secs)
  292. {
  293. int err, key;
  294. while (1) {
  295. off_t offset;
  296. pthread_mutex_lock(&ui__lock);
  297. err = __ui_browser__refresh(browser);
  298. SLsmg_refresh();
  299. pthread_mutex_unlock(&ui__lock);
  300. if (err < 0)
  301. break;
  302. key = ui__getch(delay_secs);
  303. if (key == K_RESIZE) {
  304. ui__refresh_dimensions(false);
  305. ui_browser__refresh_dimensions(browser);
  306. __ui_browser__show_title(browser, browser->title);
  307. ui_helpline__puts(browser->helpline);
  308. continue;
  309. }
  310. if (browser->use_navkeypressed && !browser->navkeypressed) {
  311. if (key == K_DOWN || key == K_UP ||
  312. key == K_PGDN || key == K_PGUP ||
  313. key == K_HOME || key == K_END ||
  314. key == ' ') {
  315. browser->navkeypressed = true;
  316. continue;
  317. } else
  318. return key;
  319. }
  320. switch (key) {
  321. case K_DOWN:
  322. if (browser->index == browser->nr_entries - 1)
  323. break;
  324. ++browser->index;
  325. if (browser->index == browser->top_idx + browser->height) {
  326. ++browser->top_idx;
  327. browser->seek(browser, +1, SEEK_CUR);
  328. }
  329. break;
  330. case K_UP:
  331. if (browser->index == 0)
  332. break;
  333. --browser->index;
  334. if (browser->index < browser->top_idx) {
  335. --browser->top_idx;
  336. browser->seek(browser, -1, SEEK_CUR);
  337. }
  338. break;
  339. case K_PGDN:
  340. case ' ':
  341. if (browser->top_idx + browser->height > browser->nr_entries - 1)
  342. break;
  343. offset = browser->height;
  344. if (browser->index + offset > browser->nr_entries - 1)
  345. offset = browser->nr_entries - 1 - browser->index;
  346. browser->index += offset;
  347. browser->top_idx += offset;
  348. browser->seek(browser, +offset, SEEK_CUR);
  349. break;
  350. case K_PGUP:
  351. if (browser->top_idx == 0)
  352. break;
  353. if (browser->top_idx < browser->height)
  354. offset = browser->top_idx;
  355. else
  356. offset = browser->height;
  357. browser->index -= offset;
  358. browser->top_idx -= offset;
  359. browser->seek(browser, -offset, SEEK_CUR);
  360. break;
  361. case K_HOME:
  362. ui_browser__reset_index(browser);
  363. break;
  364. case K_END:
  365. offset = browser->height - 1;
  366. if (offset >= browser->nr_entries)
  367. offset = browser->nr_entries - 1;
  368. browser->index = browser->nr_entries - 1;
  369. browser->top_idx = browser->index - offset;
  370. browser->seek(browser, -offset, SEEK_END);
  371. break;
  372. default:
  373. return key;
  374. }
  375. }
  376. return -1;
  377. }
  378. unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
  379. {
  380. struct list_head *pos;
  381. struct list_head *head = browser->entries;
  382. int row = 0;
  383. if (browser->top == NULL || browser->top == browser->entries)
  384. browser->top = ui_browser__list_head_filter_entries(browser, head->next);
  385. pos = browser->top;
  386. list_for_each_from(pos, head) {
  387. if (!browser->filter || !browser->filter(browser, pos)) {
  388. ui_browser__gotorc(browser, row, 0);
  389. browser->write(browser, pos, row);
  390. if (++row == browser->height)
  391. break;
  392. }
  393. }
  394. return row;
  395. }
  396. static struct ui_browser_colorset {
  397. const char *name, *fg, *bg;
  398. int colorset;
  399. } ui_browser__colorsets[] = {
  400. {
  401. .colorset = HE_COLORSET_TOP,
  402. .name = "top",
  403. .fg = "red",
  404. .bg = "default",
  405. },
  406. {
  407. .colorset = HE_COLORSET_MEDIUM,
  408. .name = "medium",
  409. .fg = "green",
  410. .bg = "default",
  411. },
  412. {
  413. .colorset = HE_COLORSET_NORMAL,
  414. .name = "normal",
  415. .fg = "default",
  416. .bg = "default",
  417. },
  418. {
  419. .colorset = HE_COLORSET_SELECTED,
  420. .name = "selected",
  421. .fg = "black",
  422. .bg = "lightgray",
  423. },
  424. {
  425. .colorset = HE_COLORSET_CODE,
  426. .name = "code",
  427. .fg = "blue",
  428. .bg = "default",
  429. },
  430. {
  431. .colorset = HE_COLORSET_ADDR,
  432. .name = "addr",
  433. .fg = "magenta",
  434. .bg = "default",
  435. },
  436. {
  437. .colorset = HE_COLORSET_ROOT,
  438. .name = "root",
  439. .fg = "white",
  440. .bg = "blue",
  441. },
  442. {
  443. .name = NULL,
  444. }
  445. };
  446. static int ui_browser__color_config(const char *var, const char *value,
  447. void *data __maybe_unused)
  448. {
  449. char *fg = NULL, *bg;
  450. int i;
  451. /* same dir for all commands */
  452. if (prefixcmp(var, "colors.") != 0)
  453. return 0;
  454. for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
  455. const char *name = var + 7;
  456. if (strcmp(ui_browser__colorsets[i].name, name) != 0)
  457. continue;
  458. fg = strdup(value);
  459. if (fg == NULL)
  460. break;
  461. bg = strchr(fg, ',');
  462. if (bg == NULL)
  463. break;
  464. *bg = '\0';
  465. while (isspace(*++bg));
  466. ui_browser__colorsets[i].bg = bg;
  467. ui_browser__colorsets[i].fg = fg;
  468. return 0;
  469. }
  470. free(fg);
  471. return -1;
  472. }
  473. void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
  474. {
  475. switch (whence) {
  476. case SEEK_SET:
  477. browser->top = browser->entries;
  478. break;
  479. case SEEK_CUR:
  480. browser->top = browser->top + browser->top_idx + offset;
  481. break;
  482. case SEEK_END:
  483. browser->top = browser->top + browser->nr_entries + offset;
  484. break;
  485. default:
  486. return;
  487. }
  488. }
  489. unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
  490. {
  491. unsigned int row = 0, idx = browser->top_idx;
  492. char **pos;
  493. if (browser->top == NULL)
  494. browser->top = browser->entries;
  495. pos = (char **)browser->top;
  496. while (idx < browser->nr_entries) {
  497. if (!browser->filter || !browser->filter(browser, *pos)) {
  498. ui_browser__gotorc(browser, row, 0);
  499. browser->write(browser, pos, row);
  500. if (++row == browser->height)
  501. break;
  502. }
  503. ++idx;
  504. ++pos;
  505. }
  506. return row;
  507. }
  508. void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
  509. u16 start, u16 end)
  510. {
  511. SLsmg_set_char_set(1);
  512. ui_browser__gotorc(browser, start, column);
  513. SLsmg_draw_vline(end - start + 1);
  514. SLsmg_set_char_set(0);
  515. }
  516. void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
  517. int graph)
  518. {
  519. SLsmg_set_char_set(1);
  520. SLsmg_write_char(graph);
  521. SLsmg_set_char_set(0);
  522. }
  523. static void __ui_browser__line_arrow_up(struct ui_browser *browser,
  524. unsigned int column,
  525. u64 start, u64 end)
  526. {
  527. unsigned int row, end_row;
  528. SLsmg_set_char_set(1);
  529. if (start < browser->top_idx + browser->height) {
  530. row = start - browser->top_idx;
  531. ui_browser__gotorc(browser, row, column);
  532. SLsmg_write_char(SLSMG_LLCORN_CHAR);
  533. ui_browser__gotorc(browser, row, column + 1);
  534. SLsmg_draw_hline(2);
  535. if (row-- == 0)
  536. goto out;
  537. } else
  538. row = browser->height - 1;
  539. if (end > browser->top_idx)
  540. end_row = end - browser->top_idx;
  541. else
  542. end_row = 0;
  543. ui_browser__gotorc(browser, end_row, column);
  544. SLsmg_draw_vline(row - end_row + 1);
  545. ui_browser__gotorc(browser, end_row, column);
  546. if (end >= browser->top_idx) {
  547. SLsmg_write_char(SLSMG_ULCORN_CHAR);
  548. ui_browser__gotorc(browser, end_row, column + 1);
  549. SLsmg_write_char(SLSMG_HLINE_CHAR);
  550. ui_browser__gotorc(browser, end_row, column + 2);
  551. SLsmg_write_char(SLSMG_RARROW_CHAR);
  552. }
  553. out:
  554. SLsmg_set_char_set(0);
  555. }
  556. static void __ui_browser__line_arrow_down(struct ui_browser *browser,
  557. unsigned int column,
  558. u64 start, u64 end)
  559. {
  560. unsigned int row, end_row;
  561. SLsmg_set_char_set(1);
  562. if (start >= browser->top_idx) {
  563. row = start - browser->top_idx;
  564. ui_browser__gotorc(browser, row, column);
  565. SLsmg_write_char(SLSMG_ULCORN_CHAR);
  566. ui_browser__gotorc(browser, row, column + 1);
  567. SLsmg_draw_hline(2);
  568. if (row++ == 0)
  569. goto out;
  570. } else
  571. row = 0;
  572. if (end >= browser->top_idx + browser->height)
  573. end_row = browser->height - 1;
  574. else
  575. end_row = end - browser->top_idx;;
  576. ui_browser__gotorc(browser, row, column);
  577. SLsmg_draw_vline(end_row - row + 1);
  578. ui_browser__gotorc(browser, end_row, column);
  579. if (end < browser->top_idx + browser->height) {
  580. SLsmg_write_char(SLSMG_LLCORN_CHAR);
  581. ui_browser__gotorc(browser, end_row, column + 1);
  582. SLsmg_write_char(SLSMG_HLINE_CHAR);
  583. ui_browser__gotorc(browser, end_row, column + 2);
  584. SLsmg_write_char(SLSMG_RARROW_CHAR);
  585. }
  586. out:
  587. SLsmg_set_char_set(0);
  588. }
  589. void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
  590. u64 start, u64 end)
  591. {
  592. if (start > end)
  593. __ui_browser__line_arrow_up(browser, column, start, end);
  594. else
  595. __ui_browser__line_arrow_down(browser, column, start, end);
  596. }
  597. void ui_browser__init(void)
  598. {
  599. int i = 0;
  600. perf_config(ui_browser__color_config, NULL);
  601. while (ui_browser__colorsets[i].name) {
  602. struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
  603. sltt_set_color(c->colorset, c->name, c->fg, c->bg);
  604. }
  605. annotate_browser__init();
  606. }