wayland.c - wmenu - [fork] efficient dynamic menu for wayland
(HTM) git clone https://git.drkhsh.at/wmenu.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
wayland.c (15754B)
---
1 #define _POSIX_C_SOURCE 200809L
2 #include <assert.h>
3 #include <errno.h>
4 #include <poll.h>
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <strings.h>
10 #include <time.h>
11 #include <unistd.h>
12 #include <sys/mman.h>
13 #include <sys/timerfd.h>
14 #include <wayland-client.h>
15 #include <wayland-client-protocol.h>
16 #include <xkbcommon/xkbcommon.h>
17
18 #include "menu.h"
19 #include "pool-buffer.h"
20 #include "render.h"
21 #include "wayland.h"
22 #include "xdg-activation-v1-client-protocol.h"
23 #include "wlr-layer-shell-unstable-v1-client-protocol.h"
24
25 // A Wayland output.
26 struct output {
27 struct wl_context *context;
28 struct wl_output *output;
29 const char *name; // output name
30 int32_t scale; // output scale
31 struct output *next; // next output
32 };
33
34 // Creates and returns a new output.
35 static struct output *output_create(struct wl_context *context, struct wl_output *wl_output) {
36 struct output *output = calloc(1, sizeof(struct output));
37 output->context = context;
38 output->output = wl_output;
39 output->scale = 1;
40 return output;
41 }
42
43 // Keyboard state.
44 struct keyboard {
45 struct menu *menu;
46 struct wl_keyboard *keyboard;
47 struct xkb_context *context;
48 struct xkb_keymap *keymap;
49 struct xkb_state *state;
50
51 int repeat_timer;
52 int repeat_delay;
53 int repeat_period;
54 enum wl_keyboard_key_state repeat_key_state;
55 xkb_keysym_t repeat_sym;
56 };
57
58 // Creates and returns a new keyboard.
59 static struct keyboard *keyboard_create(struct menu *menu, struct wl_keyboard *wl_keyboard) {
60 struct keyboard *keyboard = calloc(1, sizeof(struct keyboard));
61 keyboard->menu = menu;
62 keyboard->keyboard = wl_keyboard;
63 keyboard->context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
64 assert(keyboard->context != NULL);
65 keyboard->repeat_timer = timerfd_create(CLOCK_MONOTONIC, 0);
66 assert(keyboard->repeat_timer != -1);
67 return keyboard;
68 }
69
70 // Frees the keyboard.
71 static void free_keyboard(struct keyboard *keyboard) {
72 wl_keyboard_release(keyboard->keyboard);
73 xkb_state_unref(keyboard->state);
74 xkb_keymap_unref(keyboard->keymap);
75 xkb_context_unref(keyboard->context);
76 free(keyboard);
77 }
78
79 // Wayland context.
80 struct wl_context {
81 struct menu *menu;
82
83 struct wl_display *display;
84 struct wl_registry *registry;
85 struct wl_compositor *compositor;
86 struct wl_shm *shm;
87 struct wl_seat *seat;
88 struct wl_data_device_manager *data_device_manager;
89 struct zwlr_layer_shell_v1 *layer_shell;
90 struct output *output_list;
91 struct xdg_activation_v1 *activation;
92
93 struct keyboard *keyboard;
94 struct wl_data_device *data_device;
95 struct wl_surface *surface;
96 struct zwlr_layer_surface_v1 *layer_surface;
97 struct wl_data_offer *data_offer;
98 struct output *output;
99
100 struct pool_buffer buffers[2];
101 struct pool_buffer *current;
102 };
103
104 // Returns the current output_scale.
105 int context_get_scale(struct wl_context *context) {
106 return context->output ? context->output->scale : 1;
107 }
108
109 // Returns the current buffer from the pool.
110 struct pool_buffer *context_get_current_buffer(struct wl_context *context) {
111 return context->current;
112 }
113
114 // Returns the next buffer from the pool.
115 struct pool_buffer *context_get_next_buffer(struct wl_context *context, int scale) {
116 struct menu *menu = context->menu;
117 context->current = get_next_buffer(context->shm, context->buffers, menu->width, menu->height, scale);
118 return context->current;
119 }
120
121 // Returns the Wayland surface for the context.
122 struct wl_surface *context_get_surface(struct wl_context *context) {
123 return context->surface;
124 }
125
126 // Returns the XKB state for the context.
127 struct xkb_state *context_get_xkb_state(struct wl_context *context) {
128 return context->keyboard->state;
129 }
130
131 // Returns the XDG activation object for the context.
132 struct xdg_activation_v1 *context_get_xdg_activation(struct wl_context *context) {
133 return context->activation;
134 }
135
136 // Retrieves pasted text from a Wayland data offer.
137 bool context_paste(struct wl_context *context) {
138 if (!context->data_offer) {
139 return false;
140 }
141
142 int fds[2];
143 if (pipe(fds) == -1) {
144 // Pipe failed
145 return false;
146 }
147 wl_data_offer_receive(context->data_offer, "text/plain", fds[1]);
148 close(fds[1]);
149
150 wl_display_roundtrip(context->display);
151
152 while (true) {
153 char buf[1024];
154 ssize_t n = read(fds[0], buf, sizeof(buf));
155 if (n <= 0) {
156 break;
157 }
158 menu_paste(context->menu, buf, n);
159 }
160 close(fds[0]);
161
162 wl_data_offer_destroy(context->data_offer);
163 context->data_offer = NULL;
164 return true;
165 }
166
167 // Adds an output to the output list.
168 static void context_add_output(struct wl_context *context, struct output *output) {
169 output->next = context->output_list;
170 context->output_list = output;
171 }
172
173 // Frees the outputs.
174 static void free_outputs(struct wl_context *context) {
175 struct output *next = context->output_list;
176 while (next) {
177 struct output *output = next;
178 next = output->next;
179 wl_output_destroy(output->output);
180 free(output);
181 }
182 }
183
184 // Destroys the Wayland context, freeing memory associated with it.
185 static void context_destroy(struct wl_context *context) {
186 wl_registry_destroy(context->registry);
187 wl_compositor_destroy(context->compositor);
188 wl_shm_destroy(context->shm);
189 wl_seat_destroy(context->seat);
190 wl_data_device_manager_destroy(context->data_device_manager);
191 zwlr_layer_shell_v1_destroy(context->layer_shell);
192 free_outputs(context);
193
194 free_keyboard(context->keyboard);
195 wl_data_device_destroy(context->data_device);
196 wl_surface_destroy(context->surface);
197 zwlr_layer_surface_v1_destroy(context->layer_surface);
198 xdg_activation_v1_destroy(context->activation);
199
200 wl_display_disconnect(context->display);
201 free(context);
202 }
203
204 static void noop() {
205 // Do nothing
206 }
207
208 static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *wl_output) {
209 struct wl_context *context = data;
210 context->output = wl_output_get_user_data(wl_output);
211 menu_invalidate(context->menu);
212 }
213
214 static const struct wl_surface_listener surface_listener = {
215 .enter = surface_enter,
216 .leave = noop,
217 };
218
219 static void layer_surface_configure(void *data,
220 struct zwlr_layer_surface_v1 *surface,
221 uint32_t serial, uint32_t width, uint32_t height) {
222 struct wl_context *context = data;
223 if (context->menu->customwidth > 0) {
224 context->menu->width = context->menu->customwidth;
225 } else {
226 context->menu->width = width;
227 }
228 context->menu->height = height;
229 zwlr_layer_surface_v1_ack_configure(surface, serial);
230 }
231
232 static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) {
233 struct wl_context *context = data;
234 context->menu->exit = true;
235 }
236
237 static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
238 .configure = layer_surface_configure,
239 .closed = layer_surface_closed,
240 };
241
242 static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) {
243 struct output *output = data;
244 output->scale = factor;
245 }
246
247 static void output_name(void *data, struct wl_output *wl_output, const char *name) {
248 struct output *output = data;
249 output->name = name;
250
251 struct wl_context *context = output->context;
252 if (context->menu->output_name && strcmp(context->menu->output_name, name) == 0) {
253 context->output = output;
254 }
255 }
256
257 static const struct wl_output_listener output_listener = {
258 .geometry = noop,
259 .mode = noop,
260 .done = noop,
261 .scale = output_scale,
262 .name = output_name,
263 .description = noop,
264 };
265
266 static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
267 uint32_t format, int32_t fd, uint32_t size) {
268 struct keyboard *keyboard = data;
269 assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
270
271 char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
272 assert(map_shm != MAP_FAILED);
273
274 keyboard->keymap = xkb_keymap_new_from_string(keyboard->context,
275 map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
276 munmap(map_shm, size);
277 close(fd);
278
279 keyboard->state = xkb_state_new(keyboard->keymap);
280 }
281
282 static void keyboard_repeat(struct keyboard *keyboard) {
283 menu_keypress(keyboard->menu, keyboard->repeat_key_state, keyboard->repeat_sym);
284 struct itimerspec spec = { 0 };
285 spec.it_value.tv_sec = keyboard->repeat_period / 1000;
286 spec.it_value.tv_nsec = (keyboard->repeat_period % 1000) * 1000000l;
287 timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
288 }
289
290 static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
291 uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) {
292 struct keyboard *keyboard = data;
293
294 enum wl_keyboard_key_state key_state = _key_state;
295 xkb_keysym_t sym = xkb_state_key_get_one_sym(keyboard->state, key + 8);
296 menu_keypress(keyboard->menu, key_state, sym);
297
298 if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED && keyboard->repeat_period >= 0) {
299 keyboard->repeat_key_state = key_state;
300 keyboard->repeat_sym = sym;
301
302 struct itimerspec spec = { 0 };
303 spec.it_value.tv_sec = keyboard->repeat_delay / 1000;
304 spec.it_value.tv_nsec = (keyboard->repeat_delay % 1000) * 1000000l;
305 timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
306 } else if (key_state == WL_KEYBOARD_KEY_STATE_RELEASED) {
307 struct itimerspec spec = { 0 };
308 timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
309 }
310 }
311
312 static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
313 int32_t rate, int32_t delay) {
314 struct keyboard *keyboard = data;
315 keyboard->repeat_delay = delay;
316 if (rate > 0) {
317 keyboard->repeat_period = 1000 / rate;
318 } else {
319 keyboard->repeat_period = -1;
320 }
321 }
322
323 static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
324 uint32_t serial, uint32_t mods_depressed,
325 uint32_t mods_latched, uint32_t mods_locked,
326 uint32_t group) {
327 struct keyboard *keyboard = data;
328 xkb_state_update_mask(keyboard->state, mods_depressed, mods_latched,
329 mods_locked, 0, 0, group);
330 }
331
332 static const struct wl_keyboard_listener keyboard_listener = {
333 .keymap = keyboard_keymap,
334 .enter = noop,
335 .leave = noop,
336 .key = keyboard_key,
337 .modifiers = keyboard_modifiers,
338 .repeat_info = keyboard_repeat_info,
339 };
340
341 static void seat_capabilities(void *data, struct wl_seat *seat,
342 enum wl_seat_capability caps) {
343 struct wl_context *context = data;
344 if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
345 struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(seat);
346 struct keyboard *keyboard = keyboard_create(context->menu, wl_keyboard);
347 wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, keyboard);
348 context->keyboard = keyboard;
349 }
350 }
351
352 static const struct wl_seat_listener seat_listener = {
353 .capabilities = seat_capabilities,
354 .name = noop,
355 };
356
357 static void data_device_selection(void *data, struct wl_data_device *data_device,
358 struct wl_data_offer *data_offer) {
359 struct wl_context *context = data;
360 context->data_offer = data_offer;
361 }
362
363 static const struct wl_data_device_listener data_device_listener = {
364 .data_offer = noop,
365 .enter = noop,
366 .leave = noop,
367 .motion = noop,
368 .drop = noop,
369 .selection = data_device_selection,
370 };
371
372 static void handle_global(void *data, struct wl_registry *registry,
373 uint32_t name, const char *interface, uint32_t version) {
374 struct wl_context *context = data;
375 if (strcmp(interface, wl_compositor_interface.name) == 0) {
376 context->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
377 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
378 context->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
379 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
380 context->seat = wl_registry_bind(registry, name, &wl_seat_interface, 4);
381 wl_seat_add_listener(context->seat, &seat_listener, data);
382 } else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
383 context->data_device_manager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);
384 } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
385 context->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
386 } else if (strcmp(interface, wl_output_interface.name) == 0) {
387 struct wl_output *wl_output = wl_registry_bind(registry, name, &wl_output_interface, 4);
388 struct output *output = output_create(context, wl_output);
389 wl_output_set_user_data(wl_output, output);
390 wl_output_add_listener(wl_output, &output_listener, output);
391 context_add_output(context, output);
392 } else if (strcmp(interface, xdg_activation_v1_interface.name) == 0) {
393 context->activation = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1);
394 }
395 }
396
397 static const struct wl_registry_listener registry_listener = {
398 .global = handle_global,
399 .global_remove = noop,
400 };
401
402 // Connect to the Wayland display and run the menu.
403 int menu_run(struct menu *menu) {
404 struct wl_context *context = calloc(1, sizeof(struct wl_context));
405 context->menu = menu;
406 menu->context = context;
407
408 context->display = wl_display_connect(NULL);
409 if (!context->display) {
410 fprintf(stderr, "Failed to connect to display.\n");
411 exit(EXIT_FAILURE);
412 }
413
414 struct wl_registry *registry = wl_display_get_registry(context->display);
415 wl_registry_add_listener(registry, ®istry_listener, context);
416 wl_display_roundtrip(context->display);
417 assert(context->compositor != NULL);
418 assert(context->shm != NULL);
419 assert(context->seat != NULL);
420 assert(context->data_device_manager != NULL);
421 assert(context->layer_shell != NULL);
422 assert(context->activation != NULL);
423 context->registry = registry;
424
425 // Get data device for seat
426 struct wl_data_device *data_device = wl_data_device_manager_get_data_device(
427 context->data_device_manager, context->seat);
428 wl_data_device_add_listener(data_device, &data_device_listener, context);
429 context->data_device = data_device;
430
431 // Second roundtrip for seat and output listeners
432 wl_display_roundtrip(context->display);
433 assert(context->keyboard != NULL);
434
435 if (menu->output_name && !context->output) {
436 fprintf(stderr, "Output %s not found\n", menu->output_name);
437 exit(EXIT_FAILURE);
438 }
439
440 context->surface = wl_compositor_create_surface(context->compositor);
441 wl_surface_add_listener(context->surface, &surface_listener, context);
442
443 struct zwlr_layer_surface_v1 *layer_surface = zwlr_layer_shell_v1_get_layer_surface(
444 context->layer_shell,
445 context->surface,
446 context->output ? context->output->output : NULL,
447 ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
448 "menu"
449 );
450 assert(layer_surface != NULL);
451 context->layer_surface = layer_surface;
452
453 uint32_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
454 ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
455 if (menu->bottom) {
456 anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
457 } else {
458 anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
459 }
460
461 zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
462 zwlr_layer_surface_v1_set_size(layer_surface, 0, menu->height);
463 zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, -1);
464 zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, true);
465 zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, context);
466
467 wl_surface_commit(context->surface);
468 wl_display_roundtrip(context->display);
469 menu_render_items(menu);
470
471 struct pollfd fds[] = {
472 { wl_display_get_fd(context->display), POLLIN },
473 { context->keyboard->repeat_timer, POLLIN },
474 };
475 const size_t nfds = sizeof(fds) / sizeof(*fds);
476
477 while (!menu->exit) {
478 errno = 0;
479 do {
480 if (wl_display_flush(context->display) == -1 && errno != EAGAIN) {
481 fprintf(stderr, "wl_display_flush: %s\n", strerror(errno));
482 break;
483 }
484 } while (errno == EAGAIN);
485
486 if (poll(fds, nfds, -1) < 0) {
487 fprintf(stderr, "poll: %s\n", strerror(errno));
488 break;
489 }
490
491 if (fds[0].revents & POLLIN) {
492 if (wl_display_dispatch(context->display) < 0) {
493 menu->exit = true;
494 }
495 }
496
497 if (fds[1].revents & POLLIN) {
498 keyboard_repeat(context->keyboard);
499 }
500
501 // Render the menu if necessary
502 if (!menu->rendered) {
503 render_menu(menu);
504 }
505 }
506
507 context_destroy(context);
508 menu->context = NULL;
509
510 if (menu->failure) {
511 return EXIT_FAILURE;
512 }
513 return EXIT_SUCCESS;
514 }