AtlatestRepositorysigil-wm

sigil-wm / tree / testdata-client.c

1/* SPDX-License-Identifier: BSD-3-Clause
2 * Real clipboard/primary clients with serials obtained from keyboard input. */
3#define _GNU_SOURCE
4#include <errno.h>
5#include <stdbool.h>
6#include <stdint.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/mman.h>
11#include <unistd.h>
12#include <wayland-client.h>
13#include "xdg-shell-client-protocol.h"
14#include "primary-selection-unstable-v1-client-protocol.h"
15struct client {
16 struct wl_display *display;
17 struct wl_compositor *compositor;
18 struct wl_shm *shm;
19 struct xdg_wm_base *shell;
20 struct wl_seat *seat;
21 struct wl_keyboard *keyboard;
22 struct wl_surface *surface;
23 struct xdg_surface *xdg;
24 struct xdg_toplevel *top;
25 struct wl_buffer *buffer;
26 struct wl_data_device_manager *manager;
27 struct wl_data_device *device;
28 struct wl_data_source *source;
29 struct zwp_primary_selection_device_manager_v1 *primary_manager;
30 struct zwp_primary_selection_device_v1 *primary_device;
31 struct zwp_primary_selection_source_v1 *primary_source;
32 bool primary,writer,published,running;
33 const char *text;
34 int width,height;
35};
36static const char *mime="text/plain;charset=utf-8";
37static void send_text(struct client *c,int fd) {
38 size_t offset=0,length=strlen(c->text);
39 while(offset<length) {
40 ssize_t n=write(fd,c->text+offset,length-offset);
41 if(n<0 && errno==EINTR) continue;
42 if(n<=0) exit(10);
43 offset+=(size_t)n;
44 }
45 close(fd);
47static void receive_text(struct client *c,int fd) {
48 char buffer[256]={0};
49 size_t offset=0;
50 for(;;) {
51 ssize_t n=read(fd,buffer+offset,sizeof(buffer)-1-offset);
52 if(n<0 && errno==EINTR) continue;
53 if(n<0 || offset+(size_t)n>=sizeof(buffer)-1) exit(11);
54 if(!n) break;
55 offset+=(size_t)n;
56 }
57 close(fd);
58 if(strcmp(buffer,c->text)) exit(12);
59 puts("DATA_RECEIVED=PASS"); fflush(stdout); c->running=false;
61static void source_target(void *data,struct wl_data_source *source,const char *type) { (void)data;(void)source;(void)type; }
62static void source_send(void *data,struct wl_data_source *source,const char *type,int fd) {
63 (void)source;
64 if(strcmp(type,mime)) exit(13);
65 send_text(data,fd);
67static void source_cancel(void *data,struct wl_data_source *source) { (void)source; ((struct client *)data)->running=false; }
68static const struct wl_data_source_listener source_listener={.target=source_target,.send=source_send,.cancelled=source_cancel};
69static void primary_send(void *data,struct zwp_primary_selection_source_v1 *source,const char *type,int fd) {
70 (void)source;
71 if(strcmp(type,mime)) exit(13);
72 send_text(data,fd);
74static void primary_cancel(void *data,struct zwp_primary_selection_source_v1 *source) { (void)source; ((struct client *)data)->running=false; }
75static const struct zwp_primary_selection_source_v1_listener primary_source_listener={.send=primary_send,.cancelled=primary_cancel};
76static void offer_mime(void *data,struct wl_data_offer *offer,const char *type) { (void)data;(void)offer;(void)type; }
77static void offer_actions(void *data,struct wl_data_offer *offer,uint32_t actions) { (void)data;(void)offer;(void)actions; }
78static const struct wl_data_offer_listener offer_listener={.offer=offer_mime,.source_actions=offer_actions,.action=offer_actions};
79static void device_offer(void *data,struct wl_data_device *device,struct wl_data_offer *offer) { (void)device;wl_data_offer_add_listener(offer,&offer_listener,data); }
80static void device_selection(void *data,struct wl_data_device *device,struct wl_data_offer *offer) {
81 (void)device;
82 struct client *c=data;
83 if(c->writer || !offer) return;
84 int fds[2]; if(pipe(fds)) exit(14);
85 wl_data_offer_receive(offer,mime,fds[1]); close(fds[1]);
86 wl_display_flush(c->display); receive_text(c,fds[0]); wl_data_offer_destroy(offer);
88static const struct wl_data_device_listener device_listener={.data_offer=device_offer,.selection=device_selection};
89static void primary_mime(void *data,struct zwp_primary_selection_offer_v1 *offer,const char *type) { (void)data;(void)offer;(void)type; }
90static const struct zwp_primary_selection_offer_v1_listener primary_offer_listener={.offer=primary_mime};
91static void primary_offer(void *data,struct zwp_primary_selection_device_v1 *device,struct zwp_primary_selection_offer_v1 *offer) {
92 (void)device; zwp_primary_selection_offer_v1_add_listener(offer,&primary_offer_listener,data);
94static void primary_selection(void *data,struct zwp_primary_selection_device_v1 *device,struct zwp_primary_selection_offer_v1 *offer) {
95 (void)device;
96 struct client *c=data;
97 if(c->writer || !offer) return;
98 int fds[2]; if(pipe(fds)) exit(14);
99 zwp_primary_selection_offer_v1_receive(offer,mime,fds[1]); close(fds[1]);
100 wl_display_flush(c->display); receive_text(c,fds[0]); zwp_primary_selection_offer_v1_destroy(offer);
102static const struct zwp_primary_selection_device_v1_listener primary_device_listener={.data_offer=primary_offer,.selection=primary_selection};
103static void publish(struct client *c,uint32_t serial) {
104 if(!c->writer || c->published) return;
105 c->published=true;
106 if(c->primary) {
107 c->primary_source=zwp_primary_selection_device_manager_v1_create_source(c->primary_manager);
108 zwp_primary_selection_source_v1_add_listener(c->primary_source,&primary_source_listener,c);
109 zwp_primary_selection_source_v1_offer(c->primary_source,mime);
110 zwp_primary_selection_device_v1_set_selection(c->primary_device,c->primary_source,serial);
111 if(getenv("SIGIL_WM_DATA_RETIRE")) { zwp_primary_selection_source_v1_destroy(c->primary_source);c->primary_source=NULL; }
112 } else {
113 c->source=wl_data_device_manager_create_data_source(c->manager);
114 wl_data_source_add_listener(c->source,&source_listener,c);
115 wl_data_source_offer(c->source,mime);
116 wl_data_device_set_selection(c->device,c->source,serial);
117 if(getenv("SIGIL_WM_DATA_RETIRE")) { wl_data_source_destroy(c->source);c->source=NULL; }
118 }
119 puts("DATA_PUBLISHED=PASS"); fflush(stdout);
121static void keymap(void *data,struct wl_keyboard *keyboard,uint32_t format,int fd,uint32_t size) { (void)data;(void)keyboard;(void)format;(void)size;close(fd); }
122static void key_enter(void *data,struct wl_keyboard *keyboard,uint32_t serial,struct wl_surface *surface,struct wl_array *keys) {
123 (void)data;(void)keyboard;(void)serial;(void)surface;(void)keys;puts("DATA_FOCUS=PASS");fflush(stdout);
125static void key_leave(void *data,struct wl_keyboard *keyboard,uint32_t serial,struct wl_surface *surface) { (void)data;(void)keyboard;(void)serial;(void)surface; }
126static void key(void *data,struct wl_keyboard *keyboard,uint32_t serial,uint32_t time,uint32_t code,uint32_t state) { (void)keyboard;(void)time;(void)code;if(state)publish(data,serial); }
127static void modifiers(void *data,struct wl_keyboard *keyboard,uint32_t serial,uint32_t depressed,uint32_t latched,uint32_t locked,uint32_t group) { (void)data;(void)keyboard;(void)serial;(void)depressed;(void)latched;(void)locked;(void)group; }
128static void repeat(void *data,struct wl_keyboard *keyboard,int32_t rate,int32_t delay) { (void)data;(void)keyboard;(void)rate;(void)delay; }
129static const struct wl_keyboard_listener keyboard_listener={.keymap=keymap,.enter=key_enter,.leave=key_leave,.key=key,.modifiers=modifiers,.repeat_info=repeat};
130static void capabilities(void *data,struct wl_seat *seat,uint32_t caps) {
131 struct client *c=data;
132 if((caps&WL_SEAT_CAPABILITY_KEYBOARD) && !c->keyboard) { c->keyboard=wl_seat_get_keyboard(seat);wl_keyboard_add_listener(c->keyboard,&keyboard_listener,c); }
134static void seat_name(void *data,struct wl_seat *seat,const char *name) { (void)data;(void)seat;(void)name; }
135static const struct wl_seat_listener seat_listener={.capabilities=capabilities,.name=seat_name};
136static void ping(void *data,struct xdg_wm_base *shell,uint32_t serial) { (void)data;xdg_wm_base_pong(shell,serial); }
137static const struct xdg_wm_base_listener shell_listener={.ping=ping};
138static void configure_top(void *data,struct xdg_toplevel *top,int32_t width,int32_t height,struct wl_array *states) {
139 (void)top;(void)states;struct client *c=data;if(width>0)c->width=width;if(height>0)c->height=height;
141static void close_top(void *data,struct xdg_toplevel *top) { (void)top;((struct client *)data)->running=false; }
142static void configure_bounds(void *data,struct xdg_toplevel *top,int32_t width,int32_t height) { (void)data;(void)top;(void)width;(void)height; }
143static void wm_capabilities(void *data,struct xdg_toplevel *top,struct wl_array *capabilities) { (void)data;(void)top;(void)capabilities; }
144static const struct xdg_toplevel_listener top_listener={.configure=configure_top,.close=close_top,.configure_bounds=configure_bounds,.wm_capabilities=wm_capabilities};
145static void configure(void *data,struct xdg_surface *xdg,uint32_t serial) {
146 struct client *c=data;
147 xdg_surface_ack_configure(xdg,serial);
148 int length=c->width*c->height*4;
149 int fd=memfd_create("sigil-wm-data-client",MFD_CLOEXEC);
150 if(fd<0 || ftruncate(fd,length)) exit(15);
151 uint32_t *pixels=mmap(NULL,length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
152 if(pixels==MAP_FAILED) exit(16);
153 for(int i=0;i<c->width*c->height;i++) pixels[i]=0xff1155bb;
154 struct wl_shm_pool *pool=wl_shm_create_pool(c->shm,fd,length);
155 if(c->buffer) wl_buffer_destroy(c->buffer);
156 c->buffer=wl_shm_pool_create_buffer(pool,0,c->width,c->height,c->width*4,WL_SHM_FORMAT_XRGB8888);
157 wl_shm_pool_destroy(pool);munmap(pixels,length);close(fd);
158 wl_surface_attach(c->surface,c->buffer,0,0);wl_surface_damage_buffer(c->surface,0,0,c->width,c->height);wl_surface_commit(c->surface);
160static const struct xdg_surface_listener surface_listener={.configure=configure};
161static void global(void *data,struct wl_registry *registry,uint32_t name,const char *interface,uint32_t version) {
162 struct client *c=data;
163 if(!strcmp(interface,"wl_compositor")) c->compositor=wl_registry_bind(registry,name,&wl_compositor_interface,4);
164 else if(!strcmp(interface,"wl_shm")) c->shm=wl_registry_bind(registry,name,&wl_shm_interface,1);
165 else if(!strcmp(interface,"xdg_wm_base")) { c->shell=wl_registry_bind(registry,name,&xdg_wm_base_interface,version<6?version:6);xdg_wm_base_add_listener(c->shell,&shell_listener,c); }
166 else if(!strcmp(interface,"wl_seat")) { c->seat=wl_registry_bind(registry,name,&wl_seat_interface,5);wl_seat_add_listener(c->seat,&seat_listener,c); }
167 else if(!strcmp(interface,"wl_data_device_manager")) c->manager=wl_registry_bind(registry,name,&wl_data_device_manager_interface,version<3?version:3);
168 else if(!strcmp(interface,"zwp_primary_selection_device_manager_v1")) c->primary_manager=wl_registry_bind(registry,name,&zwp_primary_selection_device_manager_v1_interface,1);
170static void removed(void *data,struct wl_registry *registry,uint32_t name) { (void)data;(void)registry;(void)name; }
171static const struct wl_registry_listener registry_listener={.global=global,.global_remove=removed};
172int main(int argc,char **argv) {
173 if(argc!=4 || (strcmp(argv[1],"clipboard") && strcmp(argv[1],"primary")) || (strcmp(argv[2],"source") && strcmp(argv[2],"target"))) return 2;
174 struct client c={.primary=!strcmp(argv[1],"primary"),.writer=!strcmp(argv[2],"source"),.text=argv[3],.running=true,.width=300,.height=200};
175 c.display=wl_display_connect(NULL);if(!c.display)return 3;
176 struct wl_registry *registry=wl_display_get_registry(c.display);wl_registry_add_listener(registry,&registry_listener,&c);
177 if(wl_display_roundtrip(c.display)<0 || !c.compositor || !c.shm || !c.shell || !c.seat || !c.manager || (c.primary && !c.primary_manager)) return 4;
178 if(c.primary) { c.primary_device=zwp_primary_selection_device_manager_v1_get_device(c.primary_manager,c.seat);zwp_primary_selection_device_v1_add_listener(c.primary_device,&primary_device_listener,&c); }
179 else { c.device=wl_data_device_manager_get_data_device(c.manager,c.seat);wl_data_device_add_listener(c.device,&device_listener,&c); }
180 c.surface=wl_compositor_create_surface(c.compositor);c.xdg=xdg_wm_base_get_xdg_surface(c.shell,c.surface);xdg_surface_add_listener(c.xdg,&surface_listener,&c);
181 c.top=xdg_surface_get_toplevel(c.xdg);xdg_toplevel_add_listener(c.top,&top_listener,&c);xdg_toplevel_set_title(c.top,argv[2]);xdg_toplevel_set_app_id(c.top,"sigil-wm-data-client");wl_surface_commit(c.surface);
182 while(c.running && wl_display_dispatch(c.display)>=0) {}
183 wl_display_disconnect(c.display);
184 return c.running ? 5 : 0;