diff --git a/include/sway/layers.h b/include/sway/layers.h index fd6384e0..d9cf6497 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -13,6 +13,7 @@ struct sway_layer_surface { struct wl_listener node_destroy; struct wl_listener new_popup; + bool show_over_lockscreen; bool mapped; struct wlr_scene_tree *popups; diff --git a/include/sway/lockscreen-overlay.h b/include/sway/lockscreen-overlay.h new file mode 100644 index 00000000..3b9e8921 --- /dev/null +++ b/include/sway/lockscreen-overlay.h @@ -0,0 +1,4 @@ +#include + +struct sway_lockscreen_overlay; +struct sway_lockscreen_overlay *sway_lockscreen_overlay_create(struct wl_display *display); diff --git a/include/sway/server.h b/include/sway/server.h index ccf4a9cc..aea746e3 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -97,6 +97,8 @@ struct sway_server { struct wl_listener manager_destroy; } session_lock; + struct sway_lockscreen_overlay *lockscreen_overlay; + struct wlr_output_power_manager_v1 *output_power_manager_v1; struct wl_listener output_power_manager_set_mode; struct wlr_input_method_manager_v2 *input_method; diff --git a/protocols/kde-lockscreen-overlay-v1.xml b/protocols/kde-lockscreen-overlay-v1.xml new file mode 100644 index 00000000..51872139 --- /dev/null +++ b/protocols/kde-lockscreen-overlay-v1.xml @@ -0,0 +1,38 @@ + + + + + SPDX-License-Identifier: LGPL-2.1-or-later + ]]> + + + + Allows a client to request a surface to be visible when the system is locked. + + This is meant to be used for specific high urgency cases like phone calls or alarms. + + Warning! The protocol described in this file is a desktop environment + implementation detail. Regular clients must not use this protocol. + Backward incompatible changes may be added without bumping the major + version of the extension. + + + + + + + + + Informs the compositor that the surface could be shown when the screen is locked. This request should be called while the surface is unmapped. + + + + + + + This won't affect the surface previously marked with the allow request. + + + + diff --git a/protocols/meson.build b/protocols/meson.build index d96f8757..04b5288a 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -18,6 +18,7 @@ protocols = [ 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', 'wlr-output-power-management-unstable-v1.xml', + 'kde-lockscreen-overlay-v1.xml', ] wl_protos_src = [] diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 333c09b4..8ed5a395 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -15,6 +15,7 @@ #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" +#include "sway/lock.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" @@ -54,7 +55,7 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( } static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, - struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) { + struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive, bool locked) { struct wlr_scene_node *node; wl_list_for_each(node, &tree->children, link) { struct sway_layer_surface *surface = scene_descriptor_try_get(node, @@ -64,6 +65,17 @@ static void arrange_surface(struct sway_output *output, const struct wlr_box *fu continue; } + if (!surface->show_over_lockscreen) { + wlr_scene_node_set_enabled(node, !locked); + /* wlr_scene_node_set_enabled(node, true); */ + if (locked) { + continue; + } + } else { + // TODO: not sure why this is required + wlr_scene_node_set_enabled(node, true); + } + if (!surface->scene->layer_surface->initialized) { continue; } @@ -82,19 +94,26 @@ void arrange_layers(struct sway_output *output) { &usable_area.width, &usable_area.height); const struct wlr_box full_area = usable_area; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); + bool locked = server.session_lock.lock; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); + if (locked) { + sway_log(SWAY_INFO, "arranging locked layout"); + } + + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true, locked); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true, locked); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true, locked); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true, locked); + + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false, locked); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false, locked); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false, locked); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false, locked); if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); output->usable_area = usable_area; + arrange_locks(); arrange_output(output); } else { arrange_popups(root->layers.popup); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 50a597a6..2dd18ac5 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -623,13 +623,22 @@ void arrange_popups(struct wlr_scene_tree *popups) { static void arrange_root(struct sway_root *root) { struct sway_container *fs = root->fullscreen_global; + bool locked = server.session_lock.lock; wlr_scene_node_set_enabled(&root->layers.shell_background->node, !fs); wlr_scene_node_set_enabled(&root->layers.shell_bottom->node, !fs); - wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs); - wlr_scene_node_set_enabled(&root->layers.floating->node, !fs); + wlr_scene_node_set_enabled(&root->layers.tiling->node, !fs && !locked); + wlr_scene_node_set_enabled(&root->layers.floating->node, !fs && !locked); wlr_scene_node_set_enabled(&root->layers.shell_top->node, !fs); - wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs); + wlr_scene_node_set_enabled(&root->layers.fullscreen->node, !fs && !locked); + wlr_scene_node_set_enabled(&root->layers.fullscreen_global->node, !locked); +#if WLR_HAS_XWAYLAND + wlr_scene_node_set_enabled(&root->layers.unmanaged->node, !locked); +#endif + + if (server.session_lock.lock) { + sway_log(SWAY_INFO, "arranging locked layout"); + } // hide all contents in the scratchpad for (int i = 0; i < root->scratchpad->length; i++) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 0dd26290..69c414f4 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1068,7 +1068,20 @@ void seat_configure_xcursor(struct sway_seat *seat) { bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface) { if (server.session_lock.lock) { - return sway_session_lock_has_surface(server.session_lock.lock, surface); + if (sway_session_lock_has_surface(server.session_lock.lock, surface)) { + return true; + } + + struct wlr_layer_surface_v1 *layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(surface); + struct sway_layer_surface *layer = + layer_surface ? layer_surface->data : NULL; + + if (layer && layer->show_over_lockscreen) { + return true; + } + + return false; } return true; } diff --git a/sway/lock.c b/sway/lock.c index 43f31330..a4086f0f 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -4,10 +4,12 @@ #include "log.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" +#include "sway/desktop/transaction.h" #include "sway/input/seat.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/lock.h" struct sway_session_lock_output { @@ -79,13 +81,13 @@ static void handle_surface_destroy(struct wl_listener *listener, void *data) { } static void lock_output_reconfigure(struct sway_session_lock_output *output) { - int width = output->output->width; - int height = output->output->height; + struct wlr_box usable_area = output->output->usable_area; - wlr_scene_rect_set_size(output->background, width, height); + wlr_scene_node_set_position(&output->tree->node, usable_area.x, usable_area.y); + wlr_scene_rect_set_size(output->background, usable_area.width, usable_area.height); if (output->surface) { - wlr_session_lock_surface_v1_configure(output->surface, width, height); + wlr_session_lock_surface_v1_configure(output->surface, usable_area.width, usable_area.height); } } @@ -234,6 +236,9 @@ static void handle_unlock(struct wl_listener *listener, void *data) { struct sway_output *output = root->outputs->items[i]; arrange_layers(output); } + + arrange_root(); + transaction_commit_dirty(); } static void handle_abandon(struct wl_listener *listener, void *data) { @@ -295,8 +300,17 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { sway_lock->destroy.notify = handle_abandon; wl_signal_add(&lock->events.destroy, &sway_lock->destroy); - wlr_session_lock_v1_send_locked(lock); server.session_lock.lock = sway_lock; + + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *output = root->outputs->items[i]; + arrange_layers(output); + } + + arrange_root(); + transaction_commit_dirty(); + + wlr_session_lock_v1_send_locked(lock); } static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/lockscreen-overlay.c b/sway/lockscreen-overlay.c new file mode 100644 index 00000000..865bce21 --- /dev/null +++ b/sway/lockscreen-overlay.c @@ -0,0 +1,87 @@ +#include "sway/lockscreen-overlay.h" +#include "sway/desktop/transaction.h" +#include "sway/layers.h" + +#include "kde-lockscreen-overlay-v1-protocol.h" +#include "log.h" +#include "sway/tree/arrange.h" + +#include +#include + +#include +#include + +struct sway_lockscreen_overlay { +}; + +static void kde_lockscreen_overlay_allow(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *surface) { + sway_log(SWAY_ERROR, "at %s:%d", __func__, __LINE__); + + struct wlr_surface *overlay_surface = wlr_surface_from_resource(surface); + // TODO: send a protocol error for each of these checks? + if (!overlay_surface) { + sway_log(SWAY_ERROR, "No overlay surface found"); + return; + } + + struct wlr_layer_surface_v1 *wlr_layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(overlay_surface); + if (!wlr_layer_surface) { + sway_log(SWAY_ERROR, "no wlr_layer surface found"); + return; + } + + struct sway_layer_surface *layer_surface = wlr_layer_surface->data; + if (!layer_surface) { + sway_log(SWAY_ERROR, "no sway_layer surface found"); + return; + } + + layer_surface->show_over_lockscreen = true; + + arrange_layers(layer_surface->output); + arrange_root(); + transaction_commit_dirty(); +} + +static void kde_lockscreen_overlay_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct kde_lockscreen_overlay_v1_interface impl = { + .allow = kde_lockscreen_overlay_allow, + .destroy = kde_lockscreen_overlay_destroy, +}; + +static void lockscreen_overlay_bind( + struct wl_client *client, + void *data, + uint32_t version, + uint32_t id +) { + struct sway_lockscreen_overlay *overlay = data; + + struct wl_resource *resource = wl_resource_create(client, + &kde_lockscreen_overlay_v1_interface, version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &impl, overlay, NULL); +} + +struct sway_lockscreen_overlay *sway_lockscreen_overlay_create(struct wl_display *display) { + struct sway_lockscreen_overlay *overlay = calloc(1, sizeof(*overlay)); + + wl_global_create( + display, + &kde_lockscreen_overlay_v1_interface, 1, + overlay, lockscreen_overlay_bind + ); + + return overlay; +} diff --git a/sway/meson.build b/sway/meson.build index 8042c89b..1867f56a 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -6,6 +6,7 @@ sway_sources = files( 'ipc-json.c', 'ipc-server.c', 'lock.c', + 'lockscreen-overlay.c', 'main.c', 'realtime.c', 'scene_descriptor.c', diff --git a/sway/server.c b/sway/server.c index e091d946..023b0405 100644 --- a/sway/server.c +++ b/sway/server.c @@ -57,6 +57,7 @@ #include "sway/server.h" #include "sway/input/cursor.h" #include "sway/tree/root.h" +#include "sway/lockscreen-overlay.h" #if WLR_HAS_XWAYLAND #include @@ -352,6 +353,8 @@ bool server_init(struct sway_server *server) { wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); server->foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server->wl_display); + server->lockscreen_overlay = + sway_lockscreen_overlay_create(server->wl_display); sway_session_lock_init(); diff --git a/sway/tree/root.c b/sway/tree/root.c index 19d072b5..5e5219f9 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -50,10 +50,10 @@ struct sway_root *root_create(struct wl_display *wl_display) { #if WLR_HAS_XWAYLAND root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); #endif + root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); root->layers.popup = alloc_scene_tree(root->layer_tree, &failed); root->layers.seat = alloc_scene_tree(root->layer_tree, &failed); - root->layers.session_lock = alloc_scene_tree(root->layer_tree, &failed); if (!failed && !scene_descriptor_assign(&root->layers.seat->node, SWAY_SCENE_DESC_NON_INTERACTIVE, (void *)1)) {