diff --git a/src/Makefile.am b/src/Makefile.am index a5586bd..3775ffd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -192,12 +192,13 @@ wmaker_SOURCES += \ backend/wayland/wl_popup.c \ backend/wayland/wl_text_input.c \ backend/wayland/wl_xdg_activation.c \ - backend/wayland/wl_layer_shell.c \ backend/wayland/wl_output_mgmt.c \ backend/wayland/wl_output.c \ backend/wayland/wl_xdg.c \ backend/wayland/wl_xwayland.c \ backend/wayland/wl_dock.c \ + backend/wayland/wl_layer_shell.c \ + backend/wayland/wl_virtual_pointer.c \ backend/wayland/wl_input_fwd.c endif diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index e0db7fe..aa92b20 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -337,11 +337,11 @@ extern void handle_virtual_keyboard_new(struct wl_listener *listener, void *data /* Phase 35: xdg_activation request_activate handler. */ extern void handle_xdg_activation_request(struct wl_listener *listener, void *data); /* Phase 36: layer shell new_surface handler. */ -extern void handle_layer_shell_new_surface(struct wl_listener *listener, void *data); +static void handle_layer_shell_new_surface(struct wl_listener *listener, void *data); extern void handle_xdg_new_toplevel(struct wl_listener *listener, void *data); extern void handle_new_toplevel_decoration(struct wl_listener *listener, void *data); /* Phase 37: virtual pointer handler. */ -static void handle_virtual_pointer_new(struct wl_listener *listener, void *data); +extern void handle_virtual_pointer_new(struct wl_listener *listener, void *data); /* Phase 17: XWayland new surface forward declaration. */ extern void handle_xwayland_new_surface(struct wl_listener *listener, void *data); extern void handle_xwayland_ready(struct wl_listener *listener, void *data); @@ -1704,21 +1704,194 @@ handle_new_shortcuts_inhibitor(struct wl_listener *listener, void *data) /* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */ -/* Phase 37: data control, input inhibitor, virtual pointer, idle, */ -/* xdg-foreign */ +/* ------------------------------------------------------------------ */ +/* Phase 36: layer shell — panels, docks, wallpapers, overlays */ /* ------------------------------------------------------------------ */ +#define LAYER_VIEW_FROM(l, field) \ + ((struct wl_layer_view *)((char *)(l) - offsetof(struct wl_layer_view, field))) + /* - * handle_virtual_pointer_new — a client created a virtual pointer device - * (e.g. a VNC / remote desktop server injecting cursor events). + * Compute the geometry for a layer surface given its anchor, margins, + * desired size and the output dimensions. */ static void -handle_virtual_pointer_new(struct wl_listener *listener, void *data) +layer_surface_compute_geometry(struct wlr_layer_surface_v1 *ls, + uint32_t ow, uint32_t oh, + uint32_t *out_w, uint32_t *out_h) { - (void)listener; (void)data; - wwarning("wl_backend: virtual pointer created"); + const struct wlr_layer_surface_v1_state *s = &ls->current; + uint32_t w = s->desired_width; + uint32_t h = s->desired_height; + + bool anchor_lr = (s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) && + (s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); + bool anchor_tb = (s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) && + (s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM); + + if (w == 0 && anchor_lr) + w = ow - s->margin.left - s->margin.right; + if (h == 0 && anchor_tb) + h = oh - s->margin.top - s->margin.bottom; + + /* Ensure minimum 1×1. */ + *out_w = w > 0 ? w : 1; + *out_h = h > 0 ? h : 1; } +/* + * Compute the position of a layer surface on the output given its + * anchor, margins and final dimensions. + */ +static void +layer_surface_compute_position(struct wlr_layer_surface_v1 *ls, + uint32_t ow, uint32_t oh, + uint32_t fw, uint32_t fh, + int *out_x, int *out_y) +{ + const struct wlr_layer_surface_v1_state *s = &ls->current; + int x = 0, y = 0; + + bool anchor_l = s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT; + bool anchor_r = s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + bool anchor_t = s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP; + bool anchor_b = s->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + if (anchor_l && anchor_r) + x = s->margin.left; + else if (anchor_l) + x = s->margin.left; + else if (anchor_r) + x = (int)ow - (int)fw - s->margin.right; + else + x = ((int)ow - (int)fw) / 2; + + if (anchor_t && anchor_b) + y = s->margin.top; + else if (anchor_t) + y = s->margin.top; + else if (anchor_b) + y = (int)oh - (int)fh - s->margin.bottom; + else + y = ((int)oh - (int)fh) / 2; + + *out_x = x; + *out_y = y; +} + +static void +handle_layer_surface_map(struct wl_listener *listener, void *data) +{ + (void)data; + struct wl_layer_view *lv = LAYER_VIEW_FROM(listener, map_listener); + wwarning("wl_backend: layer surface mapped (layer=%d namespace=%s)", + lv->layer_surface->current.layer, + lv->layer_surface->namespace ? lv->layer_surface->namespace : ""); +} + +static void +handle_layer_surface_unmap(struct wl_listener *listener, void *data) +{ + (void)data; + struct wl_layer_view *lv = LAYER_VIEW_FROM(listener, unmap_listener); + wwarning("wl_backend: layer surface unmapped"); + if (lv->scene_node) { + wlr_scene_node_destroy(&lv->scene_node->node); + lv->scene_node = NULL; + } +} + +static void +handle_layer_surface_destroy(struct wl_listener *listener, void *data) +{ + (void)data; + struct wl_layer_view *lv = LAYER_VIEW_FROM(listener, destroy_listener); + wl_list_remove(&lv->map_listener.link); + wl_list_remove(&lv->unmap_listener.link); + wl_list_remove(&lv->destroy_listener.link); + if (lv->scene_node) { + wlr_scene_node_destroy(&lv->scene_node->node); + lv->scene_node = NULL; + } + wl_list_remove(&lv->link); + free(lv); +} + +/* + * handle_layer_shell_new_surface — a client created a layer surface. + * Assign the output if unset, compute geometry, send configure, and + * wire map/unmap/destroy lifecycle. + */ +static void +handle_layer_shell_new_surface(struct wl_listener *listener, void *data) +{ + (void)listener; + struct wlr_layer_surface_v1 *ls = data; + + /* Assign output if client did not specify one. */ + if (!ls->output && wl_state.output) + ls->output = wl_state.output; + + struct wl_layer_view *lv = calloc(1, sizeof(*lv)); + if (!lv) { + wlr_layer_surface_v1_destroy(ls); + return; + } + lv->layer_surface = ls; + + /* Add to scene graph in the correct level bucket. + * Layer-shell layer enum maps to WL_LAYER_* buckets: + * BACKGROUND -> WL_LAYER_BACKGROUND + * BOTTOM -> WL_LAYER_DESKTOP + * TOP -> WL_LAYER_DOCK + * OVERLAY -> WL_LAYER_OVERLAY */ + if (wl_state.scene) { + int _lb; + switch (ls->pending.layer) { + case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND: _lb = WL_LAYER_BACKGROUND; break; + case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM: _lb = WL_LAYER_DESKTOP; break; + case ZWLR_LAYER_SHELL_V1_LAYER_TOP: _lb = WL_LAYER_DOCK; break; + case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY: _lb = WL_LAYER_OVERLAY; break; + default: _lb = WL_LAYER_NORMAL; break; + } + struct wlr_scene_tree *_lp = wl_state.level_trees[_lb] + ? wl_state.level_trees[_lb] : &wl_state.scene->tree; + lv->scene_node = wlr_scene_subsurface_tree_create(_lp, ls->surface); + } + + /* Compute and send initial configure. */ + uint32_t ow = 1920, oh = 1080; + if (ls->output) { + ow = ls->output->width > 0 ? (uint32_t)ls->output->width : 1920; + oh = ls->output->height > 0 ? (uint32_t)ls->output->height : 1080; + } + uint32_t fw, fh; + layer_surface_compute_geometry(ls, ow, oh, &fw, &fh); + wlr_layer_surface_v1_configure(ls, fw, fh); + + /* Position the scene node. */ + if (lv->scene_node) { + int sx, sy; + layer_surface_compute_position(ls, ow, oh, fw, fh, &sx, &sy); + wlr_scene_node_set_position(&lv->scene_node->node, sx, sy); + } + + /* Wire lifecycle listeners. */ + lv->map_listener.notify = handle_layer_surface_map; + lv->unmap_listener.notify = handle_layer_surface_unmap; + lv->destroy_listener.notify = handle_layer_surface_destroy; + wl_signal_add(&ls->surface->events.map, &lv->map_listener); + wl_signal_add(&ls->surface->events.unmap, &lv->unmap_listener); + wl_signal_add(&ls->events.destroy, &lv->destroy_listener); + + wl_list_insert(&wl_state.layer_list, &lv->link); + + wwarning("wl_backend: layer surface new layer=%d %ux%u namespace=%s", + ls->current.layer, fw, fh, + ls->namespace ? ls->namespace : ""); +} + +/* ------------------------------------------------------------------ */ void handle_pointer_button(struct wl_listener *listener, void *data) { diff --git a/src/backend/wayland/wl_virtual_pointer.c b/src/backend/wayland/wl_virtual_pointer.c new file mode 100644 index 0000000..f9033d3 --- /dev/null +++ b/src/backend/wayland/wl_virtual_pointer.c @@ -0,0 +1,21 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include "wl_types.h" + +/* Phase 37: data control, input inhibitor, virtual pointer, idle, */ +/* xdg-foreign */ +/* ------------------------------------------------------------------ */ + +/* + * handle_virtual_pointer_new — a client created a virtual pointer device + * (e.g. a VNC / remote desktop server injecting cursor events). + */ +void +handle_virtual_pointer_new(struct wl_listener *listener, void *data) +{ + (void)listener; (void)data; + wwarning("wl_backend: virtual pointer created"); +}