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