AtlatestRepositorysigil-wm
1
/* SPDX-License-Identifier: BSD-3-Clause2
* Real clipboard/primary clients with serials obtained from keyboard input. */3
#define _GNU_SOURCE4
#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"15
struct 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
};36
static const char *mime="text/plain;charset=utf-8";37
static 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);46
}47
static 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;60
}61
static void source_target(void *data,struct wl_data_source *source,const char *type) { (void)data;(void)source;(void)type; }62
static 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);66
}67
static void source_cancel(void *data,struct wl_data_source *source) { (void)source; ((struct client *)data)->running=false; }68
static const struct wl_data_source_listener source_listener={.target=source_target,.send=source_send,.cancelled=source_cancel};69
static 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);73
}74
static void primary_cancel(void *data,struct zwp_primary_selection_source_v1 *source) { (void)source; ((struct client *)data)->running=false; }75
static const struct zwp_primary_selection_source_v1_listener primary_source_listener={.send=primary_send,.cancelled=primary_cancel};76
static void offer_mime(void *data,struct wl_data_offer *offer,const char *type) { (void)data;(void)offer;(void)type; }77
static void offer_actions(void *data,struct wl_data_offer *offer,uint32_t actions) { (void)data;(void)offer;(void)actions; }78
static const struct wl_data_offer_listener offer_listener={.offer=offer_mime,.source_actions=offer_actions,.action=offer_actions};79
static 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); }80
static 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);87
}88
static const struct wl_data_device_listener device_listener={.data_offer=device_offer,.selection=device_selection};89
static void primary_mime(void *data,struct zwp_primary_selection_offer_v1 *offer,const char *type) { (void)data;(void)offer;(void)type; }90
static const struct zwp_primary_selection_offer_v1_listener primary_offer_listener={.offer=primary_mime};91
static 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);93
}94
static 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);101
}102
static const struct zwp_primary_selection_device_v1_listener primary_device_listener={.data_offer=primary_offer,.selection=primary_selection};103
static 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);120
}121
static 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); }122
static 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);124
}125
static void key_leave(void *data,struct wl_keyboard *keyboard,uint32_t serial,struct wl_surface *surface) { (void)data;(void)keyboard;(void)serial;(void)surface; }126
static 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); }127
static 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; }128
static void repeat(void *data,struct wl_keyboard *keyboard,int32_t rate,int32_t delay) { (void)data;(void)keyboard;(void)rate;(void)delay; }129
static const struct wl_keyboard_listener keyboard_listener={.keymap=keymap,.enter=key_enter,.leave=key_leave,.key=key,.modifiers=modifiers,.repeat_info=repeat};130
static 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); }133
}134
static void seat_name(void *data,struct wl_seat *seat,const char *name) { (void)data;(void)seat;(void)name; }135
static const struct wl_seat_listener seat_listener={.capabilities=capabilities,.name=seat_name};136
static void ping(void *data,struct xdg_wm_base *shell,uint32_t serial) { (void)data;xdg_wm_base_pong(shell,serial); }137
static const struct xdg_wm_base_listener shell_listener={.ping=ping};138
static 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;140
}141
static void close_top(void *data,struct xdg_toplevel *top) { (void)top;((struct client *)data)->running=false; }142
static void configure_bounds(void *data,struct xdg_toplevel *top,int32_t width,int32_t height) { (void)data;(void)top;(void)width;(void)height; }143
static void wm_capabilities(void *data,struct xdg_toplevel *top,struct wl_array *capabilities) { (void)data;(void)top;(void)capabilities; }144
static const struct xdg_toplevel_listener top_listener={.configure=configure_top,.close=close_top,.configure_bounds=configure_bounds,.wm_capabilities=wm_capabilities};145
static 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);159
}160
static const struct xdg_surface_listener surface_listener={.configure=configure};161
static 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);169
}170
static void removed(void *data,struct wl_registry *registry,uint32_t name) { (void)data;(void)registry;(void)name; }171
static const struct wl_registry_listener registry_listener={.global=global,.global_remove=removed};172
int 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,®istry_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;185
}