diff --git a/cage.c b/cage.c index 8f14746..7114349 100644 --- a/cage.c +++ b/cage.c @@ -1,7 +1,7 @@ /* * Cage: A Wayland kiosk. * - * Copyright (C) 2018-2020 Jente Hidskes + * Copyright (C) 2018-2021 Jente Hidskes * * See the LICENSE file accompanying this file. */ @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ #endif #include "idle_inhibit_v1.h" +#include "layer_shell_v1.h" #include "output.h" #include "seat.h" #include "server.h" @@ -461,6 +463,15 @@ main(int argc, char *argv[]) goto end; } + server.layer_shell_v1 = wlr_layer_shell_v1_create(server.wl_display, 4); + if (!server.layer_shell_v1) { + wlr_log(WLR_ERROR, "Unable to create the layer shell"); + ret = 1; + goto end; + } + server.new_layer_shell_v1_surface.notify = handle_layer_shell_v1_surface_new; + wl_signal_add(&server.layer_shell_v1->events.new_surface, &server.new_layer_shell_v1_surface); + if (!wlr_export_dmabuf_manager_v1_create(server.wl_display)) { wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager"); ret = 1; @@ -596,11 +607,25 @@ main(int argc, char *argv[]) wl_display_run(server.wl_display); #if CAGE_HAS_XWAYLAND + if (xwayland) { + wl_list_remove(&server.new_xwayland_surface.link); + } wlr_xwayland_destroy(xwayland); wlr_xcursor_manager_destroy(xcursor_manager); #endif wl_display_destroy_clients(server.wl_display); + wl_list_remove(&server.new_virtual_pointer.link); + wl_list_remove(&server.new_virtual_keyboard.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); + wl_list_remove(&server.new_xdg_toplevel.link); + wl_list_remove(&server.new_xdg_popup.link); + wl_list_remove(&server.new_idle_inhibitor_v1.link); + wl_list_remove(&server.new_output.link); + wl_list_remove(&server.output_layout_change.link); + end: if (pid != 0) app_ret = cleanup_primary_client(pid); diff --git a/layer_shell_v1.c b/layer_shell_v1.c new file mode 100644 index 0000000..373da62 --- /dev/null +++ b/layer_shell_v1.c @@ -0,0 +1,207 @@ +/* + * Cage: A Wayland kiosk. + * + * Copyright (C) 2021 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ + +#include "layer_shell_v1.h" +#include "output.h" +#include "seat.h" +#include "server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_LAYERS (4) + +static void +arrange_surface(struct cg_output *output, const struct wlr_box *full_area, struct wlr_box *usable_area, + struct wlr_scene_tree *tree) +{ + struct wlr_scene_node *node; + wl_list_for_each (node, &tree->children, link) { + struct cg_view *view = node->data; + if (!view) { + continue; + } + struct cg_layer_surface *surface = (struct cg_layer_surface *) view; + + if (!surface->scene->layer_surface->initialized) { + 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, output->layers.shell_background); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); + + if (!wlr_box_equal(&usable_area, &output->usable_area)) { + wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); + output->usable_area = usable_area; + // arrange_output(output); + } else { + // arrange_popups(root->layers.popup); + // FIXME: popup is not implemented + } +} + +static struct wlr_scene_tree * +cg_layer_get_scene(struct cg_output *output, enum zwlr_layer_shell_v1_layer layer_type) +{ + assert(layer_type <= NUM_LAYERS); + switch (layer_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; + } + return NULL; +} + +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; + struct cg_server *server = surface->server; + + // focus on new surface + 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)) { + struct cg_seat *seat = server->seat; + 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) +{ +} + +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->scene->layer_surface; + if (!layer_surface->initialized) { + return; + } + + uint32_t committed = layer_surface->current.committed; + if (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 = cg_layer_get_scene(surface->output, layer_type); + wlr_scene_node_reparent(&surface->scene->tree->node, output_layer); + } + + if (layer_surface->initial_commit || committed || layer_surface->surface->mapped != surface->mapped) { + surface->mapped = layer_surface->surface->mapped; + arrange_layers(surface->output); + } +} + +static void +handle_output_destroy(struct wl_listener *listener, void *data) +{ +} + +void +handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data) +{ + struct cg_server *server = wl_container_of(listener, server, new_layer_shell_v1_surface); + struct cg_seat *seat = server->seat; + struct wlr_layer_surface_v1 *layer_surface = data; + + wlr_log(WLR_DEBUG, + "New layer shell surface: namespace %s layer %d anchor %" PRIu32 " size %" PRIu32 "x%" PRIu32 + " margin %" PRIu32 ",%" PRIu32 ",%" PRIu32 ",%" PRIu32 ",", + layer_surface->namespace, layer_surface->pending.layer, layer_surface->pending.anchor, + 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 wlr_output *wlr_output = + wlr_output_layout_output_at(server->output_layout, seat->cursor->x, seat->cursor->y); + if (wlr_output) { + layer_surface->output = wlr_output; + } else { + struct cg_output *output = wl_container_of(server->outputs.prev, output, link); + layer_surface->output = output->wlr_output; + } + } + struct cg_output *output = layer_surface->output->data; + */ + struct cg_output *output; + if (layer_surface->output) { + output = layer_surface->output->data; + } else { + struct wlr_output *wlr_output = + wlr_output_layout_output_at(server->output_layout, seat->cursor->x, seat->cursor->y); + layer_surface->output = wlr_output; + output = wlr_output->data; + } + + enum zwlr_layer_shell_v1_layer layer_type = layer_surface->pending.layer; + struct wlr_scene_tree *output_layer = cg_layer_get_scene(output, layer_type); + + struct cg_layer_surface *cg_surface = calloc(1, sizeof(*cg_surface)); + if (!cg_surface) { + wlr_layer_surface_v1_destroy(layer_surface); + wlr_log(WLR_ERROR, "Failed to allocate layer shell"); + return; + } + + struct wlr_scene_layer_surface_v1 *scene = wlr_scene_layer_surface_v1_create(output_layer, layer_surface); + if (!scene) { + wlr_log(WLR_ERROR, "Could not allocate a layer_surface_v1"); + return; + } + + cg_surface->server = server; + cg_surface->layer_surface = scene->layer_surface; + cg_surface->scene = scene; + cg_surface->tree = scene->tree; + cg_surface->tree->node.data =cg_surface; + cg_surface->layer_surface->data = cg_surface; + cg_surface->output = output; + + cg_surface->map.notify = handle_map; + wl_signal_add(&layer_surface->surface->events.map, &cg_surface->map); + cg_surface->unmap.notify = handle_unmap; + wl_signal_add(&layer_surface->surface->events.unmap, &cg_surface->unmap); + cg_surface->surface_commit.notify = handle_surface_commit; + wl_signal_add(&layer_surface->surface->events.commit, &cg_surface->surface_commit); + + cg_surface->output_destroy.notify = handle_output_destroy; + wl_signal_add(&layer_surface->surface->events.destroy, &cg_surface->output_destroy); +} diff --git a/layer_shell_v1.h b/layer_shell_v1.h new file mode 100644 index 0000000..70ab1cb --- /dev/null +++ b/layer_shell_v1.h @@ -0,0 +1,29 @@ +#ifndef CG_LAYER_SHELL_V1_H +#define CG_LAYER_SHELL_V1_H + +#include +#include +#include + +void handle_layer_shell_v1_surface_new(struct wl_listener *listener, void *data); + +struct cg_layer_surface { + struct cg_server *server; + struct wl_list link; // cg_output::layers + + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener surface_commit; +// struct wl_listener destroy; + + struct wl_listener output_destroy; + + bool mapped; + + struct cg_output *output; + struct wlr_scene_tree *tree; + struct wlr_scene_layer_surface_v1 *scene; + struct wlr_layer_surface_v1 *layer_surface; +}; + +#endif diff --git a/meson.build b/meson.build index 7b58cd8..ba2469b 100644 --- a/meson.build +++ b/meson.build @@ -41,28 +41,7 @@ wayland_server = dependency('wayland-server') xkbcommon = dependency('xkbcommon') math = cc.find_library('m') -wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') -wayland_scanner = find_program('wayland-scanner') -wayland_scanner_server = generator( - wayland_scanner, - output: '@BASENAME@-protocol.h', - arguments: ['server-header', '@INPUT@', '@OUTPUT@'], -) - -server_protocols = [ - [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], -] - -server_protos_headers = [] - -foreach p : server_protocols - xml = join_paths(p) - server_protos_headers += wayland_scanner_server.process(xml) -endforeach - -server_protos = declare_dependency( - sources: server_protos_headers, -) +subdir('protocols') have_xwayland = wlroots.get_variable(pkgconfig: 'have_xwayland', internal: 'have_xwayland') == 'true' @@ -113,6 +92,7 @@ endif cage_sources = [ 'cage.c', 'idle_inhibit_v1.c', + 'layer_shell_v1.c', 'output.c', 'seat.c', 'view.c', @@ -124,6 +104,7 @@ cage_headers = [ output: 'config.h', configuration: conf_data), 'idle_inhibit_v1.h', + 'layer_shell_v1.h', 'output.h', 'seat.h', 'server.h', diff --git a/output.c b/output.c index 5ccc939..49dafcf 100644 --- a/output.c +++ b/output.c @@ -234,6 +234,11 @@ output_destroy(struct cg_output *output) 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); @@ -254,6 +259,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) { @@ -319,6 +336,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); diff --git a/output.h b/output.h index fa72545..2945348 100644 --- a/output.h +++ b/output.h @@ -17,6 +17,15 @@ 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 link; // cg_server::outputs }; diff --git a/protocols/meson.build b/protocols/meson.build new file mode 100644 index 0000000..140419c --- /dev/null +++ b/protocols/meson.build @@ -0,0 +1,39 @@ +wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') +wayland_scanner = find_program('wayland-scanner', native: true) + +wayland_scanner_server_header = generator( + wayland_scanner, + output: '@BASENAME@-protocol.h', + arguments: ['server-header', '@INPUT@', '@OUTPUT@'], +) + +wayland_scanner_private_code = generator( + wayland_scanner, + output: '@BASENAME@-protocol.c', + arguments: ['private-code', '@INPUT@', '@OUTPUT@'], +) + +server_protocols = [ + [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], + ['wlr-layer-shell-unstable-v1.xml'], +] + +wl_protos_headers = [] +wl_protos_src = [] + +foreach p : server_protocols + xml = join_paths(p) + wl_protos_headers += wayland_scanner_server_header.process(xml) + wl_protos_src += wayland_scanner_private_code.process(xml) +endforeach + +lib_server_protos = static_library( + 'server_protos', + wl_protos_headers + wl_protos_src, + dependencies: wayland_server.partial_dependency(compile_args: true), +) + +server_protos = declare_dependency( + link_with: lib_server_protos, + sources: wl_protos_headers, +) diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..d62fd51 --- /dev/null +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + After creating a layer_surface object and setting it up, the client + must perform an initial commit without any buffer attached. + The compositor will reply with a layer_surface.configure event. + The client must acknowledge it and is then allowed to attach a buffer + to map the surface. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + + This request indicates that the client will not use the layer_shell + object any more. Objects that have been created through this instance + are not affected. + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (layer, size, anchor, exclusive zone, + margin, interactivity) is double-buffered, and will be applied at the + time wl_surface.commit of the corresponding wl_surface is called. + + Attaching a null buffer to a layer surface unmaps it. + + Unmapping a layer_surface means that the surface cannot be shown by the + compositor until it is explicitly mapped again. The layer_surface + returns to the state it had right after layer_shell.get_layer_surface. + The client can re-map the surface by performing a commit without any + buffer attached, waiting for a configure event and handling it as usual. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthogonal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area with other + surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to one + edge or an edge and both perpendicular edges. If the surface is not + anchored, anchored to only two perpendicular edges (a corner), anchored + to only two parallel edges or anchored to all edges, a positive value + will be treated the same as zero. + + A positive zone is the distance from the edge in surface-local + coordinates to consider exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive exclusive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Types of keyboard interaction possible for layer shell surfaces. The + rationale for this is twofold: (1) some applications are not interested + in keyboard events and not allowing them to be focused can improve the + desktop experience; (2) some applications will want to take exclusive + keyboard focus. + + + + + This value indicates that this surface is not interested in keyboard + events and the compositor should never assign it the keyboard focus. + + This is the default value, set for newly created layer shell surfaces. + + This is useful for e.g. desktop widgets that display information or + only have interaction with non-keyboard input devices. + + + + + Request exclusive keyboard focus if this surface is above the shell surface layer. + + For the top and overlay layers, the seat will always give + exclusive keyboard focus to the top-most layer which has keyboard + interactivity set to exclusive. If this layer contains multiple + surfaces with keyboard interactivity set to exclusive, the compositor + determines the one receiving keyboard events in an implementation- + defined manner. In this case, no guarantee is made when this surface + will receive keyboard focus (if ever). + + For the bottom and background layers, the compositor is allowed to use + normal focus semantics. + + This setting is mainly intended for applications that need to ensure + they receive all keyboard events, such as a lock screen or a password + prompt. + + + + + This requests the compositor to allow this surface to be focused and + unfocused by the user in an implementation-defined manner. The user + should be able to unfocus this surface even regardless of the layer + it is on. + + Typically, the compositor will want to use its normal mechanism to + manage keyboard focus between layer shell surfaces with this setting + and regular toplevels on the desktop layer (e.g. click to focus). + Nevertheless, it is possible for a compositor to require a special + interaction to focus or unfocus layer shell surfaces (e.g. requiring + a click even if focus follows the mouse normally, or providing a + keybinding to switch focus between layers). + + This setting is mainly intended for desktop shell components (e.g. + panels) that allow keyboard interaction. Using this option can allow + implementing a desktop shell that can be fully usable without the + mouse. + + + + + + + Set how keyboard events are delivered to this surface. By default, + layer shell surfaces do not receive keyboard events; this request can + be used to change this. + + This setting is inherited by child surfaces set by the get_popup + request. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Keyboard interactivity is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/seat.c b/seat.c index cdf8798..9acbdd3 100644 --- a/seat.c +++ b/seat.c @@ -380,6 +380,16 @@ cleanup: free(cg_group); } +static void +keyboard_group_destroy(struct cg_keyboard_group *keyboard_group) +{ + wl_list_remove(&keyboard_group->key.link); + wl_list_remove(&keyboard_group->modifiers.link); + wlr_keyboard_group_destroy(keyboard_group->wlr_group); + wl_list_remove(&keyboard_group->link); + free(keyboard_group); +} + static void handle_new_keyboard(struct cg_seat *seat, struct wlr_keyboard *keyboard, bool virtual) { @@ -893,6 +903,11 @@ seat_destroy(struct cg_seat *seat) wl_list_remove(&seat->request_start_drag.link); wl_list_remove(&seat->start_drag.link); + struct cg_keyboard_group *keyboard_group, *keyboard_group_tmp; + wl_list_for_each_safe (keyboard_group, keyboard_group_tmp, &seat->keyboard_groups, link) { + keyboard_group_destroy(keyboard_group); + } + // Destroying the wlr seat will trigger the destroy handler on our seat, // which will in turn free it. wlr_seat_destroy(seat->seat); diff --git a/seat.h b/seat.h index 4b7bfda..1712a60 100644 --- a/seat.h +++ b/seat.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,8 @@ 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 cg_keyboard_group { diff --git a/server.h b/server.h index 00c2a61..95a083b 100644 --- a/server.h +++ b/server.h @@ -52,6 +52,9 @@ struct cg_server { struct wl_listener new_virtual_keyboard; struct wl_listener new_virtual_pointer; + + struct wlr_layer_shell_v1 *layer_shell_v1; + struct wl_listener new_layer_shell_v1_surface; #if CAGE_HAS_XWAYLAND struct wl_listener new_xwayland_surface; #endif