browser.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  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. zfree(&browser->helpline);
  217. va_start(ap, helpline);
  218. err = vasprintf(&browser->helpline, helpline, ap);
  219. va_end(ap);
  220. if (err > 0)
  221. ui_helpline__push(browser->helpline);
  222. pthread_mutex_unlock(&ui__lock);
  223. return err ? 0 : -1;
  224. }
  225. void ui_browser__hide(struct ui_browser *browser)
  226. {
  227. pthread_mutex_lock(&ui__lock);
  228. ui_helpline__pop();
  229. zfree(&browser->helpline);
  230. pthread_mutex_unlock(&ui__lock);
  231. }
  232. static void ui_browser__scrollbar_set(struct ui_browser *browser)
  233. {
  234. int height = browser->height, h = 0, pct = 0,
  235. col = browser->width,
  236. row = browser->y - 1;
  237. if (browser->nr_entries > 1) {
  238. pct = ((browser->index * (browser->height - 1)) /
  239. (browser->nr_entries - 1));
  240. }
  241. SLsmg_set_char_set(1);
  242. while (h < height) {
  243. ui_browser__gotorc(browser, row++, col);
  244. SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR);
  245. ++h;
  246. }
  247. SLsmg_set_char_set(0);
  248. }
  249. static int __ui_browser__refresh(struct ui_browser *browser)
  250. {
  251. int row;
  252. int width = browser->width;
  253. row = browser->refresh(browser);
  254. ui_browser__set_color(browser, HE_COLORSET_NORMAL);
  255. if (!browser->use_navkeypressed || browser->navkeypressed)
  256. ui_browser__scrollbar_set(browser);
  257. else
  258. width += 1;
  259. SLsmg_fill_region(browser->y + row, browser->x,
  260. browser->height - row, width, ' ');
  261. return 0;
  262. }
  263. int ui_browser__refresh(struct ui_browser *browser)
  264. {
  265. pthread_mutex_lock(&ui__lock);
  266. __ui_browser__refresh(browser);
  267. pthread_mutex_unlock(&ui__lock);
  268. return 0;
  269. }
  270. /*
  271. * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
  272. * forget about any reference to any entry in the underlying data structure,
  273. * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
  274. * after an output_resort and hist decay.
  275. */
  276. void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
  277. {
  278. off_t offset = nr_entries - browser->nr_entries;
  279. browser->nr_entries = nr_entries;
  280. if (offset < 0) {
  281. if (browser->top_idx < (u64)-offset)
  282. offset = -browser->top_idx;
  283. browser->index += offset;
  284. browser->top_idx += offset;
  285. }
  286. browser->top = NULL;
  287. browser->seek(browser, browser->top_idx, SEEK_SET);
  288. }
  289. int ui_browser__run(struct ui_browser *browser, int delay_secs)
  290. {
  291. int err, key;
  292. while (1) {
  293. off_t offset;
  294. pthread_mutex_lock(&ui__lock);
  295. err = __ui_browser__refresh(browser);
  296. SLsmg_refresh();
  297. pthread_mutex_unlock(&ui__lock);
  298. if (err < 0)
  299. break;
  300. key = ui__getch(delay_secs);
  301. if (key == K_RESIZE) {
  302. ui__refresh_dimensions(false);
  303. ui_browser__refresh_dimensions(browser);
  304. __ui_browser__show_title(browser, browser->title);
  305. ui_helpline__puts(browser->helpline);
  306. continue;
  307. }
  308. if (browser->use_navkeypressed && !browser->navkeypressed) {
  309. if (key == K_DOWN || key == K_UP ||
  310. key == K_PGDN || key == K_PGUP ||
  311. key == K_HOME || key == K_END ||
  312. key == ' ') {
  313. browser->navkeypressed = true;
  314. continue;
  315. } else
  316. return key;
  317. }
  318. switch (key) {
  319. case K_DOWN:
  320. if (browser->index == browser->nr_entries - 1)
  321. break;
  322. ++browser->index;
  323. if (browser->index == browser->top_idx + browser->height) {
  324. ++browser->top_idx;
  325. browser->seek(browser, +1, SEEK_CUR);
  326. }
  327. break;
  328. case K_UP:
  329. if (browser->index == 0)
  330. break;
  331. --browser->index;
  332. if (browser->index < browser->top_idx) {
  333. --browser->top_idx;
  334. browser->seek(browser, -1, SEEK_CUR);
  335. }
  336. break;
  337. case K_PGDN:
  338. case ' ':
  339. if (browser->top_idx + browser->height > browser->nr_entries - 1)
  340. break;
  341. offset = browser->height;
  342. if (browser->index + offset > browser->nr_entries - 1)
  343. offset = browser->nr_entries - 1 - browser->index;
  344. browser->index += offset;
  345. browser->top_idx += offset;
  346. browser->seek(browser, +offset, SEEK_CUR);
  347. break;
  348. case K_PGUP:
  349. if (browser->top_idx == 0)
  350. break;
  351. if (browser->top_idx < browser->height)
  352. offset = browser->top_idx;
  353. else
  354. offset = browser->height;
  355. browser->index -= offset;
  356. browser->top_idx -= offset;
  357. browser->seek(browser, -offset, SEEK_CUR);
  358. break;
  359. case K_HOME:
  360. ui_browser__reset_index(browser);
  361. break;
  362. case K_END:
  363. offset = browser->height - 1;
  364. if (offset >= browser->nr_entries)
  365. offset = browser->nr_entries - 1;
  366. browser->index = browser->nr_entries - 1;
  367. browser->top_idx = browser->index - offset;
  368. browser->seek(browser, -offset, SEEK_END);
  369. break;
  370. default:
  371. return key;
  372. }
  373. }
  374. return -1;
  375. }
  376. unsigned int ui_browser__list_head_refresh(struct ui_browser *browser)
  377. {
  378. struct list_head *pos;
  379. struct list_head *head = browser->entries;
  380. int row = 0;
  381. if (browser->top == NULL || browser->top == browser->entries)
  382. browser->top = ui_browser__list_head_filter_entries(browser, head->next);
  383. pos = browser->top;
  384. list_for_each_from(pos, head) {
  385. if (!browser->filter || !browser->filter(browser, pos)) {
  386. ui_browser__gotorc(browser, row, 0);
  387. browser->write(browser, pos, row);
  388. if (++row == browser->height)
  389. break;
  390. }
  391. }
  392. return row;
  393. }
  394. static struct ui_browser_colorset {
  395. const char *name, *fg, *bg;
  396. int colorset;
  397. } ui_browser__colorsets[] = {
  398. {
  399. .colorset = HE_COLORSET_TOP,
  400. .name = "top",
  401. .fg = "red",
  402. .bg = "default",
  403. },
  404. {
  405. .colorset = HE_COLORSET_MEDIUM,
  406. .name = "medium",
  407. .fg = "green",
  408. .bg = "default",
  409. },
  410. {
  411. .colorset = HE_COLORSET_NORMAL,
  412. .name = "normal",
  413. .fg = "default",
  414. .bg = "default",
  415. },
  416. {
  417. .colorset = HE_COLORSET_SELECTED,
  418. .name = "selected",
  419. .fg = "black",
  420. .bg = "lightgray",
  421. },
  422. {
  423. .colorset = HE_COLORSET_CODE,
  424. .name = "code",
  425. .fg = "blue",
  426. .bg = "default",
  427. },
  428. {
  429. .colorset = HE_COLORSET_ADDR,
  430. .name = "addr",
  431. .fg = "magenta",
  432. .bg = "default",
  433. },
  434. {
  435. .colorset = HE_COLORSET_ROOT,
  436. .name = "root",
  437. .fg = "white",
  438. .bg = "blue",
  439. },
  440. {
  441. .name = NULL,
  442. }
  443. };
  444. static int ui_browser__color_config(const char *var, const char *value,
  445. void *data __maybe_unused)
  446. {
  447. char *fg = NULL, *bg;
  448. int i;
  449. /* same dir for all commands */
  450. if (prefixcmp(var, "colors.") != 0)
  451. return 0;
  452. for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
  453. const char *name = var + 7;
  454. if (strcmp(ui_browser__colorsets[i].name, name) != 0)
  455. continue;
  456. fg = strdup(value);
  457. if (fg == NULL)
  458. break;
  459. bg = strchr(fg, ',');
  460. if (bg == NULL)
  461. break;
  462. *bg = '\0';
  463. while (isspace(*++bg));
  464. ui_browser__colorsets[i].bg = bg;
  465. ui_browser__colorsets[i].fg = fg;
  466. return 0;
  467. }
  468. free(fg);
  469. return -1;
  470. }
  471. void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
  472. {
  473. switch (whence) {
  474. case SEEK_SET:
  475. browser->top = browser->entries;
  476. break;
  477. case SEEK_CUR:
  478. browser->top = browser->top + browser->top_idx + offset;
  479. break;
  480. case SEEK_END:
  481. browser->top = browser->top + browser->nr_entries - 1 + offset;
  482. break;
  483. default:
  484. return;
  485. }
  486. }
  487. unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
  488. {
  489. unsigned int row = 0, idx = browser->top_idx;
  490. char **pos;
  491. if (browser->top == NULL)
  492. browser->top = browser->entries;
  493. pos = (char **)browser->top;
  494. while (idx < browser->nr_entries) {
  495. if (!browser->filter || !browser->filter(browser, *pos)) {
  496. ui_browser__gotorc(browser, row, 0);
  497. browser->write(browser, pos, row);
  498. if (++row == browser->height)
  499. break;
  500. }
  501. ++idx;
  502. ++pos;
  503. }
  504. return row;
  505. }
  506. void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
  507. u16 start, u16 end)
  508. {
  509. SLsmg_set_char_set(1);
  510. ui_browser__gotorc(browser, start, column);
  511. SLsmg_draw_vline(end - start + 1);
  512. SLsmg_set_char_set(0);
  513. }
  514. void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
  515. int graph)
  516. {
  517. SLsmg_set_char_set(1);
  518. SLsmg_write_char(graph);
  519. SLsmg_set_char_set(0);
  520. }
  521. static void __ui_browser__line_arrow_up(struct ui_browser *browser,
  522. unsigned int column,
  523. u64 start, u64 end)
  524. {
  525. unsigned int row, end_row;
  526. SLsmg_set_char_set(1);
  527. if (start < browser->top_idx + browser->height) {
  528. row = start - browser->top_idx;
  529. ui_browser__gotorc(browser, row, column);
  530. SLsmg_write_char(SLSMG_LLCORN_CHAR);
  531. ui_browser__gotorc(browser, row, column + 1);
  532. SLsmg_draw_hline(2);
  533. if (row-- == 0)
  534. goto out;
  535. } else
  536. row = browser->height - 1;
  537. if (end > browser->top_idx)
  538. end_row = end - browser->top_idx;
  539. else
  540. end_row = 0;
  541. ui_browser__gotorc(browser, end_row, column);
  542. SLsmg_draw_vline(row - end_row + 1);
  543. ui_browser__gotorc(browser, end_row, column);
  544. if (end >= browser->top_idx) {
  545. SLsmg_write_char(SLSMG_ULCORN_CHAR);
  546. ui_browser__gotorc(browser, end_row, column + 1);
  547. SLsmg_write_char(SLSMG_HLINE_CHAR);
  548. ui_browser__gotorc(browser, end_row, column + 2);
  549. SLsmg_write_char(SLSMG_RARROW_CHAR);
  550. }
  551. out:
  552. SLsmg_set_char_set(0);
  553. }
  554. static void __ui_browser__line_arrow_down(struct ui_browser *browser,
  555. unsigned int column,
  556. u64 start, u64 end)
  557. {
  558. unsigned int row, end_row;
  559. SLsmg_set_char_set(1);
  560. if (start >= browser->top_idx) {
  561. row = start - browser->top_idx;
  562. ui_browser__gotorc(browser, row, column);
  563. SLsmg_write_char(SLSMG_ULCORN_CHAR);
  564. ui_browser__gotorc(browser, row, column + 1);
  565. SLsmg_draw_hline(2);
  566. if (row++ == 0)
  567. goto out;
  568. } else
  569. row = 0;
  570. if (end >= browser->top_idx + browser->height)
  571. end_row = browser->height - 1;
  572. else
  573. end_row = end - browser->top_idx;
  574. ui_browser__gotorc(browser, row, column);
  575. SLsmg_draw_vline(end_row - row + 1);
  576. ui_browser__gotorc(browser, end_row, column);
  577. if (end < browser->top_idx + browser->height) {
  578. SLsmg_write_char(SLSMG_LLCORN_CHAR);
  579. ui_browser__gotorc(browser, end_row, column + 1);
  580. SLsmg_write_char(SLSMG_HLINE_CHAR);
  581. ui_browser__gotorc(browser, end_row, column + 2);
  582. SLsmg_write_char(SLSMG_RARROW_CHAR);
  583. }
  584. out:
  585. SLsmg_set_char_set(0);
  586. }
  587. void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column,
  588. u64 start, u64 end)
  589. {
  590. if (start > end)
  591. __ui_browser__line_arrow_up(browser, column, start, end);
  592. else
  593. __ui_browser__line_arrow_down(browser, column, start, end);
  594. }
  595. void ui_browser__init(void)
  596. {
  597. int i = 0;
  598. perf_config(ui_browser__color_config, NULL);
  599. while (ui_browser__colorsets[i].name) {
  600. struct ui_browser_colorset *c = &ui_browser__colorsets[i++];
  601. sltt_set_color(c->colorset, c->name, c->fg, c->bg);
  602. }
  603. annotate_browser__init();
  604. }