From 5b29514b09aaaa3c6e79160f1bdf5e8adcee0f30 Mon Sep 17 00:00:00 2001 From: DanyLE Date: Mon, 15 Apr 2024 17:32:53 +0200 Subject: [PATCH] feat: add lock session protocol support (WIP) --- Makefile | 1 + diyac.c | 5 + diyac.h | 31 ++++++- output.c | 13 ++- seat.c | 9 +- seat.h | 1 + session.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ session.h | 10 ++ 8 files changed, 339 insertions(+), 4 deletions(-) create mode 100644 session.c create mode 100644 session.h diff --git a/Makefile b/Makefile index 3819676..1ed69ea 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ OBJS=\ xdg.c \ xdg-shell-protocol.c \ layer.c \ + session.c \ wlr-layer-shell-unstable-v1-protocol.c # wayland-scanner is a tool which generates C headers and rigging for Wayland diff --git a/diyac.c b/diyac.c index 7eb628f..a7c53a5 100644 --- a/diyac.c +++ b/diyac.c @@ -9,6 +9,7 @@ #include "cursor.h" #include "seat.h" #include "layer.h" +#include "session.h" int main(int argc, char *argv[]) { @@ -121,6 +122,9 @@ int main(int argc, char *argv[]) * https://drewdevault.com/2018/07/29/Wayland-shells.html. */ wl_list_init(&server.views); + /** + * TODO: free these tree when finish + */ server.view_tree = wlr_scene_tree_create(&server.scene->tree); server.xdg_popup_tree = wlr_scene_tree_create(&server.scene->tree); @@ -134,6 +138,7 @@ int main(int argc, char *argv[]) wl_signal_add(&server.layer_shell->events.new_surface, &server.new_layer_surface); + diyac_init_session_lock(&server); diyac_init_cursor_manager(&server); /* diff --git a/diyac.h b/diyac.h index fb3d4de..726f11b 100644 --- a/diyac.h +++ b/diyac.h @@ -16,6 +16,7 @@ #include #include #include +#include #define LAYER_TREE_SZ 4 @@ -100,6 +101,18 @@ struct diyac_node_descriptor struct wl_listener destroy; }; +struct diyac_session_lock +{ + bool abandoned; + struct wlr_surface * focused; + + struct wlr_session_lock_v1* wlr_session_lock; + struct wl_listener new_surface; + struct wl_listener unlock; + struct wl_listener destroy; + //struct wl_listener new_output; +}; + struct diyac_server { struct wl_display *wl_display; @@ -117,7 +130,6 @@ struct diyac_server struct wl_list views; struct diyac_view * active_view; struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; - /* * Popups need to be rendered above always-on-top views, so we reparent * them to this dedicated tree @@ -135,6 +147,8 @@ struct diyac_server struct wlr_output_layout *output_layout; struct wl_list outputs; struct wl_listener new_output; + + struct diyac_session_lock * lock; }; struct diyac_layer_surface @@ -150,6 +164,19 @@ struct diyac_layer_surface struct wl_listener new_popup; }; +struct diyac_output_lock_handle +{ + struct wlr_scene_tree *tree; + struct wlr_scene_rect *background; + + struct wlr_session_lock_surface_v1 *surface; + struct diyac_output * output; + struct wl_listener surface_destroy; + struct wl_listener surface_map; + + struct wl_listener commit; +}; + struct diyac_output { struct wl_list link; @@ -164,6 +191,8 @@ struct diyac_output struct diyac_output_scenes scenes; // alias to diyac_output_scenes elements struct wlr_scene_tree *layer_tree[LAYER_TREE_SZ]; + // lock handle + struct diyac_output_lock_handle * lock_handle; }; struct foreign_toplevel diff --git a/output.c b/output.c index cd1cf6e..e0f8c82 100644 --- a/output.c +++ b/output.c @@ -7,6 +7,8 @@ #include "layer.h" #include "node.h" #include "view.h" +#include "session.h" + static void output_frame(struct wl_listener *listener, void *data) { /* This function is called every time an output is ready to display a frame, @@ -38,7 +40,10 @@ static void output_request_state(struct wl_listener *listener, void *data) static void output_destroy(struct wl_listener *listener, void *data) { struct diyac_output *output = wl_container_of(listener, output, destroy); - + if(output->lock_handle) + { + diyac_session_unlock_output(output); + } wlr_scene_node_destroy(&output->scenes.background->node); wlr_scene_node_destroy(&output->scenes.bottom->node); wlr_scene_node_destroy(&output->scenes.top->node); @@ -147,6 +152,12 @@ void diyac_server_new_output(struct wl_listener *listener, void *data) output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM] = output->scenes.bottom; output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_TOP] = output->scenes.top; output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY] = output->scenes.overlay; + + output->lock_handle = NULL; + if(server->lock) + { + diyac_session_lock_output(output); + } /* * Set the z-positions to achieve the following order (from top to * bottom): diff --git a/seat.c b/seat.c index b3a35ca..abc0446 100644 --- a/seat.c +++ b/seat.c @@ -271,10 +271,10 @@ static void seat_focus(struct diyac_seat *seat, struct wlr_surface *surface, boo * lock screen may lose focus and become impossible to unlock. */ struct diyac_server *server = seat->server; - /*if (server->session_lock && !is_lock_surface) + if (server->lock && !is_lock_surface) { return; - }*/ + } if (!surface) { @@ -314,6 +314,11 @@ void diyac_seat_focus_surface(struct diyac_seat *seat, struct wlr_surface *surfa seat_focus(seat, surface, /*is_lock_surface*/ false); } +void diyac_seat_focus_lock_surface(struct diyac_seat *seat, struct wlr_surface *surface) +{ + seat_focus(seat, surface, /*is_lock_surface*/ true); +} + void diyac_seat_focus_layer(struct diyac_seat *seat, struct wlr_layer_surface_v1 *layer) { if (!layer) diff --git a/seat.h b/seat.h index a485bf8..21d0527 100644 --- a/seat.h +++ b/seat.h @@ -4,5 +4,6 @@ void diyac_init_seat(struct diyac_server* server); void diyac_seat_focus_surface(struct diyac_seat *seat, struct wlr_surface *surface); +void diyac_seat_focus_lock_surface(struct diyac_seat *seat, struct wlr_surface *surface); void diyac_seat_focus_layer(struct diyac_seat *seat, struct wlr_layer_surface_v1 *layer); #endif \ No newline at end of file diff --git a/session.c b/session.c new file mode 100644 index 0000000..524a31b --- /dev/null +++ b/session.c @@ -0,0 +1,273 @@ +#include +#include +#include +#include "session.h" +#include "view.h" +#include "seat.h" + +static struct wl_listener g_lock_new; +static struct wl_listener g_lock_manager_destroy; +static struct wlr_session_lock_manager_v1 *g_session_lock_manager; +static struct diyac_server * g_server; + +static void focus_output(struct diyac_output * output) +{ + g_server->lock->focused = NULL; + if(output) + { + g_server->lock->focused = output->lock_handle->surface->surface; + } + diyac_seat_focus_lock_surface(&g_server->seat, g_server->lock->focused); +} + +static void session_lock_update_geometry(struct diyac_output* output, bool align) +{ + struct wlr_box box; + if(align) + { + wlr_output_layout_get_box(g_server->output_layout, output->wlr_output, &box); + wlr_scene_node_set_position(&output->scenes.session->node, box.x, box.y); + } + wlr_scene_rect_set_size(output->lock_handle->background, box.width, box.height); + if (output->lock_handle->surface) { + wlr_session_lock_surface_v1_configure(output->lock_handle->surface, box.width, box.height); + } +} + +static void handle_surface_map(struct wl_listener *listener, void *data) +{ + struct diyac_output_lock_handle *handle = wl_container_of(listener, handle, surface_map); + if (!g_server->lock->focused) + { + focus_output(handle->output); + } +} + +static void handle_surface_destroy(struct wl_listener *listener, void *data) +{ + struct diyac_output_lock_handle *handle = wl_container_of(listener, handle, surface_destroy); + + if(g_server->lock->focused == handle->surface->surface) + { + struct diyac_output *output; + wl_list_for_each(output, &g_server->outputs, link) + { + if(output == handle->output || + !output->lock_handle || + !output->lock_handle->surface || + !output->lock_handle->surface->surface) + { + continue; + } + if(output->lock_handle->surface->surface->mapped) + { + // focus surface + focus_output(output); + break; + } + } + focus_output(NULL); + } + + assert(handle->surface); + handle->surface = NULL; + wl_list_remove(&handle->surface_destroy.link); + wl_list_remove(&handle->surface_map.link); +} + +static void handle_new_surface(struct wl_listener *listener, void *data) +{ + struct diyac_session_lock *lock = wl_container_of(listener, lock, new_surface); + struct wlr_session_lock_surface_v1 *lock_surface = data; + struct diyac_output *output = lock_surface->output->data; + + assert(output->lock_handle); + + output->lock_handle->surface = lock_surface; + + wlr_scene_subsurface_tree_create(output->lock_handle->tree, lock_surface->surface); + + output->lock_handle->surface_destroy.notify = handle_surface_destroy; + wl_signal_add(&lock_surface->events.destroy, &output->lock_handle->surface_destroy); + + output->lock_handle->surface_map.notify = handle_surface_map; + wl_signal_add(&lock_surface->surface->events.map, &output->lock_handle->surface_map); + + session_lock_update_geometry(output, false); +} + +static void session_lock_destroy(struct diyac_session_lock *lock) +{ + struct diyac_output *output; + wl_list_for_each(output, &g_server->outputs, link) + { + diyac_session_unlock_output(output); + } + if (g_server->lock == lock) + { + g_server->lock = NULL; + } + if (!lock->abandoned) + { + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); + } + free(lock); +} + +static void handle_unlock(struct wl_listener *listener, void *data) +{ + struct diyac_session_lock *lock = wl_container_of(listener, lock, unlock); + session_lock_destroy(lock); + diyac_focus_topmost_view(g_server, true); +} + +static void handle_session_lock_destroy(struct wl_listener *listener, void *data) +{ + struct diyac_session_lock *lock = wl_container_of(listener, lock, destroy); + lock->abandoned = true; + wl_list_remove(&lock->destroy.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->new_surface.link); +} + +static void handle_new_session_lock(struct wl_listener *listener, void *data) +{ + struct wlr_session_lock_v1 *lock = data; + if(g_server->lock) + { + if(g_server->lock->abandoned) + { + session_lock_destroy(g_server->lock); + } + else + { + wlr_log(WLR_ERROR, "session already locked"); + return; + } + } + struct diyac_session_lock *session = malloc(sizeof(struct diyac_session_lock)); + if (!session) { + wlr_log(WLR_ERROR, "session-lock: out of memory"); + wlr_session_lock_v1_destroy(lock); + return; + } + session->abandoned = false; + session->wlr_session_lock = lock; + session->focused = NULL; + g_server->lock = session; + + struct diyac_output *output; + wl_list_for_each(output, &g_server->outputs, link) + { + diyac_session_lock_output(output); + } + + session->new_surface.notify = handle_new_surface; + wl_signal_add(&lock->events.new_surface, &session->new_surface); + + session->unlock.notify = handle_unlock; + wl_signal_add(&lock->events.unlock, &session->unlock); + + session->destroy.notify = handle_session_lock_destroy; + wl_signal_add(&lock->events.destroy, &session->destroy); + + //session->new_output.notify = handle_new_output; + //wl_signal_add(&server.backend->events.new_output, &session->new_output); + + wlr_session_lock_v1_send_locked(lock); +} + +static void handle_commit(struct wl_listener *listener, void *data) +{ + struct wlr_output_event_commit *event = data; + struct diyac_output_lock_handle *handle = wl_container_of(listener, handle, commit); + uint32_t require_reconfigure = WLR_OUTPUT_STATE_MODE + | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM; + if (event->state->committed & require_reconfigure) + { + session_lock_update_geometry(handle->output, false); + } +} + + +static void handle_lock_manager_destroy(struct wl_listener *listener, void *data) +{ + if (g_server->lock) + { + session_lock_destroy(g_server->lock); + g_server->lock = NULL; + } + wl_list_remove(&g_lock_new.link); + wl_list_remove(&g_lock_manager_destroy.link); + g_session_lock_manager = NULL; +} +void diyac_init_session_lock(struct diyac_server * server) +{ + g_session_lock_manager = wlr_session_lock_manager_v1_create(server->wl_display); + + g_lock_new.notify = handle_new_session_lock; + wl_signal_add(&g_session_lock_manager->events.new_lock, &g_lock_new); + + g_lock_manager_destroy.notify = handle_lock_manager_destroy; + wl_signal_add(&g_session_lock_manager->events.destroy, &g_lock_manager_destroy); + + g_server = server; +} + +void diyac_session_lock_output(struct diyac_output * output) +{ + if(output->lock_handle) + { + diyac_session_unlock_output(output); + } + struct diyac_output_lock_handle * handle = malloc(sizeof(struct diyac_output_lock_handle)); + if(!handle) + { + wlr_log(WLR_ERROR, "diyac_session_lock_output: out of memory"); + exit(EXIT_FAILURE); + } + handle->tree = wlr_scene_tree_create(output->scenes.session); + if(!handle->tree) + { + free(handle); + wlr_log(WLR_ERROR, "diyac_session_lock_output:Unable to create lock tree"); + exit(EXIT_FAILURE); + } + float *black = (float[4]) { 0.f, 0.f, 0.f, 1.f }; + handle->background = wlr_scene_rect_create(handle->tree, 0, 0, black); + if(!handle->background) + { + free(handle); + wlr_log(WLR_ERROR, "diyac_session_lock_output:Unable to create lock background"); + wlr_scene_node_destroy(&handle->tree->node); + exit(EXIT_FAILURE); + } + handle->surface = NULL; + handle->output = output; + + handle->commit.notify = handle_commit; + wl_signal_add(&output->wlr_output->events.commit, &handle->commit); + + output->lock_handle = handle; + + session_lock_update_geometry(output, true); + +} + +void diyac_session_unlock_output(struct diyac_output * output) +{ + if(output->lock_handle) + { + wlr_scene_node_destroy(&output->lock_handle->tree->node); + if(output->lock_handle->surface) + { + wl_list_remove(&output->lock_handle->surface_destroy.link); + wl_list_remove(&output->lock_handle->surface_map.link); + } + wl_list_remove(&output->lock_handle->commit.link); + free(output->lock_handle); + output->lock_handle = NULL; + } +} \ No newline at end of file diff --git a/session.h b/session.h new file mode 100644 index 0000000..29b2223 --- /dev/null +++ b/session.h @@ -0,0 +1,10 @@ +#ifndef DIYAC_SESSION_H +#define DIYAC_SESSION_H + +#include "diyac.h" + + +void diyac_init_session_lock(struct diyac_server * server); +void diyac_session_lock_output(struct diyac_output * output); +void diyac_session_unlock_output(struct diyac_output* output); +#endif \ No newline at end of file