/*
- * Copyright (C) 1998-2010 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 1998-2011 Andre Noll <maan@systemlinux.org>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#include <regex.h>
#include <signal.h>
#include <sys/types.h>
-#include <dirent.h>
#include <curses.h>
#include "gui.cmdline.h"
#include "list.h"
#include "sched.h"
#include "signal.h"
+#include "version.h"
/** define the array of error lists needed by para_gui */
INIT_GUI_ERRLISTS;
static struct win_data {
WINDOW *win;
- NCURSES_SIZE_T begx;
- NCURSES_SIZE_T begy;
- NCURSES_SIZE_T cols;
- NCURSES_SIZE_T lines;
+ size_t begx;
+ size_t begy;
+ size_t cols;
+ size_t lines;
} top, bot, sb, in, sep;
#define RINGBUFFER_SIZE 512
static pid_t cmd_pid;
static int command_pipe = -1;
-static int audiod_pipe = -1;
+static int stat_pipe = -1;
static struct gui_args_info conf;
enum {GETCH_MODE, COMMAND_MODE, EXTERNAL_MODE};
static void com_enlarge_top_win(void);
static void com_shrink_top_win(void);
static void com_version(void);
-static void com_quit(void);
+__noreturn static void com_quit(void);
static void com_refresh(void);
static void com_ll_incr(void);
static void com_ll_decr(void);
static void com_scroll_down(void);
static void com_page_up(void);
static void com_page_down(void);
+static void com_cancel_scrolling(void);
+static void com_scroll_top(void);
static struct gui_command command_list[] = {
{
.name = "page_down",
.description = "scroll down one page",
.handler = com_page_down
+ }, {
+ .key = "<home>",
+ .name = "scroll_top",
+ .description = "scroll to top of buffer",
+ .handler = com_scroll_top
+ }, {
+ .key = "<end>",
+ .name = "cancel_scroll",
+ .description = "deactivate scroll mode",
+ .handler = com_cancel_scrolling
}, {
.handler = NULL
}
};
-static int para_open_audiod_pipe(char *cmd)
-{
- int fds[3] = {0, 1, 0};
- pid_t pid;
- int ret = para_exec_cmdline_pid(&pid, cmd, fds);
- if (ret < 0)
- return ret;
- ret = mark_fd_nonblocking(fds[1]);
- if (ret > 0)
- return fds[1];
- close(fds[1]);
- return ret;
-}
-
static int find_cmd_byname(char *name)
{
int i;
sprintf(buf, "<ppage>");
return buf;
}
+ if (c == KEY_HOME) {
+ sprintf(buf, "<home>");
+ return buf;
+ }
+ if (c == KEY_END) {
+ sprintf(buf, "<end>");
+ return buf;
+ }
if (c < 256 && c > -128 && iscntrl((unsigned char) c)) {
if (c < 0)
c += 256;
str[len] = '\0';
num = 0;
}
- /* replace newlines by spaces */
+ /* replace control characters by spaces */
for (i = 0; i < len && str[i]; i++) {
- if (str[i] == '\n')
+ if (str[i] == '\n' || str[i] == '\r' || str[i] == '\f')
str[i] = ' ';
}
if (align == LEFT) {
rbe = ringbuffer_get(bot_win_rb, fvr);
if (!rbe)
return -1;
- /* first rbe might be only partially visible */
- offset = (*lines - bot.lines) * bot.cols;
len = strlen(rbe->msg);
- if (offset < 0 || len < offset)
- return -1;
+ if (*lines > bot.lines) {
+ /* first rbe is only partially visible */
+ offset = (*lines - bot.lines) * bot.cols;
+ assert(offset <= len);
+ } else
+ offset = 0;
wattron(bot.win, COLOR_PAIR(rbe->color));
waddstr(bot.win, rbe->msg + offset);
*lines = NUM_LINES(len - offset);
doupdate();
}
-static void check_geometry(void)
-{
- if (LINES < theme.lines_min || COLS < theme.cols_min)
- msg_n_exit(EXIT_FAILURE, "Error: Terminal (%dx%d) too small"
- " (need at least %dx%d)\n", COLS, LINES,
- theme.cols_min, theme.lines_min);
-}
-
/*
* Print stat item #i to curses window
*/
return 1;
}
-static int read_audiod_pipe(int fd)
+static int read_stat_pipe(fd_set *rfds)
{
static char *buf;
static int bufsize, loaded;
- int ret;
+ int ret, ret2;
+ size_t sz;
+ if (stat_pipe < 0)
+ return 0;
if (loaded >= bufsize) {
if (bufsize > 1000 * 1000) {
loaded = 0;
buf = para_realloc(buf, bufsize);
}
assert(loaded < bufsize);
- ret = read(fd, buf + loaded, bufsize - loaded);
- if (ret <= 0)
- return ret;
- loaded += ret;
- ret = for_each_stat_item(buf, loaded, update_item);
- if (ret < 0)
- return ret;
- if (ret > 0 && ret < loaded)
- memmove(buf, buf + loaded - ret, ret);
- loaded = ret;
+ ret = read_nonblock(stat_pipe, buf + loaded, bufsize - loaded,
+ rfds, &sz);
+ loaded += sz;
+ ret2 = for_each_stat_item(buf, loaded, update_item);
+ if (ret < 0 || ret2 < 0) {
+ loaded = 0;
+ return ret2 < 0? ret2 : ret;
+ }
+ sz = ret2; /* what is left */
+ if (sz > 0 && sz < loaded)
+ memmove(buf, buf + loaded - sz, sz);
+ loaded = sz;
return 1;
}
}
}
-static void init_colors(void)
+static void init_pair_or_die(short pair, short f, short b)
+{
+ if (init_pair(pair, f, b) == ERR)
+ msg_n_exit(EXIT_FAILURE, "fatal: init_pair() failed\n");
+}
+
+static void init_colors_or_die(void)
{
int i;
if (!has_colors())
- msg_n_exit(EXIT_FAILURE, "Error: No color term\n");
- start_color();
+ msg_n_exit(EXIT_FAILURE, "fatal: No color term\n");
+ if (start_color() == ERR)
+ msg_n_exit(EXIT_FAILURE, "fatal: failed to start colors\n");
FOR_EACH_STATUS_ITEM(i)
if (theme.data[i].len)
- init_pair(i + 1, theme.data[i].fg, theme.data[i].bg);
- init_pair(COLOR_STATUSBAR, theme.sb_fg, theme.sb_bg);
- init_pair(COLOR_COMMAND, theme.cmd_fg, theme.cmd_bg);
- init_pair(COLOR_OUTPUT, theme.output_fg, theme.output_bg);
- init_pair(COLOR_MSG, theme.msg_fg, theme.msg_bg);
- init_pair(COLOR_ERRMSG, theme.err_msg_fg, theme.err_msg_bg);
- init_pair(COLOR_WELCOME, theme.welcome_fg, theme.welcome_bg);
- init_pair(COLOR_SEPARATOR, theme.sep_fg, theme.sep_bg);
- init_pair(COLOR_TOP, theme.default_fg, theme.default_bg);
- init_pair(COLOR_BOT, theme.default_fg, theme.default_bg);
+ init_pair_or_die(i + 1, theme.data[i].fg,
+ theme.data[i].bg);
+ init_pair_or_die(COLOR_STATUSBAR, theme.sb_fg, theme.sb_bg);
+ init_pair_or_die(COLOR_COMMAND, theme.cmd_fg, theme.cmd_bg);
+ init_pair_or_die(COLOR_OUTPUT, theme.output_fg, theme.output_bg);
+ init_pair_or_die(COLOR_MSG, theme.msg_fg, theme.msg_bg);
+ init_pair_or_die(COLOR_ERRMSG, theme.err_msg_fg, theme.err_msg_bg);
+ init_pair_or_die(COLOR_WELCOME, theme.welcome_fg, theme.welcome_bg);
+ init_pair_or_die(COLOR_SEPARATOR, theme.sep_fg, theme.sep_bg);
+ init_pair_or_die(COLOR_TOP, theme.default_fg, theme.default_bg);
+ init_pair_or_die(COLOR_BOT, theme.default_fg, theme.default_bg);
}
-/*
- * (re-)initialize the curses library FIXME: Error checking
- */
+/* (Re-)initialize the curses library. */
static void init_curses(void)
{
curses_active = 1;
- if (top.win && refresh() == ERR) { /* refesh is really needed */
+ if (top.win && refresh() == ERR) /* refesh is really needed */
msg_n_exit(EXIT_FAILURE, "refresh() failed\n");
- }
- check_geometry();
+ if (LINES < theme.lines_min || COLS < theme.cols_min)
+ msg_n_exit(EXIT_FAILURE, "Error: Terminal (%dx%d) too small"
+ " (need at least %dx%d)\n", COLS, LINES,
+ theme.cols_min, theme.lines_min);
curs_set(0); /* make cursor invisible, ignore errors */
-// if (noraw() == ERR);
-// msg_n_exit(EXIT_FAILURE, "can not place terminal out of "
-// "raw mode\n");
- nonl(); /* tell curses not to do NL->CR/NL on output */
- noecho(); /* don't echo input */
- cbreak(); /* take input chars one at a time, no wait for \n */
- //reset_prog_mode();
- init_colors();
- clear();
+ nonl(); /* do not NL->CR/NL on output, always returns OK */
+ /* don't echo input */
+ if (noecho() == ERR)
+ msg_n_exit(EXIT_FAILURE, "fatal: noecho() failed\n");
+ /* take input chars one at a time, no wait for \n */
+ if (cbreak() == ERR)
+ msg_n_exit(EXIT_FAILURE, "fatal: cbreak() failed\n");
+ init_colors_or_die();
+ clear(); /* ignore non-fatal errors */
init_wins(theme.top_lines_default);
print_all_items();
- noecho(); /* don't echo input */
+ // noecho(); /* don't echo input */
}
-
static void check_sigchld(void)
{
int ret;
}
}
-static int open_audiod_pipe(void)
+static int open_stat_pipe(void)
{
static int init = 1;
+ int ret, fds[3] = {0, 1, 0};
+ pid_t pid;
if (init)
init = 0;
else
- sleep(1);
- return para_open_audiod_pipe(conf.stat_cmd_arg);
+ /*
+ * Sleep a bit to avoid a busy loop. As the call to sleep() may
+ * be interrupted by SIGCHLD, we simply wait until the call
+ * succeeds.
+ */
+ while (sleep(2))
+ ; /* nothing */
+ ret = para_exec_cmdline_pid(&pid, conf.stat_cmd_arg, fds);
+ if (ret < 0)
+ return ret;
+ ret = mark_fd_nonblocking(fds[1]);
+ if (ret >= 0)
+ return fds[1];
+ close(fds[1]);
+ return ret;
}
/*
{
fd_set rfds;
int ret;
- int max_fileno, cp_numread = 1;
+ int max_fileno;
char command_buf[4096] = "";
int cbo = 0; /* command buf offset */
struct timeval tv;
// ret = refresh_status();
FD_ZERO(&rfds);
max_fileno = 0;
- /* audiod pipe */
- if (audiod_pipe < 0)
- audiod_pipe = open_audiod_pipe();
- if (audiod_pipe >= 0)
- para_fd_set(audiod_pipe, &rfds, &max_fileno);
+ if (stat_pipe < 0)
+ stat_pipe = open_stat_pipe();
+ if (stat_pipe >= 0)
+ para_fd_set(stat_pipe, &rfds, &max_fileno);
/* signal pipe */
para_fd_set(signal_pipe, &rfds, &max_fileno);
/* command pipe only for COMMAND_MODE */
if (ret <= 0)
goto check_return; /* skip fd checks */
/* signals */
- if (FD_ISSET(signal_pipe, &rfds)) {
- int sig_nr = para_next_signal();
- if (sig_nr > 0)
- handle_signal(sig_nr);
- }
+ ret = para_next_signal(&rfds);
+ if (ret > 0)
+ handle_signal(ret);
/* read command pipe if ready */
- if (command_pipe >= 0 && mode == COMMAND_MODE &&
- FD_ISSET(command_pipe, &rfds)) {
- cp_numread = read(command_pipe, command_buf + cbo,
- sizeof(command_buf) - 1 - cbo);
- if (cp_numread >= 0)
- cbo += cp_numread;
- else {
- if (cp_numread < 0)
- PARA_ERROR_LOG("read error (%d)", cp_numread);
+ if (command_pipe >= 0 && mode == COMMAND_MODE) {
+ size_t sz;
+ ret = read_nonblock(command_pipe, command_buf + cbo,
+ sizeof(command_buf) - 1 - cbo, &rfds, &sz);
+ cbo += sz;
+ sz = cbo;
+ cbo = for_each_line(command_buf, cbo, &add_output_line, NULL);
+ if (sz != cbo)
+ wrefresh(bot.win);
+ if (ret < 0) {
+ PARA_NOTICE_LOG("closing command pipe: %s",
+ para_strerror(-ret));
close(command_pipe);
command_pipe = -1;
+ return 0;
}
}
- if (audiod_pipe >= 0 && FD_ISSET(audiod_pipe, &rfds))
- if (read_audiod_pipe(audiod_pipe) <= 0) {
- close(audiod_pipe);
- audiod_pipe = -1;
- clear_all_items();
- free(stat_content[SI_BASENAME]);
- stat_content[SI_BASENAME] =
- para_strdup("audiod not running!?");
- print_all_items();
- }
+ ret = read_stat_pipe(&rfds);
+ if (ret < 0) {
+ PARA_NOTICE_LOG("closing stat pipe: %s\n", para_strerror(-ret));
+ close(stat_pipe);
+ stat_pipe = -1;
+ clear_all_items();
+ free(stat_content[SI_BASENAME]);
+ stat_content[SI_BASENAME] =
+ para_strdup("stat command terminated!?");
+ print_all_items();
+ }
check_return:
switch (mode) {
case COMMAND_MODE:
- if (cp_numread <= 0 && !cbo) /* command complete */
- return 0;
- if (cbo)
- cbo = for_each_line(command_buf, cbo,
- &add_output_line, NULL);
- if (cp_numread <= 0)
- cbo = 0;
- wrefresh(bot.win);
ret = wgetch(top.win);
if (ret != ERR && ret != KEY_RESIZE) {
if (command_pipe) {
filled - scroll_position, ringbuffer_filled(bot_win_rb));
}
+static void com_scroll_top(void)
+{
+ int i = RINGBUFFER_SIZE - 1;
+ unsigned lines = 0;
+
+ while (i > 0 && !ringbuffer_get(bot_win_rb, i))
+ i--;
+ /* i is oldest entry */
+ for (; lines < bot.lines && i >= 0; i--) {
+ struct rb_entry *rbe = ringbuffer_get(bot_win_rb, i);
+ if (!rbe)
+ break;
+ lines += NUM_LINES(strlen(rbe->msg));
+ }
+ i++;
+ if (lines > 0 && scroll_position != i) {
+ scroll_position = i;
+ redraw_bot_win();
+ print_scroll_msg();
+ return;
+ }
+ print_in_bar(COLOR_ERRMSG, "top of buffer is shown\n");
+}
+
+static void com_cancel_scrolling(void)
+{
+
+ if (scroll_position == 0) {
+ print_in_bar(COLOR_ERRMSG, "bottom of buffer is shown\n");
+ return;
+ }
+ scroll_position = 0;
+ redraw_bot_win();
+}
+
static void com_page_down(void)
{
unsigned lines = 0;
.override = 1,
.initialize = 1,
.check_required = 0,
- .check_ambiguity = 0
+ .check_ambiguity = 0,
+ .print_errors = 0,
};
if (!cf) {
return;
}
PARA_INFO_LOG("rereading command line options and config file");
- gui_cmdline_parser(_argc, _argv, &conf);
- gui_cmdline_parser_config_file(cf, &conf, ¶ms);
+ gui_cmdline_parser_ext(_argc, _argv, &conf, ¶ms);
+ if (gui_cmdline_parser_config_file(cf, &conf, ¶ms) != 0) {
+ PARA_EMERG_LOG("errors in config file");
+ finish(EXIT_FAILURE);
+ }
PARA_NOTICE_LOG("config file reloaded");
if (check_key_map_args() < 0)
finish(EXIT_FAILURE);
command_list[num].handler();
}
free(tmp);
+ return;
}
/* not found, check internal key bindings */
for (i = 0; command_list[i].handler; i++) {
_argc = argc;
_argv = argv;
- if (gui_cmdline_parser(argc, argv, &conf)) {
- fprintf(stderr, "parse error while reading command line\n");
+ if (gui_cmdline_parser(argc, argv, &conf) != 0)
exit(EXIT_FAILURE);
- }
HANDLE_VERSION_FLAG("gui", conf);
- init_theme(0, &theme);
- top.lines = theme.top_lines_default;
- if (check_key_map_args() < 0) {
- fprintf(stderr, "invalid key map\n");
- exit(EXIT_FAILURE);
- }
cf = configfile_exists();
if (!cf && conf.config_file_given) {
fprintf(stderr, "can not read config file %s\n",
.override = 0,
.initialize = 0,
.check_required = 0,
- .check_ambiguity = 0
+ .check_ambiguity = 0,
+ .print_errors = 1,
};
- gui_cmdline_parser_config_file(cf, &conf, ¶ms);
+ if (gui_cmdline_parser_config_file(cf, &conf, ¶ms) != 0)
+ exit(EXIT_FAILURE);
}
loglevel = get_loglevel_by_name(conf.loglevel_arg);
if (check_key_map_args() < 0) {
- fprintf(stderr, "invalid key map in config file\n");
+ fprintf(stderr, "invalid key map\n");
exit(EXIT_FAILURE);
}
+ init_theme_or_die(conf.theme_arg, &theme);
+ top.lines = theme.top_lines_default;
setup_signal_handling();
bot_win_rb = ringbuffer_new(RINGBUFFER_SIZE);
initscr(); /* needed only once, always successful */