aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-gtk
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-gtk
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-gtk')
-rw-r--r--src/interface-gtk/interface.c47
1 files changed, 25 insertions, 22 deletions
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index d22009b..6301a71 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -70,6 +70,12 @@ static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *
gpointer user_data);
static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED;
+/**
+ * Interval between polling for keypresses.
+ * In other words, this is the maximum latency to detect CTRL+C interruptions.
+ */
+#define TECO_POLL_INTERVAL 100000 /* microseconds */
+
#define UNNAMED_FILE "(Unnamed)"
#define USER_CSS_FILE ".teco_css"
@@ -719,22 +725,27 @@ teco_interface_popup_clear(void)
* system call overhead.
* But the GDK lock that would be necessary for synchronization
* has been deprecated.
- *
- * @todo It would be great to have platform-specific optimizations,
- * so we can detect interruptions without having to drive the Glib
- * event loop (e.g. using libX11 or Win32 APIs).
- * There already is a keyboard hook for Win32 in interface-curses.
- * On the downside, such solutions will probably freeze the window
- * while SciTECO is busy. However we currently freeze the window
- * anyway while being busy to avoid flickering.
*/
gboolean
teco_interface_is_interrupted(void)
{
- if (gtk_main_level() > 0)
- gtk_main_iteration_do(FALSE);
+ if (!gtk_main_level())
+ /* batch mode */
+ return teco_interrupted != FALSE;
- return teco_sigint_occurred != FALSE;
+ /*
+ * By polling only every TECO_POLL_INTERVAL microseconds
+ * we save 75-90% of runtime.
+ */
+ static guint64 last_poll_ts = 0;
+ guint64 now_ts = g_get_monotonic_time();
+
+ if (G_LIKELY(last_poll_ts+TECO_POLL_INTERVAL > now_ts))
+ return teco_interrupted != FALSE;
+ last_poll_ts = now_ts;
+
+ gtk_main_iteration_do(FALSE);
+ return teco_interrupted != FALSE;
}
static void
@@ -1172,12 +1183,10 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us
gdk_keyval_to_upper(event->keyval) == GDK_KEY_C)
/*
* Handle asynchronous interruptions if CTRL+C is pressed.
- * This will usually send SIGINT to the entire process
- * group and set `teco_sigint_occurred`.
* If the execution thread is currently blocking,
* the key is delivered like an ordinary key press.
*/
- teco_interrupt();
+ teco_interrupted = TRUE;
else
g_queue_push_tail(teco_interface.event_queue,
gdk_event_copy((GdkEvent *)event));
@@ -1209,9 +1218,9 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us
*/
gdk_window_freeze_updates(top_window);
- teco_sigint_occurred = FALSE;
+ teco_interrupted = FALSE;
teco_interface_handle_key_press(&event->key, &error);
- teco_sigint_occurred = FALSE;
+ teco_interrupted = FALSE;
gdk_window_thaw_updates(top_window);
@@ -1256,12 +1265,6 @@ static gboolean
teco_interface_sigterm_handler(gpointer user_data)
{
/*
- * Since this handler replaces the default signal handler,
- * we also have to make sure it interrupts.
- */
- teco_interrupt();
-
- /*
* Similar to window deletion - emulate "close" key press.
*/
g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS);