AtlatestRepositorysigil-wm
1
/* SPDX-License-Identifier: BSD-3-Clause2
* Real xdg-shell client for configuration, input and composed-pixel tests. */3
#define _POSIX_C_SOURCE 200809L4
#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"18
struct 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
};43
static void open_popup(struct client *c,uint32_t serial);44
static void close_popup(struct client *c);45
static bool hold_configure(void) {46
const char *path=getenv("SIGIL_WM_CLIENT_HOLD");47
return path && access(path,F_OK)==0;48
}49
static void draw(struct client *c);50
static void request_frame(struct client *c);51
static 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);70
}71
static const struct wl_callback_listener frame_listener={.done=frame};72
static 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);75
}76
static 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);103
}104
static 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);115
}116
static const struct xdg_surface_listener surface_listener={.configure=configure};117
static 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);123
}124
static void popup_done(void *data,struct xdg_popup *popup) {125
(void)popup;126
close_popup(data);127
}128
static void popup_repositioned(void *data,struct xdg_popup *popup,uint32_t token) {129
(void)data; (void)popup; (void)token;130
}131
static const struct xdg_popup_listener popup_listener={132
.configure=popup_configure,.popup_done=popup_done,.repositioned=popup_repositioned,133
};134
static 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);142
}143
static 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);164
}165
static 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);174
}175
static void layer_closed(void *data, struct zwlr_layer_surface_v1 *surface) {176
(void)surface;177
((struct client *)data)->running = false;178
}179
static const struct zwlr_layer_surface_v1_listener layer_listener = {180
.configure = layer_configure, .closed = layer_closed,181
};182
static 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);199
}200
static void top_close(void *data,struct xdg_toplevel *top) { (void)top; ((struct client *)data)->running=false; }201
static void top_bounds(void *data,struct xdg_toplevel *top,int32_t width,int32_t height) {202
(void)data; (void)top; (void)width; (void)height;203
}204
static void top_capabilities(void *data,struct xdg_toplevel *top,struct wl_array *capabilities) {205
(void)data; (void)top; (void)capabilities;206
}207
static const struct xdg_toplevel_listener top_listener={.configure=top_configure,.close=top_close,208
.configure_bounds=top_bounds,.wm_capabilities=top_capabilities};209
static void ping(void *data,struct xdg_wm_base *shell,uint32_t serial) { (void)data; xdg_wm_base_pong(shell,serial); }210
static const struct xdg_wm_base_listener shell_listener={.ping=ping};211
static 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);213
}214
static 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);216
}217
static 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;219
}220
static 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);222
}223
static 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);225
}226
static void repeat(void *data,struct wl_keyboard *keyboard,int32_t rate,int32_t delay) {227
(void)data;(void)keyboard;(void)rate;(void)delay;228
}229
static const struct wl_keyboard_listener keyboard_listener={.keymap=keymap,.enter=key_enter,.leave=key_leave,.key=key,.modifiers=modifiers,.repeat_info=repeat};230
static 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);234
}235
static 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;237
}238
static 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);253
}254
static 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);269
}270
static 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);272
}273
static void pointer_frame(void *data,struct wl_pointer *p) {(void)data;(void)p;}274
static void pointer_source(void *data,struct wl_pointer *p,uint32_t source) {(void)data;(void)p;(void)source;}275
static void pointer_stop(void *data,struct wl_pointer *p,uint32_t time,uint32_t axis) {(void)data;(void)p;(void)time;(void)axis;}276
static void pointer_discrete(void *data,struct wl_pointer *p,uint32_t axis,int32_t discrete) {(void)data;(void)p;(void)axis;(void)discrete;}277
static 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};278
static 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
}286
}287
static void seat_name(void *data,struct wl_seat *seat,const char *name) {(void)data;(void)seat;(void)name;}288
static const struct wl_seat_listener seat_listener={.capabilities=capabilities,.name=seat_name};289
static 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;293
}294
static 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;297
}298
static const struct wl_output_listener output_listener={.geometry=output_geometry,.mode=output_mode};299
static 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
}319
}320
static void global_remove(void *data,struct wl_registry *registry,uint32_t name) {(void)data;(void)registry;(void)name;}321
static const struct wl_registry_listener registry_listener={.global=global,.global_remove=global_remove};322
int 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,®istry_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;358
}