Skip to content

Commit

Permalink
Curses: always wgetch() on a dummy pad, avoiding unnecessary wrefresh()
Browse files Browse the repository at this point in the history
* 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
  Bill-Gray/PDCursesMod#322
  • Loading branch information
rhaberkorn committed Sep 22, 2024
1 parent 4ca4ba2 commit 84cc45e
Showing 1 changed file with 45 additions and 35 deletions.
80 changes: 45 additions & 35 deletions src/interface-curses/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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") ? : "");
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -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;
Expand All @@ -1381,13 +1395,19 @@ 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;
*len = str_base64->len;

g_string_free(str_base64, !str);
return TRUE;

error:
cbreak();
keypad(teco_interface.input_pad, TRUE);
return FALSE;
}

#else /* !PDCURSES && !CURSES_TTY */
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 <CTRL/C> 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 <CTRL/C> */
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();
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit 84cc45e

Please sign in to comment.