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.c824
1 files changed, 455 insertions, 369 deletions
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index a71ca20..b1c806f 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2025 Robin Haberkorn
+ * Copyright (C) 2012-2026 Robin Haberkorn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +22,6 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
-#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
@@ -108,6 +107,8 @@
#define CURSES_TTY
#endif
+//#define DEBUG
+
#ifdef G_OS_WIN32
/**
@@ -135,7 +136,7 @@ teco_console_ctrl_handler(DWORD type)
static gint teco_xterm_version(void) G_GNUC_UNUSED;
-#define UNNAMED_FILE "(Unnamed)"
+static gint teco_interface_blocking_getch(void);
/**
* Get bright variant of one of the 8 standard
@@ -163,19 +164,98 @@ static gint teco_xterm_version(void) G_GNUC_UNUSED;
#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;
+ /* current document's name or empty string for "(Unnamed)" buffer */
+ teco_string_t info_current;
+ gboolean info_dirty;
+
+ /** timer to track the recovery interval */
+ GTimer *recovery_timer;
+
+ 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`.
+ * @note Scinterm now also has scintilla_set_color_offsets(),
+ * so we could use the lower 127 color pairs as well.
+ *
+ * @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;
}
/**
@@ -333,61 +413,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 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);
@@ -401,7 +426,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)
@@ -411,15 +435,16 @@ teco_interface_init(void)
for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++)
teco_interface.orig_color_table[i].r = -1;
- teco_interface.stdout_orig = teco_interface.stderr_orig = -1;
+ teco_interface.stdin_orig = teco_interface.stdout_orig = teco_interface.stderr_orig = -1;
teco_curses_info_popup_init(&teco_interface.popup);
+ teco_cmdline_init();
/*
- * Make sure we have a string for the info line
- * even if teco_interface_info_update() is never called.
+ * The default INDIC_STRIKE wouldn't be visible.
+ * Instead we use INDIC_SQUIGGLE, which is rendered as A_UNDERLINE.
*/
- teco_string_init(&teco_interface.info_current, PACKAGE_NAME, strlen(PACKAGE_NAME));
+ teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_SQUIGGLE);
/*
* On all platforms except Curses/XTerm, it's
@@ -558,7 +583,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;
@@ -580,22 +605,46 @@ teco_interface_init_color(guint color, guint32 rgb)
static void
teco_interface_init_screen(void)
{
- teco_interface.screen_tty = g_fopen("/dev/tty", "r+");
+ teco_interface.screen_tty = g_fopen("/dev/tty", "a");
/* should never fail */
g_assert(teco_interface.screen_tty != NULL);
- teco_interface.screen = newterm(NULL, teco_interface.screen_tty, teco_interface.screen_tty);
+ /*
+ * At least on NetBSD we loose keypresses when passing in a
+ * handle for /dev/tty.
+ * We therefore redirect stdin in interactive mode.
+ * This works always if stdin was already redirected or not (isatty(0))
+ * since we are guaranteed not to read from stdin outside of curses.
+ * When returning to batch mode, we can restore the original stdin.
+ */
+ teco_interface.stdin_orig = dup(0);
+ g_assert(teco_interface.stdin_orig >= 0);
+ G_GNUC_UNUSED FILE *stdin_new = g_freopen("/dev/tty", "r", stdin);
+ g_assert(stdin_new != NULL);
+
+ teco_interface.screen = newterm(NULL, teco_interface.screen_tty, stdin);
if (G_UNLIKELY(!teco_interface.screen)) {
g_fprintf(stderr, "Error initializing interactive mode. "
"$TERM may be incorrect.\n");
exit(EXIT_FAILURE);
}
+ /* initscr() does that in ncurses */
+ def_prog_mode();
+
/*
* If stdout or stderr would go to the terminal,
* redirect it. Otherwise, they are already redirected
* (e.g. to a file) and writing to them does not
* interrupt terminal interaction.
+ *
+ * This cannot of course preserve all messages written to stdout/stderr.
+ * Only those messages written before flushing will be preserved and
+ * be visible after program termination since they are still in a user-
+ * space stdio-buffer.
+ * All messages could only be preserved if we redirected to a temporary
+ * file and replayed it afterwards. It wouldn't preserve the order of
+ * stdout vs. stderr messages.
*/
if (isatty(1)) {
teco_interface.stdout_orig = dup(1);
@@ -690,6 +739,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
@@ -716,12 +768,8 @@ teco_interface_init_interactive(GError **error)
* Disables click-detection.
* If we'd want to discern PRESSED and CLICKED events,
* we'd have to emulate the same feature on GTK.
- *
- * On PDCurses/Wincon we currently rely on click detection
- * since it does not report BUTTONX_RELEASED unless also
- * moving the mouse cursor.
*/
-#if NCURSES_MOUSE_VERSION >= 2 && !defined(PDCURSES_WINCON)
+#if NCURSES_MOUSE_VERSION >= 2
mouseinterval(0);
#endif
@@ -745,8 +793,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);
/*
@@ -819,10 +871,14 @@ teco_interface_restore_batch(void)
teco_interface_restore_colors();
/*
- * Restore stdout and stderr, so output goes to
+ * Restore stdin, stdout and stderr, so output goes to
* the terminal again in case we "muted" them.
*/
#ifdef CURSES_TTY
+ if (teco_interface.stdin_orig >= 0) {
+ G_GNUC_UNUSED int fd = dup2(teco_interface.stdin_orig, 0);
+ g_assert(fd == 0);
+ }
if (teco_interface.stdout_orig >= 0) {
G_GNUC_UNUSED int fd = dup2(teco_interface.stdout_orig, 1);
g_assert(fd == 1);
@@ -834,40 +890,38 @@ 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_vmsg(teco_msg_t type, const gchar *fmt, va_list ap)
+teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
{
- if (!teco_interface.cmdline_window) { /* batch mode */
- teco_interface_stdio_vmsg(type, fmt, ap);
+ if (!teco_interface.input_pad) { /* batch mode */
+ teco_interface_stdio_msg(type, str, len);
return;
}
@@ -876,10 +930,7 @@ teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap)
* even in interactive mode.
*/
#if defined(PDCURSES_GUI) || defined(CURSES_TTY) || defined(NCURSES_WIN32)
- va_list aq;
- va_copy(aq, ap);
- teco_interface_stdio_vmsg(type, fmt, aq);
- va_end(aq);
+ teco_interface_stdio_msg(type, str, len);
#endif
short fg, bg;
@@ -903,27 +954,68 @@ teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap)
break;
}
- /*
- * NOTE: This is safe since we don't have to cancel out any A_REVERSE,
- * that could be set in the background attributes.
- */
wmove(teco_interface.msg_window, 0, 0);
- wbkgdset(teco_interface.msg_window, teco_color_attr(fg, bg));
- vw_printw(teco_interface.msg_window, fmt, ap);
- wclrtoeol(teco_interface.msg_window);
+ wattrset(teco_interface.msg_window, teco_color_attr(fg, bg));
+ teco_curses_format_str(teco_interface.msg_window, str, len, -1);
+ teco_curses_clrtobot(teco_interface.msg_window);
}
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));
short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
- wbkgdset(teco_interface.msg_window, teco_color_attr(fg, bg));
- werase(teco_interface.msg_window);
+ wmove(teco_interface.msg_window, 0, 0);
+ wattrset(teco_interface.msg_window, teco_color_attr(fg, bg));
+ teco_curses_clrtobot(teco_interface.msg_window);
+}
+
+teco_int_t
+teco_interface_getch(gboolean widechar)
+{
+ if (!teco_interface.input_pad) /* batch mode */
+ return teco_interface_stdio_getch(widechar);
+
+ teco_interface_refresh(FALSE);
+
+ /*
+ * Signal that we accept input by drawing a real cursor in the message bar.
+ */
+ wmove(teco_interface.msg_window, 0, 0);
+ curs_set(1);
+ wrefresh(teco_interface.msg_window);
+
+ gchar buf[4];
+ gint i = 0;
+ gint32 cp;
+
+ do {
+ cp = teco_interface_blocking_getch();
+ if (cp == TECO_CTL_KEY('C'))
+ teco_interrupted = TRUE;
+ if (cp == TECO_CTL_KEY('C') || cp == TECO_CTL_KEY('D')) {
+ cp = -1;
+ break;
+ }
+ if (cp < 0 || cp > 0xFF)
+ continue;
+
+ if (!widechar || !cp)
+ break;
+
+ /* doesn't work as expected when passed a null byte */
+ buf[i] = cp;
+ cp = g_utf8_get_char_validated(buf, ++i);
+ if (i >= sizeof(buf) || cp != -2)
+ i = 0;
+ } while (cp < 0);
+
+ curs_set(0);
+ return cp;
}
void
@@ -931,7 +1023,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);
@@ -940,9 +1032,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);
}
@@ -972,40 +1062,64 @@ teco_interface_set_window_title(const gchar *title)
#elif defined(CURSES_TTY) && defined(HAVE_TIGETSTR)
+/*
+ * Many Modern terminal emulators map the window title to
+ * the historic status line.
+ * This feature is not standardized in ncurses,
+ * so we query the terminfo database.
+ * This feature may make problems with terminal emulators
+ * that do support a status line but do not map them
+ * to the window title.
+ * Some emulators (like xterm, rxvt and many pseudo-xterms)
+ * support setting the window title via custom escape
+ * sequences and via the status line but their
+ * terminfo entry does not say so.
+ * Real XTerm can also save and restore window titles but
+ * there is not even a terminfo capability defined for this.
+ * Currently, SciTECO just leaves the title set after we quit.
+ *
+ * TODO: Once we support customizing the UI,
+ * there could be a special status line that's sent
+ * to the terminal that may be set up in the profile
+ * depending on $TERM.
+ */
static void
teco_interface_set_window_title(const gchar *title)
{
- if (!has_status_line || !to_status_line || !from_status_line)
+ static const gchar *term = NULL;
+ static const gchar *title_start = NULL;
+ static const gchar *title_end = NULL;
+
+ if (G_UNLIKELY(!term)) {
+ term = g_getenv("TERM");
+
+ title_start = to_status_line;
+ title_end = from_status_line;
+
+ if ((!title_start || !title_end) && term &&
+ (g_str_has_prefix(term, "xterm") || g_str_has_prefix(term, "rxvt"))) {
+ /*
+ * Just assume that any whitelisted $TERM has the OSC-0
+ * escape sequence or at least ignores it.
+ * This might also set the window's icon, but it's more widely
+ * used than OSC-2.
+ */
+ title_start = "\e]0;";
+ title_end = "\a";
+ }
+ }
+
+ if (!title_start || !title_end)
return;
/*
- * Modern terminal emulators map the window title to
- * the historic status line.
- * This feature is not standardized in ncurses,
- * so we query the terminfo database.
- * This feature may make problems with terminal emulators
- * that do support a status line but do not map them
- * to the window title. Some emulators (like xterm)
- * support setting the window title via custom escape
- * sequences and via the status line but their
- * terminfo entry does not say so. (xterm can also
- * save and restore window titles but there is not
- * even a terminfo capability defined for this.)
- * Taken the different emulator incompatibilites
- * it may be best to make this configurable.
- * Once we support configurable status lines,
- * there could be a special status line that's sent
- * to the terminal that may be set up in the profile
- * depending on $TERM.
- *
* NOTE: The terminfo manpage advises us to use putp()
* but on ncurses/UNIX (where terminfo is available),
* we do not let curses write to stdout.
- * NOTE: This leaves the title set after we quit.
*/
- fputs(to_status_line, teco_interface.screen_tty);
+ fputs(title_start, teco_interface.screen_tty);
fputs(title, teco_interface.screen_tty);
- fputs(from_status_line, teco_interface.screen_tty);
+ fputs(title_end, teco_interface.screen_tty);
fflush(teco_interface.screen_tty);
}
@@ -1040,26 +1154,30 @@ teco_interface_draw_info(void)
waddstr(teco_interface.info_window, PACKAGE_NAME " ");
+ teco_string_t info_current = teco_interface.info_current;
+ if (!info_current.len) {
+ info_current.data = TECO_UNNAMED_FILE;
+ info_current.len = strlen(info_current.data);
+ }
+
switch (teco_interface.info_type) {
case TECO_INFO_TYPE_QREG:
info_type_str = PACKAGE_NAME " - <QRegister> ";
teco_curses_add_wc(teco_interface.info_window,
teco_ed & TECO_ED_ICONS ? TECO_CURSES_ICONS_QREG : '-');
waddstr(teco_interface.info_window, " <QRegister> ");
- /* same formatting as in command lines */
teco_curses_format_str(teco_interface.info_window,
- teco_interface.info_current.data,
- teco_interface.info_current.len, -1);
+ info_current.data, info_current.len, -1);
break;
case TECO_INFO_TYPE_BUFFER:
info_type_str = PACKAGE_NAME " - <Buffer> ";
- g_assert(!teco_string_contains(&teco_interface.info_current, '\0'));
+ g_assert(!teco_string_contains(info_current, '\0'));
+ /* "(Unnamed)" buffer has to be looked up as "" */
teco_curses_add_wc(teco_interface.info_window,
teco_ed & TECO_ED_ICONS ? teco_curses_icons_lookup_file(teco_interface.info_current.data) : '-');
waddstr(teco_interface.info_window, " <Buffer> ");
- teco_curses_format_filename(teco_interface.info_window,
- teco_interface.info_current.data,
+ teco_curses_format_filename(teco_interface.info_window, info_current.data,
getmaxx(teco_interface.info_window) -
getcurx(teco_interface.info_window) - 1);
waddch(teco_interface.info_window, teco_interface.info_dirty ? '*' : ' ');
@@ -1075,8 +1193,7 @@ teco_interface_draw_info(void)
* Make sure the title will consist only of printable characters.
*/
g_autofree gchar *info_current_printable;
- info_current_printable = teco_string_echo(teco_interface.info_current.data,
- teco_interface.info_current.len);
+ info_current_printable = teco_string_echo(info_current.data, info_current.len);
g_autofree gchar *title = g_strconcat(info_type_str, info_current_printable,
teco_interface.info_dirty ? "*" : "", NULL);
teco_interface_set_window_title(title);
@@ -1096,123 +1213,14 @@ teco_interface_info_update_qreg(const teco_qreg_t *reg)
void
teco_interface_info_update_buffer(const teco_buffer_t *buffer)
{
- const gchar *filename = buffer->filename ? : UNNAMED_FILE;
-
teco_string_clear(&teco_interface.info_current);
- teco_string_init(&teco_interface.info_current, filename, strlen(filename));
- teco_interface.info_dirty = buffer->dirty;
+ teco_string_init(&teco_interface.info_current, buffer->filename,
+ buffer->filename ? strlen(buffer->filename) : 0);
+ teco_interface.info_dirty = buffer->state > TECO_BUFFER_CLEAN;
teco_interface.info_type = TECO_INFO_TYPE_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
/*
@@ -1243,7 +1251,7 @@ teco_interface_init_clipboard(void)
if (rc == PDC_CLIP_SUCCESS)
PDC_freeclipboard(contents);
- teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
+ teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
}
gboolean
@@ -1307,8 +1315,10 @@ get_selection_by_name(const gchar *name)
* (everything gets passed down), but currently we
* only register the three standard registers
* "~", "~P", "~S" and "~C".
+ * (We are never called with "~", though.)
*/
- return g_ascii_tolower(*name) ? : 'c';
+ g_assert(*name != '\0');
+ return g_ascii_tolower(*name);
}
/*
@@ -1507,10 +1517,10 @@ teco_interface_init_clipboard(void)
!teco_qreg_table_find(&teco_qreg_table_globals, "$SCITECO_CLIPBOARD_GET", 22)))
return;
- teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
- teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new("P"));
- teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new("S"));
- teco_qreg_table_insert(&teco_qreg_table_globals, teco_qreg_clipboard_new("C"));
+ teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new(""));
+ teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new("P"));
+ teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new("S"));
+ teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new("C"));
}
gboolean
@@ -1520,7 +1530,7 @@ teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len,
if (teco_interface_osc52_is_enabled())
return teco_interface_osc52_set_clipboard(name, str, str_len, error);
- static const gchar *reg_name = "$SCITECO_CLIPBOARD_SET";
+ static const gchar reg_name[] = "$SCITECO_CLIPBOARD_SET";
teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, reg_name, strlen(reg_name));
if (!reg) {
@@ -1533,7 +1543,7 @@ teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len,
g_auto(teco_string_t) command;
if (!reg->vtable->get_string(reg, &command.data, &command.len, NULL, error))
return FALSE;
- if (teco_string_contains(&command, '\0')) {
+ if (teco_string_contains(command, '\0')) {
teco_error_qregcontainsnull_set(error, reg_name, strlen(reg_name), FALSE);
return FALSE;
}
@@ -1581,7 +1591,7 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError
if (teco_interface_osc52_is_enabled())
return teco_interface_osc52_get_clipboard(name, str, len, error);
- static const gchar *reg_name = "$SCITECO_CLIPBOARD_GET";
+ static const gchar reg_name[] = "$SCITECO_CLIPBOARD_GET";
teco_qreg_t *reg = teco_qreg_table_find(&teco_qreg_table_globals, reg_name, strlen(reg_name));
if (!reg) {
@@ -1594,7 +1604,7 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError
g_auto(teco_string_t) command;
if (!reg->vtable->get_string(reg, &command.data, &command.len, NULL, error))
return FALSE;
- if (teco_string_contains(&command, '\0')) {
+ if (teco_string_contains(command, '\0')) {
teco_error_qregcontainsnull_set(error, reg_name, strlen(reg_name), FALSE);
return FALSE;
}
@@ -1680,7 +1690,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);
}
@@ -1688,7 +1698,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;
@@ -1702,7 +1712,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;
@@ -1757,8 +1767,8 @@ teco_interface_is_interrupted(void)
* filtering out CTRL+C.
* It's currently necessary as a fallback e.g. for PDCURSES_GUI or XCurses.
*
- * NOTE: Theoretically, this can be optimized by doing wgetch() only every X
- * microseconds like on Gtk+.
+ * NOTE: Theoretically, this can be optimized by doing wgetch() only every
+ * TECO_POLL_INTERVAL microseconds like on Gtk+.
* But this turned out to slow things down, at least on PDCurses/WinGUI.
*/
gboolean
@@ -1786,9 +1796,22 @@ teco_interface_is_interrupted(void)
#endif
-static void
-teco_interface_refresh(void)
+void
+teco_interface_refresh(gboolean force)
{
+ if (!teco_interface.input_pad)
+ /* batch mode */
+ return;
+
+#ifdef NETBSD_CURSES
+ /* works around crashes in doupdate() */
+ if (G_UNLIKELY(COLS <= 1 || LINES <= 1))
+ return;
+#endif
+
+ if (G_UNLIKELY(force))
+ clearok(curscr, TRUE);
+
/*
* Info window is updated very often which is very
* costly, especially when using PDC_set_title(),
@@ -1799,7 +1822,7 @@ teco_interface_refresh(void)
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();
}
@@ -1813,42 +1836,50 @@ teco_interface_refresh(void)
(BUTTON1_##X | BUTTON2_##X | BUTTON3_##X | BUTTON4_##X | BUTTON5_##X)
static gboolean
-teco_interface_getmouse(GError **error)
+teco_interface_process_mevent(MEVENT *event, GError **error)
{
- MEVENT event;
-
- if (getmouse(&event) != OK)
- return TRUE;
+#ifdef DEBUG
+ g_printf("EVENT: 0x%016X -> bit %02d [%c%c%c%c%c]\n",
+ event->bstate, ffs(event->bstate)-1,
+ event->bstate & BUTTON_NUM(4) ? 'U' : ' ',
+ event->bstate & BUTTON_NUM(5) ? 'D' : ' ',
+ event->bstate & BUTTON_EVENT(PRESSED) ? 'P' : ' ',
+ event->bstate & BUTTON_EVENT(RELEASED) ? 'R' : ' ',
+ event->bstate & REPORT_MOUSE_POSITION ? 'M' : ' ');
+#endif
if (teco_curses_info_popup_is_shown(&teco_interface.popup) &&
- wmouse_trafo(teco_interface.popup.window, &event.y, &event.x, FALSE)) {
+ wmouse_trafo(teco_interface.popup.window, &event->y, &event->x, FALSE)) {
/*
* NOTE: Not all curses variants report the RELEASED event,
* but may also return REPORT_MOUSE_POSITION.
* So we might react to all button presses as well.
- * Others will still report CLICKED.
*/
- if (event.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED | REPORT_MOUSE_POSITION)) {
+ if (event->bstate & (BUTTON1_RELEASED | REPORT_MOUSE_POSITION)) {
teco_machine_t *machine = &teco_cmdline.machine.parent;
- const teco_string_t *insert = teco_curses_info_popup_getentry(&teco_interface.popup, event.y, event.x);
+ const teco_string_t *insert = teco_curses_info_popup_getentry(&teco_interface.popup,
+ event->y, event->x);
if (insert && machine->current->insert_completion_cb) {
- /* successfully clicked popup item */
+ /*
+ * Successfully clicked popup item.
+ * `insert` is the empty string for the "(Unnamed)" buffer.
+ */
const teco_string_t insert_suffix = {insert->data + teco_interface.popup_prefix_len,
insert->len - teco_interface.popup_prefix_len};
- if (!machine->current->insert_completion_cb(machine, &insert_suffix, error))
+ if (!machine->current->insert_completion_cb(machine, insert_suffix, error))
return FALSE;
teco_interface_popup_clear();
teco_interface_msg_clear();
- teco_interface_cmdline_update(&teco_cmdline);
+ teco_cmdline_update();
}
return TRUE;
}
- if (event.bstate & BUTTON_NUM(4))
+ if (event->bstate & BUTTON_NUM(4))
teco_curses_info_popup_scroll(&teco_interface.popup, -2);
- else if (event.bstate & BUTTON_NUM(5))
+ else if (event->bstate & BUTTON_NUM(5))
teco_curses_info_popup_scroll(&teco_interface.popup, +2);
short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
@@ -1859,106 +1890,144 @@ teco_interface_getmouse(GError **error)
}
/*
- * Return mouse coordinates relative to the view.
- * They will be in characters, but that's what SCI_POSITIONFROMPOINT
- * expects on Scinterm anyway.
- */
- WINDOW *current = teco_view_get_window(teco_interface_current_view);
- if (!wmouse_trafo(current, &event.y, &event.x, FALSE))
- /* no event inside of current view */
- return TRUE;
-
- /*
* NOTE: There will only be one of the button bits
* set in bstate, so we don't loose information translating
* them to enums.
*
- * At least on ncurses, we don't always get a RELEASED event.
- * It instead sends only REPORT_MOUSE_POSITION,
- * so make sure not to overwrite teco_mouse.button in this case.
+ * At least on ncurses, this enables the "Normal tracking mode"
+ * which only reports PRESSEND and RELEASED events, but no mouse
+ * position tracing.
*/
- if (event.bstate & BUTTON_NUM(4))
+ if (event->bstate & BUTTON_NUM(4))
/* scroll up - there will be no RELEASED event */
teco_mouse.type = TECO_MOUSE_SCROLLUP;
- else if (event.bstate & BUTTON_NUM(5))
+ else if (event->bstate & BUTTON_NUM(5))
/* scroll down - there will be no RELEASED event */
teco_mouse.type = TECO_MOUSE_SCROLLDOWN;
- else if (event.bstate & BUTTON_EVENT(RELEASED))
+ else if (event->bstate & BUTTON_EVENT(RELEASED))
teco_mouse.type = TECO_MOUSE_RELEASED;
- else if (event.bstate & BUTTON_EVENT(PRESSED))
+ else if (event->bstate & BUTTON_EVENT(PRESSED))
teco_mouse.type = TECO_MOUSE_PRESSED;
else
- /* can also be REPORT_MOUSE_POSITION */
- teco_mouse.type = TECO_MOUSE_RELEASED;
+ return TRUE;
- teco_mouse.x = event.x;
- teco_mouse.y = event.y;
+ /*
+ * Return mouse coordinates relative to the view.
+ * They will be in characters, but that's what SCI_POSITIONFROMPOINT
+ * expects on Scinterm anyway.
+ */
+ WINDOW *current = teco_view_get_window(teco_interface_current_view);
+ if (!wmouse_trafo(current, &event->y, &event->x, FALSE))
+ /* no event inside of current view */
+ return TRUE;
- if (event.bstate & BUTTON_NUM(1))
+ teco_mouse.x = event->x;
+ teco_mouse.y = event->y;
+
+ if (event->bstate & BUTTON_NUM(1))
teco_mouse.button = 1;
- else if (event.bstate & BUTTON_NUM(2))
+ else if (event->bstate & BUTTON_NUM(2))
teco_mouse.button = 2;
- else if (event.bstate & BUTTON_NUM(3))
+ else if (event->bstate & BUTTON_NUM(3))
teco_mouse.button = 3;
- else if (!(event.bstate & REPORT_MOUSE_POSITION))
+ else if (!(event->bstate & REPORT_MOUSE_POSITION))
teco_mouse.button = -1;
teco_mouse.mods = 0;
- if (event.bstate & BUTTON_SHIFT)
+ if (event->bstate & BUTTON_SHIFT)
teco_mouse.mods |= TECO_MOUSE_SHIFT;
- if (event.bstate & BUTTON_CTRL)
+ if (event->bstate & BUTTON_CTRL)
teco_mouse.mods |= TECO_MOUSE_CTRL;
- if (event.bstate & BUTTON_ALT)
+ if (event->bstate & BUTTON_ALT)
teco_mouse.mods |= TECO_MOUSE_ALT;
- if (event.bstate & BUTTON_EVENT(CLICKED)) {
- /*
- * Click detection __should__ be disabled,
- * but some Curses implementations report them anyway.
- * This has been observed on PDCurses/WinGUI.
- * On PDCurses/Wincon we especially did not disable
- * click detection since it doesn't report
- * BUTTONX_RELEASED at all.
- * We emulate separate PRESSED/RELEASE events on those
- * platforms.
- */
- teco_mouse.type = TECO_MOUSE_PRESSED;
- if (!teco_cmdline_keymacro("MOUSE", -1, error))
- return FALSE;
- teco_mouse.type = TECO_MOUSE_RELEASED;
+#if defined(NCURSES_UNIX) && NCURSES_VERSION_PATCH < 20250913
+ /*
+ * FIXME: Some terminal emulators do not send separate
+ * middle click PRESSED and RELEASED buttons
+ * (both are sent when releasing the button).
+ * Furthermore due to ncurses bugs the order
+ * of events is arbitrary.
+ * Therefore we ignore BUTTON2_PRESSED and synthesize
+ * PRESSED and RELEASED evnts on BUTTON2_RELEASED:
+ */
+ if (teco_mouse.button == 2) {
+ if (teco_mouse.type == TECO_MOUSE_PRESSED)
+ /* ignore BUTTON2_PRESSED events */
+ return TRUE;
+ if (teco_mouse.type == TECO_MOUSE_RELEASED) {
+ teco_mouse.type = TECO_MOUSE_PRESSED;
+ if (!teco_cmdline_keymacro("MOUSE", -1, error))
+ return FALSE;
+ teco_mouse.type = TECO_MOUSE_RELEASED;
+ }
}
+#endif /* NCURSES_UNIX && NCURSES_VERSION_PATCH < 20250913 */
+
return teco_cmdline_keymacro("MOUSE", -1, error);
}
+static gboolean
+teco_interface_getmouse(GError **error)
+{
+ MEVENT event;
+
+ while (getmouse(&event) == OK)
+ if (!teco_interface_process_mevent(&event, error))
+ return FALSE;
+
+ return TRUE;
+}
+
#endif /* NCURSES_MOUSE_VERSION >= 2 */
static gint
teco_interface_blocking_getch(void)
{
+ if (!g_queue_is_empty(teco_interface.input_queue))
+ return GPOINTER_TO_INT(g_queue_pop_head(teco_interface.input_queue));
+
#if NCURSES_MOUSE_VERSION >= 2
-#if defined(NCURSES_VERSION) || defined(PDCURSES_WINCON)
/*
- * REPORT_MOUSE_POSITION is necessary at least on
- * ncurses, so that BUTTONX_RELEASED events are reported.
- * At least we interpret REPORT_MOUSE_POSITION
- * like BUTTONX_RELEASED.
- * It does NOT report every cursor movement, though.
- *
- * FIXME: On PDCurses/Wincon we enable it, so we at least
- * receive something that will be interpreted as
- * BUTTONX_RELEASED, although it really just reports
- * cursor movements.
- */
- static const mmask_t mmask = ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION;
-#else
- static const mmask_t mmask = ALL_MOUSE_EVENTS;
+ * FIXME: Due to an ncurses bug, we can receive bogus BUTTON3_PRESSED events after
+ * left scrolling.
+ * If we would reset the mouse mask with every wgetch(), which resets
+ * the internal button state, we would receive bogus BUTTON3_PRESSED events
+ * repeatedly.
+ * An upstream ncurses patch will probably be merged soon.
+ */
+ static gboolean old_mousekey = FALSE;
+ gboolean new_mousekey = (teco_ed & TECO_ED_MOUSEKEY) != 0;
+ if (new_mousekey != old_mousekey) {
+ old_mousekey = new_mousekey;
+ mmask_t mmask = BUTTON_EVENT(PRESSED) | BUTTON_EVENT(RELEASED);
+#ifdef __PDCURSES__
+ /*
+ * On PDCurses it's crucial NOT to mask for BUTTONX_CLICKED.
+ * Also, scroll events are not reported without the non-standard
+ * MOUSE_WHEEL_SCROLL.
+ */
+ mmask |= MOUSE_WHEEL_SCROLL;
#endif
- mousemask(teco_ed & TECO_ED_MOUSEKEY ? mmask : 0, NULL);
+ mousemask(new_mousekey ? mmask : 0, NULL);
+ }
#endif /* NCURSES_MOUSE_VERSION >= 2 */
/* no special <CTRL/C> handling */
raw();
nodelay(teco_interface.input_pad, FALSE);
+
+ /*
+ * Make sure we return when it's time to create recovery dumps.
+ */
+ if (teco_ring_recovery_interval != 0) {
+ if (G_UNLIKELY(!teco_interface.recovery_timer))
+ teco_interface.recovery_timer = g_timer_new();
+ gdouble elapsed = g_timer_elapsed(teco_interface.recovery_timer, NULL);
+ wtimeout(teco_interface.input_pad,
+ MAX((gdouble)teco_ring_recovery_interval - elapsed, 0)*1000);
+ }
+
/*
* Memory limiting is stopped temporarily, since it might otherwise
* constantly place 100% load on the CPU.
@@ -1974,6 +2043,12 @@ teco_interface_blocking_getch(void)
cbreak();
#endif
+ if (key == ERR && teco_ring_recovery_interval != 0 &&
+ g_timer_elapsed(teco_interface.recovery_timer, NULL) >= teco_ring_recovery_interval) {
+ teco_ring_dump_recovery();
+ g_timer_start(teco_interface.recovery_timer);
+ }
+
return key;
}
@@ -1995,12 +2070,11 @@ teco_interface_event_loop_iter(void)
GError **error = &teco_interface.event_loop_error;
- gint key = g_queue_is_empty(teco_interface.input_queue)
- ? teco_interface_blocking_getch()
- : GPOINTER_TO_INT(g_queue_pop_head(teco_interface.input_queue));
+ gint key = teco_interface_blocking_getch();
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:
@@ -2008,10 +2082,11 @@ teco_interface_event_loop_iter(void)
return;
#ifdef KEY_RESIZE
case KEY_RESIZE:
-#ifdef __PDCURSES__
- /* NOTE: No longer necessary since PDCursesMod v4.3.3. */
- resize_term(0, 0);
-#endif
+ /*
+ * At least on PDCurses/Wincon, the hardware cursor is sometimes
+ * reactivated.
+ */
+ curs_set(0);
teco_interface_resize_all_windows();
break;
#endif
@@ -2078,9 +2153,10 @@ teco_interface_event_loop_iter(void)
* Do not auto-scroll on mouse events, so you can scroll the view manually
* in the ^KMOUSE macro, allowing dot to be outside of the view.
*/
- teco_interface_refresh();
+ teco_interface_unfold();
+ teco_interface_refresh(FALSE);
return;
-#endif
+#endif /* NCURSES_MOUSE_VERSION >= 2 */
/*
* Control keys and keys with printable representation
@@ -2125,6 +2201,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.
@@ -2134,9 +2214,10 @@ teco_interface_event_loop_iter(void)
*/
if (teco_interface_current_view == last_view)
teco_interface_ssm(SCI_SETFIRSTVISIBLELINE, last_vpos, 0);
+ teco_interface_unfold();
teco_interface_ssm(SCI_SCROLLCARET, 0, 0);
- teco_interface_refresh();
+ teco_interface_refresh(FALSE);
}
gboolean
@@ -2148,11 +2229,14 @@ 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);
- teco_interface_refresh();
+ /*
+ * NetBSD's Curses needs the hard refresh as it would
+ * otherwise draw the info window in the wrong row.
+ * Shouldn't cause any slowdown on ncurses.
+ */
+ teco_interface_refresh(TRUE);
#ifdef EMCURSES
PDC_emscripten_set_handler(teco_interface_event_loop_iter, TRUE);
@@ -2199,10 +2283,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)
@@ -2227,4 +2307,10 @@ 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);
+
+ if (teco_interface.recovery_timer)
+ g_timer_destroy(teco_interface.recovery_timer);
}