aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-gtk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface-gtk.cpp')
-rw-r--r--src/interface-gtk.cpp109
1 files changed, 80 insertions, 29 deletions
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 <stdarg.h>
#include <string.h>
+#include <signal.h>
#include <glib.h>
#include <glib/gprintf.h>
@@ -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 <glib-unix.h>
+#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);
@@ -383,6 +406,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
* it locks the GDK mutex.
@@ -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 */