linux/tools/thermal/tmon/tui.c
<<
>>
Prefs
   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
  36static PANEL *data_panel;
  37static PANEL *dialogue_panel;
  38static PANEL *top;
  39
  40static WINDOW *title_bar_window;
  41static WINDOW *tz_sensor_window;
  42static WINDOW *cooling_device_window;
  43static WINDOW *control_window;
  44static WINDOW *status_bar_window;
  45static WINDOW *thermal_data_window;
  46static WINDOW *dialogue_window;
  47
  48char status_bar_slots[10][40];
  49static void draw_hbar(WINDOW *win, int y, int start, int len,
  50                unsigned long pattern, bool end);
  51
  52static int maxx, maxy;
  53static 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) */
  60static int  tui_disabled;
  61
  62static void close_panel(PANEL *p)
  63{
  64        if (p) {
  65                del_panel(p);
  66                p = NULL;
  67        }
  68}
  69
  70static void close_window(WINDOW *win)
  71{
  72        if (win) {
  73                delwin(win);
  74                win = NULL;
  75        }
  76}
  77
  78void 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
  98void 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 */
 109static 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
 116void 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
 182void 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
 198const char cdev_title[] = " COOLING DEVICES ";
 199void 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
 275const char DIAG_TITLE[] = "[ TUNABLES ]";
 276void 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
 315void 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
 322const char control_title[] = " CONTROLS ";
 323void 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
 351void 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
 376void 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
 406static 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
 442static 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
 466void *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 */
 524static 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
 533static 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 */
 549static 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
 564const char data_win_title[] = " THERMAL DATA ";
 565void 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
 599const char tz_title[] = "THERMAL ZONES(SENSORS)";
 600
 601void 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
 653void disable_tui(void)
 654{
 655        tui_disabled = 1;
 656}
 657