AtlatestRepositorysigil-wm
1
/* SPDX-License-Identifier: BSD-3-Clause2
* A real taskbar consumer. Commands on stdin target advertised handle numbers. */3
#define _POSIX_C_SOURCE 200809L4
#include <errno.h>5
#include <poll.h>6
#include <stdbool.h>7
#include <stdint.h>8
#include <stdio.h>9
#include <stdlib.h>10
#include <string.h>11
#include <unistd.h>12
#include <wayland-client.h>13
#include "ext-foreign-toplevel-list-v1-client-protocol.h"14
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"16
#define LIMIT 6417
struct client;18
struct item {19
struct client *client;20
unsigned id;21
struct ext_foreign_toplevel_handle_v1 *list;22
struct zwlr_foreign_toplevel_handle_v1 *management;23
char title[4097],app_id[4097],identifier[128];24
uint32_t states;25
bool closed;26
};27
struct output { uint32_t name; struct wl_output *object; unsigned index; };28
struct client {29
struct wl_display *display;30
struct ext_foreign_toplevel_list_v1 *list;31
struct zwlr_foreign_toplevel_manager_v1 *manager;32
struct wl_seat *seat;33
struct output outputs[LIMIT];34
struct item listings[LIMIT],managed[LIMIT];35
unsigned output_count,list_count,managed_count;36
bool sandbox,running;37
};38
static void require(bool condition,const char *message) {39
if(!condition) { fprintf(stderr,"foreign-toplevel fixture: %s\n",message); exit(1); }40
}41
static void copy(char *destination,size_t size,const char *text) {42
require(strlen(text)<size,"fixture metadata exceeds buffer");43
snprintf(destination,size,"%s",text);44
}45
static void print_item(struct item *item) {46
printf("%s_DONE id=%u title=%s app=%s identifier=%s states=%u closed=%d\n",47
item->list ? "LIST" : "MANAGEMENT",item->id,item->title,item->app_id,48
item->identifier,item->states,item->closed);49
}50
static void list_title(void *data,struct ext_foreign_toplevel_handle_v1 *handle,const char *title) {51
(void)handle; struct item *item=data; copy(item->title,sizeof(item->title),title);52
}53
static void list_app(void *data,struct ext_foreign_toplevel_handle_v1 *handle,const char *app) {54
(void)handle; struct item *item=data; copy(item->app_id,sizeof(item->app_id),app);55
}56
static void list_identifier(void *data,struct ext_foreign_toplevel_handle_v1 *handle,const char *identifier) {57
(void)handle; struct item *item=data; copy(item->identifier,sizeof(item->identifier),identifier);58
}59
static void list_done(void *data,struct ext_foreign_toplevel_handle_v1 *handle) {60
(void)handle; print_item(data);61
}62
static void list_closed(void *data,struct ext_foreign_toplevel_handle_v1 *handle) {63
struct item *item=data; item->closed=true;64
printf("LIST_CLOSED id=%u\n",item->id);65
ext_foreign_toplevel_handle_v1_destroy(handle); item->list=NULL;66
}67
static const struct ext_foreign_toplevel_handle_v1_listener list_listener={68
.title=list_title,.app_id=list_app,.identifier=list_identifier,.done=list_done,.closed=list_closed};69
static void listed(void *data,struct ext_foreign_toplevel_list_v1 *list,struct ext_foreign_toplevel_handle_v1 *handle) {70
(void)list; struct client *c=data;71
require(c->list_count<LIMIT,"too many listing handles");72
struct item *item=&c->listings[c->list_count++];73
*item=(struct item){.client=c,.id=c->list_count,.list=handle};74
ext_foreign_toplevel_handle_v1_add_listener(handle,&list_listener,item);75
}76
static void list_finished(void *data,struct ext_foreign_toplevel_list_v1 *list) {77
(void)data; (void)list; puts("LIST_FINISHED");78
}79
static const struct ext_foreign_toplevel_list_v1_listener listing_listener={.toplevel=listed,.finished=list_finished};80
static void management_title(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,const char *title) {81
(void)handle; struct item *item=data; copy(item->title,sizeof(item->title),title);82
}83
static void management_app(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,const char *app) {84
(void)handle; struct item *item=data; copy(item->app_id,sizeof(item->app_id),app);85
}86
static void output_event(struct item *item,struct wl_output *output,bool enter) {87
struct client *c=item->client;88
for(unsigned i=0;i<c->output_count;i++)89
if(c->outputs[i].object==output)90
printf("OUTPUT_%s id=%u output=%u\n",enter ? "ENTER" : "LEAVE",item->id,i+1);91
}92
static void output_enter(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,struct wl_output *output) {93
(void)handle; output_event(data,output,true);94
}95
static void output_leave(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,struct wl_output *output) {96
(void)handle; output_event(data,output,false);97
}98
static void management_state(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,struct wl_array *states) {99
(void)handle; struct item *item=data; item->states=0;100
uint32_t *state;101
wl_array_for_each(state,states) { require(*state<4,"unknown management state"); item->states|=1u<<*state; }102
}103
static void management_done(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle) {104
(void)handle; print_item(data);105
}106
static void management_closed(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle) {107
struct item *item=data; item->closed=true;108
printf("MANAGEMENT_CLOSED id=%u\n",item->id);109
zwlr_foreign_toplevel_handle_v1_destroy(handle); item->management=NULL;110
}111
static void management_parent(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,struct zwlr_foreign_toplevel_handle_v1 *parent) {112
(void)data; (void)handle; (void)parent;113
}114
static const struct zwlr_foreign_toplevel_handle_v1_listener management_listener={115
.title=management_title,.app_id=management_app,.output_enter=output_enter,.output_leave=output_leave,116
.state=management_state,.done=management_done,.closed=management_closed,.parent=management_parent};117
static void managed(void *data,struct zwlr_foreign_toplevel_manager_v1 *manager,struct zwlr_foreign_toplevel_handle_v1 *handle) {118
(void)manager; struct client *c=data;119
require(c->managed_count<LIMIT,"too many management handles");120
struct item *item=&c->managed[c->managed_count++];121
*item=(struct item){.client=c,.id=c->managed_count,.management=handle};122
zwlr_foreign_toplevel_handle_v1_add_listener(handle,&management_listener,item);123
}124
static void manager_finished(void *data,struct zwlr_foreign_toplevel_manager_v1 *manager) {125
(void)data; (void)manager; puts("MANAGEMENT_FINISHED");126
}127
static const struct zwlr_foreign_toplevel_manager_v1_listener manager_listener={.toplevel=managed,.finished=manager_finished};128
static void output_geometry(void *data,struct wl_output *output,int32_t x,int32_t y,129
int32_t width,int32_t height,int32_t subpixel,const char *make,const char *model,int32_t transform) {130
(void)data; (void)output; (void)x; (void)y; (void)width; (void)height;131
(void)subpixel; (void)make; (void)model; (void)transform;132
}133
static void output_mode(void *data,struct wl_output *output,uint32_t flags,int32_t width,int32_t height,int32_t refresh) {134
(void)data; (void)output; (void)flags; (void)width; (void)height; (void)refresh;135
}136
static void output_done(void *data,struct wl_output *output) { (void)data; (void)output; }137
static void output_scale(void *data,struct wl_output *output,int32_t scale) {138
(void)data; (void)output; (void)scale;139
}140
static void output_name(void *data,struct wl_output *output,const char *name) {141
(void)output; struct output *record=data;142
printf("OUTPUT_NAME index=%u name=%s\n",record->index,name);143
}144
static void output_description(void *data,struct wl_output *output,const char *description) {145
(void)data; (void)output; (void)description;146
}147
static const struct wl_output_listener output_listener={.geometry=output_geometry,.mode=output_mode,148
.done=output_done,.scale=output_scale,.name=output_name,.description=output_description};149
static void seat_capabilities(void *data,struct wl_seat *seat,uint32_t capabilities) {150
(void)data; (void)seat; (void)capabilities;151
}152
static const struct wl_seat_listener seat_listener={.capabilities=seat_capabilities};153
static void global(void *data,struct wl_registry *registry,uint32_t name,const char *interface,uint32_t version) {154
struct client *c=data;155
if(!strcmp(interface,ext_foreign_toplevel_list_v1_interface.name)) {156
require(!c->sandbox,"sandbox received foreign list global");157
c->list=wl_registry_bind(registry,name,&ext_foreign_toplevel_list_v1_interface,1);158
ext_foreign_toplevel_list_v1_add_listener(c->list,&listing_listener,c);159
} else if(!strcmp(interface,zwlr_foreign_toplevel_manager_v1_interface.name)) {160
require(!c->sandbox,"sandbox received management global");161
c->manager=wl_registry_bind(registry,name,&zwlr_foreign_toplevel_manager_v1_interface,version<3 ? version : 3);162
zwlr_foreign_toplevel_manager_v1_add_listener(c->manager,&manager_listener,c);163
} else if(!strcmp(interface,wl_seat_interface.name)) {164
c->seat=wl_registry_bind(registry,name,&wl_seat_interface,1);165
wl_seat_add_listener(c->seat,&seat_listener,c);166
} else if(!strcmp(interface,wl_output_interface.name)) {167
require(c->output_count<LIMIT,"too many outputs");168
struct output *record=&c->outputs[c->output_count++];169
*record=(struct output){name,wl_registry_bind(registry,name,&wl_output_interface,version<4 ? version : 4),c->output_count};170
wl_output_add_listener(record->object,&output_listener,record);171
}172
}173
static void global_remove(void *data,struct wl_registry *registry,uint32_t name) {174
(void)data; (void)registry; (void)name;175
}176
static const struct wl_registry_listener registry_listener={global,global_remove};177
static void command(struct client *c,const char *line) {178
char verb[32]; unsigned id=0,output=0;179
int count=sscanf(line,"%31s %u %u",verb,&id,&output);180
require(count>=1,"empty command");181
if(!strcmp(verb,"quit")) { c->running=false; return; }182
if(!strcmp(verb,"stop")) {183
if(c->list) ext_foreign_toplevel_list_v1_stop(c->list);184
if(c->manager) zwlr_foreign_toplevel_manager_v1_stop(c->manager);185
} else if(!strcmp(verb,"dump")) {186
for(unsigned i=0;i<c->list_count;i++) if(!c->listings[i].closed) print_item(&c->listings[i]);187
for(unsigned i=0;i<c->managed_count;i++) if(!c->managed[i].closed) print_item(&c->managed[i]);188
} else {189
require(id>0 && id<=c->managed_count,"invalid management handle");190
struct zwlr_foreign_toplevel_handle_v1 *handle=c->managed[id-1].management;191
require(handle!=NULL,"closed management handle");192
if(!strcmp(verb,"activate")) zwlr_foreign_toplevel_handle_v1_activate(handle,c->seat);193
else if(!strcmp(verb,"close")) zwlr_foreign_toplevel_handle_v1_close(handle);194
else if(!strcmp(verb,"fullscreen")) {195
require(output<=c->output_count,"invalid fullscreen output");196
zwlr_foreign_toplevel_handle_v1_set_fullscreen(handle,output ? c->outputs[output-1].object : NULL);197
} else if(!strcmp(verb,"unfullscreen")) zwlr_foreign_toplevel_handle_v1_unset_fullscreen(handle);198
else if(!strcmp(verb,"maximize")) zwlr_foreign_toplevel_handle_v1_set_maximized(handle);199
else if(!strcmp(verb,"unmaximize")) zwlr_foreign_toplevel_handle_v1_unset_maximized(handle);200
else if(!strcmp(verb,"minimize")) zwlr_foreign_toplevel_handle_v1_set_minimized(handle);201
else if(!strcmp(verb,"unminimize")) zwlr_foreign_toplevel_handle_v1_unset_minimized(handle);202
else require(false,"unknown command");203
}204
require(wl_display_roundtrip(c->display)>=0,"command roundtrip");205
printf("COMMAND=%s id=%u output=%u\n",verb,id,output);206
}207
int main(int argc,char **argv) {208
require(argc==2 && (!strcmp(argv[1],"native") || !strcmp(argv[1],"sandbox")),"usage: foreign-toplevel-client native|sandbox");209
setvbuf(stdout,NULL,_IOLBF,0);210
struct client c={.sandbox=!strcmp(argv[1],"sandbox"),.running=true};211
c.display=wl_display_connect(NULL); require(c.display!=NULL,"connect");212
struct wl_registry *registry=wl_display_get_registry(c.display);213
wl_registry_add_listener(registry,®istry_listener,&c);214
require(wl_display_roundtrip(c.display)>=0,"registry roundtrip");215
require(c.sandbox || c.list,"native read-only list missing");216
require(c.sandbox || (c.manager && c.seat),"native management missing");217
require(wl_display_roundtrip(c.display)>=0,"initial handles");218
printf("FOREIGN_READY mode=%s lists=%u management=%u\n",argv[1],c.list_count,c.managed_count);219
char line[256]; size_t length=0;220
while(c.running) {221
while(wl_display_prepare_read(c.display)!=0)222
require(wl_display_dispatch_pending(c.display)>=0,"dispatch pending");223
require(wl_display_flush(c.display)>=0 || errno==EAGAIN,"flush");224
struct pollfd fds[]={{wl_display_get_fd(c.display),POLLIN,0},{STDIN_FILENO,POLLIN,0}};225
int ready=poll(fds,2,-1);226
if(ready<0) { wl_display_cancel_read(c.display); if(errno==EINTR) continue; require(false,"poll"); }227
if(fds[0].revents&POLLIN) require(wl_display_read_events(c.display)>=0,"read events");228
else wl_display_cancel_read(c.display);229
require(!(fds[0].revents&(POLLHUP|POLLERR|POLLNVAL)),"display disconnected");230
require(wl_display_dispatch_pending(c.display)>=0,"dispatch");231
if(fds[1].revents&POLLIN) {232
char bytes[256]; ssize_t n=read(STDIN_FILENO,bytes,sizeof(bytes));233
require(n>0,"command input closed");234
for(ssize_t i=0;i<n;i++) {235
if(bytes[i]=='\n') { line[length]=0; command(&c,line); length=0; }236
else { require(length+1<sizeof(line),"command too long"); line[length++]=bytes[i]; }237
}238
}239
}240
wl_display_disconnect(c.display);241
puts("FOREIGN_CLIENT=PASS");242
return 0;243
}