From 0e3d6c84a52326a1069fe4f7adc2930b974dfa5f Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Sun, 5 Apr 2026 10:01:09 +0200 Subject: Gtk: fixed interaction between `^T` and main input handling Between calls to `^T`, the original key-press-event handler might enqueue events, that we must first process and report with `^T`. Otherwise it would be easy to provoke apparent double-reporting of keys after input loops like `<^T:;>`. --- src/interface-gtk/interface.c | 44 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) (limited to 'src/interface-gtk') diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index f88813b..1540245 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -451,14 +451,15 @@ teco_interface_getch_commit_cb(GtkIMContext *context, gchar *str, gpointer user_ */ *cp = g_utf8_get_char_validated(str, -1); g_assert(*cp >= 0); - gtk_main_quit(); + + /* we might be invoked outside of a nested event loop */ + if (gtk_main_level() > 1) + gtk_main_quit(); } static gboolean -teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +teco_interface_getch_process_event(GdkEvent *event, teco_int_t *cp) { - teco_int_t *cp = user_data; - g_assert(event->type == GDK_KEY_PRESS); switch (event->key.keyval) { @@ -485,11 +486,25 @@ teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_ break; } + /* + * NOTE: The teco_interface_getch_commit_cb() could be called immediately + * without returning to the event loop. + */ gtk_im_context_filter_keypress(teco_interface.input_method, &event->key); - return TRUE; + return *cp >= 0; } - gtk_main_quit(); + return TRUE; +} + +static gboolean +teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) +{ + teco_int_t *cp = user_data; + + if (teco_interface_getch_process_event(event, cp)) + gtk_main_quit(); + return TRUE; } @@ -513,6 +528,22 @@ teco_interface_getch(gboolean widechar) commit_handler = g_signal_connect(teco_interface.input_method, "commit", G_CALLBACK(teco_interface_getch_commit_cb), &cp); + /* + * The original teco_interface_input_cb() could have already enqueued events + * (also between ^T calls). + * This must be done after registering teco_interface_getch_commit_cb() already. + * + * NOTE: Already enqueued mouse events will currently be discarded silently. + */ + for (;;) { + g_autoptr(GdkEvent) event = g_queue_pop_head(teco_interface.event_queue); + if (!event) + break; + if (event->type == GDK_KEY_PRESS && + teco_interface_getch_process_event(event, &cp)) + goto cleanup; + } + /* * Highlights the first character in the label. * This mimics what the Curses UI does. @@ -527,6 +558,7 @@ teco_interface_getch(gboolean widechar) gdk_window_freeze_updates(top_window); +cleanup: g_signal_handler_disconnect(teco_interface.input_method, commit_handler); g_signal_handlers_unblock_by_func(teco_interface.input_method, teco_interface_cmdline_commit_cb, NULL); g_signal_handler_disconnect(teco_interface.window, key_handler); -- cgit v1.2.3