2 * Copyright (C) 2011 Julien Desfossez
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include <semaphore.h>
27 #include "cursesdisplay.h"
28 #include "lttngtoptypes.h"
29 #include "iostreamtop.h"
32 #define DEFAULT_DELAY 15
33 #define MAX_LINE_LENGTH 50
34 #define MAX_LOG_LINES 4
36 /* to prevent concurrent updates of the different windows */
37 sem_t update_display_sem
;
40 WINDOW
*footer
, *header
, *center
, *status
;
41 WINDOW
*perf_panel_window
= NULL
;
42 PANEL
*perf_panel
, *main_panel
;
44 int perf_panel_visible
= 0;
45 int perf_line_selected
= 0;
47 int last_display_index
, currently_displayed_index
;
49 struct processtop
*selected_process
= NULL
;
54 int selected_line
= 0; /* select bar position */
55 int selected_in_list
= 0; /* selection relative to the whole list */
56 int list_offset
= 0; /* first index in the list to display (scroll) */
58 char log_lines
[MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
];
60 int max_elements
= 80;
62 int toggle_threads
= -1;
63 int toggle_pause
= -1;
68 pthread_t keyboard_thread
;
77 static void handle_sigterm(int signal
)
86 halfdelay(DEFAULT_DELAY
);
88 intrflush(stdscr
, false);
94 init_pair(1, COLOR_RED
, COLOR_BLACK
); /* - */
95 init_pair(2, COLOR_GREEN
, COLOR_BLACK
); /* + */
96 init_pair(3, COLOR_BLACK
, COLOR_WHITE
); /* keys */
97 init_pair(4, COLOR_WHITE
, COLOR_GREEN
); /* keys activated */
98 init_pair(5, COLOR_WHITE
, COLOR_BLUE
); /* select line */
100 termtype
= getenv("TERM");
101 if (!strcmp(termtype
, "xterm") || !strcmp(termtype
, "xterm-color") ||
102 !strcmp(termtype
, "vt220")) {
103 define_key("\033[H", KEY_HOME
);
104 define_key("\033[F", KEY_END
);
105 define_key("\033OP", KEY_F(1));
106 define_key("\033OQ", KEY_F(2));
107 define_key("\033OR", KEY_F(3));
108 define_key("\033OS", KEY_F(4));
109 define_key("\0330U", KEY_F(6));
110 define_key("\033[11~", KEY_F(1));
111 define_key("\033[12~", KEY_F(2));
112 define_key("\033[13~", KEY_F(3));
113 define_key("\033[14~", KEY_F(4));
114 define_key("\033[16~", KEY_F(6));
115 define_key("\033[17;2~", KEY_F(18));
117 signal(SIGTERM
, handle_sigterm
);
118 mousemask(BUTTON1_CLICKED
, NULL
);
122 WINDOW
*create_window(int height
, int width
, int startx
, int starty
)
125 win
= newwin(height
, width
, startx
, starty
);
131 WINDOW
*create_window_no_border(int height
, int width
, int startx
, int starty
)
134 win
= newwin(height
, width
, startx
, starty
);
139 void print_digit(WINDOW
*win
, int digit
)
142 wattron(win
, COLOR_PAIR(1));
143 wprintw(win
, "%d", digit
);
144 wattroff(win
, COLOR_PAIR(1));
145 } else if (digit
> 0) {
146 wattron(win
, COLOR_PAIR(2));
147 wprintw(win
, "+%d", digit
);
148 wattroff(win
, COLOR_PAIR(2));
154 void print_digits(WINDOW
*win
, int first
, int second
)
157 print_digit(win
, first
);
159 print_digit(win
, second
);
163 void print_headers(int line
, char *desc
, int value
, int first
, int second
)
165 wattron(header
, A_BOLD
);
166 mvwprintw(header
, line
, 4, "%s", desc
);
167 wattroff(header
, A_BOLD
);
168 mvwprintw(header
, line
, 16, "%d", value
);
169 wmove(header
, line
, 24);
170 print_digits(header
, first
, second
);
171 wmove(header
, line
, 40);
174 void set_window_title(WINDOW
*win
, char *title
)
176 wattron(win
, A_BOLD
);
177 mvwprintw(win
, 0, 1, title
);
178 wattroff(win
, A_BOLD
);
181 void print_log(char *str
)
184 int current_line
= 1;
185 int current_char
= 1;
187 /* rotate the line buffer */
188 if (nb_log_lines
>= MAX_LOG_LINES
) {
189 tmp
= strndup(log_lines
, MAX_LINE_LENGTH
* MAX_LOG_LINES
+ MAX_LOG_LINES
);
190 tmp2
= strchr(tmp
, '\n');
191 memset(log_lines
, '\0', strlen(log_lines
));
192 strncat(log_lines
, tmp2
+ 1, strlen(tmp2
) - 1);
193 log_lines
[strlen(log_lines
)] = '\n';
194 log_lines
[strlen(log_lines
)] = '\0';
199 strncat(log_lines
, str
, MAX_LINE_LENGTH
- 1);
201 if (nb_log_lines
< MAX_LOG_LINES
)
202 log_lines
[strlen(log_lines
)] = '\n';
203 log_lines
[strlen(log_lines
)] = '\0';
207 set_window_title(status
, "Status");
208 for (i
= 0; i
< strlen(log_lines
); i
++) {
209 if (log_lines
[i
] == '\n') {
210 wmove(status
, ++current_line
, 1);
213 mvwprintw(status
, current_line
, current_char
++, "%c",
220 void print_key(WINDOW
*win
, char *key
, char *desc
, int toggle
)
227 wattron(win
, COLOR_PAIR(pair
));
228 wprintw(footer
, "%s", key
);
229 wattroff(win
, COLOR_PAIR(pair
));
230 wprintw(footer
, ":%s", desc
);
235 sem_wait(&update_display_sem
);
238 print_key(footer
, "F2", "CPUtop ", current_view
== cpu
);
239 print_key(footer
, "F3", "PerfTop ", current_view
== perf
);
240 print_key(footer
, "F4", "IOTop ", current_view
== iostream
);
241 print_key(footer
, "Enter", "Details ", current_view
== process_details
);
242 print_key(footer
, "q", "Quit | ", 0);
243 print_key(footer
, "P", "Perf Pref ", 0);
244 print_key(footer
, "p", "Pause ", toggle_pause
);
247 sem_post(&update_display_sem
);
254 set_window_title(header
, "Statistics for interval [gathering data...[");
255 wattron(header
, A_BOLD
);
256 mvwprintw(header
, 1, 4, "CPUs");
257 mvwprintw(header
, 2, 4, "Threads");
258 mvwprintw(header
, 3, 4, "Files");
259 mvwprintw(header
, 4, 4, "Network");
260 wattroff(header
, A_BOLD
);
268 set_window_title(header
, "Statistics for interval ");
269 wattron(header
, A_BOLD
);
271 wprintw(header, "[%lu.%lu, %lu.%lu[",
272 data->start.tv_sec, data->start.tv_nsec,
273 data->end.tv_sec, data->end.tv_nsec);
275 wprintw(header
, "[%lu, %lu[",
278 mvwprintw(header
, 1, 4, "CPUs");
279 wattroff(header
, A_BOLD
);
280 wprintw(header
, "\t%d\t(max/cpu : %0.2f%)", data
->cpu_table
->len
,
281 100.0/data
->cpu_table
->len
);
282 print_headers(2, "Threads", data
->nbthreads
, data
->nbnewthreads
,
283 -1*(data
->nbdeadthreads
));
284 print_headers(3, "Files", data
->nbfiles
, data
->nbnewfiles
,
285 -1*(data
->nbclosedfiles
));
286 mvwprintw(header
, 3, 43, "N/A kbytes/sec");
287 print_headers(4, "Network", 0, 0, 0);
288 mvwprintw(header
, 4, 43, "N/A Mbytes/sec");
292 gint
sort_by_cpu_desc(gconstpointer p1
, gconstpointer p2
)
294 struct processtop
*n1
= *(struct processtop
**)p1
;
295 struct processtop
*n2
= *(struct processtop
**)p2
;
296 unsigned long totaln1
= n1
->totalcpunsec
;
297 unsigned long totaln2
= n2
->totalcpunsec
;
299 if (totaln1
< totaln2
)
301 if (totaln1
== totaln2
)
306 gint
sort_by_cpu_group_by_threads_desc(gconstpointer p1
, gconstpointer p2
)
308 struct processtop
*n1
= *(struct processtop
**)p1
;
309 struct processtop
*n2
= *(struct processtop
**)p2
;
310 unsigned long totaln1
= n1
->threadstotalcpunsec
;
311 unsigned long totaln2
= n2
->threadstotalcpunsec
;
313 if (totaln1
< totaln2
)
315 if (totaln1
== totaln2
)
320 void update_cputop_display()
323 int header_offset
= 2;
324 struct processtop
*tmp
;
325 unsigned long elapsed
;
327 int nblinedisplayed
= 0;
328 int current_line
= 0;
330 elapsed
= data
->end
- data
->start
;
331 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
333 g_ptr_array_sort(data
->process_table
, sort_by_cpu_desc
);
335 set_window_title(center
, "CPU Top");
336 wattron(center
, A_BOLD
);
337 mvwprintw(center
, 1, 1, "CPU(%)");
338 mvwprintw(center
, 1, 12, "TGID");
339 mvwprintw(center
, 1, 22, "PID");
340 mvwprintw(center
, 1, 32, "NAME");
341 wattroff(center
, A_BOLD
);
343 max_center_lines
= LINES
- 7 - 7 - 1 - header_offset
;
345 /* iterate the process (thread) list */
346 for (i
= list_offset
; i
< data
->process_table
->len
&&
347 nblinedisplayed
< max_center_lines
; i
++) {
348 tmp
= g_ptr_array_index(data
->process_table
, i
);
350 if (current_line
== selected_line
) {
351 selected_process
= tmp
;
352 selected_tid
= tmp
->tid
;
353 selected_comm
= tmp
->comm
;
354 wattron(center
, COLOR_PAIR(5));
355 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
358 mvwprintw(center
, current_line
+ header_offset
, 1, "%1.2f",
359 tmp
->totalcpunsec
/ maxcputime
);
361 mvwprintw(center
, current_line
+ header_offset
, 12, "%d", tmp
->pid
);
363 mvwprintw(center
, current_line
+ header_offset
, 22, "%d", tmp
->tid
);
365 mvwprintw(center
, current_line
+ header_offset
, 32, "%s", tmp
->comm
);
366 wattroff(center
, COLOR_PAIR(5));
372 gint
sort_perf(gconstpointer p1
, gconstpointer p2
, gpointer key
)
374 struct processtop
*n1
= *(struct processtop
**) p1
;
375 struct processtop
*n2
= *(struct processtop
**) p2
;
377 struct perfcounter
*tmp1
, *tmp2
;
378 unsigned long totaln2
= 0;
379 unsigned long totaln1
= 0;
384 tmp1
= g_hash_table_lookup(n1
->perf
, key
);
388 totaln1
= tmp1
->count
;
390 tmp2
= g_hash_table_lookup(n2
->perf
, key
);
394 totaln2
= tmp2
->count
;
396 if (totaln1
< totaln2
)
398 if (totaln1
== totaln2
) {
401 if (totaln1
< totaln2
)
408 void print_key_title(char *key
, int line
)
410 wattron(center
, A_BOLD
);
411 mvwprintw(center
, line
, 1, "%s\t", key
);
412 wattroff(center
, A_BOLD
);
415 void update_process_details()
417 unsigned long elapsed
;
419 struct processtop
*tmp
= find_process_tid(data
, selected_tid
, selected_comm
);
420 struct files
*file_tmp
;
423 set_window_title(center
, "Process details");
426 elapsed
= data
->end
- data
->start
;
427 maxcputime
= elapsed
* data
->cpu_table
->len
/ 100.0;
429 print_key_title("Name", 1);
430 wprintw(center
, "%s", selected_comm
);
431 print_key_title("TID", 2);
432 wprintw(center
, "%d", selected_tid
);
434 print_key_title("Does not exit at this time", 3);
438 print_key_title("PID", 3);
439 wprintw(center
, "%d", tmp
->pid
);
440 print_key_title("PPID", 4);
441 wprintw(center
, "%d", tmp
->ppid
);
442 print_key_title("CPU", 5);
443 wprintw(center
, "%1.2f %%", tmp
->totalcpunsec
/maxcputime
);
445 print_key_title("READ B/s", 6);
446 wprintw(center
, "%d", tmp
->fileread
);
448 print_key_title("WRITE B/s", 7);
449 wprintw(center
, "%d", tmp
->filewrite
);
451 wattron(center
, A_BOLD
);
452 mvwprintw(center
, 8, 1, "FD");
453 mvwprintw(center
, 8, 12, "READ");
454 mvwprintw(center
, 8, 22, "WRITE");
455 mvwprintw(center
, 8, 32, "FILENAME");
456 wattroff(center
, A_BOLD
);
458 for (i
= 0; i
< tmp
->process_files_table
->len
; i
++) {
459 file_tmp
= get_file(tmp
, i
);
460 if (file_tmp
!= NULL
) {
461 mvwprintw(center
, 9 + j
, 1, "%d", i
);
462 mvwprintw(center
, 9 + j
, 12, "%d", file_tmp
->read
);
463 mvwprintw(center
, 9 + j
, 22, "%d", file_tmp
->write
);
464 mvwprintw(center
, 9 + j
, 32, "%s", file_tmp
->name
);
473 int nblinedisplayed
= 0;
474 int current_line
= 0;
475 struct processtop
*tmp
;
476 int header_offset
= 2;
478 struct perfcounter
*perfn1
, *perfn2
;
479 char *perf_key
= NULL
;
484 set_window_title(center
, "Perf Top");
485 wattron(center
, A_BOLD
);
486 mvwprintw(center
, 1, 1, "PID");
487 mvwprintw(center
, 1, 11, "TID");
488 mvwprintw(center
, 1, 22, "NAME");
491 g_hash_table_iter_init(&iter
, data
->perf_list
);
492 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
493 if (perfn1
->visible
) {
494 /* + 5 to strip the "perf_" prefix */
495 mvwprintw(center
, 1, perf_row
, "%s",
500 perf_key
= (char *) key
;
504 wattroff(center
, A_BOLD
);
506 g_ptr_array_sort_with_data(data
->process_table
, sort_perf
, perf_key
);
508 for (i
= 0; i
< data
->process_table
->len
&&
509 nblinedisplayed
< max_center_lines
; i
++) {
510 tmp
= g_ptr_array_index(data
->process_table
, i
);
512 if (current_line
== selected_line
) {
513 selected_process
= tmp
;
514 wattron(center
, COLOR_PAIR(5));
515 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
518 mvwprintw(center
, current_line
+ header_offset
, 1, "%d", tmp
->pid
);
519 mvwprintw(center
, current_line
+ header_offset
, 11, "%d", tmp
->tid
);
520 mvwprintw(center
, current_line
+ header_offset
, 22, "%s", tmp
->comm
);
522 g_hash_table_iter_init(&iter
, data
->perf_list
);
525 while (g_hash_table_iter_next (&iter
, &key
, (gpointer
) &perfn1
)) {
526 if (perfn1
->visible
) {
527 perfn2
= g_hash_table_lookup(tmp
->perf
, (char *) key
);
529 value
= perfn2
->count
;
532 mvwprintw(center
, current_line
+ header_offset
,
533 perf_row
, "%d", value
);
538 wattroff(center
, COLOR_PAIR(5));
544 gint
sort_by_ret_desc(gconstpointer p1
, gconstpointer p2
)
546 struct processtop
*n1
= *(struct processtop
**)p1
;
547 struct processtop
*n2
= *(struct processtop
**)p2
;
549 unsigned long totaln1
= n1
->totalfileread
+ n1
->totalfilewrite
;
550 unsigned long totaln2
= n2
->totalfileread
+ n2
->totalfilewrite
;
552 if (totaln1
< totaln2
)
554 if (totaln1
== totaln2
)
559 void update_iostream()
562 int header_offset
= 2;
563 struct processtop
*tmp
;
564 int nblinedisplayed
= 0;
565 int current_line
= 0;
568 set_window_title(center
, "IO Top");
569 wattron(center
, A_BOLD
);
570 mvwprintw(center
, 1, 1, "READ (B/s)");
571 mvwprintw(center
, 1, 20, "WRITE (B/s)");
573 mvwprintw(center
, 1, 40, "TOTAL STREAM");
575 mvwprintw(center
, 1, 60, "TGID");
576 mvwprintw(center
, 1, 80, "PID");
578 mvwprintw(center
, 1, 92, "NAME");
579 wattroff(center
, A_BOLD
);
581 g_ptr_array_sort(data
->process_table
, sort_by_ret_desc
);
583 for (i
= list_offset
; i
< data
->process_table
->len
&&
584 nblinedisplayed
< max_center_lines
; i
++) {
585 tmp
= g_ptr_array_index(data
->process_table
, i
);
587 if (current_line
== selected_line
) {
588 selected_process
= tmp
;
589 selected_tid
= tmp
->tid
;
590 selected_comm
= tmp
->comm
;
591 wattron(center
, COLOR_PAIR(5));
592 mvwhline(center
, current_line
+ header_offset
, 1, ' ', COLS
-3);
595 /* READ (bytes/sec) */
596 mvwprintw(center
, current_line
+ header_offset
, 1, "%lu",
599 /* WRITE (bytes/sec) */
600 mvwprintw(center
, current_line
+ header_offset
, 20, "%lu",
604 total
= tmp
->totalfileread
+ tmp
->totalfilewrite
;
606 if (total
>= 1000000)
607 mvwprintw(center
, current_line
+ header_offset
, 40, "%lu MB",
609 else if (total
>= 1000)
610 mvwprintw(center
, current_line
+ header_offset
, 40, "%lu KB",
613 mvwprintw(center
, current_line
+ header_offset
, 40, "%lu B",
617 mvwprintw(center
, current_line
+ header_offset
, 60, "%d", tmp
->pid
);
619 mvwprintw(center
, current_line
+ header_offset
, 80, "%d", tmp
->tid
);
621 mvwprintw(center
, current_line
+ header_offset
, 92, "%s", tmp
->comm
);
622 wattroff(center
, COLOR_PAIR(5));
628 void update_current_view()
630 sem_wait(&update_display_sem
);
637 switch (current_view
) {
639 update_cputop_display();
644 case process_details
:
645 update_process_details();
651 update_cputop_display();
658 sem_post(&update_display_sem
);
661 void setup_perf_panel()
666 if (perf_panel_window
) {
667 del_panel(perf_panel
);
668 delwin(perf_panel_window
);
670 size
= g_hash_table_size(data
->perf_list
);
671 perf_panel_window
= create_window(size
+ 2, 30, 10, 10);
672 perf_panel
= new_panel(perf_panel_window
);
673 perf_panel_visible
= 0;
674 hide_panel(perf_panel
);
677 void update_perf_panel(int line_selected
, int toggle_view
, int toggle_sort
)
680 struct perfcounter
*perf
;
686 werase(perf_panel_window
);
687 box(perf_panel_window
, 0 , 0);
688 set_window_title(perf_panel_window
, "Perf Preferences ");
689 wattron(perf_panel_window
, A_BOLD
);
690 mvwprintw(perf_panel_window
, g_hash_table_size(data
->perf_list
) + 1, 1,
692 wattroff(perf_panel_window
, A_BOLD
);
694 if (toggle_sort
== 1) {
696 perflist
= g_list_first(g_hash_table_get_keys(data
->perf_list
));
698 perf
= g_hash_table_lookup(data
->perf_list
, perflist
->data
);
699 if (i
!= line_selected
)
704 perflist
= g_list_next(perflist
);
706 update_current_view();
710 perflist
= g_list_first(g_hash_table_get_keys(data
->perf_list
));
712 perf
= g_hash_table_lookup(data
->perf_list
, perflist
->data
);
713 if (i
== line_selected
&& toggle_view
== 1) {
714 perf
->visible
= perf
->visible
== 1 ? 0:1;
715 update_current_view();
717 if (i
== line_selected
) {
718 wattron(perf_panel_window
, COLOR_PAIR(5));
719 mvwhline(perf_panel_window
, i
+ 1, 1, ' ', 30 - 2);
722 wattron(perf_panel_window
, A_BOLD
);
723 mvwprintw(perf_panel_window
, i
+ 1, 1, "[%c] %s",
724 perf
->visible
== 1 ? 'x' : ' ',
725 (char *) perflist
->data
+ 5);
726 wattroff(perf_panel_window
, A_BOLD
);
727 wattroff(perf_panel_window
, COLOR_PAIR(5));
729 perflist
= g_list_next(perflist
);
736 void toggle_perf_panel(void)
738 if (perf_panel_visible
) {
739 hide_panel(perf_panel
);
740 perf_panel_visible
= 0;
743 update_perf_panel(perf_line_selected
, 0, 0);
744 show_panel(perf_panel
);
745 perf_panel_visible
= 1;
751 void display(unsigned int index
)
753 last_display_index
= index
;
754 currently_displayed_index
= index
;
755 data
= g_ptr_array_index(copies
, index
);
758 max_elements
= data
->process_table
->len
;
759 update_current_view();
769 sem_wait(&pause_sem
);
772 void resume_display()
776 sem_post(&pause_sem
);
779 void *handle_keyboard(void *p
)
782 while((ch
= getch())) {
784 /* Move the cursor and scroll */
786 if (perf_panel_visible
) {
787 if (perf_line_selected
< g_hash_table_size(data
->perf_list
) - 1)
788 perf_line_selected
++;
789 update_perf_panel(perf_line_selected
, 0, 0);
791 if (selected_line
< (max_center_lines
- 1) &&
792 selected_line
< max_elements
- 1) {
795 } else if (selected_in_list
< (max_elements
- 1)
796 && (list_offset
< (max_elements
- max_center_lines
))) {
800 update_current_view();
804 if ((selected_line
+ 10 < max_center_lines
- 1) &&
805 ((selected_line
+ 10) < max_elements
- 1)) {
807 selected_in_list
+= 10;
808 } else if (max_elements
> max_center_lines
) {
809 selected_line
= max_center_lines
- 1;
810 if (selected_in_list
+ 10 < max_elements
- 1) {
811 selected_in_list
+= 10;
812 list_offset
+= (selected_in_list
- max_center_lines
+ 1);
814 } else if (selected_line
+ 10 > max_elements
) {
815 selected_line
= max_elements
- 1;
817 update_current_view();
820 if (perf_panel_visible
) {
821 if (perf_line_selected
> 0)
822 perf_line_selected
--;
823 update_perf_panel(perf_line_selected
, 0, 0);
825 if (selected_line
> 0) {
828 } else if (selected_in_list
> 0 && list_offset
> 0) {
832 update_current_view();
836 if (selected_line
- 10 > 0)
840 update_current_view();
843 /* Navigate the history with arrows */
845 if (currently_displayed_index
> 0) {
846 currently_displayed_index
--;
847 print_log("Going back in time");
849 print_log("Cannot rewind, last data is already displayed");
851 data
= g_ptr_array_index(copies
, currently_displayed_index
);
852 max_elements
= data
->process_table
->len
;
854 /* we force to pause the display when moving in time */
855 if (toggle_pause
< 0)
858 update_current_view();
862 if (currently_displayed_index
< last_display_index
) {
863 currently_displayed_index
++;
864 print_log("Going forward in time");
865 data
= g_ptr_array_index(copies
, currently_displayed_index
);
866 max_elements
= data
->process_table
->len
;
867 update_current_view();
870 print_log("Manually moving forward");
872 /* we force to resume the refresh when moving forward */
873 if (toggle_pause
> 0)
879 if (perf_panel_visible
)
880 update_perf_panel(perf_line_selected
, 1, 0);
883 if (perf_panel_visible
)
884 update_perf_panel(perf_line_selected
, 0, 1);
887 case 13: /* FIXME : KEY_ENTER ?? */
888 if (current_view
== cpu
) {
889 current_view
= process_details
;
891 update_current_view();
897 update_current_view();
901 update_current_view();
906 update_current_view();
909 current_view
= iostream
;
911 update_current_view();
918 toggle_threads
*= -1;
919 update_current_view();
922 if (toggle_pause
< 0) {
933 update_current_view();
943 sem_init(&update_display_sem
, 0, 1);
946 header
= create_window(6, COLS
- 1, 0, 0);
947 center
= create_window(LINES
- 7 - 7, COLS
- 1, 6, 0);
948 status
= create_window(MAX_LOG_LINES
+ 2, COLS
- 1, LINES
- 7, 0);
949 footer
= create_window(1, COLS
- 1, LINES
- 1, 0);
951 print_log("Starting display");
953 main_panel
= new_panel(center
);
961 pthread_create(&keyboard_thread
, NULL
, handle_keyboard
, (void *)NULL
);