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
This commit is contained in:
Daniel Colascione 2024-12-23 13:34:11 -05:00
parent 0334b0743f
commit 72eb5f8bed
3 changed files with 71 additions and 0 deletions

View file

@ -333,6 +333,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
+++

View file

@ -179,6 +179,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")
@ -1085,6 +1088,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.
@ -1106,6 +1111,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)
@ -1425,6 +1433,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.

View file

@ -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);