diff options
-rw-r--r-- | include/sway/input/seat.h | 5 | ||||
-rw-r--r-- | include/swaylock/swaylock.h | 1 | ||||
-rw-r--r-- | protocols/meson.build | 6 | ||||
-rw-r--r-- | protocols/wlr-input-inhibitor-unstable-v1.xml | 67 | ||||
-rw-r--r-- | sway/input/cursor.c | 6 | ||||
-rw-r--r-- | sway/input/input-manager.c | 8 | ||||
-rw-r--r-- | sway/input/seat.c | 68 | ||||
-rw-r--r-- | swaylock/main.c | 10 |
8 files changed, 160 insertions, 11 deletions
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 53031d70..4b0fc3c1 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h | |||
@@ -32,6 +32,9 @@ struct sway_seat { | |||
32 | // If the focused layer is set, views cannot receive keyboard focus | 32 | // If the focused layer is set, views cannot receive keyboard focus |
33 | struct wlr_layer_surface *focused_layer; | 33 | struct wlr_layer_surface *focused_layer; |
34 | 34 | ||
35 | // If exclusive_client is set, no other clients will receive input events | ||
36 | struct wl_client *exclusive_client; | ||
37 | |||
35 | struct wl_listener focus_destroy; | 38 | struct wl_listener focus_destroy; |
36 | struct wl_listener new_container; | 39 | struct wl_listener new_container; |
37 | 40 | ||
@@ -88,4 +91,6 @@ void seat_apply_config(struct sway_seat *seat, struct seat_config *seat_config); | |||
88 | 91 | ||
89 | struct seat_config *seat_get_config(struct sway_seat *seat); | 92 | struct seat_config *seat_get_config(struct sway_seat *seat); |
90 | 93 | ||
94 | bool seat_allow_input(struct sway_seat *seat, struct wlr_surface *surface); | ||
95 | |||
91 | #endif | 96 | #endif |
diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index ddca633d..06c94ead 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h | |||
@@ -33,6 +33,7 @@ struct swaylock_state { | |||
33 | struct wl_display *display; | 33 | struct wl_display *display; |
34 | struct wl_compositor *compositor; | 34 | struct wl_compositor *compositor; |
35 | struct zwlr_layer_shell_v1 *layer_shell; | 35 | struct zwlr_layer_shell_v1 *layer_shell; |
36 | struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager; | ||
36 | struct wl_shm *shm; | 37 | struct wl_shm *shm; |
37 | struct wl_list surfaces; | 38 | struct wl_list surfaces; |
38 | struct swaylock_args args; | 39 | struct swaylock_args args; |
diff --git a/protocols/meson.build b/protocols/meson.build index 0887cf86..7f83b16b 100644 --- a/protocols/meson.build +++ b/protocols/meson.build | |||
@@ -22,12 +22,14 @@ wayland_scanner_server = generator( | |||
22 | 22 | ||
23 | client_protocols = [ | 23 | client_protocols = [ |
24 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], | 24 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], |
25 | ['wlr-layer-shell-unstable-v1.xml'] | 25 | ['wlr-layer-shell-unstable-v1.xml'], |
26 | ['wlr-input-inhibitor-unstable-v1.xml'] | ||
26 | ] | 27 | ] |
27 | 28 | ||
28 | server_protocols = [ | 29 | server_protocols = [ |
29 | [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], | 30 | [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], |
30 | ['wlr-layer-shell-unstable-v1.xml'] | 31 | ['wlr-layer-shell-unstable-v1.xml'], |
32 | ['wlr-input-inhibitor-unstable-v1.xml'] | ||
31 | ] | 33 | ] |
32 | 34 | ||
33 | client_protos_src = [] | 35 | client_protos_src = [] |
diff --git a/protocols/wlr-input-inhibitor-unstable-v1.xml b/protocols/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 00000000..b62d1bb4 --- /dev/null +++ b/protocols/wlr-input-inhibitor-unstable-v1.xml | |||
@@ -0,0 +1,67 @@ | |||
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <protocol name="wlr_input_inhibit_unstable_v1"> | ||
3 | <copyright> | ||
4 | Copyright © 2018 Drew DeVault | ||
5 | |||
6 | Permission to use, copy, modify, distribute, and sell this | ||
7 | software and its documentation for any purpose is hereby granted | ||
8 | without fee, provided that the above copyright notice appear in | ||
9 | all copies and that both that copyright notice and this permission | ||
10 | notice appear in supporting documentation, and that the name of | ||
11 | the copyright holders not be used in advertising or publicity | ||
12 | pertaining to distribution of the software without specific, | ||
13 | written prior permission. The copyright holders make no | ||
14 | representations about the suitability of this software for any | ||
15 | purpose. It is provided "as is" without express or implied | ||
16 | warranty. | ||
17 | |||
18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||
19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||
20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | ||
23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | ||
24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | ||
25 | THIS SOFTWARE. | ||
26 | </copyright> | ||
27 | |||
28 | <interface name="zwlr_input_inhibit_manager_v1" version="1"> | ||
29 | <description summary="inhibits input events to other clients"> | ||
30 | Clients can use this interface to prevent input events from being sent to | ||
31 | any surfaces but its own, which is useful for example in lock screen | ||
32 | software. It is assumed that access to this interface will be locked down | ||
33 | to whitelisted clients by the compositor. | ||
34 | </description> | ||
35 | |||
36 | <request name="get_inhibitor"> | ||
37 | <description summary="inhibit input to other clients"> | ||
38 | Activates the input inhibitor. As long as the inhibitor is active, the | ||
39 | compositor will not send input events to other clients. | ||
40 | </description> | ||
41 | <arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/> | ||
42 | </request> | ||
43 | |||
44 | <enum name="error"> | ||
45 | <entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/> | ||
46 | </enum> | ||
47 | </interface> | ||
48 | |||
49 | <interface name="zwlr_input_inhibitor_v1" version="1"> | ||
50 | <description summary="inhibits input to other clients"> | ||
51 | While this resource exists, input to clients other than the owner of the | ||
52 | inhibitor resource will not receive input events. The client that owns | ||
53 | this resource will receive all input events normally. The compositor will | ||
54 | also disable all of its own input processing (such as keyboard shortcuts) | ||
55 | while the inhibitor is active. | ||
56 | |||
57 | The compositor may continue to send input events to selected clients, | ||
58 | such as an on-screen keyboard (via the input-method protocol). | ||
59 | </description> | ||
60 | |||
61 | <request name="destroy" type="destructor"> | ||
62 | <description summary="destroy the input inhibitor object"> | ||
63 | Destroy the inhibitor and allow other clients to receive input. | ||
64 | </description> | ||
65 | </request> | ||
66 | </interface> | ||
67 | </protocol> | ||
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 9229e92d..c56445eb 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -146,8 +146,10 @@ static void cursor_send_pointer_motion(struct sway_cursor *cursor, | |||
146 | 146 | ||
147 | // send pointer enter/leave | 147 | // send pointer enter/leave |
148 | if (surface != NULL) { | 148 | if (surface != NULL) { |
149 | wlr_seat_pointer_notify_enter(seat, surface, sx, sy); | 149 | if (seat_allow_input(cursor->seat, surface)) { |
150 | wlr_seat_pointer_notify_motion(seat, time, sx, sy); | 150 | wlr_seat_pointer_notify_enter(seat, surface, sx, sy); |
151 | wlr_seat_pointer_notify_motion(seat, time, sx, sy); | ||
152 | } | ||
151 | } else { | 153 | } else { |
152 | wlr_seat_pointer_clear_focus(seat); | 154 | wlr_seat_pointer_clear_focus(seat); |
153 | } | 155 | } |
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 3b2d1d55..f71a06e4 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -279,6 +279,14 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) | |||
279 | struct sway_seat *seat; | 279 | struct sway_seat *seat; |
280 | wl_list_for_each(seat, &input_manager->seats, link) { | 280 | wl_list_for_each(seat, &input_manager->seats, link) { |
281 | seat_set_exclusive_client(seat, NULL); | 281 | seat_set_exclusive_client(seat, NULL); |
282 | struct sway_container *previous = seat_get_focus(seat); | ||
283 | if (previous) { | ||
284 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, | ||
285 | container_type_to_str(previous->type), previous->name); | ||
286 | // Hack to get seat to re-focus the return value of get_focus | ||
287 | seat_set_focus(seat, previous->parent); | ||
288 | seat_set_focus(seat, previous); | ||
289 | } | ||
282 | } | 290 | } |
283 | } | 291 | } |
284 | 292 | ||
diff --git a/sway/input/seat.c b/sway/input/seat.c index 318fa9f6..0e26dde4 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,5 +1,7 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #define _POSIX_C_SOURCE 199309L | ||
2 | #include <assert.h> | 3 | #include <assert.h> |
4 | #include <time.h> | ||
3 | #include <wlr/types/wlr_cursor.h> | 5 | #include <wlr/types/wlr_cursor.h> |
4 | #include <wlr/types/wlr_output_layout.h> | 6 | #include <wlr/types/wlr_output_layout.h> |
5 | #include <wlr/types/wlr_xcursor_manager.h> | 7 | #include <wlr/types/wlr_xcursor_manager.h> |
@@ -9,6 +11,7 @@ | |||
9 | #include "sway/input/input-manager.h" | 11 | #include "sway/input/input-manager.h" |
10 | #include "sway/input/keyboard.h" | 12 | #include "sway/input/keyboard.h" |
11 | #include "sway/ipc-server.h" | 13 | #include "sway/ipc-server.h" |
14 | #include "sway/layers.h" | ||
12 | #include "sway/output.h" | 15 | #include "sway/output.h" |
13 | #include "sway/tree/container.h" | 16 | #include "sway/tree/container.h" |
14 | #include "sway/tree/view.h" | 17 | #include "sway/tree/view.h" |
@@ -350,6 +353,11 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
350 | seat->cursor->cursor->y); | 353 | seat->cursor->cursor->y); |
351 | } | 354 | } |
352 | 355 | ||
356 | bool seat_allow_input(struct sway_seat *seat, struct wlr_surface *surface) { | ||
357 | struct wl_client *client = wl_resource_get_client(surface->resource); | ||
358 | return !seat->exclusive_client || seat->exclusive_client == client; | ||
359 | } | ||
360 | |||
353 | void seat_set_focus_warp(struct sway_seat *seat, | 361 | void seat_set_focus_warp(struct sway_seat *seat, |
354 | struct sway_container *container, bool warp) { | 362 | struct sway_container *container, bool warp) { |
355 | if (seat->focused_layer) { | 363 | if (seat->focused_layer) { |
@@ -371,6 +379,12 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
371 | wl_list_remove(&seat_con->link); | 379 | wl_list_remove(&seat_con->link); |
372 | wl_list_insert(&seat->focus_stack, &seat_con->link); | 380 | wl_list_insert(&seat->focus_stack, &seat_con->link); |
373 | 381 | ||
382 | if (container->type == C_VIEW && !seat_allow_input( | ||
383 | seat, container->sway_view->surface)) { | ||
384 | wlr_log(L_DEBUG, "Refusing to set focus, input is inhibited"); | ||
385 | return; | ||
386 | } | ||
387 | |||
374 | if (container->type == C_VIEW) { | 388 | if (container->type == C_VIEW) { |
375 | seat_send_focus(seat, container); | 389 | seat_send_focus(seat, container); |
376 | } | 390 | } |
@@ -426,13 +440,13 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
426 | struct wlr_layer_surface *layer) { | 440 | struct wlr_layer_surface *layer) { |
427 | if (!layer && seat->focused_layer) { | 441 | if (!layer && seat->focused_layer) { |
428 | seat->focused_layer = NULL; | 442 | seat->focused_layer = NULL; |
429 | struct sway_container *c = seat_get_focus(seat); | 443 | struct sway_container *previous = seat_get_focus(seat); |
430 | if (c) { | 444 | if (previous) { |
431 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", c, | 445 | wlr_log(L_DEBUG, "Returning focus to %p %s '%s'", previous, |
432 | container_type_to_str(c->type), c->name); | 446 | container_type_to_str(previous->type), previous->name); |
433 | // Hack to get seat to re-focus the return value of get_focus | 447 | // Hack to get seat to re-focus the return value of get_focus |
434 | seat_set_focus(seat, c->parent); | 448 | seat_set_focus(seat, previous->parent); |
435 | seat_set_focus(seat, c); | 449 | seat_set_focus(seat, previous); |
436 | } | 450 | } |
437 | return; | 451 | return; |
438 | } else if (!layer || seat->focused_layer == layer) { | 452 | } else if (!layer || seat->focused_layer == layer) { |
@@ -462,7 +476,47 @@ void seat_set_focus_layer(struct sway_seat *seat, | |||
462 | 476 | ||
463 | void seat_set_exclusive_client(struct sway_seat *seat, | 477 | void seat_set_exclusive_client(struct sway_seat *seat, |
464 | struct wl_client *client) { | 478 | struct wl_client *client) { |
465 | // TODO | 479 | if (!client) { |
480 | seat->exclusive_client = client; | ||
481 | // Triggers a refocus of the topmost surface layer if necessary | ||
482 | // TODO: Make layer surface focus per-output based on cursor position | ||
483 | for (int i = 0; i < root_container.children->length; ++i) { | ||
484 | struct sway_container *output = root_container.children->items[i]; | ||
485 | if (!sway_assert(output->type == C_OUTPUT, | ||
486 | "root container has non-output child")) { | ||
487 | continue; | ||
488 | } | ||
489 | arrange_layers(output->sway_output); | ||
490 | } | ||
491 | return; | ||
492 | } | ||
493 | if (seat->focused_layer) { | ||
494 | if (wl_resource_get_client(seat->focused_layer->resource) != client) { | ||
495 | seat_set_focus_layer(seat, NULL); | ||
496 | } | ||
497 | } | ||
498 | if (seat->has_focus) { | ||
499 | struct sway_container *focus = seat_get_focus(seat); | ||
500 | if (focus->type == C_VIEW && wl_resource_get_client( | ||
501 | focus->sway_view->surface->resource) != client) { | ||
502 | seat_set_focus(seat, NULL); | ||
503 | } | ||
504 | } | ||
505 | if (seat->wlr_seat->pointer_state.focused_client) { | ||
506 | if (seat->wlr_seat->pointer_state.focused_client->client != client) { | ||
507 | wlr_seat_pointer_clear_focus(seat->wlr_seat); | ||
508 | } | ||
509 | } | ||
510 | struct timespec now; | ||
511 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
512 | struct wlr_touch_point *point; | ||
513 | wl_list_for_each(point, &seat->wlr_seat->touch_state.touch_points, link) { | ||
514 | if (point->client->client != client) { | ||
515 | wlr_seat_touch_point_clear_focus(seat->wlr_seat, | ||
516 | now.tv_nsec / 1000, point->touch_id); | ||
517 | } | ||
518 | } | ||
519 | seat->exclusive_client = client; | ||
466 | } | 520 | } |
467 | 521 | ||
468 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, | 522 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, |
diff --git a/swaylock/main.c b/swaylock/main.c index 7f502eb1..6cd4e41d 100644 --- a/swaylock/main.c +++ b/swaylock/main.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include "pool-buffer.h" | 19 | #include "pool-buffer.h" |
20 | #include "cairo.h" | 20 | #include "cairo.h" |
21 | #include "util.h" | 21 | #include "util.h" |
22 | #include "wlr-input-inhibitor-unstable-v1-client-protocol.h" | ||
22 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | 23 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" |
23 | 24 | ||
24 | static void daemonize() { | 25 | static void daemonize() { |
@@ -71,6 +72,9 @@ static void handle_global(void *data, struct wl_registry *registry, | |||
71 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | 72 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { |
72 | state->layer_shell = wl_registry_bind( | 73 | state->layer_shell = wl_registry_bind( |
73 | registry, name, &zwlr_layer_shell_v1_interface, 1); | 74 | registry, name, &zwlr_layer_shell_v1_interface, 1); |
75 | } else if (strcmp(interface, zwlr_input_inhibit_manager_v1_interface.name) == 0) { | ||
76 | state->input_inhibit_manager = wl_registry_bind( | ||
77 | registry, name, &zwlr_input_inhibit_manager_v1_interface, 1); | ||
74 | } else if (strcmp(interface, wl_output_interface.name) == 0) { | 78 | } else if (strcmp(interface, wl_output_interface.name) == 0) { |
75 | struct swaylock_surface *surface = | 79 | struct swaylock_surface *surface = |
76 | calloc(1, sizeof(struct swaylock_surface)); | 80 | calloc(1, sizeof(struct swaylock_surface)); |
@@ -187,6 +191,10 @@ int main(int argc, char **argv) { | |||
187 | wl_registry_add_listener(registry, ®istry_listener, &state); | 191 | wl_registry_add_listener(registry, ®istry_listener, &state); |
188 | wl_display_roundtrip(state.display); | 192 | wl_display_roundtrip(state.display); |
189 | assert(state.compositor && state.layer_shell && state.shm); | 193 | assert(state.compositor && state.layer_shell && state.shm); |
194 | if (!state.input_inhibit_manager) { | ||
195 | wlr_log(L_ERROR, "Compositor does not support the input inhibitor " | ||
196 | "protocol, refusing to run insecurely"); | ||
197 | } | ||
190 | 198 | ||
191 | if (wl_list_empty(&state.surfaces)) { | 199 | if (wl_list_empty(&state.surfaces)) { |
192 | wlr_log(L_DEBUG, "Exiting - no outputs to show on."); | 200 | wlr_log(L_DEBUG, "Exiting - no outputs to show on."); |
@@ -220,6 +228,8 @@ int main(int argc, char **argv) { | |||
220 | wl_display_roundtrip(state.display); | 228 | wl_display_roundtrip(state.display); |
221 | } | 229 | } |
222 | 230 | ||
231 | zwlr_input_inhibit_manager_v1_get_inhibitor(state.input_inhibit_manager); | ||
232 | |||
223 | state.run_display = true; | 233 | state.run_display = true; |
224 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { | 234 | while (wl_display_dispatch(state.display) != -1 && state.run_display) { |
225 | // This space intentionally left blank | 235 | // This space intentionally left blank |