diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2021-10-13 22:21:46 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2021-10-13 22:21:46 +0300 |
commit | cc929087f90da5479152e26829e10af2c1c3b937 (patch) | |
tree | 5f5dc8d6a9125cfec3bcf8a579ffa3beb69f181d /src/interface-gtk/interface.c | |
parent | f337481d17838e45020e4ce70e74181adc1cba89 (diff) | |
download | sciteco-cc929087f90da5479152e26829e10af2c1c3b937.tar.gz |
GTK: revised the key processing again to prevent crashes
* Processing a queued list of key events with an idle timer turned out
to be tricky.
Since teco_interface_pop_key_idle_cb() would eventually drive the main
loop with gtk_main_iteration_do() which may result in a recursive
invocation of teco_interface_pop_key_idle_cb() which will eventually crash.
* We'd have to mask the idle watcher during the execution time of teco_interface_pop_key_idle_cb().
* Therefore it has been decided to use a tight loop again to process the
event queue.
After thawing the window, we now manually drive the event loop with gtk_main_iteration_do()
to make sure that the UI is updated.
This could result in a recursive invocation of teco_interface_key_pressed_cb() of course but
the callback is already secured against this.
Diffstat (limited to 'src/interface-gtk/interface.c')
-rw-r--r-- | src/interface-gtk/interface.c | 76 |
1 files changed, 35 insertions, 41 deletions
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index edac479..58656b8 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -1097,39 +1097,6 @@ teco_interface_cmdline_size_allocate_cb(GtkWidget *widget, } static gboolean -teco_interface_pop_key_idle_cb(gpointer user_data) -{ - g_autoptr(GError) error = NULL; - - /* - * Avoid redraws of the current view by freezing updates - * on the view's GDK window (we're running in parallel - * to the main loop so there could be frequent redraws). - * By freezing updates, the behaviour is similar to - * the Curses UI. - */ - GdkWindow *top_window = gdk_window_get_toplevel(gtk_widget_get_window(teco_interface.window)); - gdk_window_freeze_updates(top_window); - - /* - * The event queue might be filled when pressing keys when SciTECO - * is busy executing code. - */ - g_autoptr(GdkEvent) event = g_queue_pop_head(teco_interface.event_queue); - - teco_sigint_occurred = FALSE; - teco_interface_handle_key_press(event->key.keyval, event->key.state, &error); - teco_sigint_occurred = FALSE; - - if (g_error_matches(error, TECO_ERROR, TECO_ERROR_QUIT)) - gtk_main_quit(); - - gdk_window_thaw_updates(top_window); - - return !g_queue_is_empty(teco_interface.event_queue); -} - -static gboolean teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { g_autoptr(GError) error = NULL; @@ -1169,14 +1136,41 @@ teco_interface_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, gpointer us g_queue_push_tail(teco_interface.event_queue, gdk_event_copy((GdkEvent *)event)); - /* - * This should give the UI a chance to update after every keypress. - * If we would empty the event_queue in a tight loop, then keeping - * a key pressed could result in the UI beeing frozen until the - * key is eventually released. - */ - if (teco_interface_pop_key_idle_cb(NULL) == G_SOURCE_CONTINUE) - g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, teco_interface_pop_key_idle_cb, NULL, NULL); + GdkWindow *top_window = gdk_window_get_toplevel(gtk_widget_get_window(teco_interface.window)); + + do { + /* + * The event queue might be filled when pressing keys when SciTECO + * is busy executing code. + */ + g_autoptr(GdkEvent) event = g_queue_pop_head(teco_interface.event_queue); + + /* + * Avoid redraws of the current view by freezing updates + * on the view's GDK window (we're running in parallel + * to the main loop so there could be frequent redraws). + * By freezing updates, the behaviour is similar to + * the Curses UI. + */ + gdk_window_freeze_updates(top_window); + + teco_sigint_occurred = FALSE; + teco_interface_handle_key_press(event->key.keyval, event->key.state, &error); + teco_sigint_occurred = FALSE; + + gdk_window_thaw_updates(top_window); + + if (g_error_matches(error, TECO_ERROR, TECO_ERROR_QUIT)) { + gtk_main_quit(); + break; + } + + /* + * This should give the UI a chance to update after every keypress. + * Would also be possible but tricky to implement with an idle watcher. + */ + gtk_main_iteration_do(FALSE); + } while (!g_queue_is_empty(teco_interface.event_queue)); return TRUE; } |