diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-05-09 19:08:32 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2023-05-09 19:08:32 +0200 |
commit | f557af9a9112955d3b65f6ad0d54c0791189f961 (patch) | |
tree | 9d7718cb1571b624710a182f09aca7163dc673a0 /src/interface-curses | |
parent | bac1efa51bf889d494013925586e87ef08307529 (diff) | |
download | sciteco-f557af9a9112955d3b65f6ad0d54c0791189f961.tar.gz |
fixed CTRL+C interruptions on Windows; optimized CTRL+C polling on Gtk+
* teco_interrupt() turned out to be unsuitable to kill child processes (eg. when <EB> hangs).
Instead, we have Win32-specific code now.
* Since SIGINT can be ignored on UNIX, pressing CTRL+C was not guaranteed to kill the
child process (eg. when <EB> hangs).
At the same time, it makes sense to send SIGINT first, so programs can terminate gracefully.
The behaviour has therefore been adapted: Interrupting with CTRL+C the first time will kill
gracefully. The second time, a more agressive signal is sent to kill the child process.
Unfortunately, this would be relatively tricky and complicated to do on Windows, so CTRL+C will always
"hard-kill" the child process.
* Moreover, teco_interrupt() killed the entire process on Windows when called the second time.
This resulted in any interruption to terminate SciTECO unexpectedly when tried the second time on Gtk/Win32.
* teco_sigint_occurred renamed to teco_interrupted:
There may be several different sources for setting this flag.
* Checking for CTRL+C on Gtk involves driving the main event loop repeatedly.
This is a very expensive operation. We now do that only every 100ms. This is still sufficient since
keyboard input comes from humans.
This optimization saves 75% runtime on Windows and 90% on Linux.
* The same optimization turned out to be contraproductive on PDCurses/WinGUI.
Diffstat (limited to 'src/interface-curses')
-rw-r--r-- | src/interface-curses/interface.c | 59 |
1 files changed, 34 insertions, 25 deletions
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index 25311c5..e056333 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -103,7 +103,7 @@ #define CURSES_TTY #endif -#if defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) +#ifdef G_OS_WIN32 /** * This handler is the Windows-analogue of a signal @@ -119,7 +119,7 @@ teco_console_ctrl_handler(DWORD type) { switch (type) { case CTRL_C_EVENT: - teco_sigint_occurred = TRUE; + teco_interrupted = TRUE; return TRUE; } @@ -397,6 +397,16 @@ teco_interface_init(void) #ifndef CURSES_TTY teco_interface_init_clipboard(); #endif + + /* + * The default SIGINT signal handler seems to partially work + * as the console control handler. + * However, a second CTRL+C event (or raise(SIGINT)) would + * terminate the process. + */ +#ifdef G_OS_WIN32 + SetConsoleCtrlHandler(teco_console_ctrl_handler, TRUE); +#endif } GOptionGroup * @@ -675,14 +685,11 @@ teco_interface_init_interactive(GError **error) teco_interface_init_screen(); /* - * We must register this handler for - * asynchronous interruptions via CTRL+C - * reliably. The signal handler we already - * have won't do. - * Doing this here ensures that we have a higher - * precedence than the handler installed by PDCursesMod. + * We always have a CTRL handler on Windows, but doing it + * here again, ensures that we have a higher precedence + * than the one installed by PDCurses. */ -#if defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) +#ifdef G_OS_WIN32 SetConsoleCtrlHandler(teco_console_ctrl_handler, TRUE); #endif @@ -1465,7 +1472,7 @@ teco_interface_popup_clear(void) gboolean teco_interface_is_interrupted(void) { - return teco_sigint_occurred != FALSE; + return teco_interrupted != FALSE; } #else /* !CURSES_TTY && !PDCURSES_WINCON && !NCURSES_WIN32 */ @@ -1473,27 +1480,29 @@ teco_interface_is_interrupted(void) /* * This function is called repeatedly, so we can poll the keyboard input queue, * filtering out CTRL+C. - * This is naturally very inefficient. * 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+. + * But this turned out to slow things down, at least on PDCurses/WinGUI. */ gboolean teco_interface_is_interrupted(void) { - if (teco_interface.cmdline_window) { /* interactive mode */ - gint key; - - /* - * NOTE: getch() is configured to be nonblocking. - */ - while ((key = wgetch(teco_interface.cmdline_window)) != ERR) { - if (G_UNLIKELY(key == TECO_CTL_KEY('C'))) - return TRUE; - g_queue_push_tail(teco_interface.input_queue, - GINT_TO_POINTER(key)); - } + if (!teco_interface.cmdline_window) + /* batch mode */ + return teco_interrupted != FALSE; + + /* NOTE: getch() is configured to be nonblocking. */ + gint key; + while ((key = wgetch(teco_interface.cmdline_window)) != ERR) { + if (G_UNLIKELY(key == TECO_CTL_KEY('C'))) + return TRUE; + g_queue_push_tail(teco_interface.input_queue, + GINT_TO_POINTER(key)); } - return teco_sigint_occurred != FALSE; + return teco_interrupted != FALSE; } #endif @@ -1528,7 +1537,7 @@ teco_interface_blocking_getch(void) gint key = wgetch(teco_interface.cmdline_window); teco_memory_start_limiting(); /* allow asynchronous interruptions on <CTRL/C> */ - teco_sigint_occurred = FALSE; + teco_interrupted = FALSE; nodelay(teco_interface.cmdline_window, TRUE); #if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */ |