]> git.mdlowis.com Git - proto/labwc.git/commitdiff
implement input_inhibit protocol, needed for swaylock
authorDaniel Barlow <dan@telent.net>
Sat, 21 Aug 2021 16:12:02 +0000 (17:12 +0100)
committerDaniel Barlow <dan@telent.net>
Sun, 22 Aug 2021 20:30:42 +0000 (21:30 +0100)
this is in "it appears to work" state, though I blindly copy-pasted a little
more code than I'm happy with, so might benefit from a review

include/labwc.h
protocols/meson.build
protocols/wlr-input-inhibitor-unstable-v1.xml [new file with mode: 0644]
src/cursor.c
src/desktop.c
src/keyboard.c
src/server.c

index 367838c889b4a079169396926468fe6e2fae5bce..39613272e088756380e31a0f0ef91ce1a0fa69b4 100644 (file)
@@ -65,6 +65,7 @@ struct seat {
        /* if set, views cannot receive focus */
        struct wlr_layer_surface_v1 *focused_layer;
 
+       struct wl_client *active_client_while_inhibited;
        struct wl_list inputs;
        struct wl_listener new_input;
 
@@ -99,6 +100,10 @@ struct server {
        struct wl_listener new_xwayland_surface;
 #endif
 
+       struct wlr_input_inhibit_manager *input_inhibit;
+       struct wl_listener input_inhibit_activate;
+       struct wl_listener input_inhibit_deactivate;
+
        struct wl_list views;
        struct wl_list unmanaged_surfaces;
 
@@ -369,4 +374,11 @@ void action(struct server *server, const char *action, const char *command);
 /* update onscreen display 'alt-tab' texture */
 void osd_update(struct server *server);
 
+/* wlroots "input inhibitor" extension (required for swaylock) blocks
+ * any client other than the requesting client from receiving events
+ */
+bool input_inhibit_blocks_surface(struct seat *seat,
+                                 struct wl_resource *resource);
+
+
 #endif /* __LABWC_H */
index e4841906092193bac7a72eb89eeb5f2ba8044af9..efd39e981caaa2e85fd42a46721af090b3583416 100644 (file)
@@ -16,6 +16,7 @@ wayland_scanner_server = generator(
 server_protocols = [
        [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
        ['wlr-layer-shell-unstable-v1.xml'],
+       ['wlr-input-inhibitor-unstable-v1.xml'],
 ]
 
 server_protos_src = []
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml
new file mode 100644 (file)
index 0000000..b62d1bb
--- /dev/null
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_input_inhibit_unstable_v1">
+  <copyright>
+    Copyright © 2018 Drew DeVault
+
+    Permission to use, copy, modify, distribute, and sell this
+    software and its documentation for any purpose is hereby granted
+    without fee, provided that the above copyright notice appear in
+    all copies and that both that copyright notice and this permission
+    notice appear in supporting documentation, and that the name of
+    the copyright holders not be used in advertising or publicity
+    pertaining to distribution of the software without specific,
+    written prior permission.  The copyright holders make no
+    representations about the suitability of this software for any
+    purpose.  It is provided "as is" without express or implied
+    warranty.
+
+    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+    THIS SOFTWARE.
+  </copyright>
+
+  <interface name="zwlr_input_inhibit_manager_v1" version="1">
+    <description summary="inhibits input events to other clients">
+      Clients can use this interface to prevent input events from being sent to
+      any surfaces but its own, which is useful for example in lock screen
+      software. It is assumed that access to this interface will be locked down
+      to whitelisted clients by the compositor.
+    </description>
+
+    <request name="get_inhibitor">
+      <description summary="inhibit input to other clients">
+        Activates the input inhibitor. As long as the inhibitor is active, the
+        compositor will not send input events to other clients.
+      </description>
+      <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
+    </request>
+
+    <enum name="error">
+      <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
+    </enum>
+  </interface>
+
+  <interface name="zwlr_input_inhibitor_v1" version="1">
+    <description summary="inhibits input to other clients">
+      While this resource exists, input to clients other than the owner of the
+      inhibitor resource will not receive input events. The client that owns
+      this resource will receive all input events normally. The compositor will
+      also disable all of its own input processing (such as keyboard shortcuts)
+      while the inhibitor is active.
+
+      The compositor may continue to send input events to selected clients,
+      such as an on-screen keyboard (via the input-method protocol).
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the input inhibitor object">
+        Destroy the inhibitor and allow other clients to receive input.
+      </description>
+    </request>
+  </interface>
+</protocol>
index 4f7c3830e03f850724068d6b7bfee1123a1f25bc..7070959372c489d002b0bc8f2cf4e2b1261a0a28 100644 (file)
@@ -105,6 +105,15 @@ set_cursor(struct server *server, const char *cursor_name)
                server->seat.xcursor_manager, cursor_name, server->seat.cursor);
 }
 
+bool input_inhibit_blocks_surface(struct seat *seat,
+                                 struct wl_resource *resource)
+{
+       struct wl_client * inhibiting_client =
+               seat->active_client_while_inhibited;
+       return (inhibiting_client != NULL) &&
+               inhibiting_client != wl_resource_get_client(resource);
+}
+
 static void
 process_cursor_motion(struct server *server, uint32_t time)
 {
@@ -161,7 +170,8 @@ process_cursor_motion(struct server *server, uint32_t time)
        /* Required for iconify/maximize/close button mouse-over deco */
        damage_all_outputs(server);
 
-       if (surface) {
+       if (surface &&
+           ! input_inhibit_blocks_surface(&server->seat, surface->resource)) {
                bool focus_changed =
                        wlr_seat->pointer_state.focused_surface != surface;
                /*
index 488a6db72c239eb2fa30bbd2f65e6dc915f6f6a7..4c122fb0f27445e4f654311a07b5822d615d93bf 100644 (file)
@@ -76,6 +76,9 @@ desktop_set_focus_view_only(struct seat *seat, struct view *view)
        if (!view || view->minimized || !view->mapped) {
                return;
        }
+       if(input_inhibit_blocks_surface(seat, view->surface->resource))
+           return;
+
        struct wlr_surface *prev_surface;
        prev_surface = seat->seat->keyboard_state.focused_surface;
        if (prev_surface == view->surface) {
@@ -96,6 +99,9 @@ desktop_focus_view(struct seat *seat, struct view *view)
                seat_focus_surface(seat, NULL);
                return;
        }
+       if(input_inhibit_blocks_surface(seat, view->surface->resource))
+           return;
+
        if (view->minimized) {
                /* this will unmap and then focus */
                view_minimize(view, false);
index 7bf5ac5184eb7fde8bd2ad583906ee0248db75f6..3111a97903a8903a08d6440ce3b7cf98375a5966 100644 (file)
@@ -41,14 +41,12 @@ handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym)
        return false;
 }
 
-static void
-keyboard_key_notify(struct wl_listener *listener, void *data)
+static bool
+handle_compositor_keybindings(struct wl_listener *listener,
+                             struct wlr_event_keyboard_key *event)
 {
-       /* This event is raised when a key is pressed or released. */
        struct seat *seat = wl_container_of(listener, seat, keyboard_key);
        struct server *server = seat->server;
-       struct wlr_event_keyboard_key *event = data;
-       struct wlr_seat *wlr_seat = server->seat.seat;
        struct wlr_input_device *device = seat->keyboard_group->input_device;
 
        /* Translate libinput keycode -> xkbcommon */
@@ -68,13 +66,14 @@ keyboard_key_notify(struct wl_listener *listener, void *data)
                        /* end cycle */
                        desktop_focus_view(&server->seat, server->cycle_view);
                        server->cycle_view = NULL;
+                       /* XXX should we handled=true here? */
                } else if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
                        /* cycle to next */
                        server->cycle_view =
                                desktop_cycle_view(server, server->cycle_view);
                        osd_update(server);
                        damage_all_outputs(server);
-                       return;
+                       return true;
                }
        }
 
@@ -95,8 +94,28 @@ keyboard_key_notify(struct wl_listener *listener, void *data)
                        }
                }
        }
