From ae2f607a19b12a30374de059ec29beaf41f82c73 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Wed, 24 Jun 2015 03:23:14 +0200 Subject: added "^FCLOSE" function key macro and defined SIGTERM behaviour * ^FCLOSE is inserted when the "Close" key is pressed. It is used by the GTK+ UI to deliver window close requests and SIGTERM occurrences. (this replaces the "Break" key used before in the GTK+ UI). * The default action of ^FCLOSE is to quit SciTECO, therefore window closing is possible even in --no-profile mode for instance. * fixed a minor memleak in Cmdline::fnmacro() * added ^FCLOSE implementation to fnkeys.tes to insert EX. This currently has the disadvantage of overwriting the error message with syntax errors if there are modified buffers but it will at least not close the window if there are modified buffers. * SIGTERM will now be similar to SIGINT by default instead of terminating SciTECO right away. * the GTK+ UI handles SIGTERM by emulating the "close" key while still interrupting like SIGINT. * GTK+: SIGTERM and ^C will interrupt by sending SIGINT to the entire process group instead of simply setting `sigint_occurred`. This fixes interrupting EC and EG commands with long-running or hanging programs and is relevant to the solution of #4. --- src/interface-gtk.cpp | 109 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 29 deletions(-) (limited to 'src/interface-gtk.cpp') diff --git a/src/interface-gtk.cpp b/src/interface-gtk.cpp index 7f16629..00f4c0c 100644 --- a/src/interface-gtk.cpp +++ b/src/interface-gtk.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -43,16 +44,34 @@ #include "interface.h" #include "interface-gtk.h" +/* + * Signal handlers (e.g. for handling SIGTERM) are only + * available on Unix and beginning with v2.30, while + * we still support v2.28. + * Handlers using `signal()` cannot be used easily for + * this purpose. + */ +#if defined(G_OS_UNIX) && GLIB_CHECK_VERSION(2,30,0) +#include +#define SCITECO_HANDLE_SIGNALS +#endif + namespace SciTECO { extern "C" { + static void scintilla_notify(ScintillaObject *sci, uptr_t idFrom, SCNotification *notify, gpointer user_data); + static gpointer exec_thread_cb(gpointer data); -static gboolean cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event, - gpointer user_data); -static gboolean exit_app(GtkWidget *w, GdkEventAny *e, gpointer user_data); -} +static gboolean cmdline_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, + gpointer user_data); +static gboolean window_delete_cb(GtkWidget *w, GdkEventAny *e, + gpointer user_data); + +static gboolean sigterm_handler(gpointer user_data) G_GNUC_UNUSED; + +} /* extern "C" */ #define UNNAMED_FILE "(Unnamed)" @@ -127,7 +146,7 @@ InterfaceGtk::main_impl(int &argc, char **&argv) window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME); g_signal_connect(G_OBJECT(window), "delete-event", - G_CALLBACK(exit_app), event_queue); + G_CALLBACK(window_delete_cb), event_queue); vbox = gtk_vbox_new(FALSE, 0); @@ -156,7 +175,7 @@ InterfaceGtk::main_impl(int &argc, char **&argv) gtk_editable_set_editable(GTK_EDITABLE(cmdline_widget), FALSE); widget_set_font(cmdline_widget, "Courier"); g_signal_connect(G_OBJECT(cmdline_widget), "key-press-event", - G_CALLBACK(cmdline_key_pressed), event_queue); + G_CALLBACK(cmdline_key_pressed_cb), event_queue); gtk_box_pack_start(GTK_BOX(vbox), cmdline_widget, FALSE, FALSE, 0); gtk_container_add(GTK_CONTAINER(window), vbox); @@ -181,6 +200,10 @@ InterfaceGtk::vmsg_impl(MessageType type, const gchar *fmt, va_list ap) va_list aq; gchar buf[255]; + /* + * stdio_vmsg() leaves `ap` undefined and we are expected + * to do the same and behave like vprintf(). + */ va_copy(aq, ap); stdio_vmsg(type, fmt, ap); g_vsnprintf(buf, sizeof(buf), fmt, aq); @@ -382,6 +405,16 @@ InterfaceGtk::event_loop_impl(void) gtk_widget_show_all(window); + /* + * 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). + */ +#ifdef SCITECO_HANDLE_SIGNALS + g_unix_signal_add(SIGTERM, sigterm_handler, event_queue); +#endif + /* * Start up SciTECO execution thread. * Whenever it needs to send a Scintilla message @@ -472,14 +505,6 @@ InterfaceGtk::handle_key_press(bool is_shift, bool is_ctl, guint keyval) gdk_threads_leave(); switch (keyval) { - case GDK_Break: - /* - * FIXME: This usually means that the window's close - * button was pressed. - * It should be a function key macro, with quitting - * as the default action. - */ - throw Quit(); case GDK_Escape: cmdline.keypress(CTL_KEY_ESC); break; @@ -522,6 +547,7 @@ InterfaceGtk::handle_key_press(bool is_shift, bool is_ctl, guint keyval) FN(KP_End, C1); FN(KP_Next, C3); FNS(End, END); FNS(Help, HELP); + FN(Close, CLOSE); #undef FNS #undef FN @@ -611,8 +637,8 @@ scintilla_notify(ScintillaObject *sci, uptr_t idFrom, } static gboolean -cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event, - gpointer user_data) +cmdline_key_pressed_cb(GtkWidget *widget, GdkEventKey *event, + gpointer user_data) { GAsyncQueue *event_queue = (GAsyncQueue *)user_data; @@ -630,10 +656,12 @@ cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event, is_ctl && gdk_keyval_to_upper(event->keyval) == GDK_C) { /* * Handle asynchronous interruptions if CTRL+C is pressed. + * This will usually send SIGINT to the entire process + * group and set `sigint_occurred`. * If the execution thread is currently blocking, * the key is delivered like an ordinary key press. */ - sigint_occurred = TRUE; + interrupt(); } else { /* * Copies the key-press event, since it must be evaluated @@ -651,26 +679,49 @@ cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event, } static gboolean -exit_app(GtkWidget *w, GdkEventAny *e, gpointer user_data) +window_delete_cb(GtkWidget *w, GdkEventAny *e, gpointer user_data) { GAsyncQueue *event_queue = (GAsyncQueue *)user_data; - GdkEventKey *break_event; + GdkEventKey *close_event; /* - * We cannot yet call gtk_main_quit() as the execution - * thread must shut down properly. - * Therefore we emulate that the "break" key was pressed - * which may then be handled by the execution thread. - * It may also be used to insert a function key macro. - * NOTE: We might also create a GDK_DELETE event. + * Emulate that the "close" key was pressed + * which may then be handled by the execution thread + * which invokes the appropriate "function key macro" + * if it exists. Its default action will ensure that + * the execution thread shuts down and the main loop + * will eventually terminate. */ - break_event = (GdkEventKey *)gdk_event_new(GDK_KEY_RELEASE); - break_event->window = gtk_widget_get_parent_window(w); - break_event->keyval = GDK_Break; + close_event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS); + close_event->window = gtk_widget_get_parent_window(w); + close_event->keyval = GDK_Close; - g_async_queue_push(event_queue, break_event); + g_async_queue_push(event_queue, close_event); return TRUE; } +static gboolean +sigterm_handler(gpointer user_data) +{ + GAsyncQueue *event_queue = (GAsyncQueue *)user_data; + GdkEventKey *close_event; + + /* + * Since this handler replaces the default one, we + * also have to make sure it interrupts. + */ + interrupt(); + + /* + * Similar to window deletion - emulate "close" key press. + */ + close_event = (GdkEventKey *)gdk_event_new(GDK_KEY_PRESS); + close_event->keyval = GDK_Close; + + g_async_queue_push(event_queue, close_event); + + return G_SOURCE_CONTINUE; +} + } /* namespace SciTECO */ -- cgit v1.2.3