AtlatestRepositorysigil-wm

sigil-wm / tree / testforeign-toplevel-client.c

1/* SPDX-License-Identifier: BSD-3-Clause
2 * A real taskbar consumer. Commands on stdin target advertised handle numbers. */
3#define _POSIX_C_SOURCE 200809L
4#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 64
17struct client;
18struct 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};
27struct output { uint32_t name; struct wl_output *object; unsigned index; };
28struct 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};
38static void require(bool condition,const char *message) {
39 if(!condition) { fprintf(stderr,"foreign-toplevel fixture: %s\n",message); exit(1); }
41static 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);
45static 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);
50static 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);
53static 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);
56static 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);
59static void list_done(void *data,struct ext_foreign_toplevel_handle_v1 *handle) {
60 (void)handle; print_item(data);
62static 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;
67static 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};
69static 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);
76static void list_finished(void *data,struct ext_foreign_toplevel_list_v1 *list) {
77 (void)data; (void)list; puts("LIST_FINISHED");
79static const struct ext_foreign_toplevel_list_v1_listener listing_listener={.toplevel=listed,.finished=list_finished};
80static 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);
83static 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);
86static 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);
92static void output_enter(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,struct wl_output *output) {
93 (void)handle; output_event(data,output,true);
95static void output_leave(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle,struct wl_output *output) {
96 (void)handle; output_event(data,output,false);
98static 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; }
103static void management_done(void *data,struct zwlr_foreign_toplevel_handle_v1 *handle) {
104 (void)handle; print_item(data);
106static 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;
111static 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;
114static 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};
117static 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);
124static void manager_finished(void *data,struct zwlr_foreign_toplevel_manager_v1 *manager) {
125 (void)data; (void)manager; puts("MANAGEMENT_FINISHED");
127static const struct zwlr_foreign_toplevel_manager_v1_listener manager_listener={.toplevel=managed,.finished=manager_finished};
128static 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;
133static 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;
136static void output_done(void *data,struct wl_output *output) { (void)data; (void)output; }
137static void output_scale(void *data,struct wl_output *output,int32_t scale) {
138 (void)data; (void)output; (void)scale;
140static 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);
144static void output_description(void *data,struct wl_output *output,const char *description) {
145 (void)data; (void)output; (void)description;
147static 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};
149static void seat_capabilities(void *data,struct wl_seat *seat,uint32_t capabilities) {
150 (void)data; (void)seat; (void)capabilities;
152static const struct wl_seat_listener seat_listener={.capabilities=seat_capabilities};
153static 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 }
173static void global_remove(void *data,struct wl_registry *registry,uint32_t name) {
174 (void)data; (void)registry; (void)name;
176static const struct wl_registry_listener registry_listener={global,global_remove};
177static 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);
207int 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,&registry_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;