wmenu

wmenu fork with my settings
git clone https://git.awy.one/wmenu.git
Log | Files | Refs | README | LICENSE

wayland.c (15648B)


      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 	context->menu->width = width;
    224 	context->menu->height = height;
    225 	zwlr_layer_surface_v1_ack_configure(surface, serial);
    226 }
    227 
    228 static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) {
    229 	struct wl_context *context = data;
    230 	context->menu->exit = true;
    231 }
    232 
    233 static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
    234 	.configure = layer_surface_configure,
    235 	.closed = layer_surface_closed,
    236 };
    237 
    238 static void output_scale(void *data, struct wl_output *wl_output, int32_t factor) {
    239 	struct output *output = data;
    240 	output->scale = factor;
    241 }
    242 
    243 static void output_name(void *data, struct wl_output *wl_output, const char *name) {
    244 	struct output *output = data;
    245 	output->name = name;
    246 
    247 	struct wl_context *context = output->context;
    248 	if (context->menu->output_name && strcmp(context->menu->output_name, name) == 0) {
    249 		context->output = output;
    250 	}
    251 }
    252 
    253 static const struct wl_output_listener output_listener = {
    254 	.geometry = noop,
    255 	.mode = noop,
    256 	.done = noop,
    257 	.scale = output_scale,
    258 	.name = output_name,
    259 	.description = noop,
    260 };
    261 
    262 static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
    263 		uint32_t format, int32_t fd, uint32_t size) {
    264 	struct keyboard *keyboard = data;
    265 	assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
    266 
    267 	char *map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
    268 	assert(map_shm != MAP_FAILED);
    269 
    270 	keyboard->keymap = xkb_keymap_new_from_string(keyboard->context,
    271 		map_shm, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
    272 	munmap(map_shm, size);
    273 	close(fd);
    274 
    275 	keyboard->state = xkb_state_new(keyboard->keymap);
    276 }
    277 
    278 static void keyboard_repeat(struct keyboard *keyboard) {
    279 	menu_keypress(keyboard->menu, keyboard->repeat_key_state, keyboard->repeat_sym);
    280 	struct itimerspec spec = { 0 };
    281 	spec.it_value.tv_sec = keyboard->repeat_period / 1000;
    282 	spec.it_value.tv_nsec = (keyboard->repeat_period % 1000) * 1000000l;
    283 	timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
    284 }
    285 
    286 static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
    287 		uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) {
    288 	struct keyboard *keyboard = data;
    289 
    290 	enum wl_keyboard_key_state key_state = _key_state;
    291 	xkb_keysym_t sym = xkb_state_key_get_one_sym(keyboard->state, key + 8);
    292 	menu_keypress(keyboard->menu, key_state, sym);
    293 
    294 	if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED && keyboard->repeat_period >= 0) {
    295 		keyboard->repeat_key_state = key_state;
    296 		keyboard->repeat_sym = sym;
    297 
    298 		struct itimerspec spec = { 0 };
    299 		spec.it_value.tv_sec = keyboard->repeat_delay / 1000;
    300 		spec.it_value.tv_nsec = (keyboard->repeat_delay % 1000) * 1000000l;
    301 		timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
    302 	} else if (key_state == WL_KEYBOARD_KEY_STATE_RELEASED) {
    303 		struct itimerspec spec = { 0 };
    304 		timerfd_settime(keyboard->repeat_timer, 0, &spec, NULL);
    305 	}
    306 }
    307 
    308 static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
    309 		int32_t rate, int32_t delay) {
    310 	struct keyboard *keyboard = data;
    311 	keyboard->repeat_delay = delay;
    312 	if (rate > 0) {
    313 		keyboard->repeat_period = 1000 / rate;
    314 	} else {
    315 		keyboard->repeat_period = -1;
    316 	}
    317 }
    318 
    319 static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
    320 		uint32_t serial, uint32_t mods_depressed,
    321 		uint32_t mods_latched, uint32_t mods_locked,
    322 		uint32_t group) {
    323 	struct keyboard *keyboard = data;
    324 	xkb_state_update_mask(keyboard->state, mods_depressed, mods_latched,
    325 			mods_locked, 0, 0, group);
    326 }
    327 
    328 static const struct wl_keyboard_listener keyboard_listener = {
    329 	.keymap = keyboard_keymap,
    330 	.enter = noop,
    331 	.leave = noop,
    332 	.key = keyboard_key,
    333 	.modifiers = keyboard_modifiers,
    334 	.repeat_info = keyboard_repeat_info,
    335 };
    336 
    337 static void seat_capabilities(void *data, struct wl_seat *seat,
    338 		enum wl_seat_capability caps) {
    339 	struct wl_context *context = data;
    340 	if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
    341 		struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(seat);
    342 		struct keyboard *keyboard = keyboard_create(context->menu, wl_keyboard);
    343 		wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, keyboard);
    344 		context->keyboard = keyboard;
    345 	}
    346 }
    347 
    348 static const struct wl_seat_listener seat_listener = {
    349 	.capabilities = seat_capabilities,
    350 	.name = noop,
    351 };
    352 
    353 static void data_device_selection(void *data, struct wl_data_device *data_device,
    354 		struct wl_data_offer *data_offer) {
    355 	struct wl_context *context = data;
    356 	context->data_offer = data_offer;
    357 }
    358 
    359 static const struct wl_data_device_listener data_device_listener = {
    360 	.data_offer = noop,
    361 	.enter = noop,
    362 	.leave = noop,
    363 	.motion = noop,
    364 	.drop = noop,
    365 	.selection = data_device_selection,
    366 };
    367 
    368 static void handle_global(void *data, struct wl_registry *registry,
    369 		uint32_t name, const char *interface, uint32_t version) {
    370 	struct wl_context *context = data;
    371 	if (strcmp(interface, wl_compositor_interface.name) == 0) {
    372 		context->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4);
    373 	} else if (strcmp(interface, wl_shm_interface.name) == 0) {
    374 		context->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
    375 	} else if (strcmp(interface, wl_seat_interface.name) == 0) {
    376 		context->seat = wl_registry_bind(registry, name, &wl_seat_interface, 4);
    377 		wl_seat_add_listener(context->seat, &seat_listener, data);
    378 	} else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
    379 		context->data_device_manager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);
    380 	} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
    381 		context->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
    382 	} else if (strcmp(interface, wl_output_interface.name) == 0) {
    383 		struct wl_output *wl_output = wl_registry_bind(registry, name, &wl_output_interface, 4);
    384 		struct output *output = output_create(context, wl_output);
    385 		wl_output_set_user_data(wl_output, output);
    386 		wl_output_add_listener(wl_output, &output_listener, output);
    387 		context_add_output(context, output);
    388 	} else if (strcmp(interface, xdg_activation_v1_interface.name) == 0) {
    389 		context->activation = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1);
    390 	}
    391 }
    392 
    393 static const struct wl_registry_listener registry_listener = {
    394 	.global = handle_global,
    395 	.global_remove = noop,
    396 };
    397 
    398 // Connect to the Wayland display and run the menu.
    399 int menu_run(struct menu *menu) {
    400 	struct wl_context *context = calloc(1, sizeof(struct wl_context));
    401 	context->menu = menu;
    402 	menu->context = context;
    403 
    404 	context->display = wl_display_connect(NULL);
    405 	if (!context->display) {
    406 		fprintf(stderr, "Failed to connect to display.\n");
    407 		exit(EXIT_FAILURE);
    408 	}
    409 
    410 	struct wl_registry *registry = wl_display_get_registry(context->display);
    411 	wl_registry_add_listener(registry, &registry_listener, context);
    412 	wl_display_roundtrip(context->display);
    413 	assert(context->compositor != NULL);
    414 	assert(context->shm != NULL);
    415 	assert(context->seat != NULL);
    416 	assert(context->data_device_manager != NULL);
    417 	assert(context->layer_shell != NULL);
    418 	assert(context->activation != NULL);
    419 	context->registry = registry;
    420 
    421 	// Get data device for seat
    422 	struct wl_data_device *data_device = wl_data_device_manager_get_data_device(
    423 			context->data_device_manager, context->seat);
    424 	wl_data_device_add_listener(data_device, &data_device_listener, context);
    425 	context->data_device = data_device;
    426 
    427 	// Second roundtrip for seat and output listeners
    428 	wl_display_roundtrip(context->display);
    429 	assert(context->keyboard != NULL);
    430 
    431 	if (menu->output_name && !context->output) {
    432 		fprintf(stderr, "Output %s not found\n", menu->output_name);
    433 		exit(EXIT_FAILURE);
    434 	}
    435 
    436 	context->surface = wl_compositor_create_surface(context->compositor);
    437 	wl_surface_add_listener(context->surface, &surface_listener, context);
    438 
    439 	struct zwlr_layer_surface_v1 *layer_surface = zwlr_layer_shell_v1_get_layer_surface(
    440 		context->layer_shell,
    441 		context->surface,
    442 		context->output ? context->output->output : NULL,
    443 		ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
    444 		"menu"
    445 	);
    446 	assert(layer_surface != NULL);
    447 	context->layer_surface = layer_surface;
    448 
    449 	uint32_t anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
    450 		ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
    451 	if (menu->bottom) {
    452 		anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
    453 	} else {
    454 		anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
    455 	}
    456 
    457 	zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
    458 	zwlr_layer_surface_v1_set_size(layer_surface, 0, menu->height);
    459 	zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, -1);
    460 	zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, true);
    461 	zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, context);
    462 
    463 	wl_surface_commit(context->surface);
    464 	wl_display_roundtrip(context->display);
    465 	menu_render_items(menu);
    466 
    467 	struct pollfd fds[] = {
    468 		{ wl_display_get_fd(context->display), POLLIN },
    469 		{ context->keyboard->repeat_timer, POLLIN },
    470 	};
    471 	const size_t nfds = sizeof(fds) / sizeof(*fds);
    472 
    473 	while (!menu->exit) {
    474 		errno = 0;
    475 		do {
    476 			if (wl_display_flush(context->display) == -1 && errno != EAGAIN) {
    477 				fprintf(stderr, "wl_display_flush: %s\n", strerror(errno));
    478 				break;
    479 			}
    480 		} while (errno == EAGAIN);
    481 
    482 		if (poll(fds, nfds, -1) < 0) {
    483 			fprintf(stderr, "poll: %s\n", strerror(errno));
    484 			break;
    485 		}
    486 
    487 		if (fds[0].revents & POLLIN) {
    488 			if (wl_display_dispatch(context->display) < 0) {
    489 				menu->exit = true;
    490 			}
    491 		}
    492 
    493 		if (fds[1].revents & POLLIN) {
    494 			keyboard_repeat(context->keyboard);
    495 		}
    496 
    497 		// Render the menu if necessary
    498 		if (!menu->rendered) {
    499 			render_menu(menu);
    500 		}
    501 	}
    502 
    503 	context_destroy(context);
    504 	menu->context = NULL;
    505 
    506 	if (menu->failure) {
    507 		return EXIT_FAILURE;
    508 	}
    509 	return EXIT_SUCCESS;
    510 }