manual: Mention that clang is also a supported compiler.
[paraslash.git] / gui.c
diff --git a/gui.c b/gui.c
index 3b001a7da191a7c5f52a177ee73e0045c81ce05c..c8721565ae86502319be0ed2e6257454b876f841 100644 (file)
--- a/gui.c
+++ b/gui.c
@@ -9,7 +9,6 @@
 #include <regex.h>
 #include <signal.h>
 #include <sys/types.h>
-#include <dirent.h>
 #include <curses.h>
 
 #include "gui.cmdline.h"
@@ -22,6 +21,7 @@
 #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;
@@ -33,10 +33,10 @@ static int signal_pipe;
 
 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
@@ -109,6 +109,8 @@ static void com_scroll_up(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[] = {
        {
@@ -186,6 +188,16 @@ 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
        }
@@ -230,6 +242,14 @@ static char *km_keyname(int c)
                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;
@@ -380,11 +400,13 @@ static int draw_top_rbe(unsigned *lines)
        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);
@@ -635,14 +657,6 @@ static void init_wins(int top_lines)
        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
  */
@@ -747,53 +761,60 @@ static void clear_all_items(void)
        }
 }
 
-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;
@@ -1097,6 +1118,41 @@ static void print_scroll_msg(void)
                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;