1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | #include "../util/string2.h" |
3 | #include "../util/config.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 <linux/string.h> |
11 | #include <stdlib.h> |
12 | #include <sys/ttydefaults.h> |
13 | #include "browser.h" |
14 | #include "helpline.h" |
15 | #include "keysyms.h" |
16 | #include "../util/color.h" |
17 | #include <linux/ctype.h> |
18 | #include <linux/zalloc.h> |
19 | |
20 | static int ui_browser__percent_color(struct ui_browser *browser, |
21 | double percent, bool current) |
22 | { |
23 | if (current && (!browser->use_navkeypressed || browser->navkeypressed)) |
24 | return HE_COLORSET_SELECTED; |
25 | if (percent >= MIN_RED) |
26 | return HE_COLORSET_TOP; |
27 | if (percent >= MIN_GREEN) |
28 | return HE_COLORSET_MEDIUM; |
29 | return HE_COLORSET_NORMAL; |
30 | } |
31 | |
32 | int ui_browser__set_color(struct ui_browser *browser, int color) |
33 | { |
34 | int ret = browser->current_color; |
35 | browser->current_color = color; |
36 | SLsmg_set_color(color); |
37 | return ret; |
38 | } |
39 | |
40 | void ui_browser__set_percent_color(struct ui_browser *browser, |
41 | double percent, bool current) |
42 | { |
43 | int color = ui_browser__percent_color(browser, percent, current); |
44 | ui_browser__set_color(browser, color); |
45 | } |
46 | |
47 | void ui_browser__gotorc_title(struct ui_browser *browser, int y, int x) |
48 | { |
49 | SLsmg_gotorc(browser->y + y, browser->x + x); |
50 | } |
51 | |
52 | void ui_browser__gotorc(struct ui_browser *browser, int y, int x) |
53 | { |
54 | SLsmg_gotorc(browser->y + y + browser->extra_title_lines, browser->x + x); |
55 | } |
56 | |
57 | void ui_browser__write_nstring(struct ui_browser *browser __maybe_unused, const char *msg, |
58 | unsigned int width) |
59 | { |
60 | SLsmg_write_nstring(msg, width); |
61 | } |
62 | |
63 | void ui_browser__vprintf(struct ui_browser *browser __maybe_unused, const char *fmt, va_list args) |
64 | { |
65 | SLsmg_vprintf(fmt, args); |
66 | } |
67 | |
68 | void ui_browser__printf(struct ui_browser *browser __maybe_unused, const char *fmt, ...) |
69 | { |
70 | va_list args; |
71 | |
72 | va_start(args, fmt); |
73 | ui_browser__vprintf(browser, fmt: fmt, args); |
74 | va_end(args); |
75 | } |
76 | |
77 | static struct list_head * |
78 | ui_browser__list_head_filter_entries(struct ui_browser *browser, |
79 | struct list_head *pos) |
80 | { |
81 | do { |
82 | if (!browser->filter || !browser->filter(browser, pos)) |
83 | return pos; |
84 | pos = pos->next; |
85 | } while (pos != browser->entries); |
86 | |
87 | return NULL; |
88 | } |
89 | |
90 | static struct list_head * |
91 | ui_browser__list_head_filter_prev_entries(struct ui_browser *browser, |
92 | struct list_head *pos) |
93 | { |
94 | do { |
95 | if (!browser->filter || !browser->filter(browser, pos)) |
96 | return pos; |
97 | pos = pos->prev; |
98 | } while (pos != browser->entries); |
99 | |
100 | return NULL; |
101 | } |
102 | |
103 | void ui_browser__list_head_seek(struct ui_browser *browser, off_t offset, int whence) |
104 | { |
105 | struct list_head *head = browser->entries; |
106 | struct list_head *pos; |
107 | |
108 | if (browser->nr_entries == 0) |
109 | return; |
110 | |
111 | switch (whence) { |
112 | case SEEK_SET: |
113 | pos = ui_browser__list_head_filter_entries(browser, pos: head->next); |
114 | break; |
115 | case SEEK_CUR: |
116 | pos = browser->top; |
117 | break; |
118 | case SEEK_END: |
119 | pos = ui_browser__list_head_filter_prev_entries(browser, pos: head->prev); |
120 | break; |
121 | default: |
122 | return; |
123 | } |
124 | |
125 | assert(pos != NULL); |
126 | |
127 | if (offset > 0) { |
128 | while (offset-- != 0) |
129 | pos = ui_browser__list_head_filter_entries(browser, pos: pos->next); |
130 | } else { |
131 | while (offset++ != 0) |
132 | pos = ui_browser__list_head_filter_prev_entries(browser, pos: pos->prev); |
133 | } |
134 | |
135 | browser->top = pos; |
136 | } |
137 | |
138 | void ui_browser__rb_tree_seek(struct ui_browser *browser, off_t offset, int whence) |
139 | { |
140 | struct rb_root *root = browser->entries; |
141 | struct rb_node *nd; |
142 | |
143 | switch (whence) { |
144 | case SEEK_SET: |
145 | nd = rb_first(root); |
146 | break; |
147 | case SEEK_CUR: |
148 | nd = browser->top; |
149 | break; |
150 | case SEEK_END: |
151 | nd = rb_last(root); |
152 | break; |
153 | default: |
154 | return; |
155 | } |
156 | |
157 | if (offset > 0) { |
158 | while (offset-- != 0) |
159 | nd = rb_next(nd); |
160 | } else { |
161 | while (offset++ != 0) |
162 | nd = rb_prev(nd); |
163 | } |
164 | |
165 | browser->top = nd; |
166 | } |
167 | |
168 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *browser) |
169 | { |
170 | struct rb_node *nd; |
171 | int row = 0; |
172 | |
173 | if (browser->top == NULL) |
174 | browser->top = rb_first(browser->entries); |
175 | |
176 | nd = browser->top; |
177 | |
178 | while (nd != NULL) { |
179 | ui_browser__gotorc(browser, y: row, x: 0); |
180 | browser->write(browser, nd, row); |
181 | if (++row == browser->rows) |
182 | break; |
183 | nd = rb_next(nd); |
184 | } |
185 | |
186 | return row; |
187 | } |
188 | |
189 | bool ui_browser__is_current_entry(struct ui_browser *browser, unsigned row) |
190 | { |
191 | return browser->top_idx + row == browser->index; |
192 | } |
193 | |
194 | void ui_browser__refresh_dimensions(struct ui_browser *browser) |
195 | { |
196 | browser->width = SLtt_Screen_Cols - 1; |
197 | browser->height = browser->rows = SLtt_Screen_Rows - 2; |
198 | browser->rows -= browser->extra_title_lines; |
199 | browser->y = 1; |
200 | browser->x = 0; |
201 | } |
202 | |
203 | void ui_browser__handle_resize(struct ui_browser *browser) |
204 | { |
205 | ui__refresh_dimensions(false); |
206 | ui_browser__show(browser, title: browser->title ?: "", helpline: ui_helpline__current); |
207 | ui_browser__refresh(browser); |
208 | } |
209 | |
210 | int ui_browser__warning(struct ui_browser *browser, int timeout, |
211 | const char *format, ...) |
212 | { |
213 | va_list args; |
214 | char *text; |
215 | int key = 0, err; |
216 | |
217 | va_start(args, format); |
218 | err = vasprintf(&text, format, args); |
219 | va_end(args); |
220 | |
221 | if (err < 0) { |
222 | va_start(args, format); |
223 | ui_helpline__vpush(fmt: format, ap: args); |
224 | va_end(args); |
225 | } else { |
226 | while ((key = ui__question_window(title: "Warning!", text, |
227 | exit_msg: "Press any key...", |
228 | delay_secs: timeout)) == K_RESIZE) |
229 | ui_browser__handle_resize(browser); |
230 | free(ptr: text); |
231 | } |
232 | |
233 | return key; |
234 | } |
235 | |
236 | int ui_browser__warn_unhandled_hotkey(struct ui_browser *browser, int key, int timeout, const char *help) |
237 | { |
238 | char kname[32]; |
239 | |
240 | key_name(key, bf: kname, size: sizeof(kname)); |
241 | return ui_browser__warning(browser, timeout, format: "\n'%s' key not associated%s!\n", kname, help ?: ""); |
242 | } |
243 | |
244 | int ui_browser__help_window(struct ui_browser *browser, const char *text) |
245 | { |
246 | int key; |
247 | |
248 | while ((key = ui__help_window(text)) == K_RESIZE) |
249 | ui_browser__handle_resize(browser); |
250 | |
251 | return key; |
252 | } |
253 | |
254 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text) |
255 | { |
256 | int key; |
257 | |
258 | while ((key = ui__dialog_yesno(msg: text)) == K_RESIZE) |
259 | ui_browser__handle_resize(browser); |
260 | |
261 | return key == K_ENTER || toupper(key) == 'Y'; |
262 | } |
263 | |
264 | void ui_browser__reset_index(struct ui_browser *browser) |
265 | { |
266 | browser->index = browser->top_idx = 0; |
267 | browser->seek(browser, 0, SEEK_SET); |
268 | } |
269 | |
270 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) |
271 | { |
272 | SLsmg_gotorc(0, 0); |
273 | ui_browser__set_color(browser, HE_COLORSET_ROOT); |
274 | ui_browser__write_nstring(browser, msg: title, width: browser->width + 1); |
275 | } |
276 | |
277 | void ui_browser__show_title(struct ui_browser *browser, const char *title) |
278 | { |
279 | mutex_lock(mtx: &ui__lock); |
280 | __ui_browser__show_title(browser, title); |
281 | mutex_unlock(mtx: &ui__lock); |
282 | } |
283 | |
284 | int ui_browser__show(struct ui_browser *browser, const char *title, |
285 | const char *helpline, ...) |
286 | { |
287 | int err; |
288 | va_list ap; |
289 | |
290 | if (browser->refresh_dimensions == NULL) |
291 | browser->refresh_dimensions = ui_browser__refresh_dimensions; |
292 | |
293 | browser->refresh_dimensions(browser); |
294 | |
295 | mutex_lock(mtx: &ui__lock); |
296 | __ui_browser__show_title(browser, title); |
297 | |
298 | free(ptr: browser->title); |
299 | browser->title = strdup(s: title); |
300 | zfree(&browser->helpline); |
301 | |
302 | va_start(ap, helpline); |
303 | err = vasprintf(&browser->helpline, helpline, ap); |
304 | va_end(ap); |
305 | if (err > 0) |
306 | ui_helpline__push(msg: browser->helpline); |
307 | mutex_unlock(mtx: &ui__lock); |
308 | return err ? 0 : -1; |
309 | } |
310 | |
311 | void ui_browser__hide(struct ui_browser *browser) |
312 | { |
313 | mutex_lock(mtx: &ui__lock); |
314 | ui_helpline__pop(); |
315 | zfree(&browser->helpline); |
316 | zfree(&browser->title); |
317 | mutex_unlock(mtx: &ui__lock); |
318 | } |
319 | |
320 | static void ui_browser__scrollbar_set(struct ui_browser *browser) |
321 | { |
322 | int height = browser->height, h = 0, pct = 0, |
323 | col = browser->width, |
324 | row = 0; |
325 | |
326 | if (browser->nr_entries > 1) { |
327 | pct = ((browser->index * (browser->height - 1)) / |
328 | (browser->nr_entries - 1)); |
329 | } |
330 | |
331 | SLsmg_set_char_set(1); |
332 | |
333 | while (h < height) { |
334 | ui_browser__gotorc(browser, y: row++, x: col); |
335 | SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR); |
336 | ++h; |
337 | } |
338 | |
339 | SLsmg_set_char_set(0); |
340 | } |
341 | |
342 | static int __ui_browser__refresh(struct ui_browser *browser) |
343 | { |
344 | int row; |
345 | int width = browser->width; |
346 | |
347 | row = browser->refresh(browser); |
348 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); |
349 | |
350 | if (!browser->use_navkeypressed || browser->navkeypressed) |
351 | ui_browser__scrollbar_set(browser); |
352 | else |
353 | width += 1; |
354 | |
355 | SLsmg_fill_region(browser->y + row + browser->extra_title_lines, browser->x, |
356 | browser->rows - row, width, ' '); |
357 | |
358 | if (browser->nr_entries == 0 && browser->no_samples_msg) |
359 | __ui__info_window(NULL, text: browser->no_samples_msg, NULL); |
360 | return 0; |
361 | } |
362 | |
363 | int ui_browser__refresh(struct ui_browser *browser) |
364 | { |
365 | mutex_lock(mtx: &ui__lock); |
366 | __ui_browser__refresh(browser); |
367 | mutex_unlock(mtx: &ui__lock); |
368 | |
369 | return 0; |
370 | } |
371 | |
372 | /* |
373 | * Here we're updating nr_entries _after_ we started browsing, i.e. we have to |
374 | * forget about any reference to any entry in the underlying data structure, |
375 | * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser |
376 | * after an output_resort and hist decay. |
377 | */ |
378 | void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries) |
379 | { |
380 | off_t offset = nr_entries - browser->nr_entries; |
381 | |
382 | browser->nr_entries = nr_entries; |
383 | |
384 | if (offset < 0) { |
385 | if (browser->top_idx < (u64)-offset) |
386 | offset = -browser->top_idx; |
387 | |
388 | browser->index += offset; |
389 | browser->top_idx += offset; |
390 | } |
391 | |
392 | browser->top = NULL; |
393 | browser->seek(browser, browser->top_idx, SEEK_SET); |
394 | } |
395 | |
396 | int ui_browser__run(struct ui_browser *browser, int delay_secs) |
397 | { |
398 | int err, key; |
399 | |
400 | while (1) { |
401 | off_t offset; |
402 | |
403 | mutex_lock(mtx: &ui__lock); |
404 | err = __ui_browser__refresh(browser); |
405 | SLsmg_refresh(); |
406 | mutex_unlock(mtx: &ui__lock); |
407 | if (err < 0) |
408 | break; |
409 | |
410 | key = ui__getch(delay_secs); |
411 | |
412 | if (key == K_RESIZE) { |
413 | ui__refresh_dimensions(false); |
414 | browser->refresh_dimensions(browser); |
415 | __ui_browser__show_title(browser, title: browser->title); |
416 | ui_helpline__puts(msg: browser->helpline); |
417 | continue; |
418 | } |
419 | |
420 | if (browser->use_navkeypressed && !browser->navkeypressed) { |
421 | if (key == K_DOWN || key == K_UP || |
422 | (browser->columns && (key == K_LEFT || key == K_RIGHT)) || |
423 | key == K_PGDN || key == K_PGUP || |
424 | key == K_HOME || key == K_END || |
425 | key == ' ') { |
426 | browser->navkeypressed = true; |
427 | continue; |
428 | } else |
429 | return key; |
430 | } |
431 | |
432 | switch (key) { |
433 | case K_DOWN: |
434 | if (browser->index == browser->nr_entries - 1) |
435 | break; |
436 | ++browser->index; |
437 | if (browser->index == browser->top_idx + browser->rows) { |
438 | ++browser->top_idx; |
439 | browser->seek(browser, +1, SEEK_CUR); |
440 | } |
441 | break; |
442 | case K_UP: |
443 | if (browser->index == 0) |
444 | break; |
445 | --browser->index; |
446 | if (browser->index < browser->top_idx) { |
447 | --browser->top_idx; |
448 | browser->seek(browser, -1, SEEK_CUR); |
449 | } |
450 | break; |
451 | case K_RIGHT: |
452 | if (!browser->columns) |
453 | goto out; |
454 | if (browser->horiz_scroll < browser->columns - 1) |
455 | ++browser->horiz_scroll; |
456 | break; |
457 | case K_LEFT: |
458 | if (!browser->columns) |
459 | goto out; |
460 | if (browser->horiz_scroll != 0) |
461 | --browser->horiz_scroll; |
462 | else |
463 | goto out; |
464 | break; |
465 | case K_PGDN: |
466 | case ' ': |
467 | if (browser->top_idx + browser->rows > browser->nr_entries - 1) |
468 | break; |
469 | |
470 | offset = browser->rows; |
471 | if (browser->index + offset > browser->nr_entries - 1) |
472 | offset = browser->nr_entries - 1 - browser->index; |
473 | browser->index += offset; |
474 | browser->top_idx += offset; |
475 | browser->seek(browser, +offset, SEEK_CUR); |
476 | break; |
477 | case K_PGUP: |
478 | if (browser->top_idx == 0) |
479 | break; |
480 | |
481 | if (browser->top_idx < browser->rows) |
482 | offset = browser->top_idx; |
483 | else |
484 | offset = browser->rows; |
485 | |
486 | browser->index -= offset; |
487 | browser->top_idx -= offset; |
488 | browser->seek(browser, -offset, SEEK_CUR); |
489 | break; |
490 | case K_HOME: |
491 | ui_browser__reset_index(browser); |
492 | break; |
493 | case K_END: |
494 | offset = browser->rows - 1; |
495 | if (offset >= browser->nr_entries) |
496 | offset = browser->nr_entries - 1; |
497 | |
498 | browser->index = browser->nr_entries - 1; |
499 | browser->top_idx = browser->index - offset; |
500 | browser->seek(browser, -offset, SEEK_END); |
501 | break; |
502 | default: |
503 | out: |
504 | return key; |
505 | } |
506 | } |
507 | return -1; |
508 | } |
509 | |
510 | unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) |
511 | { |
512 | struct list_head *pos; |
513 | struct list_head *head = browser->entries; |
514 | int row = 0; |
515 | |
516 | if (browser->top == NULL || browser->top == browser->entries) |
517 | browser->top = ui_browser__list_head_filter_entries(browser, pos: head->next); |
518 | |
519 | pos = browser->top; |
520 | |
521 | list_for_each_from(pos, head) { |
522 | if (!browser->filter || !browser->filter(browser, pos)) { |
523 | ui_browser__gotorc(browser, y: row, x: 0); |
524 | browser->write(browser, pos, row); |
525 | if (++row == browser->rows) |
526 | break; |
527 | } |
528 | } |
529 | |
530 | return row; |
531 | } |
532 | |
533 | static struct ui_browser_colorset { |
534 | const char *name, *fg, *bg; |
535 | int colorset; |
536 | } ui_browser__colorsets[] = { |
537 | { |
538 | .colorset = HE_COLORSET_TOP, |
539 | .name = "top", |
540 | .fg = "red", |
541 | .bg = "default", |
542 | }, |
543 | { |
544 | .colorset = HE_COLORSET_MEDIUM, |
545 | .name = "medium", |
546 | .fg = "green", |
547 | .bg = "default", |
548 | }, |
549 | { |
550 | .colorset = HE_COLORSET_NORMAL, |
551 | .name = "normal", |
552 | .fg = "default", |
553 | .bg = "default", |
554 | }, |
555 | { |
556 | .colorset = HE_COLORSET_SELECTED, |
557 | .name = "selected", |
558 | .fg = "black", |
559 | .bg = "yellow", |
560 | }, |
561 | { |
562 | .colorset = HE_COLORSET_JUMP_ARROWS, |
563 | .name = "jump_arrows", |
564 | .fg = "blue", |
565 | .bg = "default", |
566 | }, |
567 | { |
568 | .colorset = HE_COLORSET_ADDR, |
569 | .name = "addr", |
570 | .fg = "magenta", |
571 | .bg = "default", |
572 | }, |
573 | { |
574 | .colorset = HE_COLORSET_ROOT, |
575 | .name = "root", |
576 | .fg = "white", |
577 | .bg = "blue", |
578 | }, |
579 | { |
580 | .name = NULL, |
581 | } |
582 | }; |
583 | |
584 | |
585 | static int ui_browser__color_config(const char *var, const char *value, |
586 | void *data __maybe_unused) |
587 | { |
588 | char *fg = NULL, *bg; |
589 | int i; |
590 | |
591 | /* same dir for all commands */ |
592 | if (!strstarts(var, "colors.") != 0) |
593 | return 0; |
594 | |
595 | for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) { |
596 | const char *name = var + 7; |
597 | |
598 | if (strcmp(s1: ui_browser__colorsets[i].name, s2: name) != 0) |
599 | continue; |
600 | |
601 | fg = strdup(s: value); |
602 | if (fg == NULL) |
603 | break; |
604 | |
605 | bg = strchr(s: fg, c: ','); |
606 | if (bg == NULL) |
607 | break; |
608 | |
609 | *bg = '\0'; |
610 | bg = skip_spaces(bg + 1); |
611 | ui_browser__colorsets[i].bg = bg; |
612 | ui_browser__colorsets[i].fg = fg; |
613 | return 0; |
614 | } |
615 | |
616 | free(ptr: fg); |
617 | return -1; |
618 | } |
619 | |
620 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) |
621 | { |
622 | switch (whence) { |
623 | case SEEK_SET: |
624 | browser->top = browser->entries; |
625 | break; |
626 | case SEEK_CUR: |
627 | browser->top = (char **)browser->top + offset; |
628 | break; |
629 | case SEEK_END: |
630 | browser->top = (char **)browser->entries + browser->nr_entries - 1 + offset; |
631 | break; |
632 | default: |
633 | return; |
634 | } |
635 | assert((char **)browser->top < (char **)browser->entries + browser->nr_entries); |
636 | assert((char **)browser->top >= (char **)browser->entries); |
637 | } |
638 | |
639 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser) |
640 | { |
641 | unsigned int row = 0, idx = browser->top_idx; |
642 | char **pos; |
643 | |
644 | if (browser->top == NULL) |
645 | browser->top = browser->entries; |
646 | |
647 | pos = (char **)browser->top; |
648 | while (idx < browser->nr_entries && |
649 | row < (unsigned)SLtt_Screen_Rows - 1) { |
650 | assert(pos < (char **)browser->entries + browser->nr_entries); |
651 | if (!browser->filter || !browser->filter(browser, *pos)) { |
652 | ui_browser__gotorc(browser, y: row, x: 0); |
653 | browser->write(browser, pos, row); |
654 | if (++row == browser->rows) |
655 | break; |
656 | } |
657 | |
658 | ++idx; |
659 | ++pos; |
660 | } |
661 | |
662 | return row; |
663 | } |
664 | |
665 | void __ui_browser__vline(struct ui_browser *browser, unsigned int column, |
666 | u16 start, u16 end) |
667 | { |
668 | SLsmg_set_char_set(1); |
669 | ui_browser__gotorc(browser, y: start, x: column); |
670 | SLsmg_draw_vline(end - start + 1); |
671 | SLsmg_set_char_set(0); |
672 | } |
673 | |
674 | void ui_browser__write_graph(struct ui_browser *browser __maybe_unused, |
675 | int graph) |
676 | { |
677 | SLsmg_set_char_set(1); |
678 | SLsmg_write_char(graph); |
679 | SLsmg_set_char_set(0); |
680 | } |
681 | |
682 | static void __ui_browser__line_arrow_up(struct ui_browser *browser, |
683 | unsigned int column, |
684 | u64 start, u64 end) |
685 | { |
686 | unsigned int row, end_row; |
687 | |
688 | SLsmg_set_char_set(1); |
689 | |
690 | if (start < browser->top_idx + browser->rows) { |
691 | row = start - browser->top_idx; |
692 | ui_browser__gotorc(browser, y: row, x: column); |
693 | SLsmg_write_char(SLSMG_LLCORN_CHAR); |
694 | ui_browser__gotorc(browser, y: row, x: column + 1); |
695 | SLsmg_draw_hline(2); |
696 | |
697 | if (row-- == 0) |
698 | goto out; |
699 | } else |
700 | row = browser->rows - 1; |
701 | |
702 | if (end > browser->top_idx) |
703 | end_row = end - browser->top_idx; |
704 | else |
705 | end_row = 0; |
706 | |
707 | ui_browser__gotorc(browser, y: end_row, x: column); |
708 | SLsmg_draw_vline(row - end_row + 1); |
709 | |
710 | ui_browser__gotorc(browser, y: end_row, x: column); |
711 | if (end >= browser->top_idx) { |
712 | SLsmg_write_char(SLSMG_ULCORN_CHAR); |
713 | ui_browser__gotorc(browser, y: end_row, x: column + 1); |
714 | SLsmg_write_char(SLSMG_HLINE_CHAR); |
715 | ui_browser__gotorc(browser, y: end_row, x: column + 2); |
716 | SLsmg_write_char(SLSMG_RARROW_CHAR); |
717 | } |
718 | out: |
719 | SLsmg_set_char_set(0); |
720 | } |
721 | |
722 | static void __ui_browser__line_arrow_down(struct ui_browser *browser, |
723 | unsigned int column, |
724 | u64 start, u64 end) |
725 | { |
726 | unsigned int row, end_row; |
727 | |
728 | SLsmg_set_char_set(1); |
729 | |
730 | if (start >= browser->top_idx) { |
731 | row = start - browser->top_idx; |
732 | ui_browser__gotorc(browser, y: row, x: column); |
733 | SLsmg_write_char(SLSMG_ULCORN_CHAR); |
734 | ui_browser__gotorc(browser, y: row, x: column + 1); |
735 | SLsmg_draw_hline(2); |
736 | |
737 | if (++row == 0) |
738 | goto out; |
739 | } else |
740 | row = 0; |
741 | |
742 | if (end >= browser->top_idx + browser->rows) |
743 | end_row = browser->rows - 1; |
744 | else |
745 | end_row = end - browser->top_idx; |
746 | |
747 | ui_browser__gotorc(browser, y: row, x: column); |
748 | SLsmg_draw_vline(end_row - row + 1); |
749 | |
750 | ui_browser__gotorc(browser, y: end_row, x: column); |
751 | if (end < browser->top_idx + browser->rows) { |
752 | SLsmg_write_char(SLSMG_LLCORN_CHAR); |
753 | ui_browser__gotorc(browser, y: end_row, x: column + 1); |
754 | SLsmg_write_char(SLSMG_HLINE_CHAR); |
755 | ui_browser__gotorc(browser, y: end_row, x: column + 2); |
756 | SLsmg_write_char(SLSMG_RARROW_CHAR); |
757 | } |
758 | out: |
759 | SLsmg_set_char_set(0); |
760 | } |
761 | |
762 | void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, |
763 | u64 start, u64 end) |
764 | { |
765 | if (start > end) |
766 | __ui_browser__line_arrow_up(browser, column, start, end); |
767 | else |
768 | __ui_browser__line_arrow_down(browser, column, start, end); |
769 | } |
770 | |
771 | void ui_browser__mark_fused(struct ui_browser *browser, unsigned int column, |
772 | unsigned int row, int diff, bool arrow_down) |
773 | { |
774 | int end_row; |
775 | |
776 | if (diff <= 0) |
777 | return; |
778 | |
779 | SLsmg_set_char_set(1); |
780 | |
781 | if (arrow_down) { |
782 | if (row + diff <= browser->top_idx) |
783 | return; |
784 | |
785 | end_row = row + diff - browser->top_idx; |
786 | ui_browser__gotorc(browser, y: end_row, x: column - 1); |
787 | SLsmg_write_char(SLSMG_LTEE_CHAR); |
788 | |
789 | while (--end_row >= 0 && end_row > (int)(row - browser->top_idx)) { |
790 | ui_browser__gotorc(browser, y: end_row, x: column - 1); |
791 | SLsmg_draw_vline(1); |
792 | } |
793 | |
794 | end_row = (int)(row - browser->top_idx); |
795 | if (end_row >= 0) { |
796 | ui_browser__gotorc(browser, y: end_row, x: column - 1); |
797 | SLsmg_write_char(SLSMG_ULCORN_CHAR); |
798 | ui_browser__gotorc(browser, y: end_row, x: column); |
799 | SLsmg_draw_hline(2); |
800 | } |
801 | } else { |
802 | if (row < browser->top_idx) |
803 | return; |
804 | |
805 | end_row = row - browser->top_idx; |
806 | ui_browser__gotorc(browser, y: end_row, x: column - 1); |
807 | SLsmg_write_char(SLSMG_LTEE_CHAR); |
808 | ui_browser__gotorc(browser, y: end_row, x: column); |
809 | SLsmg_draw_hline(2); |
810 | } |
811 | |
812 | SLsmg_set_char_set(0); |
813 | } |
814 | |
815 | void ui_browser__init(void) |
816 | { |
817 | int i = 0; |
818 | |
819 | perf_config(fn: ui_browser__color_config, NULL); |
820 | |
821 | while (ui_browser__colorsets[i].name) { |
822 | struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; |
823 | SLtt_set_color(c->colorset, c->name, c->fg, c->bg); |
824 | } |
825 | } |
826 |
Definitions
- ui_browser__percent_color
- ui_browser__set_color
- ui_browser__set_percent_color
- ui_browser__gotorc_title
- ui_browser__gotorc
- ui_browser__write_nstring
- ui_browser__vprintf
- ui_browser__printf
- ui_browser__list_head_filter_entries
- ui_browser__list_head_filter_prev_entries
- ui_browser__list_head_seek
- ui_browser__rb_tree_seek
- ui_browser__rb_tree_refresh
- ui_browser__is_current_entry
- ui_browser__refresh_dimensions
- ui_browser__handle_resize
- ui_browser__warning
- ui_browser__warn_unhandled_hotkey
- ui_browser__help_window
- ui_browser__dialog_yesno
- ui_browser__reset_index
- __ui_browser__show_title
- ui_browser__show_title
- ui_browser__show
- ui_browser__hide
- ui_browser__scrollbar_set
- __ui_browser__refresh
- ui_browser__refresh
- ui_browser__update_nr_entries
- ui_browser__run
- ui_browser__list_head_refresh
- ui_browser_colorset
- ui_browser__colorsets
- ui_browser__color_config
- ui_browser__argv_seek
- ui_browser__argv_refresh
- __ui_browser__vline
- ui_browser__write_graph
- __ui_browser__line_arrow_up
- __ui_browser__line_arrow_down
- __ui_browser__line_arrow
- ui_browser__mark_fused
Improve your Profiling and Debugging skills
Find out more