]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Add xdg-activation protocol
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Sat, 4 Feb 2023 09:07:46 +0000 (10:07 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Fri, 10 Feb 2023 20:51:29 +0000 (20:51 +0000)
This PR allows applications to activate themselves *if they provide
a valid xdg_activation token* (e.g. raise to the top and get keyboard
focus).

These tokens are given out by the xdg_activation protocol implemented
by wlroots and can be configured by the client requesting the token
in three ways:
- an "empty" token
  (apparently used to tell the compositor about "urgency")
- seat / input serial attached
- surface attached

Wlroots makes sure that
- If the client attached the seat / input serial: those two are valid.
- If the client attached a surface: that it has keyboard focus at the
  point where the request is finalized. There is a patch [1] pending
  for backport to wlroots 0.16 that also allows valid tokens when the
  supplied surface had cursor focus.
- a token is only valid for 30 seconds after being given out

The token can then be used by the client or given to other clients by
unspecified means (e.g. via environment variable or dbus) which then
may use the token on their own surface and request activation.

We only handle the actual request activation part:
- If the seat is set on the token we know wlroots validated seat and
  input serial
- Thus, if no seat is set we deny the activation request so we don't
  have windows suddenly popping up and taking over the keyboard focus
  (focus stealing prevention)
- We should also check for the surface being set but we can't do that
  with wlroots 0.16 as it will reset the surface to `NULL` when it is
  destroyed (which is something that usually happens for
  notifications). Once we move to wlroots 0.17.x we can add the
  missing surface check because it provides a `new_token` signal.
  We can use it to attach further details to the token which are then
  verified later when we decide if we allow the activate request or
  not.

With this PR in place the following setup should activate windows:
- launching an URL in foot should activate the target application if
  it is already running, foot requests a proper token and then sets it
  as `XDG_ACTIVATION_TOKEN` environment var before spawning `xdg-open`
- clicking on a `mako` notification with a `default` action defined
  should request a proper token which is then given to the application
  starting the notification and can thus be used to activate itself

This protocol is still very much in the process of being
implemented / finalized all over the place (e.g. GTK / QT / Firefox /
notification daemons, ..) but we should do our part and remove labwc
from the puzzle of potential issues causing this not to work.

[1] https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/f6008ffff41f67e3c20bd8b3be8f216da6a4bb30)

include/labwc.h
src/server.c
src/xdg.c

index b8d839e61f09c4077366a80cddf077f8b2b3b9a2..c95b18106d25aa708636d8c0009cf9f6f98e8287 100644 (file)
@@ -37,6 +37,7 @@
 #include <wlr/types/wlr_server_decoration.h>
 #include <wlr/types/wlr_subcompositor.h>
 #include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_activation_v1.h>
 #include <wlr/types/wlr_xdg_decoration_v1.h>
 #include <wlr/types/wlr_xdg_shell.h>
 #include <wlr/types/wlr_drm_lease_v1.h>
@@ -226,6 +227,9 @@ struct server {
        struct wl_listener input_inhibit_activate;
        struct wl_listener input_inhibit_deactivate;
 
+       struct wlr_xdg_activation_v1 *xdg_activation;
+       struct wl_listener xdg_activation_request;
+
        struct wl_list views;
        struct wl_list unmanaged_surfaces;
 
@@ -342,6 +346,8 @@ void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup);
 
 void xdg_toplevel_decoration(struct wl_listener *listener, void *data);
 
+void xdg_activation_handle_request(struct wl_listener *listener, void *data);
+
 void xdg_surface_new(struct wl_listener *listener, void *data);
 
 void foreign_toplevel_handle_create(struct view *view);
index 3d36526caabe92d7c8c3c2020a4fd483e001447a..bf74b871389a915fdf8bf6914193421376742f3b 100644 (file)
@@ -331,6 +331,15 @@ server_init(struct server *server)
                ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
                : WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
 
+       server->xdg_activation = wlr_xdg_activation_v1_create(server->wl_display);
+       if (!server->xdg_activation) {
+               wlr_log(WLR_ERROR, "unable to create xdg_activation interface");
+               exit(EXIT_FAILURE);
+       }
+       server->xdg_activation_request.notify = xdg_activation_handle_request;
+       wl_signal_add(&server->xdg_activation->events.request_activate,
+               &server->xdg_activation_request);
+
        struct wlr_presentation *presentation =
                wlr_presentation_create(server->wl_display, server->backend);
        if (!presentation) {
index dcf41b6ee3e083e122d9b0981235b4197b544c4c..ee2e50a779f8fab37e756980b1d4aa3c181f55a9 100644 (file)
--- a/src/xdg.c
+++ b/src/xdg.c
@@ -390,6 +390,46 @@ static const struct view_impl xdg_toplevel_view_impl = {
        .maximize = xdg_toplevel_view_maximize,
 };
 
+void
+xdg_activation_handle_request(struct wl_listener *listener, void *data)
+{
+       const struct wlr_xdg_activation_v1_request_activate_event *event = data;
+
+       if (!wlr_surface_is_xdg_surface(event->surface)) {
+               return;
+       }
+       struct wlr_xdg_surface *xdg_surface =
+               wlr_xdg_surface_from_wlr_surface(event->surface);
+       struct view *view = xdg_surface ? xdg_surface->data : NULL;
+
+       if (!view) {
+               wlr_log(WLR_INFO, "Not activating surface - no view attached to surface");
+               return;
+       }
+       if (!event->token->seat) {
+               wlr_log(WLR_INFO, "Denying focus request, seat wasn't supplied");
+               return;
+       }
+       /*
+        * We do not check for event->token->surface here because it may already
+        * be destroyed and thus being NULL. With wlroots 0.17 we can hook into
+        * the `new_token` signal, attach further information to the token and
+        * then react to that information here instead. For now we just check
+        * for the seat / serial being correct and then allow the request.
+        */
+
+       /*
+        * TODO: This is the exact same code as used in foreign.c.
+        *       Refactor it into a public helper function somewhere.
+        */
+       wlr_log(WLR_DEBUG, "Activating surface");
+       if (view->workspace != view->server->workspace_current) {
+               workspaces_switch_to(view->workspace);
+       }
+       desktop_focus_and_activate_view(&view->server->seat, view);
+       desktop_move_to_front(view);
+}
+
 /*
  * We use the following struct user_data pointers:
  *   - wlr_xdg_surface->data = view