AtlatestRepositorysigil-wm

sigil-wm / tree / testgeometry-client.c

1/* SPDX-License-Identifier: BSD-3-Clause
2 * Real xdg-shell client for configuration, input and composed-pixel tests. */
3#define _POSIX_C_SOURCE 200809L
4#include <errno.h>
5#include <fcntl.h>
6#include <poll.h>
7#include <stdbool.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/mman.h>
13#include <time.h>
14#include <unistd.h>
15#include <wayland-client.h>
16#include "xdg-shell-client-protocol.h"
17#include "wlr-layer-shell-unstable-v1-client-protocol.h"
18struct client {
19 struct wl_display *display;
20 struct wl_compositor *compositor;
21 struct wl_shm *shm;
22 struct wl_seat *seat;
23 struct wl_output *chosen_output;
24 unsigned output_count;
25 struct wl_keyboard *keyboard;
26 struct wl_pointer *pointer;
27 struct xdg_wm_base *shell;
28 struct zwlr_layer_shell_v1 *layers;
29 struct zwlr_layer_surface_v1 *layer;
30 struct wl_surface *surface;
31 struct xdg_surface *xdg;
32 struct xdg_toplevel *top;
33 int width,height;
34 uint32_t color;
35 bool running;
36 int geometry_x,geometry_y;
37 uint32_t held_configure;
38 double pointer_x,pointer_y,press_x,press_y;
39 bool button_down,shifted;
40 struct client *parent,*popup;
41 struct xdg_popup *popup_role;
42};
43static void open_popup(struct client *c,uint32_t serial);
44static void close_popup(struct client *c);
45static bool hold_configure(void) {
46 const char *path=getenv("SIGIL_WM_CLIENT_HOLD");
47 return path && access(path,F_OK)==0;
49static void draw(struct client *c);
50static void request_frame(struct client *c);
51static void frame(void *data,struct wl_callback *callback,uint32_t time) {
52 (void)time;
53 struct client *c=data;
54 wl_callback_destroy(callback);
55 if(!c->running) return;
56 const char *close=getenv("SIGIL_WM_CLIENT_POPUP_CLOSE");
57 if(!c->parent && c->popup && close && access(close,F_OK)==0) {
58 close_popup(c->popup);
59 unlink(close);
60 }
61 if(c->held_configure && !hold_configure()) {
62 xdg_surface_ack_configure(c->xdg,c->held_configure);
63 c->held_configure=0;
64 draw(c);
65 return;
66 }
67 request_frame(c);
68 wl_surface_damage_buffer(c->surface,0,0,INT32_MAX,INT32_MAX);
69 wl_surface_commit(c->surface);
71static const struct wl_callback_listener frame_listener={.done=frame};
72static void request_frame(struct client *c) {
73 struct wl_callback *callback=wl_surface_frame(c->surface);
74 wl_callback_add_listener(callback,&frame_listener,c);
76static void draw(struct client *c) {
77 char path[]="/tmp/sigil-wm-client-XXXXXX";
78 int fd=mkstemp(path);
79 unlink(path);
80 if(fd<0) exit(2);
81 int width=c->width+2*c->geometry_x,height=c->height+2*c->geometry_y;
82 size_t size=(size_t)width*height*4;
83 if(ftruncate(fd,size)) exit(2);
84 uint32_t *pixels=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
85 if(pixels==MAP_FAILED) exit(2);
86 for(size_t i=0;i<size/4;i++) pixels[i]=c->color;
87 struct wl_shm_pool *pool=wl_shm_create_pool(c->shm,fd,size);
88 struct wl_buffer *buffer=wl_shm_pool_create_buffer(pool,0,width,height,width*4,WL_SHM_FORMAT_XRGB8888);
89 wl_shm_pool_destroy(pool);
90 munmap(pixels,size); close(fd);
91 if(c->xdg) {
92 xdg_surface_set_window_geometry(c->xdg,c->geometry_x,c->geometry_y,c->width,c->height);
93 struct wl_region *region=wl_compositor_create_region(c->compositor);
94 wl_region_add(region,c->geometry_x,c->geometry_y,c->width,c->height);
95 wl_surface_set_input_region(c->surface,region);
96 wl_region_destroy(region);
97 }
98 wl_surface_attach(c->surface,buffer,0,0);
99 wl_surface_damage_buffer(c->surface,0,0,INT32_MAX,INT32_MAX);
100 request_frame(c);
101 wl_surface_commit(c->surface);
102 wl_buffer_destroy(buffer);
104static void configure(void *data,struct xdg_surface *surface,uint32_t serial) {
105 struct client *c=data;
106 if(hold_configure()) {
107 c->held_configure=serial;
108 printf("CLIENT_CONFIGURE_HELD=%u\n",serial); fflush(stdout);
109 return;
110 }
111 xdg_surface_ack_configure(surface,serial);
112 draw(c);
113 if(c->parent) { puts("POPUP_MAPPED"); fflush(stdout); }
114 printf("CLIENT_CONFIGURE=%dx%d\n",c->width,c->height); fflush(stdout);
116static const struct xdg_surface_listener surface_listener={.configure=configure};
117static void popup_configure(void *data,struct xdg_popup *popup,
118 int32_t x,int32_t y,int32_t width,int32_t height) {
119 struct client *c=data;
120 (void)popup;
121 c->width=width; c->height=height;
122 printf("POPUP_CONFIGURE=%d,%d,%d,%d\n",x,y,width,height); fflush(stdout);
124static void popup_done(void *data,struct xdg_popup *popup) {
125 (void)popup;
126 close_popup(data);
128static void popup_repositioned(void *data,struct xdg_popup *popup,uint32_t token) {
129 (void)data; (void)popup; (void)token;
131static const struct xdg_popup_listener popup_listener={
132 .configure=popup_configure,.popup_done=popup_done,.repositioned=popup_repositioned,
133};
134static void close_popup(struct client *c) {
135 if(!c->running) return;
136 c->running=false;
137 xdg_popup_destroy(c->popup_role);
138 xdg_surface_destroy(c->xdg);
139 wl_surface_destroy(c->surface);
140 c->parent->popup=NULL;
141 puts("POPUP_DESTROYED"); fflush(stdout);
143static void open_popup(struct client *c,uint32_t serial) {
144 struct client *popup=calloc(1,sizeof(*popup));
145 if(!popup) exit(2);
146 *popup=(struct client){.display=c->display,.compositor=c->compositor,.shm=c->shm,
147 .shell=c->shell,.seat=c->seat,.parent=c,.width=120,.height=80,
148 .geometry_x=6,.geometry_y=4,.color=0xffeeaa22,.running=true};
149 c->popup=popup;
150 popup->surface=wl_compositor_create_surface(c->compositor);
151 popup->xdg=xdg_wm_base_get_xdg_surface(c->shell,popup->surface);
152 xdg_surface_add_listener(popup->xdg,&surface_listener,popup);
153 struct xdg_positioner *positioner=xdg_wm_base_create_positioner(c->shell);
154 xdg_positioner_set_size(positioner,120,80);
155 xdg_positioner_set_anchor_rect(positioner,180,20,1,1);
156 xdg_positioner_set_anchor(positioner,XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT);
157 xdg_positioner_set_gravity(positioner,XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
158 popup->popup_role=xdg_surface_get_popup(popup->xdg,c->xdg,positioner);
159 xdg_popup_add_listener(popup->popup_role,&popup_listener,popup);
160 xdg_positioner_destroy(positioner);
161 xdg_popup_grab(popup->popup_role,c->seat,serial);
162 wl_surface_commit(popup->surface);
163 puts("POPUP_GRAB_REQUESTED"); fflush(stdout);
165static void layer_configure(void *data, struct zwlr_layer_surface_v1 *surface,
166 uint32_t serial, uint32_t width, uint32_t height) {
167 struct client *client = data;
168 client->width = width;
169 client->height = height;
170 zwlr_layer_surface_v1_ack_configure(surface, serial);
171 draw(client);
172 printf("LAYER_CONFIGURE=%ux%u\n", width, height);
173 fflush(stdout);
175static void layer_closed(void *data, struct zwlr_layer_surface_v1 *surface) {
176 (void)surface;
177 ((struct client *)data)->running = false;
179static const struct zwlr_layer_surface_v1_listener layer_listener = {
180 .configure = layer_configure, .closed = layer_closed,
181};
182static void top_configure(void *data,struct xdg_toplevel *top,int32_t width,int32_t height,struct wl_array *states) {
183 (void)top;
184 struct client *c=data;
185 if(width>0 && width<=16384) c->width=width;
186 if(height>0 && height<=16384) c->height=height;
187 uint32_t tiled=0,*state;
188 bool fullscreen=false;
189 wl_array_for_each(state,states) {
190 switch(*state) {
191 case XDG_TOPLEVEL_STATE_TILED_TOP: tiled|=1; break;
192 case XDG_TOPLEVEL_STATE_TILED_BOTTOM: tiled|=2; break;
193 case XDG_TOPLEVEL_STATE_TILED_LEFT: tiled|=4; break;
194 case XDG_TOPLEVEL_STATE_TILED_RIGHT: tiled|=8; break;
195 case XDG_TOPLEVEL_STATE_FULLSCREEN: fullscreen=true; break;
196 }
197 }
198 printf("CLIENT_TOPLEVEL tiled=%u fullscreen=%d\n",tiled,fullscreen); fflush(stdout);
200static void top_close(void *data,struct xdg_toplevel *top) { (void)top; ((struct client *)data)->running=false; }
201static void top_bounds(void *data,struct xdg_toplevel *top,int32_t width,int32_t height) {
202 (void)data; (void)top; (void)width; (void)height;
204static void top_capabilities(void *data,struct xdg_toplevel *top,struct wl_array *capabilities) {
205 (void)data; (void)top; (void)capabilities;
207static const struct xdg_toplevel_listener top_listener={.configure=top_configure,.close=top_close,
208 .configure_bounds=top_bounds,.wm_capabilities=top_capabilities};
209static void ping(void *data,struct xdg_wm_base *shell,uint32_t serial) { (void)data; xdg_wm_base_pong(shell,serial); }
210static const struct xdg_wm_base_listener shell_listener={.ping=ping};
211static void keymap(void *data,struct wl_keyboard *keyboard,uint32_t format,int fd,uint32_t size) {
212 (void)data; (void)keyboard; (void)format; (void)size; close(fd);
214static void key_enter(void *data,struct wl_keyboard *keyboard,uint32_t serial,struct wl_surface *surface,struct wl_array *keys) {
215 (void)data;(void)keyboard;(void)serial;(void)surface;(void)keys; puts("CLIENT_KEYBOARD_ENTER"); fflush(stdout);
217static void key_leave(void *data,struct wl_keyboard *keyboard,uint32_t serial,struct wl_surface *surface) {
218 (void)data;(void)keyboard;(void)serial;(void)surface;
220static void key(void *data,struct wl_keyboard *keyboard,uint32_t serial,uint32_t time,uint32_t code,uint32_t state) {
221 (void)data;(void)keyboard;(void)serial;(void)time; printf("CLIENT_KEY=%u,%u\n",code,state); fflush(stdout);
223static void modifiers(void *data,struct wl_keyboard *keyboard,uint32_t serial,uint32_t dep,uint32_t lat,uint32_t lock,uint32_t group) {
224 (void)data;(void)keyboard;(void)serial; printf("CLIENT_MODIFIERS=%u,%u,%u,%u\n",dep,lat,lock,group); fflush(stdout);
226static void repeat(void *data,struct wl_keyboard *keyboard,int32_t rate,int32_t delay) {
227 (void)data;(void)keyboard;(void)rate;(void)delay;
229static const struct wl_keyboard_listener keyboard_listener={.keymap=keymap,.enter=key_enter,.leave=key_leave,.key=key,.modifiers=modifiers,.repeat_info=repeat};
230static void pointer_enter(void *data,struct wl_pointer *p,uint32_t serial,struct wl_surface *surface,wl_fixed_t x,wl_fixed_t y) {
231 struct client *c=data;
232 c->pointer_x=wl_fixed_to_double(x); c->pointer_y=wl_fixed_to_double(y);
233 (void)p;(void)serial;(void)surface; printf("CLIENT_POINTER_ENTER=%d,%d\n",wl_fixed_to_int(x),wl_fixed_to_int(y)); fflush(stdout);
235static void pointer_leave(void *data,struct wl_pointer *p,uint32_t serial,struct wl_surface *surface) {
236 (void)data;(void)p;(void)serial;(void)surface;
238static void pointer_motion(void *data,struct wl_pointer *p,uint32_t time,wl_fixed_t x,wl_fixed_t y) {
239 struct client *c=data;
240 c->pointer_x=wl_fixed_to_double(x); c->pointer_y=wl_fixed_to_double(y);
241 if(c->button_down && (c->pointer_x!=c->press_x || c->pointer_y!=c->press_y)) {
242 c->shifted=true;
243 printf("HELD_COORDINATE_SHIFT=%.3f,%.3f\n",c->pointer_x-c->press_x,c->pointer_y-c->press_y);
244 }
245 if(getenv("SIGIL_WM_CLIENT_TIMESTAMPS")) {
246 struct timespec now;
247 clock_gettime(CLOCK_MONOTONIC,&now);
248 printf("CLIENT_POINTER_TIME=%lld %d %d protocol-ms=%u\n",
249 (long long)now.tv_sec*1000000000+now.tv_nsec,
250 wl_fixed_to_int(x),wl_fixed_to_int(y),time);
251 }
252 (void)data;(void)p;(void)time; printf("CLIENT_POINTER_MOTION=%d,%d\n",wl_fixed_to_int(x),wl_fixed_to_int(y)); fflush(stdout);
254static void pointer_button(void *data,struct wl_pointer *p,uint32_t serial,uint32_t time,uint32_t code,uint32_t state) {
255 struct client *c=data;
256 if(state) {
257 c->button_down=true; c->shifted=false;
258 c->press_x=c->pointer_x; c->press_y=c->pointer_y;
259 const char *trigger=getenv("SIGIL_WM_CLIENT_POPUP_OPEN");
260 if(!c->parent && !c->popup && trigger && access(trigger,F_OK)==0) {
261 open_popup(c,serial);
262 unlink(trigger);
263 }
264 } else {
265 printf("STATIONARY_CLICK=%s\n",c->shifted ? "FAIL" : "PASS");
266 c->button_down=false;
267 }
268 (void)p;(void)serial;(void)time; printf("CLIENT_BUTTON=%u,%u\n",code,state); fflush(stdout);
270static void pointer_axis(void *data,struct wl_pointer *p,uint32_t time,uint32_t axis,wl_fixed_t value) {
271 (void)data;(void)p;(void)time; printf("CLIENT_AXIS=%u,%d\n",axis,wl_fixed_to_int(value)); fflush(stdout);
273static void pointer_frame(void *data,struct wl_pointer *p) {(void)data;(void)p;}
274static void pointer_source(void *data,struct wl_pointer *p,uint32_t source) {(void)data;(void)p;(void)source;}
275static void pointer_stop(void *data,struct wl_pointer *p,uint32_t time,uint32_t axis) {(void)data;(void)p;(void)time;(void)axis;}
276static void pointer_discrete(void *data,struct wl_pointer *p,uint32_t axis,int32_t discrete) {(void)data;(void)p;(void)axis;(void)discrete;}
277static const struct wl_pointer_listener pointer_listener={.enter=pointer_enter,.leave=pointer_leave,.motion=pointer_motion,.button=pointer_button,.axis=pointer_axis,.frame=pointer_frame,.axis_source=pointer_source,.axis_stop=pointer_stop,.axis_discrete=pointer_discrete};
278static void capabilities(void *data,struct wl_seat *seat,uint32_t mask) {
279 struct client *c=data;
280 if((mask&WL_SEAT_CAPABILITY_KEYBOARD) && !c->keyboard) {
281 c->keyboard=wl_seat_get_keyboard(seat); wl_keyboard_add_listener(c->keyboard,&keyboard_listener,c);
282 }
283 if((mask&WL_SEAT_CAPABILITY_POINTER) && !c->pointer) {
284 c->pointer=wl_seat_get_pointer(seat); wl_pointer_add_listener(c->pointer,&pointer_listener,c);
285 }
287static void seat_name(void *data,struct wl_seat *seat,const char *name) {(void)data;(void)seat;(void)name;}
288static const struct wl_seat_listener seat_listener={.capabilities=capabilities,.name=seat_name};
289static void output_geometry(void *data,struct wl_output *output,int32_t x,int32_t y,
290 int32_t pw,int32_t ph,int32_t subpixel,const char *make,const char *model,int32_t transform) {
291 (void)data; (void)output; (void)x; (void)y; (void)pw; (void)ph;
292 (void)subpixel; (void)make; (void)model; (void)transform;
294static void output_mode(void *data,struct wl_output *output,uint32_t flags,
295 int32_t width,int32_t height,int32_t refresh) {
296 (void)data; (void)output; (void)flags; (void)width; (void)height; (void)refresh;
298static const struct wl_output_listener output_listener={.geometry=output_geometry,.mode=output_mode};
299static void global(void *data,struct wl_registry *registry,uint32_t name,const char *interface,uint32_t version) {
300 struct client *c=data;
301 if(!strcmp(interface,wl_compositor_interface.name)) c->compositor=wl_registry_bind(registry,name,&wl_compositor_interface,4);
302 else if(!strcmp(interface,wl_shm_interface.name)) c->shm=wl_registry_bind(registry,name,&wl_shm_interface,1);
303 else if(!strcmp(interface,xdg_wm_base_interface.name)) {
304 c->shell=wl_registry_bind(registry,name,&xdg_wm_base_interface,version<6 ? version : 6);
305 xdg_wm_base_add_listener(c->shell,&shell_listener,c);
306 } else if(!strcmp(interface,zwlr_layer_shell_v1_interface.name)) {
307 c->layers=wl_registry_bind(registry,name,&zwlr_layer_shell_v1_interface,version<4 ? version : 4);
308 } else if(!strcmp(interface,wl_output_interface.name)) {
309 c->output_count++;
310 const char *selected=getenv("SIGIL_WM_LAYER_OUTPUT");
311 if(selected && strtoul(selected,NULL,10)==c->output_count) {
312 c->chosen_output=wl_registry_bind(registry,name,&wl_output_interface,1);
313 wl_output_add_listener(c->chosen_output,&output_listener,c);
314 }
315 } else if(!strcmp(interface,wl_seat_interface.name)) {
316 c->seat=wl_registry_bind(registry,name,&wl_seat_interface,version<5 ? version : 5);
317 wl_seat_add_listener(c->seat,&seat_listener,c);
318 }
320static void global_remove(void *data,struct wl_registry *registry,uint32_t name) {(void)data;(void)registry;(void)name;}
321static const struct wl_registry_listener registry_listener={.global=global,.global_remove=global_remove};
322int main(int argc,char **argv) {
323 if(argc!=3 && argc!=4) { fprintf(stderr,"usage: pixel-client RGB TITLE [layer]\n"); return 2; }
324 struct client c={.width=320,.height=240,.color=0xff000000|(uint32_t)strtoul(argv[1],NULL,16),.running=true};
325 const char *geometry=getenv("SIGIL_WM_CLIENT_GEOMETRY");
326 if(geometry && (sscanf(geometry,"%d,%d",&c.geometry_x,&c.geometry_y)!=2 ||
327 c.geometry_x<0 || c.geometry_y<0 || c.geometry_x>128 || c.geometry_y>128)) return 2;
328 c.display=wl_display_connect(NULL);
329 if(!c.display) return 2;
330 struct wl_registry *registry=wl_display_get_registry(c.display);
331 wl_registry_add_listener(registry,&registry_listener,&c);
332 if(wl_display_roundtrip(c.display)<0 || !c.compositor || !c.shm || !c.shell) return 2;
333 c.surface=wl_compositor_create_surface(c.compositor);
334 if(argc==4) {
335 if(strcmp(argv[3], "layer") || !c.layers) return 2;
336 c.layer=zwlr_layer_shell_v1_get_layer_surface(c.layers,c.surface,c.chosen_output,
337 ZWLR_LAYER_SHELL_V1_LAYER_TOP,"sigil-wm-test");
338 zwlr_layer_surface_v1_add_listener(c.layer,&layer_listener,&c);
339 zwlr_layer_surface_v1_set_anchor(c.layer,ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
340 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
341 zwlr_layer_surface_v1_set_size(c.layer,0,32);
342 zwlr_layer_surface_v1_set_margin(c.layer,3,4,5,6);
343 zwlr_layer_surface_v1_set_exclusive_zone(c.layer,32);
344 zwlr_layer_surface_v1_set_keyboard_interactivity(c.layer,
345 ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE);
346 } else {
347 c.xdg=xdg_wm_base_get_xdg_surface(c.shell,c.surface);
348 xdg_surface_add_listener(c.xdg,&surface_listener,&c);
349 c.top=xdg_surface_get_toplevel(c.xdg);
350 xdg_toplevel_add_listener(c.top,&top_listener,&c);
351 xdg_toplevel_set_title(c.top,argv[2]);
352 xdg_toplevel_set_app_id(c.top,"sigil-wm-pixel-client");
353 }
354 wl_surface_commit(c.surface);
355 while(c.running && wl_display_dispatch(c.display)>=0) {}
356 wl_display_disconnect(c.display);
357 return 0;