From 674bec3ddef93c9d7432bcd7c8f352ce8ab42273 Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Sun, 13 Jun 2021 13:01:13 -0400 Subject: [PATCH] Screenlock protocol prototype Note: none of the events on zwp_screenlocker_v1 are wired up yet. Should work otherwise. --- include/sway/lock.h | 2 - include/sway/security.h | 10 -- protocols/meson.build | 2 +- protocols/wp-screenlocker-unstable-v1.xml | 117 ++++++++++++++ sway/commands.c | 1 - sway/commands/lock_screen.c | 49 ------ sway/lock.c | 182 +++++++++------------- sway/meson.build | 2 - sway/security.c | 16 -- sway/server.c | 3 - 10 files changed, 188 insertions(+), 196 deletions(-) delete mode 100644 include/sway/security.h create mode 100644 protocols/wp-screenlocker-unstable-v1.xml delete mode 100644 sway/commands/lock_screen.c delete mode 100644 sway/security.c diff --git a/include/sway/lock.h b/include/sway/lock.h index 28cbbd3659..5d553fca0a 100644 --- a/include/sway/lock.h +++ b/include/sway/lock.h @@ -11,7 +11,6 @@ struct sway_lock_state { // if this is not NULL, screen is locked. If the lock screen crashed, // this may be set to PERMALOCK_CLIENT . struct wl_client *client; - struct wl_listener client_destroy; struct wlr_texture *permalock_message; struct wl_global *ext_unlocker_v1_global; @@ -20,7 +19,6 @@ struct sway_lock_state { // todo: need destroy void sway_lock_state_create(struct sway_lock_state *state, struct wl_display *display); -struct cmd_results *run_lockscreen_cmd(const char *cmd, bool fail_locked); struct sway_output; /** Create a texture which briefly explains the permalock state. */ diff --git a/include/sway/security.h b/include/sway/security.h deleted file mode 100644 index ad32bd3e97..0000000000 --- a/include/sway/security.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _SWAY_SECURITY_H -#define _SWAY_SECURITY_H - -#include -#include - -bool security_global_filter(const struct wl_client *client, - const struct wl_global *global, void *data); - -#endif /* _SWAY_SECURITY_H */ diff --git a/protocols/meson.build b/protocols/meson.build index 7858aaabe5..eaa3b03712 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -19,7 +19,7 @@ protocols = [ ['idle.xml'], ['wlr-input-inhibitor-unstable-v1.xml'], ['wlr-output-power-management-unstable-v1.xml'], - ['ext-unlocker-v1.xml'], + ['wp-screenlocker-unstable-v1.xml'], ] client_protocols = [ diff --git a/protocols/wp-screenlocker-unstable-v1.xml b/protocols/wp-screenlocker-unstable-v1.xml new file mode 100644 index 0000000000..41a2d254b4 --- /dev/null +++ b/protocols/wp-screenlocker-unstable-v1.xml @@ -0,0 +1,117 @@ + + + + + This interface provides notification of screen lock/unlock events and + (if supported) acting as a screenlocker. + + This is a privileged protocol; clients do not need access to this + protocol if they only want to display windows on the locked desktop. + + + + + + + + + This event will be sent on creation if the screen is currently locked. + + Windows on the unlocked desktop may still be visible due to a locking + animation. Frame callbacks should be used to identify when specific + surfaces are no longer visible. + + + + + + Windows on the locked desktop may still be visible due to a locking + animation. Frame callbacks should be used to identify when specific + surfaces are no longer visible. + + + + + + A new screen-locker should be started as soon as possible in order to + allow the user to unlock the session. + + + + + + Requesting a new lock handle will succeed if this client has permission + to lock the screen and either the screen is not currently locked or the + original locker of the screen is no longer present. If the screen was + not previously locked, the lock will be created as a temporary lock; see + zwp_screenlocker_lock_v1.set_persistent to change this. If a persistent + lock was abandoned, the lock will start out persistent. + + + + + + + + + This object is inert and should be destroyed. + + TODO: should the reason be an enum? Possible values: permission_denied, locker_exists. + + + + + + + The lock handle is active and only the locked desktop is visible on all + outputs. If a compositor implements a locking animation or similar + effect that results in both the locked and unlocked desktops being + visible, this event is sent after the effect completes. + + + + + + This avoids the screen unlocking if the client is killed or otherwise + disconnects without calling either set_temporary or unlock. It is valid + to call this on a newly created lock handle without waiting for an + event. + + This may be called immediately on creation of the lock handle or at any + point prior to calling unlock. For example, a lock triggered due to idle + may delay enabling persistance for a brief period after triggering the + lock where an immediate resumption of user activity does not require + authentication. + + + + + + This is intended for lockers that do not require authenticating or which + do not want to risk leaving the session in a locked state if the locking + process disconnects. + + + + + + Let the compositor know that it should unlock the screen. + + It is valid to call this on a newly created lock handle without waiting + for an event; this will either cancel the lock or do nothing if the lock + handle is inert. + + After this request has been sent by the client, this object will become + inert and should be destroyed. + + + + + + Destroying an active lock handle without first unlocking it will abandon + the lock, resulting in either a wp_screenlocker.lock_abandoned event or + an immediate unlock depending on if the lock is persistent. + + + + diff --git a/sway/commands.c b/sway/commands.c index d33529eba5..b09a04c71d 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -73,7 +73,6 @@ static const struct cmd_handler handlers[] = { { "gaps", cmd_gaps }, { "hide_edge_borders", cmd_hide_edge_borders }, { "input", cmd_input }, - { "lock_screen", cmd_lock_screen}, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, { "new_float", cmd_new_float }, diff --git a/sway/commands/lock_screen.c b/sway/commands/lock_screen.c deleted file mode 100644 index c1c64a24c1..0000000000 --- a/sway/commands/lock_screen.c +++ /dev/null @@ -1,49 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include - -#include "log.h" -#include "util.h" -#include "stringop.h" -#include "sway/commands.h" -#include "sway/config.h" -#include "sway/server.h" -#include "sway/input/seat.h" -#include "sway/output.h" - -struct cmd_results *cmd_lock_screen(int argc, char **argv) { - struct cmd_results *error = NULL; - if ((error = cmd_exec_validate(argc, argv))) { - return error; - } - bool fail_locked; - if (strcmp(argv[0], "--fail-locked") == 0) { - fail_locked = true; - argv++; - argc--; - } else if (strcmp(argv[0], "--fail-unlocked") == 0) { - fail_locked = false; - argv++; - argc--; - } else { - return cmd_results_new(CMD_FAILURE, "must set either --fail-locked or --fail-unlocked"); - } - - char *cmd; - if (argc == 1 && (argv[0][0] == '\'' || argv[0][0] == '"')) { - cmd = strdup(argv[0]); - strip_quotes(cmd); - } else { - cmd = join_args(argv, argc); - } - - if (config->reloading) { - char *args = join_args(argv, argc); - sway_log(SWAY_DEBUG, "Ignoring 'cmd_lock_screen %s' due to reload", args); - free(args); - return cmd_results_new(CMD_SUCCESS, NULL); - } - - struct cmd_results *res = run_lockscreen_cmd(cmd, fail_locked); - free(cmd); - return res; -} diff --git a/sway/lock.c b/sway/lock.c index c4786805d1..e9b40c0cb5 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -5,7 +5,7 @@ #include #include #include "cairo_util.h" -#include "ext-unlocker-v1-protocol.h" +#include "wp-screenlocker-unstable-v1-protocol.h" #include "log.h" #include "pango.h" #include "sway/input/seat.h" @@ -16,7 +16,9 @@ #include "sway/server.h" #include "util.h" -static void handle_unlocker_unlock(struct wl_client *client, +static void handle_locker_lock(struct wl_client *client, struct wl_resource *resource, uint32_t id); + +static void handle_lock_unlock(struct wl_client *client, struct wl_resource *resource) { if (server.lock_screen.client != client) { sway_log(SWAY_ERROR, "INVALID UNLOCK, IGNORING"); @@ -24,11 +26,13 @@ static void handle_unlocker_unlock(struct wl_client *client, } // The lockscreen may now shut down on its own schedule + wl_resource_set_user_data(resource, NULL); server.lock_screen.client = NULL; - wl_list_remove(&server.lock_screen.client_destroy.link); - wl_list_init(&server.lock_screen.client_destroy.link); + server.lock_screen.fail_locked = false; sway_log(SWAY_ERROR, "RECEIVED UNLOCK"); + // TODO broadcast unlock event + struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat_set_exclusive_client(seat, NULL); @@ -48,19 +52,44 @@ static void handle_unlocker_unlock(struct wl_client *client, } } -static const struct ext_unlocker_v1_interface unlock_impl = { - .unlock = handle_unlocker_unlock, +static void handle_lock_persist(struct wl_client *client, struct wl_resource *resource) +{ + void* data = wl_resource_get_user_data(resource); + if (!data) + return; + server.lock_screen.fail_locked = true; +} + +static void handle_lock_temporary(struct wl_client *client, struct wl_resource *resource) +{ + void* data = wl_resource_get_user_data(resource); + if (!data) + return; + server.lock_screen.fail_locked = false; +} + +static void resource_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zwp_screenlocker_v1_interface unlock_impl = { + .lock = handle_locker_lock, + .destroy = resource_destroy, +}; + +static const struct zwp_screenlocker_lock_v1_interface lock_impl = { + .unlock = handle_lock_unlock, + .set_persistent = handle_lock_persist, + .set_temporary = handle_lock_temporary, + .destroy = resource_destroy, }; static void screenlock_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) { - if (client != server.lock_screen.client) { - sway_log(SWAY_ERROR, "WRONG LOCKSCREEN CLIENT"); - wl_client_post_implementation_error(client, "wrong client, todo filter globals"); - return; - } struct wl_resource *resource = wl_resource_create(client, - &ext_unlocker_v1_interface, version, id); + &zwp_screenlocker_v1_interface, version, id); + // TODO make a list of screenlock objects for dispatching locked/unlocked events if (!resource) { wl_client_post_no_memory(client); return; @@ -69,45 +98,27 @@ static void screenlock_bind(struct wl_client *client, void *data, } -static void handle_lockscreen_client_destroy(struct wl_listener *listener, void *data) { - struct wl_client *client = data; - if (server.lock_screen.client != client) { - // this was an earlier lock screen client that had already - // unlocked; ignore it. No ABA risk since lock_screen.client - // must be live. - sway_log(SWAY_ERROR, "AN OLD LOCKSCREEN CLIENT DIED"); +static void lock_resource_destroy(struct wl_resource *resource) { + void* data = wl_resource_get_user_data(resource); + // ignore inert objects + if (!data) return; - } - wl_list_remove(&server.lock_screen.client_destroy.link); - wl_list_init(&server.lock_screen.client_destroy.link); + // assert(server.lock_screen.client == wl_resource_get_client(resource)); /* client closed, but did not unlock and reset the server.lock_screen_client */ if (server.lock_screen.fail_locked) { - // TODO: implement 'unlock_screen' command which which to recover - // from permalocking; or make a repeated 'lock_screen' work? - // or force people to swaymsg -- lock_screen --fail-unlocked /bin/false ? sway_log(SWAY_ERROR, "THE LOCKSCREEN CLIENT DIED, PERMALOCKING"); server.lock_screen.client = PERMALOCK_CLIENT; + // TODO broadcast lock_abandoned event struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat_set_exclusive_client(seat, PERMALOCK_CLIENT); } } else { - server.lock_screen.client = NULL; - sway_log(SWAY_ERROR, "THE LOCKSCREEN CLIENT DIED, UNLOCKING"); - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - seat_set_exclusive_client(seat, NULL); - // copied from input_manager -- deduplicate? - struct sway_node *previous = seat_get_focus(seat); - if (previous) { - // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, NULL); - seat_set_focus(seat, previous); - } - } + + handle_lock_unlock(wl_resource_get_client(resource), resource); } // redraw everything @@ -120,7 +131,7 @@ static void handle_lockscreen_client_destroy(struct wl_listener *listener, void void sway_lock_state_create(struct sway_lock_state *state, struct wl_display *display) { state->ext_unlocker_v1_global = - wl_global_create(display, &ext_unlocker_v1_interface, + wl_global_create(display, &zwp_screenlocker_v1_interface, 1, NULL, screenlock_bind); } @@ -174,42 +185,30 @@ struct wlr_texture *draw_permalock_message(struct sway_output *output) { return tex; } - -struct cmd_results *run_lockscreen_cmd(const char *cmd, bool fail_locked) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { - sway_log_errno(SWAY_ERROR, "socketpair failed"); - return cmd_results_new(CMD_FAILURE, "socketpair failed"); - } - if (!sway_set_cloexec(sockets[0], true) || !sway_set_cloexec(sockets[1], true)) { - return cmd_results_new(CMD_FAILURE, "cloexec failed"); - } - - /* Replace any existing lock screen with the new one */ - if (server.lock_screen.client) { - if (server.lock_screen.client != PERMALOCK_CLIENT) { - wl_list_remove(&server.lock_screen.client_destroy.link); - wl_list_init(&server.lock_screen.client_destroy.link); - wl_client_destroy(server.lock_screen.client); - } - server.lock_screen.client = NULL; +static void handle_locker_lock(struct wl_client *client, struct wl_resource *locker_resource, uint32_t id) +{ + bool inert = server.lock_screen.client && server.lock_screen.client != PERMALOCK_CLIENT; + struct wl_resource *lock_resource = wl_resource_create(client, + &zwp_screenlocker_lock_v1_interface, 1, id); + if (lock_resource == NULL) { + wl_client_post_no_memory(client); + return; } - server.lock_screen.fail_locked = fail_locked; + wl_resource_set_implementation(lock_resource, + &lock_impl, inert ? NULL : &server.lock_screen, + lock_resource_destroy); - server.lock_screen.client = wl_client_create(server.wl_display, sockets[0]); - if (!server.lock_screen.client) { - sway_log_errno(SWAY_ERROR, "wl_client_create failed"); - if (!fail_locked) { - return cmd_results_new(CMD_FAILURE, "wl_client_create failed"); - } - server.lock_screen.client = PERMALOCK_CLIENT; - } else { - server.lock_screen.client_destroy.notify = handle_lockscreen_client_destroy; - wl_client_add_destroy_listener(server.lock_screen.client, - &server.lock_screen.client_destroy); + if (inert) { + zwp_screenlocker_lock_v1_send_rejected(lock_resource, NULL); + return; } + server.lock_screen.client = client; + // TODO broadcast lock event + // TODO delay this send until next frame + zwp_screenlocker_lock_v1_send_locked(lock_resource); + // only lock screen gets input; this applies immediately, // before the lock screen program is set up struct sway_seat *seat; @@ -217,46 +216,5 @@ struct cmd_results *run_lockscreen_cmd(const char *cmd, bool fail_locked) { seat_set_exclusive_client(seat, server.lock_screen.client); } - pid_t pid = fork(); - if (pid < 0) { - sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); - return cmd_results_new(CMD_FAILURE, "fork failed"); - } else if (pid == 0) { - // Remove the SIGUSR1 handler that wlroots adds for xwayland - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGPIPE, SIG_DFL); - - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!sway_set_cloexec(sockets[1], false)) { - _exit(EXIT_FAILURE); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - execlp("sh", "sh", "-c", cmd, (void *)NULL); - _exit(EXIT_FAILURE); - } - _exit(EXIT_SUCCESS); - } - - if (close(sockets[1]) != 0) { - sway_log_errno(SWAY_ERROR, "close failed"); - return cmd_results_new(CMD_FAILURE, "close failed"); - } - - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return cmd_results_new(CMD_FAILURE, "waitpid failed"); - } - sway_log(SWAY_ERROR, "LOCKSCREEN CLIENT STARTED"); - return cmd_results_new(CMD_SUCCESS, NULL); + sway_log(SWAY_ERROR, "LOCKSCREEN STARTED"); } diff --git a/sway/meson.build b/sway/meson.build index 5a03051f0f..e7b1adedad 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -7,7 +7,6 @@ sway_sources = files( 'ipc-server.c', 'lock.c', 'main.c', - 'security.c', 'server.c', 'swaynag.c', 'xdg_activation_v1.c', @@ -69,7 +68,6 @@ sway_sources = files( 'commands/gaps.c', 'commands/hide_edge_borders.c', 'commands/inhibit_idle.c', - 'commands/lock_screen.c', 'commands/kill.c', 'commands/mark.c', 'commands/max_render_time.c', diff --git a/sway/security.c b/sway/security.c deleted file mode 100644 index 2ee60539df..0000000000 --- a/sway/security.c +++ /dev/null @@ -1,16 +0,0 @@ -#include "sway/security.h" -#include "sway/server.h" - -bool security_global_filter(const struct wl_client *client, - const struct wl_global *global, void *data) { - struct sway_server *server = data; - if (global == server->lock_screen.ext_unlocker_v1_global - && client != server->lock_screen.client) { - // do not provide the unlocker global to non-lockscreen - // clients. Note: a client that _used_ to be the lock screen - // will not be sent this global again, but may still have - // access to it from before. - return false; - } - return true; -} diff --git a/sway/server.c b/sway/server.c index 6ab6d1396b..8fae716277 100644 --- a/sway/server.c +++ b/sway/server.c @@ -38,7 +38,6 @@ #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/input-manager.h" #include "sway/output.h" -#include "sway/security.h" #include "sway/server.h" #include "sway/tree/root.h" #if HAVE_XWAYLAND @@ -170,8 +169,6 @@ bool server_init(struct sway_server *server) { sway_lock_state_create(&server->lock_screen, server->wl_display); - wl_display_set_global_filter(server->wl_display, security_global_filter, server); - // Avoid using "wayland-0" as display socket char name_candidate[16]; for (int i = 1; i <= 32; ++i) {