diff options
-rw-r--r-- | include/sway/output.h | 1 | ||||
-rw-r--r-- | include/sway/server.h | 25 | ||||
-rw-r--r-- | include/sway/tree/root.h | 1 | ||||
-rw-r--r-- | sway/desktop/output.c | 4 | ||||
-rw-r--r-- | sway/desktop/render.c | 39 | ||||
-rw-r--r-- | sway/input/cursor.c | 18 | ||||
-rw-r--r-- | sway/input/keyboard.c | 2 | ||||
-rw-r--r-- | sway/input/seat.c | 19 | ||||
-rw-r--r-- | sway/input/switch.c | 2 | ||||
-rw-r--r-- | sway/lock.c | 350 | ||||
-rw-r--r-- | sway/tree/output.c | 2 | ||||
-rw-r--r-- | sway/tree/root.c | 1 |
12 files changed, 276 insertions, 188 deletions
diff --git a/include/sway/output.h b/include/sway/output.h index 28240819..8405f78d 100644 --- a/include/sway/output.h +++ b/include/sway/output.h | |||
@@ -24,6 +24,7 @@ struct sway_output { | |||
24 | struct { | 24 | struct { |
25 | struct wlr_scene_tree *tiling; | 25 | struct wlr_scene_tree *tiling; |
26 | struct wlr_scene_tree *fullscreen; | 26 | struct wlr_scene_tree *fullscreen; |
27 | struct wlr_scene_tree *session_lock; | ||
27 | } layers; | 28 | } layers; |
28 | 29 | ||
29 | // when a container is fullscreen, in case the fullscreen surface is | 30 | // when a container is fullscreen, in case the fullscreen surface is |
diff --git a/include/sway/server.h b/include/sway/server.h index f3d25980..33ffbf09 100644 --- a/include/sway/server.h +++ b/include/sway/server.h | |||
@@ -28,6 +28,19 @@ | |||
28 | 28 | ||
29 | struct sway_transaction; | 29 | struct sway_transaction; |
30 | 30 | ||
31 | struct sway_session_lock { | ||
32 | struct wlr_session_lock_v1 *lock; | ||
33 | struct wlr_surface *focused; | ||
34 | bool abandoned; | ||
35 | |||
36 | struct wl_list outputs; // struct sway_session_lock_output | ||
37 | |||
38 | // invalid if the session is abandoned | ||
39 | struct wl_listener new_surface; | ||
40 | struct wl_listener unlock; | ||
41 | struct wl_listener destroy; | ||
42 | }; | ||
43 | |||
31 | struct sway_server { | 44 | struct sway_server { |
32 | struct wl_display *wl_display; | 45 | struct wl_display *wl_display; |
33 | struct wl_event_loop *wl_event_loop; | 46 | struct wl_event_loop *wl_event_loop; |
@@ -92,15 +105,9 @@ struct sway_server { | |||
92 | struct wl_listener gamma_control_set_gamma; | 105 | struct wl_listener gamma_control_set_gamma; |
93 | 106 | ||
94 | struct { | 107 | struct { |
95 | bool locked; | 108 | struct sway_session_lock *lock; |
96 | struct wlr_session_lock_manager_v1 *manager; | 109 | struct wlr_session_lock_manager_v1 *manager; |
97 | 110 | ||
98 | struct wlr_session_lock_v1 *lock; | ||
99 | struct wlr_surface *focused; | ||
100 | struct wl_listener lock_new_surface; | ||
101 | struct wl_listener lock_unlock; | ||
102 | struct wl_listener lock_destroy; | ||
103 | |||
104 | struct wl_listener new_lock; | 111 | struct wl_listener new_lock; |
105 | struct wl_listener manager_destroy; | 112 | struct wl_listener manager_destroy; |
106 | } session_lock; | 113 | } session_lock; |
@@ -174,6 +181,10 @@ void handle_new_output(struct wl_listener *listener, void *data); | |||
174 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); | 181 | void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); |
175 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); | 182 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); |
176 | void sway_session_lock_init(void); | 183 | void sway_session_lock_init(void); |
184 | void sway_session_lock_add_output(struct sway_session_lock *lock, | ||
185 | struct sway_output *output); | ||
186 | bool sway_session_lock_has_surface(struct sway_session_lock *lock, | ||
187 | struct wlr_surface *surface); | ||
177 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); | 188 | void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); |
178 | #if HAVE_XWAYLAND | 189 | #if HAVE_XWAYLAND |
179 | void handle_xwayland_surface(struct wl_listener *listener, void *data); | 190 | void handle_xwayland_surface(struct wl_listener *listener, void *data); |
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 4b48a651..0aae8938 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h | |||
@@ -41,6 +41,7 @@ struct sway_root { | |||
41 | struct wlr_scene_tree *fullscreen; | 41 | struct wlr_scene_tree *fullscreen; |
42 | struct wlr_scene_tree *fullscreen_global; | 42 | struct wlr_scene_tree *fullscreen_global; |
43 | struct wlr_scene_tree *seat; | 43 | struct wlr_scene_tree *seat; |
44 | struct wlr_scene_tree *session_lock; | ||
44 | } layers; | 45 | } layers; |
45 | 46 | ||
46 | #if HAVE_XWAYLAND | 47 | #if HAVE_XWAYLAND |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 321e2a72..1e4474ce 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -798,6 +798,10 @@ void handle_new_output(struct wl_listener *listener, void *data) { | |||
798 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, | 798 | output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, |
799 | output_repaint_timer_handler, output); | 799 | output_repaint_timer_handler, output); |
800 | 800 | ||
801 | if (server->session_lock.lock) { | ||
802 | sway_session_lock_add_output(server->session_lock.lock, output); | ||
803 | } | ||
804 | |||
801 | struct output_config *oc = find_output_config(output); | 805 | struct output_config *oc = find_output_config(output); |
802 | apply_output_config(oc, output); | 806 | apply_output_config(oc, output); |
803 | free_output_config(oc); | 807 | free_output_config(oc); |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 735dddb8..60431d79 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c | |||
@@ -1012,43 +1012,6 @@ void output_render(struct render_context *ctx) { | |||
1012 | pixman_region32_copy(&transformed_damage, damage); | 1012 | pixman_region32_copy(&transformed_damage, damage); |
1013 | transform_output_damage(&transformed_damage, wlr_output); | 1013 | transform_output_damage(&transformed_damage, wlr_output); |
1014 | 1014 | ||
1015 | if (server.session_lock.locked) { | ||
1016 | struct wlr_render_color clear_color = { | ||
1017 | .a = 1.0f | ||
1018 | }; | ||
1019 | if (server.session_lock.lock == NULL) { | ||
1020 | // abandoned lock -> red BG | ||
1021 | clear_color.r = 1.f; | ||
1022 | } | ||
1023 | |||
1024 | wlr_render_pass_add_rect(ctx->pass, &(struct wlr_render_rect_options){ | ||
1025 | .box = { .width = wlr_output->width, .height = wlr_output->height }, | ||
1026 | .color = clear_color, | ||
1027 | .clip = &transformed_damage, | ||
1028 | }); | ||
1029 | |||
1030 | if (server.session_lock.lock != NULL) { | ||
1031 | struct render_data data = { | ||
1032 | .alpha = 1.0f, | ||
1033 | .ctx = ctx, | ||
1034 | }; | ||
1035 | |||
1036 | struct wlr_session_lock_surface_v1 *lock_surface; | ||
1037 | wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) { | ||
1038 | if (lock_surface->output != wlr_output) { | ||
1039 | continue; | ||
1040 | } | ||
1041 | if (!lock_surface->surface->mapped) { | ||
1042 | continue; | ||
1043 | } | ||
1044 | |||
1045 | output_surface_for_each_surface(output, lock_surface->surface, | ||
1046 | 0.0, 0.0, render_surface_iterator, &data); | ||
1047 | } | ||
1048 | } | ||
1049 | goto renderer_end; | ||
1050 | } | ||
1051 | |||
1052 | if (output_has_opaque_overlay_layer_surface(output)) { | 1015 | if (output_has_opaque_overlay_layer_surface(output)) { |
1053 | goto render_overlay; | 1016 | goto render_overlay; |
1054 | } | 1017 | } |
@@ -1122,8 +1085,6 @@ render_overlay: | |||
1122 | &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | 1085 | &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); |
1123 | render_layer_popups(ctx, | 1086 | render_layer_popups(ctx, |
1124 | &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | 1087 | &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); |
1125 | |||
1126 | renderer_end: | ||
1127 | pixman_region32_fini(&transformed_damage); | 1088 | pixman_region32_fini(&transformed_damage); |
1128 | wlr_output_add_software_cursors_to_render_pass(wlr_output, ctx->pass, damage); | 1089 | wlr_output_add_software_cursors_to_render_pass(wlr_output, ctx->pass, damage); |
1129 | } | 1090 | } |
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index e8604193..107424c9 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -95,24 +95,6 @@ struct sway_node *node_at_coords( | |||
95 | double ox = lx, oy = ly; | 95 | double ox = lx, oy = ly; |
96 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); | 96 | wlr_output_layout_output_coords(root->output_layout, wlr_output, &ox, &oy); |
97 | 97 | ||
98 | if (server.session_lock.locked) { | ||
99 | if (server.session_lock.lock == NULL) { | ||
100 | return NULL; | ||
101 | } | ||
102 | struct wlr_session_lock_surface_v1 *lock_surf; | ||
103 | wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) { | ||
104 | if (lock_surf->output != wlr_output) { | ||
105 | continue; | ||
106 | } | ||
107 | |||
108 | *surface = wlr_surface_surface_at(lock_surf->surface, ox, oy, sx, sy); | ||
109 | if (*surface != NULL) { | ||
110 | return NULL; | ||
111 | } | ||
112 | } | ||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | // layer surfaces on the overlay layer are rendered on top | 98 | // layer surfaces on the overlay layer are rendered on top |
117 | if ((*surface = layer_surface_at(output, | 99 | if ((*surface = layer_surface_at(output, |
118 | &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], | 100 | &output->shell_layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index dea7a7bd..b97f0152 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -405,7 +405,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, | |||
405 | char *device_identifier = input_device_get_identifier(wlr_device); | 405 | char *device_identifier = input_device_get_identifier(wlr_device); |
406 | bool exact_identifier = keyboard->wlr->group != NULL; | 406 | bool exact_identifier = keyboard->wlr->group != NULL; |
407 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); | 407 | seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); |
408 | bool locked = server.session_lock.locked; | 408 | bool locked = server.session_lock.lock; |
409 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = | 409 | struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = |
410 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); | 410 | keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); |
411 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; | 411 | bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; |
diff --git a/sway/input/seat.c b/sway/input/seat.c index d2cd1ba4..b8daa297 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1060,19 +1060,10 @@ void seat_configure_xcursor(struct sway_seat *seat) { | |||
1060 | 1060 | ||
1061 | bool seat_is_input_allowed(struct sway_seat *seat, | 1061 | bool seat_is_input_allowed(struct sway_seat *seat, |
1062 | struct wlr_surface *surface) { | 1062 | struct wlr_surface *surface) { |
1063 | if (!server.session_lock.locked) { | 1063 | if (server.session_lock.lock) { |
1064 | return true; | 1064 | return sway_session_lock_has_surface(server.session_lock.lock, surface); |
1065 | } | ||
1066 | if (server.session_lock.lock == NULL) { | ||
1067 | return false; | ||
1068 | } | ||
1069 | struct wlr_session_lock_surface_v1 *lock_surf; | ||
1070 | wl_list_for_each(lock_surf, &server.session_lock.lock->surfaces, link) { | ||
1071 | if (lock_surf->surface == surface) { | ||
1072 | return true; | ||
1073 | } | ||
1074 | } | 1065 | } |
1075 | return false; | 1066 | return true; |
1076 | } | 1067 | } |
1077 | 1068 | ||
1078 | static void send_unfocus(struct sway_container *con, void *data) { | 1069 | static void send_unfocus(struct sway_container *con, void *data) { |
@@ -1277,8 +1268,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { | |||
1277 | } else { | 1268 | } else { |
1278 | seat_set_workspace_focus(seat, node); | 1269 | seat_set_workspace_focus(seat, node); |
1279 | } | 1270 | } |
1280 | if (server.session_lock.locked) { | 1271 | if (server.session_lock.lock) { |
1281 | seat_set_focus_surface(seat, server.session_lock.focused, false); | 1272 | seat_set_focus_surface(seat, server.session_lock.lock->focused, false); |
1282 | } | 1273 | } |
1283 | } | 1274 | } |
1284 | 1275 | ||
diff --git a/sway/input/switch.c b/sway/input/switch.c index f483cd23..831f4dbf 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c | |||
@@ -34,7 +34,7 @@ static bool sway_switch_trigger_test(enum sway_switch_trigger trigger, | |||
34 | 34 | ||
35 | static void execute_binding(struct sway_switch *sway_switch) { | 35 | static void execute_binding(struct sway_switch *sway_switch) { |
36 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; | 36 | struct sway_seat *seat = sway_switch->seat_device->sway_seat; |
37 | bool locked = server.session_lock.locked; | 37 | bool locked = server.session_lock.lock; |
38 | 38 | ||
39 | list_t *bindings = config->current_mode->switch_bindings; | 39 | list_t *bindings = config->current_mode->switch_bindings; |
40 | struct sway_switch_binding *matched_binding = NULL; | 40 | struct sway_switch_binding *matched_binding = NULL; |
diff --git a/sway/lock.c b/sway/lock.c index c4fbfe0e..2856ae67 100644 --- a/sway/lock.c +++ b/sway/lock.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <assert.h> | 2 | #include <assert.h> |
3 | #include <wlr/types/wlr_scene.h> | ||
3 | #include "log.h" | 4 | #include "log.h" |
4 | #include "sway/input/cursor.h" | 5 | #include "sway/input/cursor.h" |
5 | #include "sway/input/keyboard.h" | 6 | #include "sway/input/keyboard.h" |
@@ -9,19 +10,28 @@ | |||
9 | #include "sway/server.h" | 10 | #include "sway/server.h" |
10 | #include "sway/surface.h" | 11 | #include "sway/surface.h" |
11 | 12 | ||
12 | struct sway_session_lock_surface { | 13 | struct sway_session_lock_output { |
13 | struct wlr_session_lock_surface_v1 *lock_surface; | 14 | struct wlr_scene_tree *tree; |
15 | struct wlr_scene_rect *background; | ||
16 | struct sway_session_lock *lock; | ||
17 | |||
14 | struct sway_output *output; | 18 | struct sway_output *output; |
15 | struct wlr_surface *surface; | 19 | |
16 | struct wl_listener map; | 20 | struct wl_list link; // sway_session_lock::outputs |
21 | |||
17 | struct wl_listener destroy; | 22 | struct wl_listener destroy; |
18 | struct wl_listener surface_commit; | 23 | struct wl_listener commit; |
19 | struct wl_listener output_commit; | 24 | |
20 | struct wl_listener output_destroy; | 25 | struct wlr_session_lock_surface_v1 *surface; |
26 | |||
27 | // invalid if surface is NULL | ||
28 | struct wl_listener surface_destroy; | ||
29 | struct wl_listener surface_map; | ||
21 | }; | 30 | }; |
22 | 31 | ||
23 | static void set_lock_focused_surface(struct wlr_surface *focused) { | 32 | static void focus_surface(struct sway_session_lock *lock, |
24 | server.session_lock.focused = focused; | 33 | struct wlr_surface *focused) { |
34 | lock->focused = focused; | ||
25 | 35 | ||
26 | struct sway_seat *seat; | 36 | struct sway_seat *seat; |
27 | wl_list_for_each(seat, &server.input->seats, link) { | 37 | wl_list_for_each(seat, &server.input->seats, link) { |
@@ -29,104 +39,189 @@ static void set_lock_focused_surface(struct wlr_surface *focused) { | |||
29 | } | 39 | } |
30 | } | 40 | } |
31 | 41 | ||
42 | static void refocus_output(struct sway_session_lock_output *output) { | ||
43 | // Move the seat focus to another surface if one is available | ||
44 | if (output->lock->focused == output->surface->surface) { | ||
45 | struct wlr_surface *next_focus = NULL; | ||
46 | |||
47 | struct sway_session_lock_output *candidate; | ||
48 | wl_list_for_each(candidate, &output->lock->outputs, link) { | ||
49 | if (candidate == output || !candidate->surface) { | ||
50 | continue; | ||
51 | } | ||
52 | |||
53 | if (candidate->surface->surface->mapped) { | ||
54 | next_focus = candidate->surface->surface; | ||
55 | break; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | focus_surface(output->lock, next_focus); | ||
60 | } | ||
61 | } | ||
62 | |||
32 | static void handle_surface_map(struct wl_listener *listener, void *data) { | 63 | static void handle_surface_map(struct wl_listener *listener, void *data) { |
33 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map); | 64 | struct sway_session_lock_output *surf = wl_container_of(listener, surf, surface_map); |
34 | if (server.session_lock.focused == NULL) { | 65 | if (surf->lock->focused == NULL) { |
35 | set_lock_focused_surface(surf->surface); | 66 | focus_surface(surf->lock, surf->surface->surface); |
36 | } | 67 | } |
37 | cursor_rebase_all(); | 68 | cursor_rebase_all(); |
38 | surface_enter_output(surf->surface, surf->output); | ||
39 | output_damage_whole(surf->output); | ||
40 | } | 69 | } |
41 | 70 | ||
42 | static void handle_surface_commit(struct wl_listener *listener, void *data) { | 71 | static void handle_surface_destroy(struct wl_listener *listener, void *data) { |
43 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit); | 72 | struct sway_session_lock_output *output = |
44 | output_damage_surface(surf->output, 0, 0, surf->surface, false); | 73 | wl_container_of(listener, output, surface_destroy); |
74 | refocus_output(output); | ||
75 | |||
76 | sway_assert(output->surface, "Trying to destroy a surface that the lock doesn't think exists"); | ||
77 | output->surface = NULL; | ||
78 | wl_list_remove(&output->surface_destroy.link); | ||
79 | wl_list_remove(&output->surface_map.link); | ||
80 | } | ||
81 | |||
82 | static void lock_output_reconfigure(struct sway_session_lock_output *output) { | ||
83 | int width = output->output->width; | ||
84 | int height = output->output->height; | ||
85 | |||
86 | wlr_scene_rect_set_size(output->background, width, height); | ||
87 | |||
88 | if (output->surface) { | ||
89 | wlr_session_lock_surface_v1_configure(output->surface, width, height); | ||
90 | } | ||
45 | } | 91 | } |
46 | 92 | ||
47 | static void handle_output_commit(struct wl_listener *listener, void *data) { | 93 | static void handle_new_surface(struct wl_listener *listener, void *data) { |
94 | struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); | ||
95 | struct wlr_session_lock_surface_v1 *lock_surface = data; | ||
96 | struct sway_output *output = lock_surface->output->data; | ||
97 | |||
98 | sway_log(SWAY_DEBUG, "new lock layer surface"); | ||
99 | |||
100 | struct sway_session_lock_output *current_lock_output, *lock_output = NULL; | ||
101 | wl_list_for_each(current_lock_output, &lock->outputs, link) { | ||
102 | if (current_lock_output->output == output) { | ||
103 | lock_output = current_lock_output; | ||
104 | break; | ||
105 | } | ||
106 | } | ||
107 | sway_assert(lock_output, "Couldn't find output to lock"); | ||
108 | sway_assert(!lock_output->surface, "Tried to reassign a surface to an existing output"); | ||
109 | |||
110 | lock_output->surface = lock_surface; | ||
111 | |||
112 | wlr_scene_subsurface_tree_create(lock_output->tree, lock_surface->surface); | ||
113 | |||
114 | lock_output->surface_destroy.notify = handle_surface_destroy; | ||
115 | wl_signal_add(&lock_surface->events.destroy, &lock_output->surface_destroy); | ||
116 | lock_output->surface_map.notify = handle_surface_map; | ||
117 | wl_signal_add(&lock_surface->surface->events.map, &lock_output->surface_map); | ||
118 | |||
119 | lock_output_reconfigure(lock_output); | ||
120 | } | ||
121 | |||
122 | static void sway_session_lock_output_destroy(struct sway_session_lock_output *output) { | ||
123 | if (output->surface) { | ||
124 | refocus_output(output); | ||
125 | wl_list_remove(&output->surface_destroy.link); | ||
126 | wl_list_remove(&output->surface_map.link); | ||
127 | } | ||
128 | |||
129 | wl_list_remove(&output->commit.link); | ||
130 | wl_list_remove(&output->destroy.link); | ||
131 | wl_list_remove(&output->link); | ||
132 | |||
133 | free(output); | ||
134 | } | ||
135 | |||
136 | static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { | ||
137 | struct sway_session_lock_output *output = | ||
138 | wl_container_of(listener, output, destroy); | ||
139 | sway_session_lock_output_destroy(output); | ||
140 | } | ||
141 | |||
142 | static void lock_output_handle_commit(struct wl_listener *listener, void *data) { | ||
48 | struct wlr_output_event_commit *event = data; | 143 | struct wlr_output_event_commit *event = data; |
49 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit); | 144 | struct sway_session_lock_output *output = |
145 | wl_container_of(listener, output, commit); | ||
50 | if (event->state->committed & ( | 146 | if (event->state->committed & ( |
51 | WLR_OUTPUT_STATE_MODE | | 147 | WLR_OUTPUT_STATE_MODE | |
52 | WLR_OUTPUT_STATE_SCALE | | 148 | WLR_OUTPUT_STATE_SCALE | |
53 | WLR_OUTPUT_STATE_TRANSFORM)) { | 149 | WLR_OUTPUT_STATE_TRANSFORM)) { |
54 | wlr_session_lock_surface_v1_configure(surf->lock_surface, | 150 | lock_output_reconfigure(output); |
55 | surf->output->width, surf->output->height); | ||
56 | } | 151 | } |
57 | } | 152 | } |
58 | 153 | ||
59 | static void destroy_lock_surface(struct sway_session_lock_surface *surf) { | 154 | static struct sway_session_lock_output *session_lock_output_create( |
60 | // Move the seat focus to another surface if one is available | 155 | struct sway_session_lock *lock, struct sway_output *output) { |
61 | if (server.session_lock.focused == surf->surface) { | 156 | struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); |
62 | struct wlr_surface *next_focus = NULL; | 157 | if (!lock_output) { |
158 | sway_log(SWAY_ERROR, "failed to allocate a session lock output"); | ||
159 | return NULL; | ||
160 | } | ||
63 | 161 | ||
64 | struct wlr_session_lock_surface_v1 *other; | 162 | struct wlr_scene_tree *tree = wlr_scene_tree_create(output->layers.session_lock); |
65 | wl_list_for_each(other, &server.session_lock.lock->surfaces, link) { | 163 | if (!tree) { |
66 | if (other != surf->lock_surface && other->surface->mapped) { | 164 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene tree"); |
67 | next_focus = other->surface; | 165 | free(lock_output); |
68 | break; | 166 | return NULL; |
69 | } | ||
70 | } | ||
71 | set_lock_focused_surface(next_focus); | ||
72 | } | 167 | } |
73 | 168 | ||
74 | wl_list_remove(&surf->map.link); | 169 | struct wlr_scene_rect *background = wlr_scene_rect_create(tree, 0, 0, (float[4]){ |
75 | wl_list_remove(&surf->destroy.link); | 170 | lock->abandoned ? 1.f : 0.f, |
76 | wl_list_remove(&surf->surface_commit.link); | 171 | 0.f, |
77 | wl_list_remove(&surf->output_commit.link); | 172 | 0.f, |
78 | wl_list_remove(&surf->output_destroy.link); | 173 | 1.f, |
79 | output_damage_whole(surf->output); | 174 | }); |
80 | free(surf); | 175 | if (!background) { |
81 | } | 176 | sway_log(SWAY_ERROR, "failed to allocate a session lock output scene background"); |
177 | wlr_scene_node_destroy(&tree->node); | ||
178 | free(lock_output); | ||
179 | return NULL; | ||
180 | } | ||
82 | 181 | ||
83 | static void handle_surface_destroy(struct wl_listener *listener, void *data) { | 182 | lock_output->output = output; |
84 | struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy); | 183 | lock_output->tree = tree; |
85 | destroy_lock_surface(surf); | 184 | lock_output->background = background; |
86 | } | 185 | lock_output->lock = lock; |
186 | |||
187 | lock_output->destroy.notify = lock_node_handle_destroy; | ||
188 | wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); | ||
87 | 189 | ||
88 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | 190 | lock_output->commit.notify = lock_output_handle_commit; |
89 | struct sway_session_lock_surface *surf = | 191 | wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); |
90 | wl_container_of(listener, surf, output_destroy); | 192 | |
91 | destroy_lock_surface(surf); | 193 | lock_output_reconfigure(lock_output); |
194 | |||
195 | wl_list_insert(&lock->outputs, &lock_output->link); | ||
196 | |||
197 | return lock_output; | ||
92 | } | 198 | } |
93 | 199 | ||
94 | static void handle_new_surface(struct wl_listener *listener, void *data) { | 200 | static void sway_session_lock_destroy(struct sway_session_lock* lock) { |
95 | struct wlr_session_lock_surface_v1 *lock_surface = data; | 201 | struct sway_session_lock_output *lock_output, *tmp_lock_output; |
96 | struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf)); | 202 | wl_list_for_each_safe(lock_output, tmp_lock_output, &lock->outputs, link) { |
97 | if (surf == NULL) { | 203 | // destroying the node will also destroy the whole lock output |
98 | return; | 204 | wlr_scene_node_destroy(&lock_output->tree->node); |
99 | } | 205 | } |
100 | 206 | ||
101 | sway_log(SWAY_DEBUG, "new lock layer surface"); | 207 | if (server.session_lock.lock == lock) { |
208 | server.session_lock.lock = NULL; | ||
209 | } | ||
102 | 210 | ||
103 | struct sway_output *output = lock_surface->output->data; | 211 | if (!lock->abandoned) { |
104 | wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height); | 212 | wl_list_remove(&lock->destroy.link); |
105 | 213 | wl_list_remove(&lock->unlock.link); | |
106 | surf->lock_surface = lock_surface; | 214 | wl_list_remove(&lock->new_surface.link); |
107 | surf->surface = lock_surface->surface; | 215 | } |
108 | surf->output = output; | 216 | |
109 | surf->map.notify = handle_surface_map; | 217 | free(lock); |
110 | wl_signal_add(&lock_surface->surface->events.map, &surf->map); | ||
111 | surf->destroy.notify = handle_surface_destroy; | ||
112 | wl_signal_add(&lock_surface->events.destroy, &surf->destroy); | ||
113 | surf->surface_commit.notify = handle_surface_commit; | ||
114 | wl_signal_add(&surf->surface->events.commit, &surf->surface_commit); | ||
115 | surf->output_commit.notify = handle_output_commit; | ||
116 | wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit); | ||
117 | surf->output_destroy.notify = handle_output_destroy; | ||
118 | wl_signal_add(&output->node.events.destroy, &surf->output_destroy); | ||
119 | } | 218 | } |
120 | 219 | ||
121 | static void handle_unlock(struct wl_listener *listener, void *data) { | 220 | static void handle_unlock(struct wl_listener *listener, void *data) { |
221 | struct sway_session_lock *lock = wl_container_of(listener, lock, unlock); | ||
122 | sway_log(SWAY_DEBUG, "session unlocked"); | 222 | sway_log(SWAY_DEBUG, "session unlocked"); |
123 | server.session_lock.locked = false; | ||
124 | server.session_lock.lock = NULL; | ||
125 | server.session_lock.focused = NULL; | ||
126 | 223 | ||
127 | wl_list_remove(&server.session_lock.lock_new_surface.link); | 224 | sway_session_lock_destroy(lock); |
128 | wl_list_remove(&server.session_lock.lock_unlock.link); | ||
129 | wl_list_remove(&server.session_lock.lock_destroy.link); | ||
130 | 225 | ||
131 | struct sway_seat *seat; | 226 | struct sway_seat *seat; |
132 | wl_list_for_each(seat, &server.input->seats, link) { | 227 | wl_list_for_each(seat, &server.input->seats, link) { |
@@ -145,28 +240,22 @@ static void handle_unlock(struct wl_listener *listener, void *data) { | |||
145 | struct sway_output *output = root->outputs->items[i]; | 240 | struct sway_output *output = root->outputs->items[i]; |
146 | arrange_layers(output); | 241 | arrange_layers(output); |
147 | } | 242 | } |
148 | |||
149 | // redraw everything | ||
150 | for (int i = 0; i < root->outputs->length; ++i) { | ||
151 | struct sway_output *output = root->outputs->items[i]; | ||
152 | output_damage_whole(output); | ||
153 | } | ||
154 | } | 243 | } |
155 | 244 | ||
156 | static void handle_abandon(struct wl_listener *listener, void *data) { | 245 | static void handle_abandon(struct wl_listener *listener, void *data) { |
246 | struct sway_session_lock *lock = wl_container_of(listener, lock, destroy); | ||
157 | sway_log(SWAY_INFO, "session lock abandoned"); | 247 | sway_log(SWAY_INFO, "session lock abandoned"); |
158 | server.session_lock.lock = NULL; | ||
159 | server.session_lock.focused = NULL; | ||
160 | 248 | ||
161 | wl_list_remove(&server.session_lock.lock_new_surface.link); | 249 | struct sway_session_lock_output *lock_output; |
162 | wl_list_remove(&server.session_lock.lock_unlock.link); | 250 | wl_list_for_each(lock_output, &lock->outputs, link) { |
163 | wl_list_remove(&server.session_lock.lock_destroy.link); | 251 | wlr_scene_rect_set_color(lock_output->background, |
164 | 252 | (float[4]){ 1.f, 0.f, 0.f, 1.f }); | |
165 | // redraw everything | ||
166 | for (int i = 0; i < root->outputs->length; ++i) { | ||
167 | struct sway_output *output = root->outputs->items[i]; | ||
168 | output_damage_whole(output); | ||
169 | } | 253 | } |
254 | |||
255 | lock->abandoned = true; | ||
256 | wl_list_remove(&lock->destroy.link); | ||
257 | wl_list_remove(&lock->unlock.link); | ||
258 | wl_list_remove(&lock->new_surface.link); | ||
170 | } | 259 | } |
171 | 260 | ||
172 | static void handle_session_lock(struct wl_listener *listener, void *data) { | 261 | static void handle_session_lock(struct wl_listener *listener, void *data) { |
@@ -174,44 +263,89 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { | |||
174 | struct wl_client *client = wl_resource_get_client(lock->resource); | 263 | struct wl_client *client = wl_resource_get_client(lock->resource); |
175 | 264 | ||
176 | if (server.session_lock.lock) { | 265 | if (server.session_lock.lock) { |
266 | if (server.session_lock.lock->abandoned) { | ||
267 | sway_log(SWAY_INFO, "Replacing abandoned lock"); | ||
268 | sway_session_lock_destroy(server.session_lock.lock); | ||
269 | } else { | ||
270 | sway_log(SWAY_ERROR, "Cannot lock an already locked session"); | ||
271 | wlr_session_lock_v1_destroy(lock); | ||
272 | return; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | struct sway_session_lock *sway_lock = calloc(1, sizeof(*sway_lock)); | ||
277 | if (!sway_lock) { | ||
278 | sway_log(SWAY_ERROR, "failed to allocate a session lock object"); | ||
177 | wlr_session_lock_v1_destroy(lock); | 279 | wlr_session_lock_v1_destroy(lock); |
178 | return; | 280 | return; |
179 | } | 281 | } |
180 | 282 | ||
283 | wl_list_init(&sway_lock->outputs); | ||
284 | |||
181 | sway_log(SWAY_DEBUG, "session locked"); | 285 | sway_log(SWAY_DEBUG, "session locked"); |
182 | server.session_lock.locked = true; | ||
183 | server.session_lock.lock = lock; | ||
184 | 286 | ||
185 | struct sway_seat *seat; | 287 | struct sway_seat *seat; |
186 | wl_list_for_each(seat, &server.input->seats, link) { | 288 | wl_list_for_each(seat, &server.input->seats, link) { |
187 | seat_unfocus_unless_client(seat, client); | 289 | seat_unfocus_unless_client(seat, client); |
188 | } | 290 | } |
189 | 291 | ||
190 | wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface); | 292 | struct sway_output *output; |
191 | wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock); | 293 | wl_list_for_each(output, &root->all_outputs, link) { |
192 | wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy); | 294 | sway_session_lock_add_output(sway_lock, output); |
295 | } | ||
296 | |||
297 | sway_lock->new_surface.notify = handle_new_surface; | ||
298 | wl_signal_add(&lock->events.new_surface, &sway_lock->new_surface); | ||
299 | sway_lock->unlock.notify = handle_unlock; | ||
300 | wl_signal_add(&lock->events.unlock, &sway_lock->unlock); | ||
301 | sway_lock->destroy.notify = handle_abandon; | ||
302 | wl_signal_add(&lock->events.destroy, &sway_lock->destroy); | ||
193 | 303 | ||
194 | wlr_session_lock_v1_send_locked(lock); | 304 | wlr_session_lock_v1_send_locked(lock); |
195 | 305 | server.session_lock.lock = sway_lock; | |
196 | // redraw everything | ||
197 | for (int i = 0; i < root->outputs->length; ++i) { | ||
198 | struct sway_output *output = root->outputs->items[i]; | ||
199 | output_damage_whole(output); | ||
200 | } | ||
201 | } | 306 | } |
202 | 307 | ||
203 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { | 308 | static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { |
204 | assert(server.session_lock.lock == NULL); | 309 | // if the server shuts down while a lock is active, destroy the lock |
310 | if (server.session_lock.lock) { | ||
311 | sway_session_lock_destroy(server.session_lock.lock); | ||
312 | } | ||
313 | |||
205 | wl_list_remove(&server.session_lock.new_lock.link); | 314 | wl_list_remove(&server.session_lock.new_lock.link); |
206 | wl_list_remove(&server.session_lock.manager_destroy.link); | 315 | wl_list_remove(&server.session_lock.manager_destroy.link); |
316 | |||
317 | server.session_lock.manager = NULL; | ||
318 | } | ||
319 | |||
320 | void sway_session_lock_add_output(struct sway_session_lock *lock, | ||
321 | struct sway_output *output) { | ||
322 | struct sway_session_lock_output *lock_output = | ||
323 | session_lock_output_create(lock, output); | ||
324 | |||
325 | // if we run out of memory while trying to lock the screen, the best we | ||
326 | // can do is kill the sway process. Security conscious users will have | ||
327 | // the sway session fall back to a login shell. | ||
328 | if (!lock_output) { | ||
329 | sway_log(SWAY_ERROR, "aborting: failed to allocate a lock output"); | ||
330 | abort(); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | bool sway_session_lock_has_surface(struct sway_session_lock *lock, | ||
335 | struct wlr_surface *surface) { | ||
336 | struct sway_session_lock_output *lock_output; | ||
337 | wl_list_for_each(lock_output, &lock->outputs, link) { | ||
338 | if (lock_output->surface && lock_output->surface->surface == surface) { | ||
339 | return true; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | return false; | ||
207 | } | 344 | } |
208 | 345 | ||
209 | void sway_session_lock_init(void) { | 346 | void sway_session_lock_init(void) { |
210 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); | 347 | server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); |
211 | 348 | ||
212 | server.session_lock.lock_new_surface.notify = handle_new_surface; | ||
213 | server.session_lock.lock_unlock.notify = handle_unlock; | ||
214 | server.session_lock.lock_destroy.notify = handle_abandon; | ||
215 | server.session_lock.new_lock.notify = handle_session_lock; | 349 | server.session_lock.new_lock.notify = handle_session_lock; |
216 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; | 350 | server.session_lock.manager_destroy.notify = handle_session_lock_destroy; |
217 | wl_signal_add(&server.session_lock.manager->events.new_lock, | 351 | wl_signal_add(&server.session_lock.manager->events.new_lock, |
diff --git a/sway/tree/output.c b/sway/tree/output.c index 12a2f969..64ca3d75 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -95,6 +95,7 @@ static void destroy_scene_layers(struct sway_output *output) { | |||
95 | 95 | ||
96 | wlr_scene_node_destroy(&output->layers.tiling->node); | 96 | wlr_scene_node_destroy(&output->layers.tiling->node); |
97 | wlr_scene_node_destroy(&output->layers.fullscreen->node); | 97 | wlr_scene_node_destroy(&output->layers.fullscreen->node); |
98 | wlr_scene_node_destroy(&output->layers.session_lock->node); | ||
98 | } | 99 | } |
99 | 100 | ||
100 | struct sway_output *output_create(struct wlr_output *wlr_output) { | 101 | struct sway_output *output_create(struct wlr_output *wlr_output) { |
@@ -104,6 +105,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { | |||
104 | bool failed = false; | 105 | bool failed = false; |
105 | output->layers.tiling = alloc_scene_tree(root->staging, &failed); | 106 | output->layers.tiling = alloc_scene_tree(root->staging, &failed); |
106 | output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); | 107 | output->layers.fullscreen = alloc_scene_tree(root->staging, &failed); |
108 | output->layers.session_lock = alloc_scene_tree(root->staging, &failed); | ||
107 | 109 | ||
108 | if (!failed) { | 110 | if (!failed) { |
109 | output->fullscreen_background = wlr_scene_rect_create( | 111 | output->fullscreen_background = wlr_scene_rect_create( |
diff --git a/sway/tree/root.c b/sway/tree/root.c index 38fcdb7c..e4941566 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c | |||
@@ -50,6 +50,7 @@ struct sway_root *root_create(struct wl_display *wl_display) { | |||
50 | root->layers.fullscreen = alloc_scene_tree(&root_scene->tree, &failed); | 50 | root->layers.fullscreen = alloc_scene_tree(&root_scene->tree, &failed); |
51 | root->layers.fullscreen_global = alloc_scene_tree(&root_scene->tree, &failed); | 51 | root->layers.fullscreen_global = alloc_scene_tree(&root_scene->tree, &failed); |
52 | root->layers.seat = alloc_scene_tree(&root_scene->tree, &failed); | 52 | root->layers.seat = alloc_scene_tree(&root_scene->tree, &failed); |
53 | root->layers.session_lock = alloc_scene_tree(&root_scene->tree, &failed); | ||
53 | 54 | ||
54 | if (failed) { | 55 | if (failed) { |
55 | wlr_scene_node_destroy(&root_scene->tree.node); | 56 | wlr_scene_node_destroy(&root_scene->tree.node); |