+       return handled;
+}
+static void
+keyboard_key_notify(struct wl_listener *listener, void *data)
+{
+        /* XXX need to check if input inhibited before doing any
+        * compositor bindings
+        */
+
+       /* This event is raised when a key is pressed or released. */
+       struct seat *seat = wl_container_of(listener, seat, keyboard_key);
+       struct server *server = seat->server;
+       struct wlr_event_keyboard_key *event = data;
+       struct wlr_seat *wlr_seat = server->seat.seat;
+       struct wlr_input_device *device = seat->keyboard_group->input_device;
+
+       bool handled = false;
+
+       if(!seat->active_client_while_inhibited)
+               /* ignore labwc keybindings if input is inhibited */
+               handled = handle_compositor_keybindings(listener, event);
 
-       /* Otherwise, pass it to the client. */
        if (!handled) {
                wlr_seat_set_keyboard(wlr_seat, device);
                wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
index 45e30ef286a0e7b5999f67d296ecf27244299cf0..af5ce56624e90f98f61a832b42e42e04c5aaedfe 100644 (file)
@@ -5,6 +5,7 @@
 #include <wlr/types/wlr_data_control_v1.h>
 #include <wlr/types/wlr_export_dmabuf_v1.h>
 #include <wlr/types/wlr_gamma_control_v1.h>
+#include <wlr/types/wlr_input_inhibitor.h>
 #include <wlr/types/wlr_primary_selection_v1.h>
 #include <wlr/types/wlr_screencopy_v1.h>
 #include "config/rcxml.h"
@@ -78,6 +79,81 @@ drop_permissions(void)
        }
 }
 
