tui.c 16 KB

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