aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-curses/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface-curses/interface.c')
-rw-r--r--src/interface-curses/interface.c344
1 files changed, 132 insertions, 212 deletions
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 6c8c812..8f41f2a 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -166,19 +166,91 @@ static gint teco_interface_blocking_getch(void);
#define COLOR_LCYAN COLOR_LIGHT(COLOR_CYAN)
#define COLOR_LWHITE COLOR_LIGHT(COLOR_WHITE)
+static struct {
+ /**
+ * Mapping of foreground and background curses color tuples
+ * (encoded into a pointer) to a color pair number.
+ */
+ GHashTable *pair_table;
+
+ /**
+ * Mapping of the first 16 curses color codes (that may or may not
+ * correspond with the standard terminal color codes) to
+ * Scintilla-compatible RGB values (red is LSB) to initialize after
+ * Curses startup.
+ * Negative values mean no color redefinition (keep the original
+ * palette entry).
+ */
+ gint32 color_table[16];
+
+ /**
+ * Mapping of the first 16 curses color codes to their
+ * original values for restoring them on shutdown.
+ * Unfortunately, this may not be supported on all
+ * curses ports, so this array may be unused.
+ */
+ struct {
+ gshort r, g, b;
+ } orig_color_table[16];
+
+ int stdin_orig, stdout_orig, stderr_orig;
+ SCREEN *screen;
+ FILE *screen_tty;
+
+ WINDOW *info_window;
+ enum {
+ TECO_INFO_TYPE_BUFFER = 0,
+ TECO_INFO_TYPE_QREG
+ } info_type;
+ teco_string_t info_current;
+ gboolean info_dirty;
+
+ WINDOW *msg_window;
+
+ /**
+ * Pad used exclusively for wgetch() as it will not
+ * result in unwanted wrefresh().
+ */
+ WINDOW *input_pad;
+ GQueue *input_queue;
+
+ teco_curses_info_popup_t popup;
+ gsize popup_prefix_len;
+
+ /**
+ * GError "thrown" by teco_interface_event_loop_iter().
+ * Having this in a variable avoids problems with EMScripten.
+ */
+ GError *event_loop_error;
+} teco_interface;
+
/**
- * Returns the curses `COLOR_PAIR` for the given curses foreground and background `COLOR`s.
- * This is used simply to enumerate every possible color combination.
- * Note: only 256 combinations are possible due to curses portability.
+ * Returns the curses color pair for the given curses foreground and background colors.
+ * Initializes a new pair if necessary.
+ *
+ * Scinterm no longer initializes all color pairs for all combinations of
+ * the builtin foreground and background colors.
+ * Since curses guarantees only 256 color pairs, we cannot do that either.
+ * Instead we allocate color pairs beginnig at 128 on demand
+ * (similar to what Scinterm does).
*
- * @param fg The curses foreground `COLOR`.
- * @param bg The curses background `COLOR`.
- * @return number for defining a curses `COLOR_PAIR`.
+ * @param fg curses foreground color
+ * @param bg curses background color
+ * @return curses color pair number
*/
-static inline gshort
+static gshort
teco_color_pair(gshort fg, gshort bg)
{
- return bg * (COLORS < 16 ? 8 : 16) + fg + 1;
+ static gshort last_pair = 127;
+
+ G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint));
+ gpointer key = GUINT_TO_POINTER(((guint)fg << 16) | bg);
+ gpointer value = g_hash_table_lookup(teco_interface.pair_table, key);
+ if (G_LIKELY(value != NULL))
+ return GPOINTER_TO_UINT(value);
+ init_pair(++last_pair, fg, bg);
+ g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(last_pair));
+ return last_pair;
}
/**
@@ -336,61 +408,6 @@ teco_view_free(teco_view_t *ctx)
scintilla_delete(ctx);
}
-static struct {
- /**
- * Mapping of the first 16 curses color codes (that may or may not
- * correspond with the standard terminal color codes) to
- * Scintilla-compatible RGB values (red is LSB) to initialize after
- * Curses startup.
- * Negative values mean no color redefinition (keep the original
- * palette entry).
- */
- gint32 color_table[16];
-
- /**
- * Mapping of the first 16 curses color codes to their
- * original values for restoring them on shutdown.
- * Unfortunately, this may not be supported on all
- * curses ports, so this array may be unused.
- */
- struct {
- gshort r, g, b;
- } orig_color_table[16];
-
- int stdin_orig, stdout_orig, stderr_orig;
- SCREEN *screen;
- FILE *screen_tty;
-
- WINDOW *info_window;
- enum {
- TECO_INFO_TYPE_BUFFER = 0,
- TECO_INFO_TYPE_QREG
- } info_type;
- teco_string_t info_current;
- gboolean info_dirty;
-
- WINDOW *msg_window;
-
- WINDOW *cmdline_window, *cmdline_pad;
- guint cmdline_len, cmdline_rubout_len;
-
- /**
- * Pad used exclusively for wgetch() as it will not
- * result in unwanted wrefresh().
- */
- WINDOW *input_pad;
- GQueue *input_queue;
-
- teco_curses_info_popup_t popup;
- gsize popup_prefix_len;
-
- /**
- * GError "thrown" by teco_interface_event_loop_iter().
- * Having this in a variable avoids problems with EMScripten.
- */
- GError *event_loop_error;
-} teco_interface;
-
static void teco_interface_init_color_safe(guint color, guint32 rgb);
static void teco_interface_restore_colors(void);
@@ -404,7 +421,6 @@ static void teco_interface_resize_all_windows(void);
static void teco_interface_set_window_title(const gchar *title);
static void teco_interface_draw_info(void);
-static void teco_interface_draw_cmdline(void);
void
teco_interface_init(void)
@@ -424,6 +440,15 @@ teco_interface_init(void)
*/
teco_string_init(&teco_interface.info_current, PACKAGE_NAME, strlen(PACKAGE_NAME));
+ teco_cmdline_init();
+ /*
+ * The default INDIC_STRIKE wouldn't be visible.
+ * Instead we use INDIC_STRAIGHTBOX, which will be rendered as underlined if
+ * the alpha is 0.
+ */
+ teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_STRAIGHTBOX);
+ teco_cmdline_ssm(SCI_INDICSETALPHA, INDICATOR_RUBBEDOUT, 0);
+
/*
* On all platforms except Curses/XTerm, it's
* safe to initialize the clipboards now.
@@ -561,7 +586,7 @@ teco_interface_init_color(guint color, guint32 rgb)
((color & 0x1) << 2) | ((color & 0x4) >> 2);
#endif
- if (teco_interface.cmdline_window) {
+ if (teco_interface.input_pad) {
/* interactive mode */
if (!can_change_color())
return;
@@ -717,6 +742,9 @@ teco_interface_init_interactive(GError **error)
teco_interface_init_screen();
+ teco_interface.pair_table = g_hash_table_new(g_direct_hash, g_direct_equal);
+ start_color();
+
/*
* On UNIX terminals, the escape key is usually
* delivered as the escape character even though function
@@ -768,8 +796,12 @@ teco_interface_init_interactive(GError **error)
leaveok(stdscr, TRUE);
teco_interface.info_window = newwin(1, 0, 0, 0);
- teco_interface.msg_window = newwin(1, 0, LINES - 2, 0);
- teco_interface.cmdline_window = newwin(0, 0, LINES - 1, 0);
+ teco_interface.msg_window = newwin(1, 0, LINES - teco_cmdline.height - 1, 0);
+
+ WINDOW *cmdline_win = teco_view_get_window(teco_cmdline.view);
+ wresize(cmdline_win, teco_cmdline.height, COLS);
+ mvwin(cmdline_win, LINES - teco_cmdline.height, 0);
+ teco_cmdline_resized(COLS);
teco_interface.input_pad = newpad(1, 1);
/*
@@ -861,39 +893,37 @@ teco_interface_restore_batch(void)
#endif
/*
- * cmdline_window determines whether we're in batch mode.
+ * input_pad determines whether we're in batch mode.
*/
- if (teco_interface.cmdline_window) {
- delwin(teco_interface.cmdline_window);
- teco_interface.cmdline_window = NULL;
+ if (teco_interface.input_pad) {
+ delwin(teco_interface.input_pad);
+ teco_interface.input_pad = NULL;
}
}
static void
teco_interface_resize_all_windows(void)
{
- int lines, cols; /* screen dimensions */
-
- getmaxyx(stdscr, lines, cols);
-
- wresize(teco_interface.info_window, 1, cols);
+ wresize(teco_interface.info_window, 1, COLS);
wresize(teco_view_get_window(teco_interface_current_view),
- lines - 3, cols);
- wresize(teco_interface.msg_window, 1, cols);
- mvwin(teco_interface.msg_window, lines - 2, 0);
- wresize(teco_interface.cmdline_window, 1, cols);
- mvwin(teco_interface.cmdline_window, lines - 1, 0);
+ LINES - 2 - teco_cmdline.height, COLS);
+ wresize(teco_interface.msg_window, 1, COLS);
+ mvwin(teco_interface.msg_window, LINES - 1 - teco_cmdline.height, 0);
+
+ WINDOW *cmdline_win = teco_view_get_window(teco_cmdline.view);
+ wresize(cmdline_win, teco_cmdline.height, COLS);
+ mvwin(cmdline_win, LINES - teco_cmdline.height, 0);
+ teco_cmdline_resized(COLS);
teco_interface_draw_info();
teco_interface_msg_clear(); /* FIXME: use saved message */
teco_interface_popup_clear();
- teco_interface_draw_cmdline();
}
void
teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
{
- if (!teco_interface.cmdline_window) { /* batch mode */
+ if (!teco_interface.input_pad) { /* batch mode */
teco_interface_stdio_msg(type, str, len);
return;
}
@@ -936,7 +966,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
void
teco_interface_msg_clear(void)
{
- if (!teco_interface.cmdline_window) /* batch mode */
+ if (!teco_interface.input_pad) /* batch mode */
return;
short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
@@ -950,7 +980,7 @@ teco_interface_msg_clear(void)
teco_int_t
teco_interface_getch(gboolean widechar)
{
- if (!teco_interface.cmdline_window) /* batch mode */
+ if (!teco_interface.input_pad) /* batch mode */
return teco_interface_stdio_getch(widechar);
teco_interface_refresh(FALSE);
@@ -996,7 +1026,7 @@ teco_interface_show_view(teco_view_t *view)
{
teco_interface_current_view = view;
- if (!teco_interface.cmdline_window) /* batch mode */
+ if (!teco_interface.input_pad) /* batch mode */
return;
WINDOW *current_view_win = teco_view_get_window(teco_interface_current_view);
@@ -1005,9 +1035,7 @@ teco_interface_show_view(teco_view_t *view)
* screen size might have changed since
* this view's WINDOW was last active
*/
- int lines, cols; /* screen dimensions */
- getmaxyx(stdscr, lines, cols);
- wresize(current_view_win, lines - 3, cols);
+ wresize(current_view_win, LINES - 2 - teco_cmdline.height, COLS);
/* Set up window position: never changes */
mvwin(current_view_win, 1, 0);
}
@@ -1194,114 +1222,6 @@ teco_interface_info_update_buffer(const teco_buffer_t *buffer)
/* NOTE: drawn in teco_interface_event_loop_iter() */
}
-void
-teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
-{
- /*
- * Especially important on PDCurses, which can crash
- * in newpad() when run with --fake-cmdline.
- */
- if (!teco_interface.cmdline_window) /* batch mode */
- return;
-
- /*
- * Replace entire pre-formatted command-line.
- * We don't know if it is similar to the last one,
- * so resizing makes no sense.
- * We approximate the size of the new formatted command-line,
- * wasting a few bytes for control characters and
- * multi-byte Unicode sequences.
- */
- if (teco_interface.cmdline_pad)
- delwin(teco_interface.cmdline_pad);
-
- int max_cols = 1;
- for (guint i = 0; i < cmdline->str.len; i++)
- max_cols += TECO_IS_CTL(cmdline->str.data[i]) ? 3 : 1;
- teco_interface.cmdline_pad = newpad(1, max_cols);
-
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- wattrset(teco_interface.cmdline_pad, teco_color_attr(fg, bg));
-
- /* format effective command line */
- teco_interface.cmdline_len =
- teco_curses_format_str(teco_interface.cmdline_pad,
- cmdline->str.data, cmdline->effective_len, -1);
-
- /*
- * A_BOLD should result in either a bold font or a brighter
- * color both on 8 and 16 color terminals.
- * This is not quite color-scheme-agnostic, but works
- * with both the `terminal` and `solarized` themes.
- * This problem will be gone once we use a Scintilla view
- * as command line, since we can then define a style
- * for rubbed out parts of the command line which will
- * be user-configurable.
- * The attributes, supported by the terminal can theoretically
- * be queried with term_attrs().
- */
- wattron(teco_interface.cmdline_pad, A_UNDERLINE | A_BOLD);
-
- /*
- * Format rubbed-out command line.
- * NOTE: This formatting will never be truncated since we're
- * writing into the pad which is large enough.
- */
- teco_interface.cmdline_rubout_len =
- teco_curses_format_str(teco_interface.cmdline_pad, cmdline->str.data + cmdline->effective_len,
- cmdline->str.len - cmdline->effective_len, -1);
-
- /*
- * Highlight cursor after effective command line
- * FIXME: This should use SCI_GETCARETFORE().
- */
- attr_t attr = A_NORMAL;
- short pair = 0;
- if (teco_interface.cmdline_rubout_len) {
- wmove(teco_interface.cmdline_pad, 0, teco_interface.cmdline_len);
- wattr_get(teco_interface.cmdline_pad, &attr, &pair, NULL);
- wchgat(teco_interface.cmdline_pad, 1,
- (attr & (A_UNDERLINE | A_REVERSE)) ^ A_REVERSE, pair, NULL);
- } else {
- teco_interface.cmdline_len++;
- wattr_get(teco_interface.cmdline_pad, &attr, &pair, NULL);
- wattr_set(teco_interface.cmdline_pad, (attr & ~(A_UNDERLINE | A_BOLD)) ^ A_REVERSE, pair, NULL);
- waddch(teco_interface.cmdline_pad, ' ');
- }
-
- teco_interface_draw_cmdline();
-}
-
-static void
-teco_interface_draw_cmdline(void)
-{
- /* total width available for command line */
- guint total_width = getmaxx(teco_interface.cmdline_window) - 1;
-
- /* beginning of command line to show */
- guint disp_offset = teco_interface.cmdline_len -
- MIN(teco_interface.cmdline_len,
- total_width/2 + teco_interface.cmdline_len % MAX(total_width/2, 1));
- /*
- * length of command line to show
- *
- * NOTE: we do not use getmaxx(cmdline_pad) here since it may be
- * larger than the text the pad contains.
- */
- guint disp_len = MIN(total_width, teco_interface.cmdline_len +
- teco_interface.cmdline_rubout_len - disp_offset);
-
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
-
- wattrset(teco_interface.cmdline_window, teco_color_attr(fg, bg));
- mvwaddch(teco_interface.cmdline_window, 0, 0, '*' | A_BOLD);
- teco_curses_clrtobot(teco_interface.cmdline_window);
- copywin(teco_interface.cmdline_pad, teco_interface.cmdline_window,
- 0, disp_offset, 0, 1, 0, disp_len, FALSE);
-}
-
#if PDCURSES
/*
@@ -1771,7 +1691,7 @@ void
teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize name_len,
gboolean highlight)
{
- if (teco_interface.cmdline_window)
+ if (teco_interface.input_pad)
/* interactive mode */
teco_curses_info_popup_add(&teco_interface.popup, type, name, name_len, highlight);
}
@@ -1779,7 +1699,7 @@ teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize
void
teco_interface_popup_show(gsize prefix_len)
{
- if (!teco_interface.cmdline_window)
+ if (!teco_interface.input_pad)
/* batch mode */
return;
@@ -1793,7 +1713,7 @@ teco_interface_popup_show(gsize prefix_len)
void
teco_interface_popup_scroll(void)
{
- if (!teco_interface.cmdline_window)
+ if (!teco_interface.input_pad)
/* batch mode */
return;
@@ -1880,15 +1800,13 @@ teco_interface_is_interrupted(void)
void
teco_interface_refresh(gboolean force)
{
- if (!teco_interface.cmdline_window)
+ if (!teco_interface.input_pad)
/* batch mode */
return;
#ifdef NETBSD_CURSES
/* works around crashes in doupdate() */
- gint y, x;
- getmaxyx(stdscr, y, x);
- if (G_UNLIKELY(x <= 1 || y <= 1))
+ if (G_UNLIKELY(COLS <= 1 || LINES <= 1))
return;
#endif
@@ -1905,7 +1823,7 @@ teco_interface_refresh(gboolean force)
wnoutrefresh(teco_interface.info_window);
teco_view_noutrefresh(teco_interface_current_view);
wnoutrefresh(teco_interface.msg_window);
- wnoutrefresh(teco_interface.cmdline_window);
+ teco_view_noutrefresh(teco_cmdline.view);
teco_curses_info_popup_noutrefresh(&teco_interface.popup);
doupdate();
}
@@ -1952,7 +1870,7 @@ teco_interface_process_mevent(MEVENT *event, GError **error)
teco_interface_popup_clear();
teco_interface_msg_clear();
- teco_interface_cmdline_update(&teco_cmdline);
+ teco_cmdline_update();
}
return TRUE;
@@ -2136,6 +2054,7 @@ teco_interface_event_loop_iter(void)
const teco_view_t *last_view = teco_interface_current_view;
sptr_t last_vpos = teco_interface_ssm(SCI_GETFIRSTVISIBLELINE, 0, 0);
+ guint last_cmdline_height = teco_cmdline.height;
switch (key) {
case ERR:
@@ -2262,6 +2181,10 @@ teco_interface_event_loop_iter(void)
}
}
+ if (G_UNLIKELY(teco_cmdline.height != last_cmdline_height))
+ /* command line height was changed with h,5EJ */
+ teco_interface_resize_all_windows();
+
/*
* Scintilla has been patched to avoid any automatic scrolling since that
* has been benchmarked to be a very costly operation.
@@ -2286,8 +2209,6 @@ teco_interface_event_loop(GError **error)
if (!teco_interface_init_interactive(error))
return FALSE;
- static const teco_cmdline_t empty_cmdline; // FIXME
- teco_interface_cmdline_update(&empty_cmdline);
teco_interface_msg_clear();
teco_interface_ssm(SCI_SCROLLCARET, 0, 0);
/*
@@ -2342,10 +2263,6 @@ teco_interface_cleanup(void)
teco_string_clear(&teco_interface.info_current);
if (teco_interface.input_queue)
g_queue_free(teco_interface.input_queue);
- if (teco_interface.cmdline_window)
- delwin(teco_interface.cmdline_window);
- if (teco_interface.cmdline_pad)
- delwin(teco_interface.cmdline_pad);
if (teco_interface.msg_window)
delwin(teco_interface.msg_window);
if (teco_interface.input_pad)
@@ -2370,4 +2287,7 @@ teco_interface_cleanup(void)
close(teco_interface.stderr_orig);
if (teco_interface.stdout_orig >= 0)
close(teco_interface.stdout_orig);
+
+ if (teco_interface.pair_table)
+ g_hash_table_destroy(teco_interface.pair_table);
}