display.c revision de3d2ce46fc25c7b67ccbae4afe5f15e5357568f
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2008-2009, Intel Corporation.
* All Rights Reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <dirent.h>
#include <curses.h>
#include <time.h>
#include <wchar.h>
#include <ctype.h>
#include <stdarg.h>
#include "latencytop.h"
#define LT_WINDOW_X 80
#define LT_WINDOW_Y 24
#define LT_COLOR_DEFAULT 1
#define LT_COLOR_HEADER 2
/* Windows created by libcurses */
static WINDOW *titlebar = NULL;
static WINDOW *captionbar = NULL;
static WINDOW *sysglobal_window = NULL;
static WINDOW *taskbar = NULL;
static WINDOW *process_window = NULL;
static WINDOW *hintbar = NULL;
/* Screen dimention */
static int screen_width = 1, screen_height = 1;
/* Is display initialized, i.e. window pointers set up. */
static int display_initialized = FALSE;
/* Is initscr() called */
static int curses_inited = FALSE;
/* Changed by user key press */
static pid_t selected_pid = INVALID_PID;
static id_t selected_tid = INVALID_TID;
static lt_sort_t sort_type = LT_SORT_TOTAL;
static int thread_mode = FALSE;
/* what kind of list are we showing now */
static int current_list_type = LT_LIST_CAUSE;
static int show_help = FALSE;
/* Help functions that append/prepend blank to the string */
#define fill_space_right(a, b, c) fill_space((a), (b), (c), TRUE)
#define fill_space_left(a, b, c) fill_space((a), (b), (c), FALSE)
static void
fill_space(char *buffer, int len, int buffer_limit, int is_right)
{
int i = 0;
int tofill;
if (len >= buffer_limit) {
len = buffer_limit - 1;
}
i = strlen(buffer);
if (i >= len) {
return;
}
tofill = len - i;
if (is_right) {
(void) memset(&buffer[i], ' ', tofill);
buffer[len] = 0;
} else {
(void) memmove(&buffer[tofill], buffer, i+1);
(void) memset(buffer, ' ', tofill);
}
}
/* Formats a human readable string out of nanosecond value */
static const char *
get_time_string(double nanoseconds, char *buffer, int len, int fill_width)
{
const double ONE_USEC = 1000.0;
const double ONE_MSEC = 1000000.0;
const double ONE_SEC = 1000000000.0;
if (nanoseconds < (ONE_USEC - .5)) {
(void) snprintf(buffer, len, "%3.1f nsec", nanoseconds);
} else if (nanoseconds < (ONE_MSEC - .5 * ONE_USEC)) {
(void) snprintf(buffer, len,
"%3.1f usec", nanoseconds / ONE_USEC);
} else if (nanoseconds < (ONE_SEC - .5 * ONE_MSEC)) {
(void) snprintf(buffer, len,
"%3.1f msec", nanoseconds / ONE_MSEC);
} else if (nanoseconds < 999.5 * ONE_SEC) {
(void) snprintf(buffer, len,
"%3.1f sec", nanoseconds / ONE_SEC);
} else {
(void) snprintf(buffer, len,
"%.0e sec", nanoseconds / ONE_SEC);
}
fill_space_left(buffer, fill_width, len);
return (buffer);
}
/*
* Print statistics in a window.
* IN: window - the global or process statistics window.
* begin_line - where to start printing.
* count - how many lines should we print.
* list - a stat_list.
*/
#define WIDTH_REASON_STRING 36
#define WIDTH_COUNT 12
#define WIDTH_SUM 12
#define WIDTH_MAX 12
#define WIDTH_PCT 8
#define BEGIN_COUNT WIDTH_REASON_STRING
#define BEGIN_SUM (BEGIN_COUNT + WIDTH_COUNT)
#define BEGIN_MAX (BEGIN_SUM + WIDTH_SUM)
#define BEGIN_PCT (BEGIN_MAX + WIDTH_MAX)
static void
print_statistics(WINDOW * window, int begin_line, int count, void *list)
{
uint64_t total;
int i = 0;
if (!display_initialized) {
return;
}
total = lt_stat_list_get_gtotal(list);
if (total == 0) {
return;
}
while (i < count && lt_stat_list_has_item(list, i)) {
/*
* We intentionally make tmp[] hold one character less
* than WIDTH_REASON_STRING, so it will look nice on the
* screen.
*/
char tmp[WIDTH_REASON_STRING];
const char *reason = lt_stat_list_get_reason(list, i);
uint64_t count = lt_stat_list_get_count(list, i);
if (count == 0) {
continue;
}
(void) snprintf(tmp, sizeof (tmp), "%s", reason);
(void) mvwprintw(window, i + begin_line, 0, "%s", tmp);
(void) snprintf(tmp, sizeof (tmp), "%d",
lt_stat_list_get_count(list, i));
fill_space_left(tmp, WIDTH_COUNT, sizeof (tmp));
(void) mvwprintw(window, i + begin_line, BEGIN_COUNT,
"%s", tmp);
(void) mvwprintw(window, i + begin_line, BEGIN_SUM,
"%s", get_time_string(
(double)lt_stat_list_get_sum(list, i) / count,
tmp, sizeof (tmp), WIDTH_SUM));
(void) mvwprintw(window, i + begin_line, BEGIN_MAX,
"%s", get_time_string(
(double)lt_stat_list_get_max(list, i),
tmp, sizeof (tmp), WIDTH_MAX));
if (LT_LIST_SPECIALS != current_list_type) {
(void) snprintf(tmp, sizeof (tmp), "%.1f %%",
(double)lt_stat_list_get_sum(list, i)
/ total * 100.0);
} else {
(void) snprintf(tmp, sizeof (tmp), "--- ");
}
fill_space_left(tmp, WIDTH_PCT, sizeof (tmp));
(void) mvwprintw(window, i + begin_line, BEGIN_PCT,
"%s", tmp);
i++;
}
}
/*
* Print global statistics. Calls print_statistics().
*/
static void
print_sysglobal(void)
{
void *list;
char header[256];
if (!display_initialized) {
return;
}
(void) werase(sysglobal_window);
(void) wattron(sysglobal_window, A_REVERSE);
(void) snprintf(header, sizeof (header),
"%s", lt_text("System wide latencies"));
fill_space_right(header, screen_width, sizeof (header));
(void) mvwprintw(sysglobal_window, 0, 0, "%s", header);
(void) wattroff(sysglobal_window, A_REVERSE);
list = lt_stat_list_create(current_list_type,
LT_LEVEL_GLOBAL, 0, 0, 10, sort_type);
print_statistics(sysglobal_window, 1, 10, list);
lt_stat_list_free(list);
(void) wrefresh(sysglobal_window);
}
/*
* Prints current operation mode: process/thread, window 1/2/3.
*/
static void
print_current_mode()
{
char type;
if (!display_initialized) {
return;
}
switch (current_list_type) {
case LT_LIST_CAUSE:
type = 'C';
break;
case LT_LIST_SPECIALS:
type = 'S';
break;
case LT_LIST_SOBJ:
type = 'L';
break;
default:
type = '?';
break;
}
(void) mvwprintw(process_window, 0, screen_width - 2, "%c%c",
type, thread_mode ? 'T' : 'P');
}
/*
* Print per-process statistics. Calls print_statistics().
* This one is used in per-process mode.
*/
static void
print_process(unsigned int pid)
{
void *list;
char header[256];
char tmp[30];
if (!display_initialized) {
return;
}
list = lt_stat_list_create(current_list_type, LT_LEVEL_PROCESS,
pid, 0, 8, sort_type);
(void) werase(process_window);
(void) wattron(process_window, A_REVERSE);
(void) snprintf(header, sizeof (header), "Process %s (%i) ",
lt_stat_proc_get_name(pid), pid);
fill_space_right(header, screen_width, sizeof (header));
(void) mvwprintw(process_window, 0, 0, "%s", header);
if (current_list_type != LT_LIST_SPECIALS) {
(void) mvwprintw(process_window, 0, 40,
lt_text("Total: %s from %d threads"),
get_time_string((double)lt_stat_list_get_gtotal(list),
tmp, sizeof (tmp), 12),
lt_stat_proc_get_nthreads(pid));
}
print_current_mode();
(void) wattroff(process_window, A_REVERSE);
print_statistics(process_window, 1, 8, list);
lt_stat_list_free(list);
(void) wrefresh(process_window);
}
/*
* List all processes in task bar.
* This one is used in per-process mode.
*/
static void
print_taskbar_process(pid_t *pidlist, int pidlist_len, int pidlist_index)
{
const int ITEM_WIDTH = 8;
int number_item;
int i;
int xpos = 0;
if (!display_initialized) {
return;
}
number_item = (screen_width / ITEM_WIDTH) - 1;
i = pidlist_index - (pidlist_index % number_item);
(void) werase(taskbar);
if (i != 0) {
(void) mvwprintw(taskbar, 0, xpos, "<-");
}
xpos = ITEM_WIDTH / 2;
while (xpos + ITEM_WIDTH <= screen_width && i < pidlist_len) {
char str[ITEM_WIDTH+1];
int slen;
const char *pname = lt_stat_proc_get_name(pidlist[i]);
if (pname && pname[0]) {
(void) snprintf(str, sizeof (str) - 1, "%s", pname);
} else {
(void) snprintf(str, sizeof (str) - 1,
"<%d>", pidlist[i]);
}
slen = strlen(str);
if (slen < ITEM_WIDTH) {
(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
}
str[sizeof (str) - 1] = 0;
if (i == pidlist_index) {
(void) wattron(taskbar, A_REVERSE);
}
(void) mvwprintw(taskbar, 0, xpos, "%s", str);
if (i == pidlist_index) {
(void) wattroff(taskbar, A_REVERSE);
}
xpos += ITEM_WIDTH;
i++;
}
if (i != pidlist_len) {
(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
}
(void) wrefresh(taskbar);
}
/*
* List all processes in task bar.
* This one is used in per-thread mode.
*/
static void
print_taskbar_thread(pid_t *pidlist, id_t *tidlist, int list_len,
int list_index)
{
const int ITEM_WIDTH = 12;
int number_item;
int i;
int xpos = 0;
const char *pname = NULL;
pid_t last_pid = INVALID_PID;
if (!display_initialized) {
return;
}
number_item = (screen_width - 8) / ITEM_WIDTH;
i = list_index - (list_index % number_item);
(void) werase(taskbar);
if (i != 0) {
(void) mvwprintw(taskbar, 0, xpos, "<-");
}
xpos = 4;
while (xpos + ITEM_WIDTH <= screen_width && i < list_len) {
char str[ITEM_WIDTH+1];
int slen, tlen;
if (pidlist[i] != last_pid) {
pname = lt_stat_proc_get_name(pidlist[i]);
last_pid = pidlist[i];
}
/*
* Calculate thread id length, leave enough space by print
* shorter process name.
*/
tlen = snprintf(NULL, 0, "_%d", tidlist[i]);
if (pname && pname[0]) {
(void) snprintf(str, sizeof (str) - tlen - 1,
"%s", pname);
} else {
(void) snprintf(str, sizeof (str) - tlen - 1,
"<%d>", pidlist[i]);
}
slen = strlen(str);
(void) snprintf(&str[slen], sizeof (str) - slen,
"_%d", tidlist[i]);
slen += tlen;
if (slen < ITEM_WIDTH) {
(void) memset(&str[slen], ' ', ITEM_WIDTH - slen);
}
str[sizeof (str) - 1] = 0;
if (i == list_index) {
(void) wattron(taskbar, A_REVERSE);
}
(void) mvwprintw(taskbar, 0, xpos, "%s", str);
if (i == list_index) {
(void) wattroff(taskbar, A_REVERSE);
}
xpos += ITEM_WIDTH;
i++;
}
if (i != list_len) {
(void) mvwprintw(taskbar, 0, screen_width - 2, "->");
}
(void) wrefresh(taskbar);
}
/*
* Print statistics. Calls print_statistics().
* This one is used in per-thread mode.
*/
static void
print_thread(pid_t pid, id_t tid)
{
void *list;
char header[256];
char tmp[30];
if (!display_initialized) {
return;
}
list = lt_stat_list_create(current_list_type, LT_LEVEL_THREAD,
pid, tid, 8, sort_type);
(void) werase(process_window);
(void) wattron(process_window, A_REVERSE);
(void) snprintf(header, sizeof (header),
"Process %s (%i), LWP %d",
lt_stat_proc_get_name(pid), pid, tid);
fill_space_right(header, screen_width, sizeof (header));
(void) mvwprintw(process_window, 0, 0, "%s", header);
if (current_list_type != LT_LIST_SPECIALS) {
(void) mvwprintw(process_window, 0, 40, lt_text("Total: %s"),
get_time_string(
(double)lt_stat_list_get_gtotal(list),
tmp, sizeof (tmp), 12));
}
print_current_mode();
(void) wattroff(process_window, A_REVERSE);
print_statistics(process_window, 1, 8, list);
lt_stat_list_free(list);
(void) wrefresh(process_window);
}
/*
* Update hint string at the bottom line. The message to print is stored in
* hint. If hint is NULL, the function will pick a message from useful tips
* and display it.
*/
static void
print_hint(const char *hint)
{
const char *HINTS[] = {
"Press '<' or '>' to switch between processes.",
"Press 'q' to exit.",
"Press 'r' to refresh immediately.",
"Press 't' to toggle Process/Thread display mode.",
"Press 'h' for help.",
"Use 'c', 'a', 'm', 'p' to change sort criteria."
"Use '1', '2', '3' to switch between windows."
};
const uint64_t update_interval = 5000; /* 5 seconds */
static int index = 0;
static uint64_t next_hint = 0;
uint64_t now = lt_millisecond();
if (!display_initialized) {
return;
}
if (hint == NULL) {
if (now < next_hint) {
return;
}
hint = HINTS[index];
index = (index + 1) % (sizeof (HINTS) / sizeof (HINTS[0]));
next_hint = now + update_interval;
} else {
/*
* To ensure important message
* show at least 2 cycles.
*/
next_hint = now + update_interval * 2;
}
(void) werase(hintbar);
(void) mvwprintw(hintbar, 0, (screen_width - strlen(hint)) / 2,
"%s", lt_text(hint));
(void) wrefresh(hintbar);
}
/*
* Get information from existing statistics, and create a PID list
* or PID/TID list based on current display mode.
*/
static void
get_plist(pid_t **plist, id_t **tlist, int *list_len, int *list_index)
{
if (!thread_mode) {
/* Per-process mode */
*list_len = lt_stat_proc_list_create(plist, NULL);
/* Search for previous selected PID */
for (*list_index = 0; *list_index < *list_len &&
(*plist)[*list_index] != selected_pid;
++*list_index) {
}
if (*list_index >= *list_len) {
/*
* The old selected pid is gone.
* Select the first one
*/
*list_index = 0;
}
} else {
/* Per-thread mode */
*list_len = lt_stat_proc_list_create(plist, tlist);
/* Search for previous selected PID & TID */
for (*list_index = 0; *list_index < *list_len;
++*list_index) {
if ((*plist)[*list_index] == selected_pid &&
(*tlist)[*list_index] == selected_tid) {
break;
}
}
if (*list_index >= *list_len) {
/*
* The old selected pid/tid is gone.
* Select the first one in the pid
*/
for (*list_index = 0;
*list_index < *list_len &&
(*plist)[*list_index] != selected_pid;
++*list_index) {
}
}
if (*list_index >= *list_len) {
/*
* The old selected pid is gone.
* Select the first one
*/
*list_index = 0;
}
}
}
static void
print_help(void)
{
const char *HELP[] = {
TITLE,
COPYRIGHT,
"",
"These single-character commands are available:",
"< - Move to previous process/thread.",
"> - Move to next process/thread.",
"q - Exit.",
"r - Refresh.",
"t - Toggle process/thread mode.",
"c - Sort by count.",
"a - Sort by average.",
"m - Sort by maximum.",
"p - Sort by percent.",
"1 - Show list by causes.",
"2 - Show list of special entries.",
"3 - Show list by synchronization objects.",
"h - Show this help.",
"",
"Press any key to continue..."
};
int i;
if (!display_initialized) {
return;
}
for (i = 0; i < sizeof (HELP) / sizeof (HELP[0]); ++i) {
(void) mvwprintw(stdscr, i, 0, "%s", HELP[i]);
}
(void) refresh();
}
/*
* Print title on screen
*/
static void
print_title(void)
{
if (!display_initialized) {
return;
}
(void) wattrset(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
(void) wbkgd(titlebar, COLOR_PAIR(LT_COLOR_HEADER));
(void) werase(titlebar);
(void) mvwprintw(titlebar, 0, (screen_width - strlen(TITLE)) / 2,
"%s", TITLE);
(void) wrefresh(titlebar);
(void) werase(captionbar);
(void) mvwprintw(captionbar, 0, 0, "%s", lt_text(
" Cause "
"Count Average Maximum Percent"));
(void) wrefresh(captionbar);
(void) wattrset(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
(void) wbkgd(hintbar, COLOR_PAIR(LT_COLOR_HEADER));
}
/*
* Signal handler on terminal resize
*/
/* ARGSUSED */
static void
on_resize(int sig)
{
lt_gpipe_break("r");
}
/*
* Initialize display part. Screen will be cleared when this function returns.
*/
void
lt_display_init(void)
{
if (display_initialized) {
return;
}
/* Window resize signal */
(void) signal(SIGWINCH, on_resize);
/* Initialize curses lib. */
(void) initscr();
(void) start_color();
(void) keypad(stdscr, TRUE);
(void) nonl();
(void) cbreak();
(void) noecho();
(void) curs_set(0);
/* Set up color pairs */
(void) init_pair(LT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
(void) init_pair(LT_COLOR_HEADER, COLOR_BLACK, COLOR_WHITE);
curses_inited = TRUE;
getmaxyx(stdscr, screen_height, screen_width);
if (screen_width < LT_WINDOW_X || screen_height < LT_WINDOW_Y) {
(void) mvwprintw(stdscr, 0, 0, "Terminal size is too small.");
(void) mvwprintw(stdscr, 1, 0,
"Please resize it to 80x24 or larger.");
(void) mvwprintw(stdscr, 2, 0, "Press q to quit.");
(void) refresh();
return;
}
/* Setup all windows on screen. */
titlebar = subwin(stdscr, 1, screen_width, 0, 0);
captionbar = subwin(stdscr, 1, screen_width, 1, 0);
sysglobal_window = subwin(stdscr, screen_height / 2 - 1,
screen_width, 2, 0);
process_window = subwin(stdscr, screen_height / 2 - 3,
screen_width, screen_height / 2 + 1, 0);
taskbar = subwin(stdscr, 1, screen_width, screen_height - 2, 0);
hintbar = subwin(stdscr, 1, screen_width, screen_height - 1, 0);
(void) werase(stdscr);
(void) refresh();
display_initialized = TRUE;
print_title();
}
/*
* The event loop. Display data on screen and handles key press. Will return
* after "duration" seconds, unless exit or refresh hotkey is pressed.
* Return 0 means main() should exit. 1 means to loop again.
*/
int
lt_display_loop(int duration)
{
uint64_t start;
int remaining;
struct timeval timeout;
fd_set read_fd;
int need_refresh = TRUE;
pid_t *plist = NULL;
id_t *tlist = NULL;
int list_len = 0;
int list_index = 0;
int retval = 1;
int next_snap;
int gpipe;
start = lt_millisecond();
gpipe = lt_gpipe_readfd();
if (!show_help) {
print_hint(NULL);
print_sysglobal();
}
get_plist(&plist, &tlist, &list_len, &list_index);
for (;;) {
if (list_len != 0 && need_refresh && !show_help) {
if (!thread_mode) {
print_taskbar_process(plist, list_len,
list_index);
print_process(plist[list_index]);
} else {
print_taskbar_thread(plist, tlist,
list_len, list_index);
print_thread(plist[list_index],
tlist[list_index]);
}
}
need_refresh = TRUE; /* Usually we need refresh. */
remaining = duration - (int)(lt_millisecond() - start);
if (remaining <= 0) {
break;
}
/* Embedded dtrace snap action here. */
next_snap = lt_dtrace_work(0);
if (next_snap == 0) {
/*
* Just did a snap, check again to get time for
* next shot.
*/
next_snap = lt_dtrace_work(0);
}
if (next_snap > 0 && remaining > next_snap) {
remaining = next_snap;
}
timeout.tv_sec = remaining / 1000;
timeout.tv_usec = (remaining % 1000) * 1000;
FD_ZERO(&read_fd);
FD_SET(0, &read_fd);
FD_SET(gpipe, &read_fd);
/* Wait for keyboard input, or signal from gpipe */
if (select(gpipe + 1, &read_fd, NULL, NULL, &timeout) > 0) {
int k = 0;
if (FD_ISSET(gpipe, &read_fd)) {
/* data from pipe has priority */
char ch; /* Need this for big-endian */
(void) read(gpipe, &ch, 1);
k = ch;
} else {
k = getch();
}
/*
* We check if we need to update hint line whenever we
* get chance.
* NOTE: current implementation depends on
* g_config.snap_interval, but it's OK because it
* doesn't have to be precise.
*/
print_hint(NULL);
/*
* If help is on, and a key press happens,
* we need to clear the help and go on.
*/
if (show_help) {
(void) werase(stdscr);
(void) refresh();
print_title();
print_sysglobal();
show_help = FALSE;
/* Drop this key and continue */
continue;
}
switch (k) {
case 'Q':
case 'q':
retval = 0;
goto quit;
case 'R':
case 'r':
lt_display_deinit();
lt_display_init();
goto quit;
case 'H':
case 'h':
show_help = TRUE;
(void) werase(stdscr);
(void) refresh();
print_help();
break;
case ',':
case '<':
case KEY_LEFT:
--list_index;
if (list_index < 0) {
list_index = 0;
}
break;
case '.':
case '>':
case KEY_RIGHT:
++list_index;
if (list_index >= list_len) {
list_index = list_len - 1;
}
break;
case 'a':
case 'A':
sort_type = LT_SORT_AVG;
print_sysglobal();
break;
case 'p':
case 'P':
sort_type = LT_SORT_TOTAL;
print_sysglobal();
break;
case 'm':
case 'M':
sort_type = LT_SORT_MAX;
print_sysglobal();
break;
case 'c':
case 'C':
sort_type = LT_SORT_COUNT;
print_sysglobal();
break;
case 't':
case 'T':
if (plist != NULL) {
selected_pid = plist[list_index];
}
selected_tid = INVALID_TID;
thread_mode = !thread_mode;
get_plist(&plist, &tlist,
&list_len, &list_index);
break;
case '1':
case '!':
current_list_type = LT_LIST_CAUSE;
print_sysglobal();
break;
case '2':
case '@':
if (g_config.low_overhead_mode) {
lt_display_error("Switching mode is "
"not available for '-f low'.");
} else {
current_list_type = LT_LIST_SPECIALS;
print_sysglobal();
}
break;
case '3':
case '#':
if (g_config.trace_syncobj) {
current_list_type = LT_LIST_SOBJ;
print_sysglobal();
} else if (g_config.low_overhead_mode) {
lt_display_error("Switching mode is "
"not available for '-f low'.");
} else {
lt_display_error("Tracing "
"synchronization objects is "
"disabled.");
}
break;
default:
/* Wake up for nothing, no need to refresh */
need_refresh = FALSE;
break;
}
} else {
need_refresh = FALSE;
}
}
quit:
if (plist != NULL) {
selected_pid = plist[list_index];
}
if (tlist != NULL) {
selected_tid = tlist[list_index];
}
lt_stat_proc_list_free(plist, tlist);
return (retval);
}
/*
* Close display part.
*/
void
lt_display_deinit(void)
{
if (curses_inited) {
(void) clear();
(void) refresh();
(void) endwin();
}
titlebar = NULL;
captionbar = NULL;
sysglobal_window = NULL;
taskbar = NULL;
process_window = NULL;
hintbar = NULL;
screen_width = 1;
screen_height = 1;
display_initialized = FALSE;
curses_inited = FALSE;
}
/*
* Print error message.
*/
/* ARGSUSED */
void
lt_display_error(const char *fmt, ...)
{
va_list vl;
char tmp[81];
int l;
va_start(vl, fmt);
(void) vsnprintf(tmp, sizeof (tmp), fmt, vl);
va_end(vl);
l = strlen(tmp);
while (l > 0 && (tmp[l - 1] == '\n' || tmp[l - 1] == '\r')) {
tmp[l - 1] = 0;
--l;
}
if (!display_initialized) {
(void) printf("%s\n", tmp);
} else if (!show_help) {
print_hint(tmp);
}
}