WIP: add protocol for clients to request to be shown over the lock screen

This commit is contained in:
Aren Moynihan 2024-10-29 19:35:40 -04:00
parent fdc4318ac6
commit d87ce180b3
13 changed files with 211 additions and 19 deletions

View file

@ -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;

View file

@ -0,0 +1,4 @@
#include <wayland-server-core.h>
struct sway_lockscreen_overlay;
struct sway_lockscreen_overlay *sway_lockscreen_overlay_create(struct wl_display *display);

View file

@ -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;

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="kde_lockscreen_overlay_v1">
<copyright><![CDATA[
SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
]]></copyright>
<interface name="kde_lockscreen_overlay_v1" version="1">
<description summary="Allow surfaces over the lockscreen">
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.
</description>
<enum name="error">
<entry name="invalid_surface_state" value="0" summary="the client provided an invalid surface state"/>
</enum>
<request name="allow">
<description summary="Tell about which surface could be raised above the lockscreen">
Informs the compositor that the surface could be shown when the screen is locked. This request should be called while the surface is unmapped.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="destroy" type="destructor">
<description summary="Destroy the kde_lockscreen_overlay_v1">
This won't affect the surface previously marked with the allow request.
</description>
</request>
</interface>
</protocol>

View file

@ -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 = []

View file

@ -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);

View file

@ -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++) {

View file

@ -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;
}

View file

@ -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) {

87
sway/lockscreen-overlay.c Normal file
View file

@ -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 <stdlib.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_layer_shell_v1.h>
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;
}

View file

@ -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',

View file

@ -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 <wlr/xwayland/shell.h>
@ -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();

View file

@ -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)) {