From 84cc45ebbdcd62a0bad33d65223a45dc146a0f07 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Sun, 22 Sep 2024 12:36:34 +0200 Subject: Curses: always wgetch() on a dummy pad, avoiding unnecessary wrefresh() * This is especially important on platforms, requiring the wgetch() poll workaround to detect CTRL+C (PDCurses/WinGUI). wgetch(cmdline_window) would implicitly wrefresh(cmdline_window), which resulted in additional flickering when pressing function keys. This is no longer so important since key macros are processed as an unity and the cmdline will be updated only after processing all of the characters contained in them, ie. only once after the key press. Still, there could have still been unwanted side effects. At the very least, wgetch(input_pad) should be faster. * The XTerm clipboard implementation was getch()ing on stdscr, so potentially suffered from the same problem. It should be tested again. * Since keypad() is now always enabled even on netbsd-curses. I assume that the function key processing bug in netbsd-curses has been fixed by now. We are not building any releases with netbsd-curses. But it should be retested. * It does not resolve all flickering issues on PDCurses/WinGUI. Both the command line and the Scintilla view still flicker near the cursor. See https://github.com/Bill-Gray/PDCursesMod/issues/322 --- src/interface-curses/interface.c | 80 ++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index f4b2796..370fc27 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -347,6 +347,11 @@ static struct { 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; @@ -697,12 +702,22 @@ teco_interface_init_interactive(GError **error) curs_set(0); 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); - keypad(teco_interface.cmdline_window, TRUE); - nodelay(teco_interface.cmdline_window, TRUE); + + teco_interface.input_pad = newpad(1, 1); + /* + * Controlling function key processing is important + * on Unix Curses, as ESCAPE is handled as the beginning + * of a escape sequence when terminal emulators are + * involved. + * Still, it's now enabled always since the ESCDELAY + * workaround works nicely. + * On some Curses variants (XCurses) keypad + * must always be TRUE so we receive KEY_RESIZE. + */ + keypad(teco_interface.input_pad, TRUE); + nodelay(teco_interface.input_pad, TRUE); teco_interface.input_queue = g_queue_new(); @@ -746,8 +761,8 @@ teco_interface_restore_batch(void) * Set window title to a reasonable default, * in case it is not reset immediately by the * shell. - * FIXME: See set_window_title() why this - * is necessary. + * FIXME: See teco_interface_set_window_title() + * why this is necessary. */ #if defined(CURSES_TTY) && defined(HAVE_TIGETSTR) teco_interface_set_window_title(g_getenv("TERM") ? : ""); @@ -1328,18 +1343,18 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError * to be on the safe side. */ halfdelay(1); /* 100ms timeout */ - keypad(stdscr, FALSE); + /* don't interpret escape sequences */ + keypad(teco_interface.input_pad, FALSE); /* * Skip "\e]52;x;" (7 characters). */ for (gint i = 0; i < 7; i++) { - if (getch() == ERR) { + if (wgetch(teco_interface.input_pad) == ERR) { /* timeout */ - cbreak(); g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, "Timed out reading XTerm clipboard"); - return FALSE; + goto error; } } @@ -1355,14 +1370,13 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError */ gchar buffer[MAX(3, 7)]; - gchar c = (gchar)getch(); + gchar c = (gchar)wgetch(teco_interface.input_pad); if (c == ERR) { /* timeout */ - cbreak(); g_string_free(str_base64, TRUE); g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED, "Timed out reading XTerm clipboard"); - return FALSE; + goto error; } if (c == '\a') break; @@ -1381,6 +1395,7 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError } cbreak(); + keypad(teco_interface.input_pad, TRUE); if (str) *str = str_base64->str; @@ -1388,6 +1403,11 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError g_string_free(str_base64, !str); return TRUE; + +error: + cbreak(); + keypad(teco_interface.input_pad, TRUE); + return FALSE; } #else /* !PDCURSES && !CURSES_TTY */ @@ -1497,13 +1517,17 @@ teco_interface_is_interrupted(void) gboolean teco_interface_is_interrupted(void) { - if (!teco_interface.cmdline_window) + if (!teco_interface.input_pad) /* batch mode */ return teco_interrupted != FALSE; - /* NOTE: getch() is configured to be nonblocking. */ + /* + * NOTE: wgetch() is configured to be nonblocking. + * We wgetch() on a dummy pad, so this does not call any + * wrefresh(). + */ gint key; - while ((key = wgetch(teco_interface.cmdline_window)) != ERR) { + while ((key = wgetch(teco_interface.input_pad)) != ERR) { if (G_UNLIKELY(key == TECO_CTL_KEY('C'))) return TRUE; g_queue_push_tail(teco_interface.input_queue, @@ -1543,35 +1567,19 @@ teco_interface_refresh(void) static gint teco_interface_blocking_getch(void) { - /* - * Setting function key processing is important - * on Unix Curses, as ESCAPE is handled as the beginning - * of a escape sequence when terminal emulators are - * involved. - * On some Curses variants (XCurses) however, keypad - * must always be TRUE so we receive KEY_RESIZE. - * - * FIXME: NetBSD's curses could be handled like ncurses, - * but gets into an undefined state when SciTECO processes - * escape sequences. - */ -#ifdef NCURSES_UNIX - keypad(teco_interface.cmdline_window, TRUE); -#endif - /* no special handling */ raw(); - nodelay(teco_interface.cmdline_window, FALSE); + nodelay(teco_interface.input_pad, FALSE); /* * Memory limiting is stopped temporarily, since it might otherwise * constantly place 100% load on the CPU. */ teco_memory_stop_limiting(); - gint key = wgetch(teco_interface.cmdline_window); + gint key = wgetch(teco_interface.input_pad); teco_memory_start_limiting(); /* allow asynchronous interruptions on */ teco_interrupted = FALSE; - nodelay(teco_interface.cmdline_window, TRUE); + nodelay(teco_interface.input_pad, TRUE); #if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */ cbreak(); @@ -1768,6 +1776,8 @@ teco_interface_cleanup(void) delwin(teco_interface.cmdline_pad); if (teco_interface.msg_window) delwin(teco_interface.msg_window); + if (teco_interface.input_pad) + delwin(teco_interface.input_pad); /* * PDCurses/WinCon crashes if initscr() wasn't called. -- cgit v1.2.3