Branch: refs/heads/master Author: Kondor Dániel kondor.dani@gmail.com Committer: Daniel Kondor kondor.dani@gmail.com Date: Wed, 27 Nov 2024 19:26:19 UTC Commit: 2b03a7c08278aa13e4e3c1f9b547f7b4f5b5203f https://github.com/geany/geany/commit/2b03a7c08278aa13e4e3c1f9b547f7b4f5b520...
Log Message: ----------- socket: support startup_id tokens on X11 and Wayland
This allows requesting the main window to be activated when opening files via the socket interface. This relies on the following protocols: - X11: Startup notification: https://specifications.freedesktop.org/startup-notification-spec/latest/ - Wayland: xdg-activation: https://wayland.app/protocols/xdg-activation-v1
Both are natively supported by GTK, the only change required is some extra plumbing to get the activation token to the already running Geany instance.
Modified Paths: -------------- src/libmain.c src/socket.c src/socket.h
Modified: src/libmain.c 16 lines changed, 15 insertions(+), 1 deletions(-) =================================================================== @@ -1056,6 +1056,18 @@ gint main_lib(gint argc, gchar **argv) #ifdef ENABLE_NLS main_locale_init(utils_resource_dir(RESOURCE_DIR_LOCALE), GETTEXT_PACKAGE); #endif + + /* Magic ID used on X11 and Wayland for bringing our existing window on top; + * need to read it here, since GTK will clear this from the environment in + * gtk_init () (called from parse_command_line_options () below). Need to + * make a copy, since the value is not guaranteed to be valid after calling + * unsetenv() (which is done by GTK). */ +#ifdef HAVE_SOCKET + gchar *desktop_startup_id = g_strdup(getenv("DESKTOP_STARTUP_ID")); + if (!desktop_startup_id) + desktop_startup_id = g_strdup(getenv("XDG_ACTIVATION_TOKEN")); +#endif + /* initialize TM before parsing command-line - needed for tag file generation */ app->tm_workspace = tm_get_workspace(); parse_command_line_options(&argc, &argv); @@ -1085,7 +1097,7 @@ gint main_lib(gint argc, gchar **argv) #endif socket_info.lock_socket = -1; socket_info.lock_socket_tag = 0; - socket_info.lock_socket = socket_init(argc, argv, socket_port); + socket_info.lock_socket = socket_init(argc, argv, socket_port, desktop_startup_id); /* Quit if filenames were sent to first instance or the list of open * documents has been printed */ if ((socket_info.lock_socket == -2 /* socket exists */ && argc > 1) || @@ -1097,6 +1109,7 @@ gint main_lib(gint argc, gchar **argv) g_free(app->datadir); g_free(app->docdir); g_free(app); + g_free(desktop_startup_id); return 0; } /* Start a new instance if no command line strings were passed, @@ -1107,6 +1120,7 @@ gint main_lib(gint argc, gchar **argv) cl_options.new_instance = TRUE; } } + g_free(desktop_startup_id); #endif
#ifdef G_OS_WIN32
Modified: src/socket.c 41 lines changed, 37 insertions(+), 4 deletions(-) =================================================================== @@ -92,7 +92,9 @@ #ifdef GDK_WINDOWING_X11 #include <gdk/gdkx.h> #endif - +#ifdef GDK_WINDOWING_WAYLAND +#include <gdk/gdkwayland.h> +#endif
#ifdef G_OS_WIN32 #define SOCKET_IS_VALID(s) ((s) != INVALID_SOCKET) @@ -124,13 +126,20 @@ static gint socket_fd_close (gint sock);
-static void send_open_command(gint sock, gint argc, gchar **argv) +static void send_open_command(gint sock, gint argc, gchar **argv, const gchar *desktop_startup_id) { gint i;
g_return_if_fail(argc > 1); geany_debug("using running instance of Geany");
+ if (desktop_startup_id != NULL) + { + socket_fd_write_all(sock, "desktop_startup_id\n", 19); + socket_fd_write_all(sock, desktop_startup_id, strlen(desktop_startup_id)); + socket_fd_write_all(sock, "\n.\n", 3); + } + if (cl_options.goto_line >= 0) { gchar *line = g_strdup_printf("%d\n", cl_options.goto_line); @@ -247,7 +256,7 @@ static void check_socket_permissions(void) * (taken from Sylpheed, thanks) * Returns the created socket, -1 if an error occurred or -2 if another socket exists and files * were sent to it. */ -gint socket_init(gint argc, gchar **argv, G_GNUC_UNUSED gushort socket_port) +gint socket_init(gint argc, gchar **argv, G_GNUC_UNUSED gushort socket_port, const gchar *desktop_startup_id) { gint sock; #ifdef G_OS_WIN32 @@ -334,7 +343,7 @@ gint socket_init(gint argc, gchar **argv, G_GNUC_UNUSED gushort socket_port) SetForegroundWindow(hwnd); #endif
- send_open_command(sock, argc, argv); + send_open_command(sock, argc, argv, desktop_startup_id); }
if (cl_options.list_documents) @@ -719,6 +728,30 @@ gboolean socket_lock_input_cb(GIOChannel *source, GIOCondition condition, gpoint cl_options.goto_column = atoi(buf); } } + else if (strncmp(buf, "desktop_startup_id", 18) == 0) + { + if (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.') + { +#if defined(GDK_WINDOWING_WAYLAND) || defined(GDK_WINDOWING_X11) + g_strstrip(buf); /* remove \n char */ + geany_debug("Received IPC command from remote instance: desktop_startup_id (%s)", buf); + GdkDisplay *display = gdk_display_get_default(); +#ifdef GDK_WINDOWING_WAYLAND + if (GDK_IS_WAYLAND_DISPLAY(display)) + gdk_wayland_display_set_startup_notification_id(display, buf); +#endif +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_DISPLAY(display)) + gdk_x11_display_set_startup_notification_id(display, buf); +#endif +#endif // GDK_WINDOWING_WAYLAND || GDK_WINDOWING_X11 + while (socket_fd_gets(sock, buf, sizeof(buf)) != -1 && *buf != '.') + { + /* we expect only one line with the token, + * skip anything until the end of the message */ + } + } + } #ifdef G_OS_WIN32 else if (strncmp(buf, "window", 6) == 0) {
Modified: src/socket.h 2 lines changed, 1 insertions(+), 1 deletions(-) =================================================================== @@ -42,7 +42,7 @@ struct SocketInfo
extern struct SocketInfo socket_info;
-gint socket_init(gint argc, gchar **argv, gushort socket_port); +gint socket_init(gint argc, gchar **argv, gushort socket_port, const gchar *desktop_startup_id);
gboolean socket_lock_input_cb(GIOChannel *source, GIOCondition condition, gpointer data);
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).