+static void seat_clear_touch_points(struct seat *seat,
+                                   struct wl_client *active_client){
+       struct timespec now;
+       clock_gettime(CLOCK_MONOTONIC, &now);
+       struct wlr_touch_point *point;
+       wl_list_for_each(point, &seat->seat->touch_state.touch_points, link) {
+               if (point->client->client != active_client) {
+                       wlr_seat_touch_point_clear_focus(seat->seat,
+                                                        now.tv_nsec / 1000, point->touch_id);
+               }
+       }
+}
+
+
+static void seat_inhibit_input(struct seat *seat,  struct wl_client *active_client)
+{
+       seat->active_client_while_inhibited = active_client;
+
+       if(seat->focused_layer &&
+          (wl_resource_get_client(seat->focused_layer->resource) !=
+           active_client))
+       {
+               wlr_log(WLR_INFO, "defocus layer");
+               seat_set_focus_layer(seat, NULL); /* ? */
+       }
+       struct wlr_surface * previous_kb_surface = seat->seat->keyboard_state.focused_surface;
+       if (previous_kb_surface &&
+           wl_resource_get_client(previous_kb_surface->resource) != active_client) {
+               wlr_log(WLR_INFO, "defocus surface");
+               seat_focus_surface(seat, NULL);   /* keyboard focus */
+       }
+
+       struct wlr_seat_client * previous_ptr_client = seat->seat->pointer_state.focused_client;
+       if (previous_ptr_client &&
+           (previous_ptr_client->client != active_client)) {
+               wlr_log(WLR_INFO, "defocus ptr");
+               wlr_seat_pointer_clear_focus(seat->seat);
+       }
+       seat_clear_touch_points(seat, active_client);
+}
+
+static void seat_disinhibit_input(struct seat *seat)
+{
+       seat->active_client_while_inhibited = NULL;
+       // Triggers a refocus of the topmost surface layer if necessary
+       // TODO: Make layer surface focus per-output based on cursor position
+/*
+  struct roots_output *output;
+       wl_list_for_each(output, &seat->input->server->desktop->outputs, link) {
+               arrange_layers(output);
+*/
+}
+
+
+
+static void handle_input_inhibit(struct wl_listener *listener, void *data) {
+       wlr_log(WLR_INFO, "activate input inhibit");
+
+       struct server *server = wl_container_of(
+           listener, server, input_inhibit_activate);
+
+       seat_inhibit_input(&server->seat,
+           server->input_inhibit->active_client);
+}
+
+static void handle_input_disinhibit(struct wl_listener *listener, void *data) {
+       wlr_log(WLR_INFO, "deactivate input inhibit");
+
+       struct server *server = wl_container_of(
+           listener, server, input_inhibit_deactivate);
+
+       seat_disinhibit_input(&server->seat);
+}
+
+
 void
 server_init(struct server *server)
 {
@@ -203,6 +279,19 @@ server_init(struct server *server)
        wlr_data_control_manager_v1_create(server->wl_display);
        wlr_gamma_control_manager_v1_create(server->wl_display);
 
+       // struct wlr_input_inhibit_manager *input_inhibit_mgr = NULL;
+       server->input_inhibit = wlr_input_inhibit_manager_create(server->wl_display);
+       if (!server->input_inhibit) {
+               wlr_log(WLR_ERROR, "unable to create the input inhibit manager");
+               exit(EXIT_FAILURE);
+       }
+
+       wl_signal_add(&server->input_inhibit->events.activate, &server->input_inhibit_activate);
+       server->input_inhibit_activate.notify = handle_input_inhibit;
+
+       wl_signal_add(&server->input_inhibit->events.deactivate, &server->input_inhibit_deactivate);
+       server->input_inhibit_deactivate.notify = handle_input_disinhibit;
+
        server->foreign_toplevel_manager =
                wlr_foreign_toplevel_manager_v1_create(server->wl_display);