unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Daniel Colascione <dancol@dancol.org>
To: emacs-devel@gnu.org
Cc: Daniel Colascione <dancol@dancol.org>
Subject: [PATCH] Make browser windows pop up when browsing URLs on Wayland
Date: Mon, 23 Dec 2024 14:23:14 -0500	[thread overview]
Message-ID: <20241223192320.39021-1-dancol@dancol.org> (raw)

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.

We really should consider simplifying the whole browse-url mechanism
by eliminating browse-url-default-browser and the distinction between
browse-url-handlers and browse-url-default-handlers.  Instead,
browse-url-handlers should have a simple list of predicated handlers
just like display-buffer-alist.

* 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
---
 lisp/net/browse-url.el | 25 +++++++++++++++++++++++++
 src/pgtkfns.c          | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 9dd990108df..e5b7fc7ba6d 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -179,6 +179,9 @@ browse-url--browser-defcustom-type
     ,@(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")
@@ -1097,6 +1100,8 @@ browse-url-emacs-display
     (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.
@@ -1118,6 +1123,9 @@ browse-url-default-browser
      '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-gnome-moz-program) 'browse-url-gnome-moz)
     ((executable-find browse-url-firefox-program) 'browse-url-firefox)
@@ -1438,6 +1446,23 @@ browse-url-default-android-browser
 (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.45.2




             reply	other threads:[~2024-12-23 19:23 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-12-23 19:23 Daniel Colascione [this message]
2024-12-24  5:49 ` [PATCH] Make browser windows pop up when browsing URLs on Wayland Tassilo Horn
2024-12-24  6:39   ` Daniel Colascione

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241223192320.39021-1-dancol@dancol.org \
    --to=dancol@dancol.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).