tui.c 16 KB

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