From 68baf7e77cab1e8181a9cfc721889ea68d984fde Mon Sep 17 00:00:00 2001 From: Daniel Colascione Date: Mon, 23 Dec 2024 13:34:11 -0500 Subject: [PATCH] Make browser windows pop up when browsing URLs on Wayland When a user invokes browse-url, the browser window viewing the URL should be raised in the user's desktop environment. On X11, running xdg-open as a subprocess does the job. However, on Wayland, this approach doesn't work: xdg-open makes the web browser browse a URL all right, but doesn't raise the browser window in the GUI. Therefore, if the browser window is behind Emacs, browse-url appears to do nothing. Repeated invocations of browse-url cause the browser to load multiple tabs in the background, surprising the user when he gives up in frustration and manually switches to the browser window. There's no subprocess we can run to make the right thing happen. Wayland requires that we pass along event activation information to the browser using the xdg_activation_v1 protocol. This change adds x-gtk-launch-uri to invoke GTK-native URL-dispatch machinery. This machinery DTRT on both X11 and Wayland. We fall back to the default browser machinery if we're not on a GTK frame. The logic is more complicated than it has to be because the GTK URI launch mechanism requires that we launch with respect to a specific GTK window, and in some environments (e.g., running emacs -nw in a PGTK Emacs) we don't have a GTK window. We also want to preserve the effect of customizing browse-url-browser-function, so adding an entry to browse-url-default-handlers that preempts URI open when we happen to be on a GTK frame is the wrong thing to do. * lisp/net/browse-url.el (browse-url--browser-defcustom-type): (browse-url--inhibit-pgtk): avoid infinite recursion (browse-url-default-browser): use pgtk launch (x-gtk-launch-uri): new function (browse-url-default-gtk-browser): ues it * src/pgtkfns.c (unwind_gerror_ptr): new function (Fx_gtk_launch_uri): new function (syms_of_pgtkfns): register it * etc/NEWS: mention the new feature (cherry picked from commit 72eb5f8bedbe441f6a3952557b9aa3ef6fd6235d) --- etc/NEWS | 7 +++++++ lisp/net/browse-url.el | 25 +++++++++++++++++++++++++ src/pgtkfns.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/etc/NEWS b/etc/NEWS index 7d9575d4de6..d37a7254b61 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -236,6 +236,13 @@ It has been obsolete since Emacs 28.1. *** Support for gnome-moz-remote has been removed. It has been obsolete since Emacs 25.1. +*** New GTK-native launch mode +For better Wayland support, the pgtk toolkit exposes a new +'x-gtk-launch-uri' browse-url handler and uses it by default when URLs +are browsed from a PGTK frame. For other frames, we fall back to the +default URL launch function. This change allows us to properly raise +browser windows under Wayland using the xdg_activation_v1 protocol. + ** CL-Lib +++ *** 'cl-labels' now also accepts (FUNC EXP) bindings, like 'cl-flet'. diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el index 252d247a9a9..b272ae6c348 100644 --- a/lisp/net/browse-url.el +++ b/lisp/net/browse-url.el @@ -178,6 +178,9 @@ ,@(when (eq system-type 'android) (list '(function-item :tag "Default Android browser" :value browse-url-default-android-browser))) + ,@(when (eq window-system 'pgtk) + (list '(function-item :tag "Default GTK browser" + :value browse-url-default-gtk-browser))) (function-item :tag "Default browser" :value browse-url-default-browser) (function :tag "Your own function") @@ -1084,6 +1087,8 @@ one showing the selected frame." (and (not (equal display (getenv "DISPLAY"))) display))) +(defvar browse-url--inhibit-pgtk nil) + (defun browse-url-default-browser (url &rest args) "Find a suitable browser and ask it to load URL. Default to the URL around or before point. @@ -1105,6 +1110,9 @@ instead of `browse-url-new-window-flag'." 'browse-url-default-haiku-browser) ((eq system-type 'android) 'browse-url-default-android-browser) + ((and (eq (frame-parameter nil 'window-system) 'pgtk) + (not browse-url--inhibit-pgtk)) + 'browse-url-default-gtk-browser) ((browse-url-can-use-xdg-open) 'browse-url-xdg-open) ((executable-find browse-url-firefox-program) 'browse-url-firefox) ((executable-find browse-url-chromium-program) 'browse-url-chromium) @@ -1424,6 +1432,23 @@ point." (function-put 'browse-url-default-android-browser 'browse-url-browser-kind 'external) +(declare-function x-gtk-launch-uri "pgtkfns.c") + +;;;###autoload +(defun browse-url-default-gtk-browser (url &optional new-window) + "Browse URL with GTK's idea of the default browser. +If the selected frame isn't a GTK frame, fall back to +`browse-url-default-browser'." + (interactive (browse-url-interactive-arg "URL: ")) + (let ((frame (selected-frame))) + (if (eq (frame-parameter frame 'window-system) 'pgtk) + (x-gtk-launch-uri frame url) + (let ((browse-url--inhibit-pgtk t)) + (browse-url-default-browser url new-window))))) + +(function-put 'browse-url-default-gtk-browser + 'browse-url-browser-kind 'external) + ;;;###autoload (defun browse-url-emacs (url &optional same-window) "Ask Emacs to load URL into a buffer and show it in another window. diff --git a/src/pgtkfns.c b/src/pgtkfns.c index 0ff3262cd11..1c2b8985ee8 100644 --- a/src/pgtkfns.c +++ b/src/pgtkfns.c @@ -3819,6 +3819,44 @@ DEFUN ("x-gtk-debug", Fx_gtk_debug, Sx_gtk_debug, 1, 1, 0, return NILP (enable) ? Qnil : Qt; } +static void +unwind_gerror_ptr (void* data) +{ + GError* error = *(GError**)data; + if (error) + g_error_free (error); +} + +DEFUN ("x-gtk-launch-uri", Fx_gtk_launch_uri, Sx_gtk_launch_uri, 2, 2, 0, + doc: /* launch URI */) + (Lisp_Object frame, Lisp_Object uri) +{ + CHECK_FRAME (frame); + + if (!FRAME_LIVE_P (XFRAME (frame)) || + !FRAME_PGTK_P (XFRAME (frame)) || + !FRAME_GTK_OUTER_WIDGET (XFRAME (frame))) + error ("GTK URI launch not available for this frame"); + + CHECK_STRING (uri); + guint32 timestamp = gtk_get_current_event_time (); + + GError *err = NULL; + specpdl_ref count = SPECPDL_INDEX (); + + record_unwind_protect_ptr (unwind_gerror_ptr, &err); + + gtk_show_uri_on_window (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (XFRAME (frame))), + SSDATA (uri), + timestamp, + &err); + + if (err) + error ("Failed to launch URI via GTK: %s", err->message); + + return unbind_to (count, Qnil); +} + void syms_of_pgtkfns (void) { @@ -3890,6 +3928,7 @@ syms_of_pgtkfns (void) defsubr (&Sx_close_connection); defsubr (&Sx_display_list); defsubr (&Sx_gtk_debug); + defsubr (&Sx_gtk_launch_uri); defsubr (&Sx_show_tip); defsubr (&Sx_hide_tip); -- 2.39.5