From 8a11a66d876246663d25a4e789b495be954486c3 Mon Sep 17 00:00:00 2001 From: Sungjoon Moon Date: Wed, 22 Oct 2025 16:13:00 +0900 Subject: [PATCH] layer-shell: initial layer shell v1 implementation --- cage.c | 1 + layer_shell_v1.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++ layer_shell_v1.h | 34 ++++++ output.c | 120 +++++++++++++++++- output.h | 12 ++ seat.c | 59 +++++++-- seat.h | 5 + view.c | 21 ++-- 8 files changed, 538 insertions(+), 24 deletions(-) diff --git a/cage.c b/cage.c index 9a0c26a..47f9e1a 100644 --- a/cage.c +++ b/cage.c @@ -617,6 +617,7 @@ main(int argc, char *argv[]) wl_list_remove(&server.new_virtual_pointer.link); wl_list_remove(&server.new_virtual_keyboard.link); + wl_list_remove(&server.new_layer_shell_v1_surface.link); wl_list_remove(&server.output_manager_apply.link); wl_list_remove(&server.output_manager_test.link); wl_list_remove(&server.xdg_toplevel_decoration.link); diff --git a/layer_shell_v1.c b/layer_shell_v1.c index c66ef05..589c650 100644 --- a/layer_shell_v1.c +++ b/layer_shell_v1.c @@ -6,13 +6,263 @@ * See the LICENSE file accompanying this file. */ +#define _POSIX_C_SOURCE 200809L + #include "layer_shell_v1.h" +#include "output.h" +#include "seat.h" #include "server.h" +#include +#include +#include #include +#include #include +#include +#include +#include #include +static struct wlr_scene_tree * +get_layer_scene(struct cg_output *output, enum zwlr_layer_shell_v1_layer type) +{ + switch (type) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: + return output->layers.shell_background; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: + return output->layers.shell_bottom; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: + return output->layers.shell_top; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: + return output->layers.shell_overlay; + } + + wlr_log(WLR_ERROR, "Invalid layer shell layer: %d", type); + return NULL; +} + +static void popup_handle_destroy(struct wl_listener *listener, void *data); +static void popup_handle_new_popup(struct wl_listener *listener, void *data); +static void popup_handle_commit(struct wl_listener *listener, void *data); + +static void +popup_unconstrain(struct cg_layer_popup *popup) +{ + struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; + struct cg_output *output = popup->toplevel->output; + + if (!output) { + return; + } + + int lx, ly; + wlr_scene_node_coords(&popup->toplevel->scene->tree->node, &lx, &ly); + + struct wlr_box output_box; + wlr_output_layout_get_box(output->server->output_layout, output->wlr_output, &output_box); + + struct wlr_box output_toplevel_sx_box = { + .x = output_box.x - lx, + .y = output_box.y - ly, + .width = output->wlr_output->width, + .height = output->wlr_output->height, + }; + + wlr_xdg_popup_unconstrain_from_box(wlr_popup, &output_toplevel_sx_box); +} + +static void +popup_handle_commit(struct wl_listener *listener, void *data) +{ + struct cg_layer_popup *popup = wl_container_of(listener, popup, commit); + if (popup->wlr_popup->base->initial_commit) { + popup_unconstrain(popup); + } +} + +static struct cg_layer_popup * +create_popup(struct wlr_xdg_popup *wlr_popup, struct cg_layer_surface *toplevel, struct wlr_scene_tree *parent) +{ + struct cg_layer_popup *popup = calloc(1, sizeof(*popup)); + if (popup == NULL) { + return NULL; + } + + popup->toplevel = toplevel; + popup->wlr_popup = wlr_popup; + popup->scene = wlr_scene_xdg_surface_create(parent, wlr_popup->base); + + if (!popup->scene) { + free(popup); + return NULL; + } + + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + popup->commit.notify = popup_handle_commit; + wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + + return popup; +} + +static void +popup_handle_destroy(struct wl_listener *listener, void *data) +{ + struct cg_layer_popup *popup = wl_container_of(listener, popup, destroy); + + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + wl_list_remove(&popup->commit.link); + free(popup); +} + +static void +popup_handle_new_popup(struct wl_listener *listener, void *data) +{ + struct cg_layer_popup *popup = wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + create_popup(wlr_popup, popup->toplevel, popup->scene); +} + +static void +handle_new_popup(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *layer_surface = wl_container_of(listener, layer_surface, new_popup); + struct wlr_xdg_popup *wlr_popup = data; + create_popup(wlr_popup, layer_surface, layer_surface->popups); +} + +static void +handle_surface_commit(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *surface = wl_container_of(listener, surface, surface_commit); + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + uint32_t committed = layer_surface->current.committed; + + if (!surface->output) { + return; + } + + if (layer_surface->initial_commit) { + wlr_fractional_scale_v1_notify_scale(layer_surface->surface, layer_surface->output->scale); + wlr_surface_set_preferred_buffer_scale(layer_surface->surface, ceil(layer_surface->output->scale)); + } + + if (layer_surface->initialized && committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; + struct wlr_scene_tree *output_layer = get_layer_scene(surface->output, layer_type); + if (output_layer) { + wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); + wlr_scene_node_reparent(&surface->popups->node, &surface->output->server->scene->tree); + } + } + + bool mapped_changed = layer_surface->surface->mapped != surface->mapped; + if (!layer_surface->initial_commit && !committed && !mapped_changed) { + return; + } + + surface->mapped = layer_surface->surface->mapped; + arrange_layers(surface->output); +} + +static void +handle_map(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *surface = wl_container_of(listener, surface, map); + struct wlr_layer_surface_v1 *layer_surface = surface->scene->layer_surface; + + if (!surface->output) { + return; + } + + struct cg_seat *seat = surface->output->server->seat; + + wlr_scene_node_set_enabled(&surface->scene->tree->node, true); + + if (layer_surface->current.keyboard_interactive && + (layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY || + layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP)) { + if (!seat->focused_layer || seat->focused_layer->current.layer >= layer_surface->current.layer) { + seat_set_focus_layer(seat, layer_surface); + } + } + + arrange_layers(surface->output); +} + +static void +handle_unmap(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *surface = wl_container_of(listener, surface, unmap); + + wlr_scene_node_set_enabled(&surface->scene->tree->node, false); + + if (!surface->output) { + return; + } + + struct cg_seat *seat = surface->output->server->seat; + + if (seat->focused_layer == surface->layer_surface) { + seat_set_focus_layer(seat, NULL); + } + + arrange_layers(surface->output); +} + +static void +handle_node_destroy(struct wl_listener *listener, void *data) +{ + struct cg_layer_surface *layer = wl_container_of(listener, layer, node_destroy); + + if (layer->output) { + arrange_layers(layer->output); + } + + wlr_scene_node_destroy(&layer->popups->node); + + wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); + wl_list_remove(&layer->surface_commit.link); + wl_list_remove(&layer->node_destroy.link); + wl_list_remove(&layer->new_popup.link); + + layer->layer_surface->data = NULL; + + wl_list_remove(&layer->link); + free(layer); +} + +static struct cg_layer_surface * +create_layer_surface(struct wlr_scene_layer_surface_v1 *scene, struct cg_output *output) +{ + struct cg_layer_surface *surface = calloc(1, sizeof(*surface)); + if (!surface) { + wlr_log(WLR_ERROR, "Could not allocate a layer surface"); + return NULL; + } + + struct wlr_scene_tree *popups = wlr_scene_tree_create(&output->server->scene->tree); + if (!popups) { + wlr_log(WLR_ERROR, "Could not allocate a layer popup node"); + free(surface); + return NULL; + } + + surface->tree = scene->tree; + surface->scene = scene; + surface->layer_surface = scene->layer_surface; + surface->popups = popups; + surface->layer_surface->data = surface; + surface->output = output; + + return surface; +} + void handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data) { @@ -24,4 +274,64 @@ handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data) layer_surface->pending.desired_width, layer_surface->pending.desired_height, layer_surface->pending.margin.top, layer_surface->pending.margin.right, layer_surface->pending.margin.bottom, layer_surface->pending.margin.left); + + if (!layer_surface->output) { + struct cg_seat *seat = server->seat; + if (seat && seat->focused_output) { + layer_surface->output = seat->focused_output; + } else if (!wl_list_empty(&server->outputs)) { + struct cg_output *output = wl_container_of(server->outputs.next, output, link); + layer_surface->output = output->wlr_output; + } else { + wlr_log(WLR_ERROR, "No output to assign layer surface '%s' to", layer_surface->namespace); + wlr_layer_surface_v1_destroy(layer_surface); + return; + } + } + + struct cg_output *output = layer_surface->output->data; + if (!output) { + wlr_log(WLR_ERROR, "Layer surface has no output data"); + wlr_layer_surface_v1_destroy(layer_surface); + return; + } + + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; + struct wlr_scene_tree *output_layer = get_layer_scene(output, layer_type); + if (!output_layer) { + wlr_log(WLR_ERROR, "Invalid layer %d for layer surface '%s'", layer_type, layer_surface->namespace); + wlr_layer_surface_v1_destroy(layer_surface); + return; + } + + struct wlr_scene_layer_surface_v1 *scene_surface = + wlr_scene_layer_surface_v1_create(output_layer, layer_surface); + if (!scene_surface) { + wlr_log(WLR_ERROR, "Could not allocate a layer_surface_v1"); + return; + } + + struct cg_layer_surface *surface = create_layer_surface(scene_surface, output); + if (!surface) { + wlr_layer_surface_v1_destroy(layer_surface); + wlr_log(WLR_ERROR, "Could not allocate a layer surface"); + return; + } + + wl_list_insert(&output->layer_surfaces, &surface->link); + + wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface, layer_surface->output->scale); + wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface, ceil(layer_surface->output->scale)); + + surface->surface_commit.notify = handle_surface_commit; + wl_signal_add(&layer_surface->surface->events.commit, &surface->surface_commit); + surface->map.notify = handle_map; + wl_signal_add(&layer_surface->surface->events.map, &surface->map); + surface->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->surface->events.unmap, &surface->unmap); + surface->new_popup.notify = handle_new_popup; + wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); + + surface->node_destroy.notify = handle_node_destroy; + wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); } diff --git a/layer_shell_v1.h b/layer_shell_v1.h index d7ade6e..cf66f69 100644 --- a/layer_shell_v1.h +++ b/layer_shell_v1.h @@ -1,7 +1,41 @@ #ifndef CG_LAYER_SHELL_V1_H #define CG_LAYER_SHELL_V1_H +#include #include +#include +#include + +struct cg_output; + +struct cg_layer_surface { + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; + struct wl_listener node_destroy; + struct wl_listener new_popup; + + bool mapped; + + struct wlr_scene_tree *popups; + + struct cg_output *output; + struct wl_list link; // cg_output::layer_surfaces + + struct wlr_scene_layer_surface_v1 *scene; + struct wlr_scene_tree *tree; + struct wlr_layer_surface_v1 *layer_surface; +}; + +struct cg_layer_popup { + struct wlr_xdg_popup *wlr_popup; + struct wlr_scene_tree *scene; + struct cg_layer_surface *toplevel; + + struct wl_listener destroy; + struct wl_listener new_popup; + struct wl_listener commit; +}; void handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data); diff --git a/output.c b/output.c index 093836b..458229f 100644 --- a/output.c +++ b/output.c @@ -21,6 +21,11 @@ #if WLR_HAS_X11_BACKEND #include #endif +#include "layer_shell_v1.h" +#include "output.h" +#include "seat.h" +#include "server.h" +#include "view.h" #include #include #include @@ -33,11 +38,6 @@ #include #include #include - -#include "output.h" -#include "seat.h" -#include "server.h" -#include "view.h" #if CAGE_HAS_XWAYLAND #include "xwayland.h" #endif @@ -205,12 +205,23 @@ output_destroy(struct cg_output *output) output->wlr_output->data = NULL; + struct cg_layer_surface *layer, *layer_tmp; + wl_list_for_each_safe (layer, layer_tmp, &output->layer_surfaces, link) { + layer->output = NULL; + wlr_layer_surface_v1_destroy(layer->layer_surface); + } + wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); wl_list_remove(&output->request_state.link); wl_list_remove(&output->frame.link); wl_list_remove(&output->link); + wlr_scene_node_destroy(&output->layers.shell_background->node); + wlr_scene_node_destroy(&output->layers.shell_bottom->node); + wlr_scene_node_destroy(&output->layers.shell_top->node); + wlr_scene_node_destroy(&output->layers.shell_overlay->node); + output_layout_remove(output); free(output); @@ -231,6 +242,18 @@ handle_output_destroy(struct wl_listener *listener, void *data) output_destroy(output); } +static struct wlr_scene_tree * +create_layer_for_output(struct cg_output *output) +{ + struct cg_server *server = output->server; + struct wlr_scene_tree *layer = wlr_scene_tree_create(&server->scene->tree); + if (layer == NULL) { + return NULL; + } + layer->node.data = output->wlr_output; + return layer; +} + void handle_new_output(struct wl_listener *listener, void *data) { @@ -252,6 +275,7 @@ handle_new_output(struct wl_listener *listener, void *data) wlr_output->data = output; output->server = server; + wl_list_init(&output->layer_surfaces); wl_list_insert(&server->outputs, &output->link); output->commit.notify = handle_output_commit; @@ -296,6 +320,11 @@ handle_new_output(struct wl_listener *listener, void *data) output_disable(next); } + output->layers.shell_background = create_layer_for_output(output); + output->layers.shell_bottom = create_layer_for_output(output); + output->layers.shell_top = create_layer_for_output(output); + output->layers.shell_overlay = create_layer_for_output(output); + if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) { wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f", wlr_output->name, wlr_output->scale); @@ -391,6 +420,87 @@ out: return ok; } +static void +arrange_surface(struct cg_output *output, const struct wlr_box *full_area, struct wlr_box *usable_area, + enum zwlr_layer_shell_v1_layer layer, bool exclusive) +{ + struct cg_layer_surface *surface; + wl_list_for_each (surface, &output->layer_surfaces, link) { + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + + if (layer_surface->current.layer != layer) { + continue; + } + + if (!layer_surface->initialized) { + continue; + } + + if ((layer_surface->current.exclusive_zone > 0) != exclusive) { + continue; + } + + wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); + } +} + +void +arrange_layers(struct cg_output *output) +{ + struct wlr_box usable_area = {0}; + wlr_output_effective_resolution(output->wlr_output, &usable_area.width, &usable_area.height); + const struct wlr_box full_area = usable_area; + + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, true); + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_TOP, true); + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, true); + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, true); + + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, false); + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_TOP, false); + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, false); + arrange_surface(output, &full_area, &usable_area, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, false); + + if (!wlr_box_equal(&usable_area, &output->usable_area)) { + wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); + output->usable_area = usable_area; + view_position_all(output->server); + } + + enum zwlr_layer_shell_v1_layer layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); + struct cg_layer_surface *topmost = NULL; + for (size_t i = 0; i < nlayers; ++i) { + struct cg_layer_surface *surface; + wl_list_for_each_reverse (surface, &output->layer_surfaces, link) { + struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; + if (layer_surface->current.layer != layers_above_shell[i]) { + continue; + } + if (layer_surface->current.keyboard_interactive == + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + layer_surface->surface->mapped) { + topmost = surface; + break; + } + } + if (topmost != NULL) { + break; + } + } + + struct cg_seat *seat = output->server->seat; + if (topmost != NULL) { + seat_set_focus_layer(seat, topmost->layer_surface); + } else if (seat->focused_layer && seat->focused_layer->current.keyboard_interactive != + ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { + seat_set_focus_layer(seat, NULL); + } +} + void handle_output_manager_apply(struct wl_listener *listener, void *data) { diff --git a/output.h b/output.h index fa72545..eaf47e0 100644 --- a/output.h +++ b/output.h @@ -17,6 +17,17 @@ struct cg_output { struct wl_listener destroy; struct wl_listener frame; + struct { + struct wlr_scene_tree *shell_background; + struct wlr_scene_tree *shell_bottom; + struct wlr_scene_tree *shell_top; + struct wlr_scene_tree *shell_overlay; + } layers; + + struct wlr_box usable_area; + + struct wl_list layer_surfaces; // cg_layer_surface::link + struct wl_list link; // cg_server::outputs }; @@ -25,5 +36,6 @@ void handle_output_manager_test(struct wl_listener *listener, void *data); void handle_output_layout_change(struct wl_listener *listener, void *data); void handle_new_output(struct wl_listener *listener, void *data); void output_set_window_title(struct cg_output *output, const char *title); +void arrange_layers(struct cg_output *output); #endif diff --git a/seat.c b/seat.c index 5f659a4..e32af18 100644 --- a/seat.c +++ b/seat.c @@ -74,17 +74,23 @@ desktop_view_at(struct cg_server *server, double lx, double ly, struct wlr_surfa /* Walk up the tree until we find a node with a data pointer. When done, * we've found the node representing the view. */ - while (!node->data) { + while (node) { + if (node->data) { + struct cg_view *candidate = node->data; + struct cg_view *view; + wl_list_for_each (view, &server->views, link) { + if (view == candidate) { + return view; + } + } + } if (!node->parent) { - node = NULL; break; } - node = &node->parent->node; } - assert(node != NULL); - return node->data; + return NULL; } static void @@ -104,7 +110,7 @@ press_cursor_button(struct cg_seat *seat, struct wlr_input_device *device, uint3 /* Focus that client if the button was pressed and it has no open dialogs. */ - if (view && !view_is_transient_for(current, view)) { + if (view && (!current || !view_is_transient_for(current, view))) { seat_set_focus(seat, view); } } @@ -625,8 +631,8 @@ process_cursor_motion(struct cg_seat *seat, uint32_t time_msec, double dx, doubl struct wlr_seat *wlr_seat = seat->seat; struct wlr_surface *surface = NULL; - struct cg_view *view = desktop_view_at(seat->server, seat->cursor->x, seat->cursor->y, &surface, &sx, &sy); - if (!view) { + desktop_view_at(seat->server, seat->cursor->x, seat->cursor->y, &surface, &sx, &sy); + if (!surface) { wlr_seat_pointer_clear_focus(wlr_seat); } else { wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); @@ -787,8 +793,7 @@ handle_destroy(struct wl_listener *listener, void *data) struct cg_keyboard_group *group, *group_tmp; wl_list_for_each_safe (group, group_tmp, &seat->keyboard_groups, link) { - wlr_keyboard_group_destroy(group->wlr_group); - free(group); + keyboard_group_destroy(group); } struct cg_pointer *pointer, *pointer_tmp; wl_list_for_each_safe (pointer, pointer_tmp, &seat->pointers, link) { @@ -910,6 +915,7 @@ seat_destroy(struct cg_seat *seat) // Destroying the wlr seat will trigger the destroy handler on our seat, // which will in turn free it. + wlr_seat_destroy(seat->seat); } @@ -920,6 +926,11 @@ seat_get_focus(struct cg_seat *seat) if (!prev_surface) { return NULL; } + + if (seat->focused_layer && seat->focused_layer->surface == prev_surface) { + return NULL; + } + return view_from_wlr_surface(prev_surface); } @@ -973,6 +984,34 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) process_cursor_motion(seat, -1, 0, 0, 0, 0); } +void +seat_set_focus_layer(struct cg_seat *seat, struct wlr_layer_surface_v1 *layer) +{ + if (seat->focused_layer == layer) { + return; + } + + seat->focused_layer = layer; + + if (!layer) { + struct cg_view *view = seat_get_focus(seat); + if (view) { + seat_set_focus(seat, view); + } + return; + } + + if (layer->current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) { + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->seat); + if (keyboard) { + wlr_seat_keyboard_notify_enter(seat->seat, layer->surface, keyboard->keycodes, + keyboard->num_keycodes, &keyboard->modifiers); + } else { + wlr_seat_keyboard_notify_enter(seat->seat, layer->surface, NULL, 0, NULL); + } + } +} + void seat_center_cursor(struct cg_seat *seat) { diff --git a/seat.h b/seat.h index 4b7bfda..dbe96b0 100644 --- a/seat.h +++ b/seat.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,9 @@ struct cg_seat { struct wl_listener request_set_cursor; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; + + struct wlr_layer_surface_v1 *focused_layer; + struct wlr_output *focused_output; }; struct cg_keyboard_group { @@ -91,6 +95,7 @@ struct cg_seat *seat_create(struct cg_server *server, struct wlr_backend *backen void seat_destroy(struct cg_seat *seat); struct cg_view *seat_get_focus(struct cg_seat *seat); void seat_set_focus(struct cg_seat *seat, struct cg_view *view); +void seat_set_focus_layer(struct cg_seat *seat, struct wlr_layer_surface_v1 *layer); void seat_center_cursor(struct cg_seat *seat); #endif diff --git a/view.c b/view.c index 8cbeb5e..038c5a0 100644 --- a/view.c +++ b/view.c @@ -115,8 +115,6 @@ view_unmap(struct cg_view *view) { wl_list_remove(&view->link); - wlr_scene_node_destroy(&view->scene_tree->node); - view->wlr_surface->data = NULL; view->wlr_surface = NULL; } @@ -124,12 +122,16 @@ view_unmap(struct cg_view *view) void view_map(struct cg_view *view, struct wlr_surface *surface) { - view->scene_tree = wlr_scene_subsurface_tree_create(&view->server->scene->tree, surface); if (!view->scene_tree) { - wl_resource_post_no_memory(surface->resource); - return; + view->scene_tree = wlr_scene_subsurface_tree_create(&view->server->scene->tree, surface); + if (!view->scene_tree) { + wl_resource_post_no_memory(surface->resource); + return; + } + } + if (view->scene_tree) { + view->scene_tree->node.data = view; } - view->scene_tree->node.data = view; view->wlr_surface = surface; surface->data = view; @@ -137,11 +139,12 @@ view_map(struct cg_view *view, struct wlr_surface *surface) #if CAGE_HAS_XWAYLAND /* We shouldn't position override-redirect windows. They set their own (x,y) coordinates in handle_wayland_surface_map. */ - if (view->type != CAGE_XWAYLAND_VIEW || xwayland_view_should_manage(view)) -#endif - { + if (view->type != CAGE_XWAYLAND_VIEW || xwayland_view_should_manage(view)) { view_position(view); } +#else + view_position(view); +#endif wl_list_insert(&view->server->views, &view->link); seat_set_focus(view->server->seat, view);