1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * tui.c ncurses text user interface for TMON program |
4 | * |
5 | * Copyright (C) 2013 Intel Corporation. All rights reserved. |
6 | * |
7 | * Author: Jacob Pan <jacob.jun.pan@linux.intel.com> |
8 | */ |
9 | |
10 | #include <unistd.h> |
11 | #include <stdio.h> |
12 | #include <stdlib.h> |
13 | #include <string.h> |
14 | #include <stdint.h> |
15 | #include <ncurses.h> |
16 | #include <time.h> |
17 | #include <syslog.h> |
18 | #include <panel.h> |
19 | #include <pthread.h> |
20 | #include <signal.h> |
21 | |
22 | #include "tmon.h" |
23 | |
24 | #define min(x, y) ({ \ |
25 | typeof(x) _min1 = (x); \ |
26 | typeof(y) _min2 = (y); \ |
27 | (void) (&_min1 == &_min2); \ |
28 | _min1 < _min2 ? _min1 : _min2; }) |
29 | |
30 | #define max(x, y) ({ \ |
31 | typeof(x) _max1 = (x); \ |
32 | typeof(y) _max2 = (y); \ |
33 | (void) (&_max1 == &_max2); \ |
34 | _max1 > _max2 ? _max1 : _max2; }) |
35 | |
36 | static PANEL *data_panel; |
37 | static PANEL *dialogue_panel; |
38 | static PANEL *top; |
39 | |
40 | static WINDOW *title_bar_window; |
41 | static WINDOW *tz_sensor_window; |
42 | static WINDOW *cooling_device_window; |
43 | static WINDOW *control_window; |
44 | static WINDOW *status_bar_window; |
45 | static WINDOW *thermal_data_window; |
46 | static WINDOW *dialogue_window; |
47 | |
48 | char status_bar_slots[10][40]; |
49 | static void draw_hbar(WINDOW *win, int y, int start, int len, |
50 | unsigned long pattern, bool end); |
51 | |
52 | static int maxx, maxy; |
53 | static int maxwidth = 200; |
54 | |
55 | #define TITLE_BAR_HIGHT 1 |
56 | #define SENSOR_WIN_HIGHT 4 /* one row for tz name, one for trip points */ |
57 | |
58 | |
59 | /* daemon mode flag (set by startup parameter -d) */ |
60 | static int tui_disabled; |
61 | |
62 | static void close_panel(PANEL *p) |
63 | { |
64 | if (p) { |
65 | del_panel(p); |
66 | p = NULL; |
67 | } |
68 | } |
69 | |
70 | static void close_window(WINDOW *win) |
71 | { |
72 | if (win) { |
73 | delwin(win); |
74 | win = NULL; |
75 | } |
76 | } |
77 | |
78 | void close_windows(void) |
79 | { |
80 | if (tui_disabled) |
81 | return; |
82 | /* must delete panels before their attached windows */ |
83 | if (dialogue_window) |
84 | close_panel(dialogue_panel); |
85 | if (cooling_device_window) |
86 | close_panel(data_panel); |
87 | |
88 | close_window(title_bar_window); |
89 | close_window(tz_sensor_window); |
90 | close_window(status_bar_window); |
91 | close_window(cooling_device_window); |
92 | close_window(control_window); |
93 | close_window(thermal_data_window); |
94 | close_window(dialogue_window); |
95 | |
96 | } |
97 | |
98 | void write_status_bar(int x, char *line) |
99 | { |
100 | mvwprintw(status_bar_window, 0, x, "%s" , line); |
101 | wrefresh(status_bar_window); |
102 | } |
103 | |
104 | /* wrap at 5 */ |
105 | #define DIAG_DEV_ROWS 5 |
106 | /* |
107 | * list cooling devices + "set temp" entry; wraps after 5 rows, if they fit |
108 | */ |
109 | static int diag_dev_rows(void) |
110 | { |
111 | int entries = ptdata.nr_cooling_dev + 1; |
112 | int rows = max(DIAG_DEV_ROWS, (entries + 1) / 2); |
113 | return min(rows, entries); |
114 | } |
115 | |
116 | void setup_windows(void) |
117 | { |
118 | int y_begin = 1; |
119 | |
120 | if (tui_disabled) |
121 | return; |
122 | |
123 | getmaxyx(stdscr, maxy, maxx); |
124 | resizeterm(maxy, maxx); |
125 | |
126 | title_bar_window = subwin(stdscr, TITLE_BAR_HIGHT, maxx, 0, 0); |
127 | y_begin += TITLE_BAR_HIGHT; |
128 | |
129 | tz_sensor_window = subwin(stdscr, SENSOR_WIN_HIGHT, maxx, y_begin, 0); |
130 | y_begin += SENSOR_WIN_HIGHT; |
131 | |
132 | cooling_device_window = subwin(stdscr, ptdata.nr_cooling_dev + 3, maxx, |
133 | y_begin, 0); |
134 | y_begin += ptdata.nr_cooling_dev + 3; /* 2 lines for border */ |
135 | /* two lines to show borders, one line per tz show trip point position |
136 | * and value. |
137 | * dialogue window is a pop-up, when needed it lays on top of cdev win |
138 | */ |
139 | |
140 | dialogue_window = subwin(stdscr, diag_dev_rows() + 5, maxx-50, |
141 | DIAG_Y, DIAG_X); |
142 | |
143 | thermal_data_window = subwin(stdscr, ptdata.nr_tz_sensor * |
144 | NR_LINES_TZDATA + 3, maxx, y_begin, 0); |
145 | y_begin += ptdata.nr_tz_sensor * NR_LINES_TZDATA + 3; |
146 | control_window = subwin(stdscr, 4, maxx, y_begin, 0); |
147 | |
148 | scrollok(cooling_device_window, TRUE); |
149 | maxwidth = maxx - 18; |
150 | status_bar_window = subwin(stdscr, 1, maxx, maxy-1, 0); |
151 | |
152 | strcpy(status_bar_slots[0], " Ctrl-c - Quit " ); |
153 | strcpy(status_bar_slots[1], " TAB - Tuning " ); |
154 | wmove(status_bar_window, 1, 30); |
155 | |
156 | /* prepare panels for dialogue, if panel already created then we must |
157 | * be doing resizing, so just replace windows with new ones, old ones |
158 | * should have been deleted by close_window |
159 | */ |
160 | data_panel = new_panel(cooling_device_window); |
161 | if (!data_panel) |
162 | syslog(LOG_DEBUG, "No data panel\n" ); |
163 | else { |
164 | if (dialogue_window) { |
165 | dialogue_panel = new_panel(dialogue_window); |
166 | if (!dialogue_panel) |
167 | syslog(LOG_DEBUG, "No dialogue panel\n" ); |
168 | else { |
169 | /* Set up the user pointer to the next panel*/ |
170 | set_panel_userptr(data_panel, dialogue_panel); |
171 | set_panel_userptr(dialogue_panel, data_panel); |
172 | top = data_panel; |
173 | } |
174 | } else |
175 | syslog(LOG_INFO, "no dialogue win, term too small\n" ); |
176 | } |
177 | doupdate(); |
178 | werase(stdscr); |
179 | refresh(); |
180 | } |
181 | |
182 | void resize_handler(int sig) |
183 | { |
184 | /* start over when term gets resized, but first we clean up */ |
185 | close_windows(); |
186 | endwin(); |
187 | refresh(); |
188 | clear(); |
189 | getmaxyx(stdscr, maxy, maxx); /* get the new screen size */ |
190 | setup_windows(); |
191 | /* rate limit */ |
192 | sleep(1); |
193 | syslog(LOG_DEBUG, "SIG %d, term resized to %d x %d\n" , |
194 | sig, maxy, maxx); |
195 | signal(SIGWINCH, resize_handler); |
196 | } |
197 | |
198 | const char cdev_title[] = " COOLING DEVICES " ; |
199 | void show_cooling_device(void) |
200 | { |
201 | int i, j, x, y = 0; |
202 | |
203 | if (tui_disabled || !cooling_device_window) |
204 | return; |
205 | |
206 | werase(cooling_device_window); |
207 | wattron(cooling_device_window, A_BOLD); |
208 | mvwprintw(cooling_device_window, 1, 1, |
209 | "ID Cooling Dev Cur Max Thermal Zone Binding" ); |
210 | wattroff(cooling_device_window, A_BOLD); |
211 | for (j = 0; j < ptdata.nr_cooling_dev; j++) { |
212 | /* draw cooling device list on the left in the order of |
213 | * cooling device instances. skip unused idr. |
214 | */ |
215 | mvwprintw(cooling_device_window, j + 2, 1, |
216 | "%02d %12.12s%6d %6d" , |
217 | ptdata.cdi[j].instance, |
218 | ptdata.cdi[j].type, |
219 | ptdata.cdi[j].cur_state, |
220 | ptdata.cdi[j].max_state); |
221 | } |
222 | |
223 | /* show cdev binding, y is the global cooling device instance */ |
224 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { |
225 | int tz_inst = ptdata.tzi[i].instance; |
226 | for (j = 0; j < ptdata.nr_cooling_dev; j++) { |
227 | int cdev_inst; |
228 | y = j; |
229 | x = tz_inst * TZONE_RECORD_SIZE + TZ_LEFT_ALIGN; |
230 | |
231 | draw_hbar(cooling_device_window, y+2, x, |
232 | TZONE_RECORD_SIZE-1, ACS_VLINE, false); |
233 | |
234 | /* draw a column of spaces to separate thermal zones */ |
235 | mvwprintw(cooling_device_window, y+2, x-1, " " ); |
236 | if (ptdata.tzi[i].cdev_binding) { |
237 | cdev_inst = ptdata.cdi[j].instance; |
238 | unsigned long trip_binding = |
239 | ptdata.tzi[i].trip_binding[cdev_inst]; |
240 | int k = 0; /* per zone trip point id that |
241 | * binded to this cdev, one to |
242 | * many possible based on the |
243 | * binding bitmask. |
244 | */ |
245 | syslog(LOG_DEBUG, |
246 | "bind tz%d cdev%d tp%lx %d cdev%lx\n" , |
247 | i, j, trip_binding, y, |
248 | ptdata.tzi[i].cdev_binding); |
249 | /* draw each trip binding for the cdev */ |
250 | while (trip_binding >>= 1) { |
251 | k++; |
252 | if (!(trip_binding & 1)) |
253 | continue; |
254 | /* draw '*' to show binding */ |
255 | mvwprintw(cooling_device_window, |
256 | y + 2, |
257 | x + ptdata.tzi[i].nr_trip_pts - |
258 | k - 1, "*" ); |
259 | } |
260 | } |
261 | } |
262 | } |
263 | /* draw border after data so that border will not be messed up |
264 | * even there is not enough space for all the data to be shown |
265 | */ |
266 | wborder(cooling_device_window, 0, 0, 0, 0, 0, 0, 0, 0); |
267 | wattron(cooling_device_window, A_BOLD); |
268 | mvwprintw(cooling_device_window, 0, maxx/2 - sizeof(cdev_title), |
269 | cdev_title); |
270 | wattroff(cooling_device_window, A_BOLD); |
271 | |
272 | wrefresh(cooling_device_window); |
273 | } |
274 | |
275 | const char DIAG_TITLE[] = "[ TUNABLES ]" ; |
276 | void show_dialogue(void) |
277 | { |
278 | int j, x = 0, y = 0; |
279 | int rows, cols; |
280 | WINDOW *w = dialogue_window; |
281 | |
282 | if (tui_disabled || !w) |
283 | return; |
284 | |
285 | getmaxyx(w, rows, cols); |
286 | |
287 | /* Silence compiler 'unused' warnings */ |
288 | (void)cols; |
289 | |
290 | werase(w); |
291 | box(w, 0, 0); |
292 | mvwprintw(w, 0, maxx/4, DIAG_TITLE); |
293 | /* list all the available tunables */ |
294 | for (j = 0; j <= ptdata.nr_cooling_dev; j++) { |
295 | y = j % diag_dev_rows(); |
296 | if (y == 0 && j != 0) |
297 | x += 20; |
298 | if (j == ptdata.nr_cooling_dev) |
299 | /* save last choice for target temp */ |
300 | mvwprintw(w, y+1, x+1, "%C-%.12s" , 'A'+j, "Set Temp" ); |
301 | else |
302 | mvwprintw(w, y+1, x+1, "%C-%.10s-%2d" , 'A'+j, |
303 | ptdata.cdi[j].type, ptdata.cdi[j].instance); |
304 | } |
305 | wattron(w, A_BOLD); |
306 | mvwprintw(w, diag_dev_rows()+1, 1, "Enter Choice [A-Z]?" ); |
307 | wattroff(w, A_BOLD); |
308 | /* print legend at the bottom line */ |
309 | mvwprintw(w, rows - 2, 1, |
310 | "Legend: A=Active, P=Passive, C=Critical" ); |
311 | |
312 | wrefresh(dialogue_window); |
313 | } |
314 | |
315 | void write_dialogue_win(char *buf, int y, int x) |
316 | { |
317 | WINDOW *w = dialogue_window; |
318 | |
319 | mvwprintw(w, y, x, "%s" , buf); |
320 | } |
321 | |
322 | const char control_title[] = " CONTROLS " ; |
323 | void show_control_w(void) |
324 | { |
325 | unsigned long state; |
326 | |
327 | get_ctrl_state(state: &state); |
328 | |
329 | if (tui_disabled || !control_window) |
330 | return; |
331 | |
332 | werase(control_window); |
333 | mvwprintw(control_window, 1, 1, |
334 | "PID gain: kp=%2.2f ki=%2.2f kd=%2.2f Output %2.2f" , |
335 | p_param.kp, p_param.ki, p_param.kd, p_param.y_k); |
336 | |
337 | mvwprintw(control_window, 2, 1, |
338 | "Target Temp: %2.1fC, Zone: %d, Control Device: %.12s" , |
339 | p_param.t_target, target_thermal_zone, ctrl_cdev); |
340 | |
341 | /* draw border last such that everything is within boundary */ |
342 | wborder(control_window, 0, 0, 0, 0, 0, 0, 0, 0); |
343 | wattron(control_window, A_BOLD); |
344 | mvwprintw(control_window, 0, maxx/2 - sizeof(control_title), |
345 | control_title); |
346 | wattroff(control_window, A_BOLD); |
347 | |
348 | wrefresh(control_window); |
349 | } |
350 | |
351 | void initialize_curses(void) |
352 | { |
353 | if (tui_disabled) |
354 | return; |
355 | |
356 | initscr(); |
357 | start_color(); |
358 | keypad(stdscr, TRUE); /* enable keyboard mapping */ |
359 | nonl(); /* tell curses not to do NL->CR/NL on output */ |
360 | cbreak(); /* take input chars one at a time */ |
361 | noecho(); /* dont echo input */ |
362 | curs_set(0); /* turn off cursor */ |
363 | use_default_colors(); |
364 | |
365 | init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK); |
366 | init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE); |
367 | init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED); |
368 | init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED); |
369 | init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW); |
370 | init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN); |
371 | init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE); |
372 | init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK); |
373 | |
374 | } |
375 | |
376 | void show_title_bar(void) |
377 | { |
378 | int i; |
379 | int x = 0; |
380 | |
381 | if (tui_disabled || !title_bar_window) |
382 | return; |
383 | |
384 | wattrset(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR)); |
385 | wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR)); |
386 | werase(title_bar_window); |
387 | |
388 | mvwprintw(title_bar_window, 0, 0, |
389 | " TMON v%s" , VERSION); |
390 | |
391 | wrefresh(title_bar_window); |
392 | |
393 | werase(status_bar_window); |
394 | |
395 | for (i = 0; i < 10; i++) { |
396 | if (strlen(status_bar_slots[i]) == 0) |
397 | continue; |
398 | wattron(status_bar_window, A_REVERSE); |
399 | mvwprintw(status_bar_window, 0, x, "%s" , status_bar_slots[i]); |
400 | wattroff(status_bar_window, A_REVERSE); |
401 | x += strlen(status_bar_slots[i]) + 1; |
402 | } |
403 | wrefresh(status_bar_window); |
404 | } |
405 | |
406 | static void handle_input_val(int ch) |
407 | { |
408 | char buf[32]; |
409 | int val; |
410 | char path[256]; |
411 | WINDOW *w = dialogue_window; |
412 | |
413 | echo(); |
414 | keypad(w, TRUE); |
415 | wgetnstr(w, buf, 31); |
416 | val = atoi(buf); |
417 | |
418 | if (ch == ptdata.nr_cooling_dev) { |
419 | snprintf(buf, 31, "Invalid Temp %d! %d-%d" , val, |
420 | MIN_CTRL_TEMP, MAX_CTRL_TEMP); |
421 | if (val < MIN_CTRL_TEMP || val > MAX_CTRL_TEMP) |
422 | write_status_bar(x: 40, line: buf); |
423 | else { |
424 | p_param.t_target = val; |
425 | snprintf(buf, 31, "Set New Target Temp %d" , val); |
426 | write_status_bar(x: 40, line: buf); |
427 | } |
428 | } else { |
429 | snprintf(path, 256, "%s/%s%d" , THERMAL_SYSFS, |
430 | CDEV, ptdata.cdi[ch].instance); |
431 | sysfs_set_ulong(path, filename: "cur_state" , val); |
432 | } |
433 | noecho(); |
434 | dialogue_on = 0; |
435 | show_data_w(); |
436 | show_control_w(); |
437 | |
438 | top = (PANEL *)panel_userptr(top); |
439 | top_panel(top); |
440 | } |
441 | |
442 | static void handle_input_choice(int ch) |
443 | { |
444 | char buf[48]; |
445 | int base = 0; |
446 | int cdev_id = 0; |
447 | |
448 | if ((ch >= 'A' && ch <= 'A' + ptdata.nr_cooling_dev) || |
449 | (ch >= 'a' && ch <= 'a' + ptdata.nr_cooling_dev)) { |
450 | base = (ch < 'a') ? 'A' : 'a'; |
451 | cdev_id = ch - base; |
452 | if (ptdata.nr_cooling_dev == cdev_id) |
453 | snprintf(buf, sizeof(buf), "New Target Temp:" ); |
454 | else |
455 | snprintf(buf, sizeof(buf), "New Value for %.10s-%2d: " , |
456 | ptdata.cdi[cdev_id].type, |
457 | ptdata.cdi[cdev_id].instance); |
458 | write_dialogue_win(buf, y: diag_dev_rows() + 2, x: 2); |
459 | handle_input_val(ch: cdev_id); |
460 | } else { |
461 | snprintf(buf, sizeof(buf), "Invalid selection %d" , ch); |
462 | write_dialogue_win(buf, y: 8, x: 2); |
463 | } |
464 | } |
465 | |
466 | void *handle_tui_events(void *arg) |
467 | { |
468 | int ch; |
469 | |
470 | keypad(cooling_device_window, TRUE); |
471 | while ((ch = wgetch(cooling_device_window)) != EOF) { |
472 | if (tmon_exit) |
473 | break; |
474 | /* when term size is too small, no dialogue panels are set. |
475 | * we need to filter out such cases. |
476 | */ |
477 | if (!data_panel || !dialogue_panel || |
478 | !cooling_device_window || |
479 | !dialogue_window) { |
480 | |
481 | continue; |
482 | } |
483 | pthread_mutex_lock(&input_lock); |
484 | if (dialogue_on) { |
485 | handle_input_choice(ch); |
486 | /* top panel filter */ |
487 | if (ch == 'q' || ch == 'Q') |
488 | ch = 0; |
489 | } |
490 | switch (ch) { |
491 | case KEY_LEFT: |
492 | box(cooling_device_window, 10, 0); |
493 | break; |
494 | case 9: /* TAB */ |
495 | top = (PANEL *)panel_userptr(top); |
496 | top_panel(top); |
497 | if (top == dialogue_panel) { |
498 | dialogue_on = 1; |
499 | show_dialogue(); |
500 | } else { |
501 | dialogue_on = 0; |
502 | /* force refresh */ |
503 | show_data_w(); |
504 | show_control_w(); |
505 | } |
506 | break; |
507 | case 'q': |
508 | case 'Q': |
509 | tmon_exit = 1; |
510 | break; |
511 | } |
512 | update_panels(); |
513 | doupdate(); |
514 | pthread_mutex_unlock(&input_lock); |
515 | } |
516 | |
517 | if (arg) |
518 | *(int *)arg = 0; /* make gcc happy */ |
519 | |
520 | return NULL; |
521 | } |
522 | |
523 | /* draw a horizontal bar in given pattern */ |
524 | static void draw_hbar(WINDOW *win, int y, int start, int len, unsigned long ptn, |
525 | bool end) |
526 | { |
527 | mvwaddch(win, y, start, ptn); |
528 | whline(win, ptn, len); |
529 | if (end) |
530 | mvwaddch(win, y, MAX_DISP_TEMP+TDATA_LEFT, ']'); |
531 | } |
532 | |
533 | static char trip_type_to_char(int type) |
534 | { |
535 | switch (type) { |
536 | case THERMAL_TRIP_CRITICAL: return 'C'; |
537 | case THERMAL_TRIP_HOT: return 'H'; |
538 | case THERMAL_TRIP_PASSIVE: return 'P'; |
539 | case THERMAL_TRIP_ACTIVE: return 'A'; |
540 | default: |
541 | return '?'; |
542 | } |
543 | } |
544 | |
545 | /* fill a string with trip point type and value in one line |
546 | * e.g. P(56) C(106) |
547 | * maintain the distance one degree per char |
548 | */ |
549 | static void draw_tp_line(int tz, int y) |
550 | { |
551 | int j; |
552 | int x; |
553 | |
554 | for (j = 0; j < ptdata.tzi[tz].nr_trip_pts; j++) { |
555 | x = ptdata.tzi[tz].tp[j].temp / 1000; |
556 | mvwprintw(thermal_data_window, y + 0, x + TDATA_LEFT, |
557 | "%c%d" , trip_type_to_char(type: ptdata.tzi[tz].tp[j].type), |
558 | x); |
559 | syslog(LOG_INFO, "%s:tz %d tp %d temp = %lu\n" , __func__, |
560 | tz, j, ptdata.tzi[tz].tp[j].temp); |
561 | } |
562 | } |
563 | |
564 | const char data_win_title[] = " THERMAL DATA " ; |
565 | void show_data_w(void) |
566 | { |
567 | int i; |
568 | |
569 | |
570 | if (tui_disabled || !thermal_data_window) |
571 | return; |
572 | |
573 | werase(thermal_data_window); |
574 | wattron(thermal_data_window, A_BOLD); |
575 | mvwprintw(thermal_data_window, 0, maxx/2 - sizeof(data_win_title), |
576 | data_win_title); |
577 | wattroff(thermal_data_window, A_BOLD); |
578 | /* draw a line as ruler */ |
579 | for (i = 10; i < MAX_DISP_TEMP; i += 10) |
580 | mvwprintw(thermal_data_window, 1, i+TDATA_LEFT, "%2d" , i); |
581 | |
582 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { |
583 | int temp = trec[cur_thermal_record].temp[i] / 1000; |
584 | int y = 0; |
585 | |
586 | y = i * NR_LINES_TZDATA + 2; |
587 | /* y at tz temp data line */ |
588 | mvwprintw(thermal_data_window, y, 1, "%6.6s%2d:[%3d][" , |
589 | ptdata.tzi[i].type, |
590 | ptdata.tzi[i].instance, temp); |
591 | draw_hbar(thermal_data_window, y, TDATA_LEFT, temp, ACS_RARROW, |
592 | true); |
593 | draw_tp_line(tz: i, y); |
594 | } |
595 | wborder(thermal_data_window, 0, 0, 0, 0, 0, 0, 0, 0); |
596 | wrefresh(thermal_data_window); |
597 | } |
598 | |
599 | const char tz_title[] = "THERMAL ZONES(SENSORS)" ; |
600 | |
601 | void show_sensors_w(void) |
602 | { |
603 | int i, j; |
604 | char buffer[512]; |
605 | |
606 | if (tui_disabled || !tz_sensor_window) |
607 | return; |
608 | |
609 | werase(tz_sensor_window); |
610 | |
611 | memset(buffer, 0, sizeof(buffer)); |
612 | wattron(tz_sensor_window, A_BOLD); |
613 | mvwprintw(tz_sensor_window, 1, 1, "Thermal Zones:" ); |
614 | wattroff(tz_sensor_window, A_BOLD); |
615 | |
616 | mvwprintw(tz_sensor_window, 1, TZ_LEFT_ALIGN, "%s" , buffer); |
617 | /* fill trip points for each tzone */ |
618 | wattron(tz_sensor_window, A_BOLD); |
619 | mvwprintw(tz_sensor_window, 2, 1, "Trip Points:" ); |
620 | wattroff(tz_sensor_window, A_BOLD); |
621 | |
622 | /* draw trip point from low to high for each tz */ |
623 | for (i = 0; i < ptdata.nr_tz_sensor; i++) { |
624 | int inst = ptdata.tzi[i].instance; |
625 | |
626 | mvwprintw(tz_sensor_window, 1, |
627 | TZ_LEFT_ALIGN+TZONE_RECORD_SIZE * inst, "%.9s%02d" , |
628 | ptdata.tzi[i].type, ptdata.tzi[i].instance); |
629 | for (j = ptdata.tzi[i].nr_trip_pts - 1; j >= 0; j--) { |
630 | /* loop through all trip points */ |
631 | char type; |
632 | int tp_pos; |
633 | /* reverse the order here since trips are sorted |
634 | * in ascending order in terms of temperature. |
635 | */ |
636 | tp_pos = ptdata.tzi[i].nr_trip_pts - j - 1; |
637 | |
638 | type = trip_type_to_char(type: ptdata.tzi[i].tp[j].type); |
639 | mvwaddch(tz_sensor_window, 2, |
640 | inst * TZONE_RECORD_SIZE + TZ_LEFT_ALIGN + |
641 | tp_pos, type); |
642 | syslog(LOG_DEBUG, "draw tz %d tp %d ch:%c\n" , |
643 | inst, j, type); |
644 | } |
645 | } |
646 | wborder(tz_sensor_window, 0, 0, 0, 0, 0, 0, 0, 0); |
647 | wattron(tz_sensor_window, A_BOLD); |
648 | mvwprintw(tz_sensor_window, 0, maxx/2 - sizeof(tz_title), tz_title); |
649 | wattroff(tz_sensor_window, A_BOLD); |
650 | wrefresh(tz_sensor_window); |
651 | } |
652 | |
653 | void disable_tui(void) |
654 | { |
655 | tui_disabled = 1; |
656 | } |
657 | |