From 35b874de6535fd91baec315364d77b241924fb4b Mon Sep 17 00:00:00 2001 From: bi4k8 Date: Mon, 28 Nov 2022 19:23:23 +0000 Subject: [PATCH] wayland: switch to zwlr_layer_shell_v1 and implement proper window placement this vendors the wlr-layer-shell-unstable-v1 protocol XML file --- src/CMakeLists.txt | 13 +- src/display-wayland.cc | 86 +++--- src/wlr-layer-shell-unstable-v1.xml | 390 ++++++++++++++++++++++++++++ 3 files changed, 439 insertions(+), 50 deletions(-) create mode 100644 src/wlr-layer-shell-unstable-v1.xml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5b8f043..b5dc5a57 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -244,9 +244,9 @@ if(BUILD_X11) endif(BUILD_X11) if(BUILD_WAYLAND) - set(wl_srcs wl.cc wl.h xdg-shell-protocol.c) + set(wl_srcs wl.cc wl.h xdg-shell-protocol.c wlr-layer-shell-protocol.c) set(optional_sources ${optional_sources} ${wl_srcs}) - # generate protocol implementation + # generate protocol implementations set(XDG_PROT_DEF "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml") add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xdg-shell-client-protocol.h @@ -256,6 +256,15 @@ if(BUILD_WAYLAND) COMMAND ${WAYLAND_SCANNER} private-code ${XDG_PROT_DEF} xdg-shell-protocol.c DEPENDS xdg-shell-client-protocol.h) + set(WLR_LAYER_SHELL_PROT_DEF "${CMAKE_CURRENT_SOURCE_DIR}/wlr-layer-shell-unstable-v1.xml") + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wlr-layer-shell-client-protocol.h + COMMAND ${WAYLAND_SCANNER} client-header ${WLR_LAYER_SHELL_PROT_DEF} wlr-layer-shell-client-protocol.h) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wlr-layer-shell-protocol.c + COMMAND ${WAYLAND_SCANNER} private-code ${WLR_LAYER_SHELL_PROT_DEF} wlr-layer-shell-protocol.c + DEPENDS wlr-layer-shell-client-protocol.h) + # include output dir in include path include_directories(${CMAKE_CURRENT_BINARY_DIR}) endif(BUILD_WAYLAND) diff --git a/src/display-wayland.cc b/src/display-wayland.cc index 63865f53..f9c70ca2 100644 --- a/src/display-wayland.cc +++ b/src/display-wayland.cc @@ -40,7 +40,7 @@ #include #include -#include +#include #endif /* BUILD_WAYLAND */ @@ -289,8 +289,7 @@ struct window { struct rectangle rectangle; struct wl_shm *shm; struct wl_surface *surface; - struct xdg_surface *xdg_surface; - struct xdg_toplevel *xdg_toplevel; + struct zwlr_layer_surface_v1 *layer_surface; int scale; cairo_surface_t *cairo_surface; cairo_t *cr; @@ -308,20 +307,10 @@ struct { struct wl_seat *seat; /* struct wl_pointer *pointer;*/ struct wl_output *output; - struct xdg_wm_base *shell; + struct zwlr_layer_shell_v1 *layer_shell; } wl_globals; -static void -xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) -{ - xdg_wm_base_pong(shell, serial); -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = &xdg_wm_base_ping, -}; - static void output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, @@ -394,9 +383,8 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, } else if(strcmp(interface, "wl_output") == 0) { wl_globals.output = static_cast(wl_registry_bind(registry, name, &wl_output_interface, 2)); wl_output_add_listener(wl_globals.output, &output_listener, nullptr); - } else if(strcmp(interface, "xdg_wm_base") == 0) { - wl_globals.shell = static_cast(wl_registry_bind(registry, name, &xdg_wm_base_interface, 1)); - xdg_wm_base_add_listener(wl_globals.shell, &xdg_wm_base_listener, nullptr); + } else if(strcmp(interface, "zwlr_layer_shell_v1") == 0) { + wl_globals.layer_shell = static_cast(wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1)); } } @@ -412,30 +400,20 @@ static const struct wl_registry_listener registry_listener = { static void -xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, - int32_t width, int32_t height, struct wl_array *states) +layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *layer_surface, + uint32_t serial, uint32_t width, uint32_t height) { + zwlr_layer_surface_v1_ack_configure(layer_surface, serial); } static void -xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) +layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *layer_surface) { } -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - .configure = &xdg_toplevel_configure, - .close = &xdg_toplevel_close, -}; - -static void -xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, - uint32_t serial) -{ - xdg_surface_ack_configure(xdg_surface, serial); -} - -static const struct xdg_surface_listener xdg_surface_listener = { - .configure = &xdg_surface_configure, +static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { + /*.configure =*/ &layer_surface_configure, + /*.closed =*/ &layer_surface_closed, }; struct window * @@ -456,6 +434,10 @@ window_commit_buffer(struct window *window); void window_get_width_height(struct window *window, int *w, int *h); +void window_layer_surface_set_size(struct window *window) { + zwlr_layer_surface_v1_set_size(global_window->layer_surface, global_window->rectangle.width, global_window->rectangle.height); +} + bool display_output_wayland::initialize() { epoll_fd = epoll_create1(0); if(epoll_fd < 0) { @@ -478,15 +460,11 @@ bool display_output_wayland::initialize() { global_window->scale = 1; window_allocate_buffer(global_window); - global_window->xdg_surface = xdg_wm_base_get_xdg_surface(wl_globals.shell, global_window->surface); - xdg_surface_add_listener(global_window->xdg_surface, &xdg_surface_listener, nullptr); + global_window->layer_surface = zwlr_layer_shell_v1_get_layer_surface(wl_globals.layer_shell, + global_window->surface, nullptr, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "conky_namespace"); + window_layer_surface_set_size(global_window); + zwlr_layer_surface_v1_add_listener(global_window->layer_surface, &layer_surface_listener, nullptr); - global_window->xdg_toplevel = xdg_surface_get_toplevel(global_window->xdg_surface); - xdg_toplevel_add_listener(global_window->xdg_toplevel, &xdg_toplevel_listener, nullptr); - - xdg_toplevel_set_app_id(global_window->xdg_toplevel, "conky"); - xdg_toplevel_set_title(global_window->xdg_toplevel, "conky"); - xdg_toplevel_set_parent(global_window->xdg_toplevel, 0); wl_surface_set_buffer_scale(global_window->surface, global_window->scale); wl_surface_commit(global_window->surface); wl_display_roundtrip(global_display); @@ -583,30 +561,38 @@ bool display_output_wayland::main_loop_wait(double t) { /* update struts */ if (changed != 0) { - int sidenum = -1; + int anchor = -1; DBGP("%s", _(PACKAGE_NAME ": defining struts\n")); fflush(stderr); switch (text_alignment.get(*state)) { case TOP_LEFT: + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; + break; case TOP_RIGHT: + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + break; case TOP_MIDDLE: { - sidenum = 2; + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; break; } case BOTTOM_LEFT: + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; + break; case BOTTOM_RIGHT: + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + break; case BOTTOM_MIDDLE: { - sidenum = 3; + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; break; } case MIDDLE_LEFT: { - sidenum = 0; + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; break; } case MIDDLE_RIGHT: { - sidenum = 1; + anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; break; } @@ -614,7 +600,10 @@ bool display_output_wayland::main_loop_wait(double t) { case MIDDLE_MIDDLE: /* XXX What about these? */; } - //set_struts(sidenum); + if (anchor != -1) { + zwlr_layer_surface_v1_set_anchor(global_window->layer_surface, anchor); + zwlr_layer_surface_v1_set_margin(global_window->layer_surface, gap_y.get(*state), gap_x.get(*state), gap_y.get(*state), gap_x.get(*state)); + } } clear_text(1); @@ -1160,6 +1149,7 @@ window_commit_buffer(struct window *window) { wl_surface_set_buffer_scale(global_window->surface, global_window->scale); wl_surface_attach(window->surface, get_buffer_from_cairo_surface(window->cairo_surface), 0, 0); /* repaint all the pixels in the surface, change size to only repaint changed area*/ + window_layer_surface_set_size(global_window); wl_surface_damage(window->surface, window->rectangle.x, window->rectangle.y, window->rectangle.width, diff --git a/src/wlr-layer-shell-unstable-v1.xml b/src/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 00000000..d8a8029a --- /dev/null +++ b/src/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. + + + + +