xref: /openbmc/linux/tools/thermal/tmon/tui.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
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  
close_panel(PANEL * p)62  static void close_panel(PANEL *p)
63  {
64  	if (p) {
65  		del_panel(p);
66  		p = NULL;
67  	}
68  }
69  
close_window(WINDOW * win)70  static void close_window(WINDOW *win)
71  {
72  	if (win) {
73  		delwin(win);
74  		win = NULL;
75  	}
76  }
77  
close_windows(void)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  
write_status_bar(int x,char * line)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   */
diag_dev_rows(void)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  
setup_windows(void)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  
resize_handler(int sig)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 ";
show_cooling_device(void)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 ]";
show_dialogue(void)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  
write_dialogue_win(char * buf,int y,int x)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 ";
show_control_w(void)323  void show_control_w(void)
324  {
325  	unsigned long state;
326  
327  	get_ctrl_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  
initialize_curses(void)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  
show_title_bar(void)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  
handle_input_val(int ch)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(40, buf);
423  		else {
424  			p_param.t_target = val;
425  			snprintf(buf, 31, "Set New Target Temp %d", val);
426  			write_status_bar(40, buf);
427  		}
428  	} else {
429  		snprintf(path, 256, "%s/%s%d", THERMAL_SYSFS,
430  			CDEV, ptdata.cdi[ch].instance);
431  		sysfs_set_ulong(path, "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  
handle_input_choice(int ch)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, diag_dev_rows() + 2, 2);
459  		handle_input_val(cdev_id);
460  	} else {
461  		snprintf(buf, sizeof(buf), "Invalid selection %d", ch);
462  		write_dialogue_win(buf, 8, 2);
463  	}
464  }
465  
handle_tui_events(void * arg)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 */
draw_hbar(WINDOW * win,int y,int start,int len,unsigned long ptn,bool end)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  
trip_type_to_char(int type)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   */
draw_tp_line(int tz,int y)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(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 ";
show_data_w(void)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(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  
show_sensors_w(void)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(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  
disable_tui(void)653  void disable_tui(void)
654  {
655  	tui_disabled = 1;
656  }
657