/* Sysmon window-handling routines. */ #include #include #include #define __USE_GNU /* for strsignal() prototype */ #include #include #include #include #include "action.h" #include "etree.h" #include "window.h" /*************************************************************************/ /* Size of the screen */ int scrwidth, scrheight; /* Is color available? */ int has_color; /* List of all windows. */ Window *winlist; /* Window which is currently being redrawn. */ Window *current_window; /*************************************************************************/ /*************************************************************************/ /* Clean up the screen on exit. */ static void screen_cleanup() { Window *w; for (w = winlist; w; w = w->next) delwin(w->curses_win); #if 0 wmove(stdscr, scrheight-1, 0); wclrtoeol(stdscr); #else wclear(stdscr); #endif wrefresh(stdscr); endwin(); } /*************************************************************************/ /* Little signal handler that just does an exit(1) (thereby getting our * cleanup routine called), except for TSTP, which does a clean suspend. */ static void (*old_tstp)(int sig); static void sighandler(int sig) { if (sig != SIGTSTP) { endwin(); if (sig != SIGINT) fprintf(stderr, "%s\n", strsignal(sig)); exit(1); } endwin(); signal(SIGTSTP, old_tstp); raise(SIGTSTP); doupdate(); signal(SIGTSTP, sighandler); } /*************************************************************************/ /*************************************************************************/ #define MAXCOLORS 256 static int colors[MAXCOLORS][3] = { {-1,-1} }; /*************************************************************************/ /* Return the color value corresponding to a given string, or -1 if the * string does not represent a valid color. */ static int strtocolor(const char *s) { if (strcasecmp(s, "black") == 0) return C_BLACK; if (strcasecmp(s, "blue") == 0) return C_BLUE; if (strcasecmp(s, "green") == 0) return C_GREEN; if (strcasecmp(s, "cyan") == 0) return C_CYAN; if (strcasecmp(s, "red") == 0) return C_RED; if (strcasecmp(s, "magenta") == 0) return C_MAGENTA; if (strcasecmp(s, "yellow") == 0) return C_YELLOW; if (strcasecmp(s, "grey") == 0 || strcasecmp(s, "gray") == 0) return C_GREY; if (strcasecmp(s, "white") == 0) return C_WHITE; return -1; } /*************************************************************************/ /* Convert one of our C_* color defines to an [n]curses color define. */ static int colortrans(int color) { switch (color) { case C_BLACK: return COLOR_BLACK; case C_BLUE: return COLOR_BLUE; case C_GREEN: return COLOR_GREEN; case C_CYAN: return COLOR_CYAN; case C_RED: return COLOR_RED; case C_MAGENTA: return COLOR_MAGENTA; case C_YELLOW: return COLOR_YELLOW; case C_GREY: return COLOR_WHITE; case C_WHITE: return COLOR_WHITE; default: return -1; } } /*************************************************************************/ /* Get the [n]curses attribute values for our C_* color defines. */ static int colorattr(int color) { if (color == C_BLACK || color == C_GREY) return 0; else return A_BOLD; } /*************************************************************************/ /* Return a color attribute value. */ static long getcolor(int realfg, int realbg) { int i, fg, bg; if (realfg & 0x1000000) fg = colortrans(realfg); else fg = -1; if (realbg & 0x1000000) bg = colortrans(realbg); else bg = -1; if (fg < 0 || bg < 0) return -1; if (colors[0][0] < 0) { memset(colors, -1, sizeof(colors)); colors[0][0] = COLOR_WHITE; colors[0][1] = COLOR_BLACK; } if (fg == COLOR_WHITE && bg == COLOR_BLACK) return COLOR_PAIR(0) | colorattr(realfg); for (i = 1; i < MAXCOLORS; i++) { if (colors[i][0] == fg && colors[i][1] == bg) { colors[i][2]++; /* One more user */ return COLOR_PAIR(i) | colorattr(realfg); } } for (i = 1; i < MAXCOLORS; i++) { if (colors[i][0] < 0) { if (init_pair(i, fg, bg) == ERR) continue; colors[i][0] = fg; colors[i][1] = bg; colors[i][2] = 1; return COLOR_PAIR(i) | colorattr(realfg); } } return -1; } /*************************************************************************/ /*************************************************************************/ /* Draw a border for the given window. */ static void drawborder(Window *win) { int color; if (has_color) { color = getcolor(win->borderfg, win->borderbg); if (color != -1) wattrset(win->curses_win, color); } box(win->curses_win, 0, 0); if (win->title) { int x = (win->width+2 - strlen(win->title)) / 2; if (x < 2) x = 2; if (has_color) { color = getcolor(win->titlefg, win->titlebg); if (color != -1) wattrset(win->curses_win, color); } mvwaddstr(win->curses_win, 0, x-1, " "); waddnstr(win->curses_win, win->title, win->width-2); waddstr(win->curses_win, " "); } wmove(win->curses_win, win->cy+1, win->cx+1); if (has_color) wattrset(win->curses_win, win->attr); } /*************************************************************************/ /* Write some text to a window. Return number of characters written. */ int vwprintf(Window *win, const char *fmt, va_list args) { char printf_buf[8192], *ptr, *eol; int printf_len, len; vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); printf_len = 0; ptr = printf_buf; do { if (win->cy >= win->height) { int dist = 0; while (win->cy >= win->height) { dist++; win->cy--; } #if 0 memmove(win->scroll_buf, win->scroll_buf + dist, sizeof(char *) * (win->scroll_bufsize - dist)); memset(win->scroll_buf + win->scroll_bufsize - dist, 0, sizeof(char *) * dist); #endif wscrl(win->curses_win, dist); if (win->flags & WF_BORDER) { drawborder(win); wmove(win->curses_win, win->cy+1, win->cx+1); } else { wmove(win->curses_win, win->cy, win->cx); } } eol = strchr(ptr, '\n'); if (eol) len = eol-ptr; else len = strlen(ptr); if (len > win->width - win->cx) { if (win->cx < win->width) { waddnstr(win->curses_win, ptr, win->width - win->cx); printf_len += win->width - win->cx; ptr += win->width - win->cx; } win->cx = 0; win->cy++; if (current_window->flags & WF_BORDER) wmove(current_window->curses_win, win->cy+1, 1); else wmove(current_window->curses_win, win->cy, 0); } else { if (eol) *eol++ = 0; waddstr(win->curses_win, ptr); printf_len += len; ptr = eol; if (eol) { printf_len++; win->cx = 0; win->cy++; if (current_window->flags & WF_BORDER) wmove(current_window->curses_win, win->cy+1, 1); else wmove(current_window->curses_win, win->cy, 0); } else { win->cx += len; } } } while (ptr && *ptr); return printf_len; } /*************************************************************************/ /*************************************************************************/ static void do_clear(void) { werase(current_window->curses_win); if (current_window->flags & WF_BORDER) drawborder(current_window); } /*************************************************************************/ static void do_cursor(ETree **args) { int x, y; x = etree_eval_i(args[0]); y = etree_eval_i(args[1]); current_window->cx = x; current_window->cy = y; if (current_window->flags & WF_BORDER) wmove(current_window->curses_win, y+1, x+1); else wmove(current_window->curses_win, y, x); } /*************************************************************************/ static void do_color(int nargs, ETree **args) { int fg, bg; int col; if (!has_color) return; if (args[0]) fg = strtocolor(etree_eval_s(args[0])); else fg = current_window->fg; if (nargs > 1 && args[1]) bg = strtocolor(etree_eval_s(args[1])); else bg = current_window->bg; if ((col = getcolor(fg, bg)) != -1) { current_window->attr = col; wattrset(current_window->curses_win, col); } current_window->fg = fg; current_window->bg = bg; } /*************************************************************************/ static void do_bordercolor(int nargs, ETree **args) { if (!has_color) return; if (args[0]) current_window->borderfg = strtocolor(etree_eval_s(args[0])); if (nargs > 1 && args[1]) current_window->borderbg = strtocolor(etree_eval_s(args[1])); drawborder(current_window); } /*************************************************************************/ static void do_titlecolor(int nargs, ETree **args) { if (!has_color) return; if (args[0]) current_window->titlefg = strtocolor(etree_eval_s(args[0])); if (nargs > 1 && args[1]) current_window->titlebg = strtocolor(etree_eval_s(args[1])); drawborder(current_window); } /*************************************************************************/ static void do_printf(int nargs, ETree **args) { char *format; char *printf_args = NULL, *ptr = NULL; int printf_argsize = 0, printf_arglen = 0; char **string_args = calloc(sizeof(char *), nargs); int i; format = strdup(etree_eval_s(args[0])); for (i = 1; i < nargs; i++) { ETree *val = etree_eval(args[i]); int typesize; if (val->type == ET_INT) typesize = sizeof(int); else if (val->type == ET_STRING) typesize = sizeof(char *); else typesize = sizeof(int); if (printf_argsize < printf_arglen + typesize) { printf_argsize = printf_arglen + typesize + 16; printf_args = realloc(printf_args, printf_argsize); ptr = printf_args + printf_arglen; } if (val->type == ET_INT) *(int *)ptr = val->u.intval; else if (val->type == ET_STRING) { if (val->u.stringval) *(char **)ptr = string_args[i] = strdup(val->u.stringval); else *(char **)ptr = NULL; } else *(int *)ptr = 0; printf_arglen += typesize; ptr += typesize; } vwprintf(current_window, format, (va_list) printf_args); free(format); free(printf_args); for (i = 1; i < nargs; i++) { if (string_args[i]) free(string_args[i]); } free(string_args); } /*************************************************************************/ static void do_beep(void) { beep(); } /*************************************************************************/ static void do_setstate(int nargs, ETree **args) { Action *act = act_find(etree_eval_s(args[0])); if (act) act->state = etree_eval_i(args[1]); } /*************************************************************************/ /* General action dispatcher. */ static void do_action(WinAction *wa) { switch (wa->what) { case WA_CLEAR: do_clear(); break; case WA_CURSOR: do_cursor(wa->args); break; case WA_COLOR: do_color(wa->nargs, wa->args); break; case WA_BORDERCOLOR: do_bordercolor(wa->nargs, wa->args); break; case WA_TITLECOLOR: do_titlecolor(wa->nargs, wa->args); break; case WA_PRINTF: do_printf(wa->nargs, wa->args); break; case WA_BEEP: do_beep(); break; case WA_SETSTATE: do_setstate(wa->nargs, wa->args); break; } } /*************************************************************************/ /*************************************************************************/ /* External interfaces. */ /*************************************************************************/ /* Set up the screen stuff. */ void screen_setup(void) { /* Avoid messy keyboard signals while we're setting up */ signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); initscr(); cbreak(); noecho(); nodelay(stdscr, TRUE); leaveok(stdscr, TRUE); if ((has_color = has_colors())) start_color(); getmaxyx(stdscr, scrheight, scrwidth); /* Cancel all this when we exit. */ atexit(screen_cleanup); /* Catch signals so we can exit cleanly. */ signal(SIGINT, sighandler); signal(SIGQUIT, sighandler); signal(SIGTERM, sighandler); signal(SIGHUP, sighandler); signal(SIGSEGV, sighandler); signal(SIGABRT, sighandler); signal(SIGIOT, sighandler); signal(SIGTRAP, sighandler); signal(SIGBUS, sighandler); signal(SIGFPE, sighandler); signal(SIGUSR1, sighandler); signal(SIGUSR2, sighandler); signal(SIGALRM, sighandler); signal(SIGSTKFLT, sighandler); signal(SIGTSTP, sighandler); signal(SIGXCPU, sighandler); signal(SIGXFSZ, sighandler); signal(SIGVTALRM, sighandler); /* Broken pipes don't want to bother us at all. */ signal(SIGPIPE, SIG_IGN); } /*************************************************************************/ /* Redraw everything on the screen. */ void screen_refresh(void) { Window *win; wnoutrefresh(stdscr); /* In order to get these in the same order they were created in, we * have to traverse the list _backwards_, so first we have to get * there. */ for (win = winlist; win && win->next; win = win->next) ; for (; win; win = win->prev) { if (win->curses_win) wnoutrefresh(win->curses_win); } doupdate(); } /*************************************************************************/ /* Like screen_refresh(), but clear the screen first. */ void screen_redraw(void) { clearok(stdscr, TRUE); wnoutrefresh(stdscr); screen_refresh(); } /*************************************************************************/ /*************************************************************************/ /* Allocate a new Window structure and link it to the global list. */ Window *win_new(void) { Window *win = calloc(sizeof(Window), 1); win->next = winlist; if (winlist) winlist->prev = win; winlist = win; if (has_color) { win->attr = getcolor(C_WHITE, C_BLACK); win->fg = C_WHITE; win->bg = C_BLACK; win->borderfg = C_WHITE; win->borderbg = C_BLACK; win->titlefg = C_WHITE; win->titlebg = C_BLACK; } return win; } /*************************************************************************/ /* Draw a window on the screen. */ void win_open(Window *w) { if (w->curses_win) return; w->cx = w->cy = 0; w->curses_win = newwin(w->height, w->width, w->y, w->x); if (!w->curses_win) { fprintf(stderr, "Couldn't open window `%s'\n", w->name ? w->name : "(unnamed)"); exit(1); } wattrset(w->curses_win, A_NORMAL); scrollok(w->curses_win, w->flags & WF_SCROLL ? TRUE : FALSE); if (w->flags & WF_BORDER) { w->x++; w->y++; w->width -= 2; w->height -= 2; drawborder(w); if (w->flags & WF_SCROLL) wsetscrreg(w->curses_win, 1, w->height); } } /*************************************************************************/ /* Update a window's contents. */ void win_refresh(Window *w) { int i; if (!w->curses_win) return; current_window = w; for (i = 0; i < w->drawlist_size; i++) { WinAction *wa = w->drawlist[i]; if (wa->what == WA_IF) { if (etree_eval_i(wa->args[0])) { /* Turn any corresponding ELSE into a NOELSE */ wa = w->drawlist[i+1 + etree_eval_i(wa->args[1])]; if (wa->what == WA_ELSE) wa->what = WA_NOELSE; } else { /* Skip this IF block */ i += etree_eval_i(wa->args[1]); } } else if (wa->what == WA_ELSE) { /* Nothing */ } else if (wa->what == WA_NOELSE) { wa->what = WA_ELSE; i += etree_eval_i(wa->args[0]); } else { do_action(wa); } } current_window = NULL; } /*************************************************************************/ /* Remove a window from the screen. */ void win_close(Window *w) { if (!w->curses_win) return; delwin(w->curses_win); if (w->flags & WF_BORDER) { w->x--; w->y--; w->width += 2; w->height += 2; } } /*************************************************************************/ /* Free a window's data. */ void win_kill(Window *w) { WinAction **wa; if (w->curses_win) win_close(w); if (w->name) free(w->name); if (w->title) free(w->title); for (wa = w->drawlist; *wa; wa++) etree_free_list((*wa)->args, (*wa)->nargs); free(w->drawlist); free(w); } /*************************************************************************/ /* Find a window by name. */ Window *win_find(const char *name) { Window *win; for (win = winlist; win; win = win->next) { if (win->name && strcasecmp(win->name, name) == 0) return win; } return NULL; } /*************************************************************************/