aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-gtk/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface-gtk/interface.c')
-rw-r--r--src/interface-gtk/interface.c93
1 files changed, 66 insertions, 27 deletions
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index 08ccf5d..9ef86a1 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -78,7 +78,7 @@ static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong
gpointer user_data);
static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event,
gpointer user_data);
-static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED;
+static gboolean teco_interface_termination_handler(gpointer user_data) G_GNUC_UNUSED;
static gchar teco_interface_get_ansi_key(GdkEventKey *event);
#define TECO_UNNAMED_FILE "(Unnamed)"
@@ -404,7 +404,7 @@ teco_interface_get_options(void)
return group;
}
-void teco_interface_init_color(guint color, guint32 rgb) {}
+void teco_interface_disable_palette(void) {}
void
teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
@@ -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) {
@@ -476,19 +477,34 @@ teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_
*cp = TECO_CTL_KEY(g_ascii_toupper(*cp));
switch (*cp) {
case TECO_CTL_KEY('C'):
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
/* fall through */
case TECO_CTL_KEY('D'):
+ /* emulates EOF on stdin */
*cp = -1;
}
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 +529,22 @@ teco_interface_getch(gboolean widechar)
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.
* Is there a better way to signal that we expect input?
@@ -526,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);
@@ -790,7 +823,7 @@ teco_interface_is_interrupted(void)
{
if (!gtk_main_level())
/* batch mode */
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
/*
* By polling only every TECO_POLL_INTERVAL microseconds
@@ -800,11 +833,11 @@ teco_interface_is_interrupted(void)
guint64 now_ts = g_get_monotonic_time();
if (G_LIKELY(last_poll_ts+TECO_POLL_INTERVAL > now_ts))
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
last_poll_ts = now_ts;
gtk_main_iteration_do(FALSE);
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
void
@@ -1265,13 +1298,12 @@ teco_interface_event_loop(GError **error)
#endif
/*
- * SIGTERM emulates the "Close" key just like when
- * closing the window if supported by this version of glib.
- * Note that this replaces SciTECO's default SIGTERM handler
- * so it will additionally raise(SIGINT).
+ * SIGTERM and SIGHUP terminate the program, but will
+ * dump recovery files first (if enabled).
*/
#ifdef G_OS_UNIX
- g_unix_signal_add(SIGTERM, teco_interface_sigterm_handler, NULL);
+ g_unix_signal_add(SIGTERM, teco_interface_termination_handler, NULL);
+ g_unix_signal_add(SIGHUP, teco_interface_termination_handler, NULL);
#endif
/* the interval might have been changed in the profile */
@@ -1395,7 +1427,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
* If the execution thread is currently blocking,
* the key is delivered like an ordinary key press.
*/
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
else
g_queue_push_tail(teco_interface.event_queue,
gdk_event_copy(event));
@@ -1436,7 +1468,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
const teco_view_t *last_view = teco_interface_current_view;
sptr_t last_vpos = teco_interface_ssm(SCI_GETFIRSTVISIBLELINE, 0, 0);
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
switch (event->type) {
case GDK_KEY_PRESS:
teco_interface_handle_key_press(&event->key, &error);
@@ -1453,7 +1485,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
default:
g_assert_not_reached();
}
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
teco_interface_update(teco_interface_current_view != last_view);
/* always expand folds, even after mouse clicks */
@@ -1563,16 +1595,23 @@ teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer
}
static gboolean
-teco_interface_sigterm_handler(gpointer user_data)
+teco_interface_termination_handler(gpointer user_data)
{
+ teco_interface_msg(TECO_MSG_WARNING, "Received termination signal - dumping recovery files");
+ teco_interrupted = TECO_TERMINATED;
+
/*
- * Similar to window deletion - emulate "close" key press.
+ * This may be an emergency shutdown or another kind of
+ * termination that we cannot ignore, so better dump
+ * recovery files before terminating.
*/
- GtkWidget *widget = teco_interface.window;
+ if (teco_ring_recovery_interval != 0)
+ teco_ring_dump_recovery();
- g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS);
- close_event->key.window = gtk_widget_get_parent_window(widget);
- close_event->key.keyval = GDK_KEY_Close;
+ /*
+ * Otherwise we terminate and clean up as if by `-EX`.
+ */
+ gtk_main_quit();
- return teco_interface_input_cb(widget, close_event, NULL);
+ return G_SOURCE_REMOVE;
}