aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-curses/interface.c
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2023-05-09 19:08:32 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2023-05-09 19:08:32 +0200
commitf557af9a9112955d3b65f6ad0d54c0791189f961 (patch)
tree9d7718cb1571b624710a182f09aca7163dc673a0 /src/interface-curses/interface.c
parentbac1efa51bf889d494013925586e87ef08307529 (diff)
downloadsciteco-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/interface.c')
-rw-r--r--src/interface-curses/interface.c59
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 */