AtlatestRepositorysigil-wm

sigil-wm / tree / testpopup-client.c

1/* SPDX-License-Identifier: BSD-3-Clause */
2#define _POSIX_C_SOURCE 200809L
3#include <fcntl.h>
4#include <stdbool.h>
5#include <stdint.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/mman.h>
10#include <unistd.h>
11#include <wayland-client.h>
12#include "xdg-shell-client-protocol.h"
13#include "wlr-layer-shell-unstable-v1-client-protocol.h"
15struct surface {
16 struct wl_surface *wl;
17 struct xdg_surface *xdg;
18 struct xdg_popup *popup;
19 int width, height;
20 bool child;
21 bool frame_pending;
22 int frame_phase;
23};
24static struct wl_display *display;
25static struct wl_compositor *compositor;
26static struct wl_shm *shm;
27static struct xdg_wm_base *shell;
28static struct zwlr_layer_shell_v1 *layers;
29static struct zwlr_layer_surface_v1 *layer;
30static struct surface parent, popup;
31static int phase, configured_phase, completed_phase = -1, checks, failures;
32static bool running = true;
34static void require(bool ok, const char *message) {
35 if (ok) return;
36 fprintf(stderr, "popup client: %s\n", message);
37 exit(2);
39static struct xdg_positioner *positioner(void) {
40 struct xdg_positioner *p = xdg_wm_base_create_positioner(shell);
41 xdg_positioner_set_size(p, 160, 100);
42 xdg_positioner_set_anchor_rect(p, phase == 2 ? 0 : 140, phase == 2 ? 0 : 140, 1, 1);
43 xdg_positioner_set_anchor(p, phase == 2 ? XDG_POSITIONER_ANCHOR_TOP_LEFT : XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT);
44 xdg_positioner_set_gravity(p, phase == 2 ? XDG_POSITIONER_GRAVITY_TOP_LEFT : XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
45 uint32_t adjust = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
46 if (phase != 1) adjust |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
47 xdg_positioner_set_constraint_adjustment(p, adjust);
48 return p;
50static void open_popup(void);
51static void frame(void *data, struct wl_callback *callback, uint32_t time) {
52 (void)time;
53 struct surface *s = data;
54 wl_callback_destroy(callback);
55 s->frame_pending = false;
56 if (!s->child) {
57 if (!popup.wl) open_popup();
58 } else if (s->frame_phase <= completed_phase) return;
59 else if ((phase = (completed_phase = s->frame_phase) + 1) < 3) {
60 struct xdg_positioner *p = positioner();
61 xdg_popup_reposition(popup.popup, p, phase);
62 xdg_positioner_destroy(p);
63 } else running = false;
65static const struct wl_callback_listener frame_listener = {.done = frame};
66static void draw(struct surface *s) {
67 char path[] = "/tmp/sigil-popup-client.XXXXXX";
68 int fd = mkstemp(path);
69 unlink(path);
70 require(fd >= 0, "shm file");
71 int margin_x = s->child || layer ? 0 : 17;
72 int margin_y = s->child || layer ? 0 : 11;
73 int width = s->width + margin_x * 2, height = s->height + margin_y * 2;
74 size_t size = (size_t)width * height * 4;
75 require(ftruncate(fd, size) == 0, "shm size");
76 uint32_t *pixels = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
77 require(pixels != MAP_FAILED, "shm map");
78 for (size_t i = 0; i < size / 4; i++) pixels[i] = s->child ? 0xffeeaa22 : 0xff00cc44;
79 struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
80 struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_XRGB8888);
81 wl_shm_pool_destroy(pool);
82 munmap(pixels, size);
83 close(fd);
84 if (s->xdg) xdg_surface_set_window_geometry(s->xdg, margin_x, margin_y, s->width, s->height);
85 wl_surface_attach(s->wl, buffer, 0, 0);
86 wl_surface_damage_buffer(s->wl, 0, 0, width, height);
87 if (!s->frame_pending && (!s->child || configured_phase > completed_phase)) {
88 s->frame_pending = true;
89 s->frame_phase = configured_phase;
90 wl_callback_add_listener(wl_surface_frame(s->wl), &frame_listener, s);
91 }
92 wl_surface_commit(s->wl);
93 wl_buffer_destroy(buffer);
95static void configured(void *data, struct xdg_surface *xdg, uint32_t serial) {
96 xdg_surface_ack_configure(xdg, serial);
97 draw(data);
99static const struct xdg_surface_listener surface_listener = {.configure = configured};
100static void popup_configure(void *data, struct xdg_popup *role, int32_t x, int32_t y, int32_t width, int32_t height) {
101 (void)data; (void)role;
102 popup.width = width; popup.height = height;
103 /* Both outputs share this parent-relative box, including the nonzero output origin. */
104 bool inside = x >= -480 && y >= -290 && x + width <= 160 && y + height <= 190;
105 bool adjustment = configured_phase == 0 ? x == -20 && y == 40 :
106 configured_phase == 1 ? x == 0 && y == 90 : x == -160 && y == -100;
107 checks |= 1 << configured_phase;
108 failures += !inside || !adjustment;
109 printf("POPUP_%s=%s phase=%d geometry=%d,%d,%d,%d inside=%d expected-adjustment=%d\n",
110 layer ? "LAYER" : "TOPLEVEL", inside && adjustment ? "PASS" : "FAIL", configured_phase,
111 x, y, width, height, inside, adjustment);
112 fflush(stdout);
114static void popup_done(void *data, struct xdg_popup *role) {
115 (void)data; (void)role;
116 require(false, "unexpected dismissal");
118static void repositioned(void *data, struct xdg_popup *role, uint32_t token) {
119 (void)data; (void)role;
120 require(token == (uint32_t)phase, "reposition token");
121 configured_phase = token;
123static const struct xdg_popup_listener popup_listener = {
124 .configure = popup_configure, .popup_done = popup_done, .repositioned = repositioned,
125};
126static void open_popup(void) {
127 popup = (struct surface){.wl = wl_compositor_create_surface(compositor), .child = true};
128 popup.xdg = xdg_wm_base_get_xdg_surface(shell, popup.wl);
129 xdg_surface_add_listener(popup.xdg, &surface_listener, &popup);
130 struct xdg_positioner *p = positioner();
131 popup.popup = xdg_surface_get_popup(popup.xdg, parent.xdg, p);
132 if (layer) zwlr_layer_surface_v1_get_popup(layer, popup.popup);
133 xdg_popup_add_listener(popup.popup, &popup_listener, &popup);
134 xdg_positioner_destroy(p);
135 wl_surface_commit(popup.wl);
137static void top_configure(void *data, struct xdg_toplevel *top, int32_t width, int32_t height, struct wl_array *states) {
138 (void)data; (void)top; (void)states;
139 if (width) parent.width = width;
140 if (height) parent.height = height;
142static void top_close(void *data, struct xdg_toplevel *top) { (void)data; (void)top; running = false; }
143static const struct xdg_toplevel_listener top_listener = {.configure = top_configure, .close = top_close};
144static void layer_configure(void *data, struct zwlr_layer_surface_v1 *role, uint32_t serial, uint32_t width, uint32_t height) {
145 (void)data;
146 parent.width = width; parent.height = height;
147 zwlr_layer_surface_v1_ack_configure(role, serial);
148 draw(&parent);
150static void layer_closed(void *data, struct zwlr_layer_surface_v1 *role) { (void)data; (void)role; running = false; }
151static const struct zwlr_layer_surface_v1_listener layer_listener = {.configure = layer_configure, .closed = layer_closed};
152static void ping(void *data, struct xdg_wm_base *base, uint32_t serial) { (void)data; xdg_wm_base_pong(base, serial); }
153static const struct xdg_wm_base_listener shell_listener = {.ping = ping};
154static void global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
155 (void)data;
156 if (!strcmp(interface, "wl_compositor")) compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
157 else if (!strcmp(interface, "wl_shm")) shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
158 else if (!strcmp(interface, "xdg_wm_base")) {
159 shell = wl_registry_bind(registry, name, &xdg_wm_base_interface, version < 3 ? version : 3);
160 xdg_wm_base_add_listener(shell, &shell_listener, NULL);
161 } else if (!strcmp(interface, "zwlr_layer_shell_v1")) layers = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
163static void removed(void *data, struct wl_registry *registry, uint32_t name) { (void)data; (void)registry; (void)name; }
164static const struct wl_registry_listener registry_listener = {.global = global, .global_remove = removed};
165int main(int argc, char **argv) {
166 require(argc == 2, "usage: popup-client toplevel|layer");
167 display = wl_display_connect(NULL);
168 require(display != NULL, "connect");
169 struct wl_registry *registry = wl_display_get_registry(display);
170 wl_registry_add_listener(registry, &registry_listener, NULL);
171 require(wl_display_roundtrip(display) >= 0 && compositor && shm && shell, "registry");
172 parent.wl = wl_compositor_create_surface(compositor);
173 parent.width = parent.height = 150;
174 struct xdg_toplevel *top = NULL;
175 if (!strcmp(argv[1], "layer")) {
176 require(layers != NULL, "layer shell");
177 layer = zwlr_layer_shell_v1_get_layer_surface(layers, parent.wl, NULL, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "popup-test");
178 zwlr_layer_surface_v1_add_listener(layer, &layer_listener, NULL);
179 zwlr_layer_surface_v1_set_size(layer, 150, 150);
180 } else {
181 require(!strcmp(argv[1], "toplevel"), "unknown role");
182 parent.xdg = xdg_wm_base_get_xdg_surface(shell, parent.wl);
183 xdg_surface_add_listener(parent.xdg, &surface_listener, &parent);
184 top = xdg_surface_get_toplevel(parent.xdg);
185 xdg_toplevel_add_listener(top, &top_listener, NULL);
186 }
187 wl_surface_commit(parent.wl);
188 while (running && wl_display_dispatch(display) >= 0) {}
189 require(checks == 7, "missing popup configurations");
190 xdg_popup_destroy(popup.popup);
191 xdg_surface_destroy(popup.xdg);
192 wl_surface_destroy(popup.wl);
193 if (top) { xdg_toplevel_destroy(top); xdg_surface_destroy(parent.xdg); }
194 if (layer) zwlr_layer_surface_v1_destroy(layer);
195 wl_surface_destroy(parent.wl);
196 wl_display_flush(display);
197 wl_display_disconnect(display);
198 return failures ? 1 : 0;