// SPDX-License-Identifier: GPL-3.0-only /* * Wayland driver. * * Copyright (c) 2024, Richard Acayan. All rights reserved. */ #include #include #include #include #include #include #include #include "input.h" #include "ufkbd.h" #include "wayland.h" static void on_global_object(void *data, struct wl_registry *reg, uint32_t name, const char *iface, uint32_t version); static const struct wl_registry_listener reg_listener = { .global = on_global_object, .global_remove = NULL, }; static void on_global_object(void *data, struct wl_registry *reg, uint32_t name, const char *iface, uint32_t version) { struct ufkbd_input_wayland *ctx = data; struct ufkbd_wl_global_listener *listener; size_t i; // This is assumed to be a small list. for (i = 0; i < ctx->n_global_listeners; i++) { listener = ctx->global_listeners[i]; if (!strcmp(iface, listener->iface->name)) listener->cb(listener->data, reg, name, version); } } int ufkbd_wl_global_listener_add(struct ufkbd_input_wayland *ctx, struct ufkbd_wl_global_listener *listener) { size_t idx = ctx->n_global_listeners; if (idx >= MAX_GLOBAL_LISTENERS) { fprintf(stderr, "wl: no space for internal callback\n"); return -ENOSPC; } ctx->global_listeners[idx] = listener; ctx->n_global_listeners++; return 0; } static void ufkbd_wayland_listen_one(void *data, bool block) { struct ufkbd_input_wayland *ctx = data; struct pollfd pollfd; int timeout = 0; int ret; if (block) timeout = -1; // Some listen events may be queued, so send them. ret = wl_display_flush(ctx->display); if (ret < 0) fprintf(stderr, "warn: failed to flush outgoing events: %d\n", -errno); pollfd.fd = ctx->fd; pollfd.events = POLLIN; pollfd.revents = 0; ret = poll(&pollfd, 1, timeout); if (ret == -1) return; if (pollfd.revents) { ret = wl_display_dispatch(ctx->display); if (ret == -1) return; } } static void ufkbd_wayland_listen_step(void *data, int timeout) { struct ufkbd_input_wayland *ctx = data; struct pollfd pollfd; int ret; // Some listen events may be queued, so send them. ret = wl_display_flush(ctx->display); if (ret < 0) fprintf(stderr, "warn: failed to flush outgoing events: %d\n", -errno); pollfd.fd = ctx->fd; pollfd.events = POLLIN; pollfd.revents = 0; ret = poll(&pollfd, 1, timeout); if (ret == -1) return; if (pollfd.revents) { ret = wl_display_dispatch(ctx->display); if (ret == -1) return; } } static int ufkbd_wayland_send_key(void *data, int code, bool repeat) { struct ufkbd_input_wayland *ctx = data; ufkbd_wl_input_send_key(ctx->input, code - 8, repeat); return 0; } static int ufkbd_wayland_send_mod(void *data, enum ufkbd_modifier mod, bool pressed) { struct ufkbd_input_wayland *ctx = data; ufkbd_wl_input_send_mod(ctx->input, mod, pressed); return 0; } static int ufkbd_wayland_draw_begin(void *data, size_t *stride, void **ptr) { struct ufkbd_input_wayland *ctx = data; struct wl_buffer *buf; int ret; ret = ufkbd_wl_surface_consume_buffer(ctx->surface, &buf, ptr); if (ret) { fprintf(stderr, "wl: warn: could not consume buffer: %s\n", strerror(-ret)); return ret; } // NOPUSH *stride = (size_t) ctx->surface->bufs[0].width * 4; return 0; } static void ufkbd_wayland_draw_touch(void *data, int x, int y, int width, int height) { struct ufkbd_input_wayland *ctx = data; wl_surface_damage_buffer(ctx->surface->wl, x, y, width, height); } static void ufkbd_wayland_draw_end(void *data) { struct ufkbd_input_wayland *ctx = data; wl_surface_commit(ctx->surface->wl); ufkbd_wl_surface_release_unused(ctx->surface); } static void *ufkbd_wayland_init(struct ufkbd_ctx *ufkbd) { struct ufkbd_input_wayland *ctx; int ret; if (ufkbd == NULL) return NULL; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) return NULL; ctx->ufkbd = ufkbd; ctx->display = wl_display_connect(NULL); if (ctx->display == NULL) goto err_free_ctx; ctx->registry = wl_display_get_registry(ctx->display); if (ctx->registry == NULL) goto err_disconnect; ret = wl_registry_add_listener(ctx->registry, ®_listener, ctx); if (ret) goto err_destroy_reg; ret = ufkbd_wl_surface_init(ctx, &ctx->surface); if (ret) goto err_destroy_reg; ret = ufkbd_wl_input_init(ctx, &ctx->input); if (ret) goto err_uninit_surface; ctx->fd = wl_display_get_fd(ctx->display); return ctx; err_uninit_surface: ufkbd_wl_surface_uninit(ctx->surface); err_destroy_reg: wl_registry_destroy(ctx->registry); err_disconnect: wl_display_disconnect(ctx->display); err_free_ctx: free(ctx); return NULL; } static void ufkbd_wayland_uninit(void *data) { struct ufkbd_input_wayland *ctx = data; ufkbd_wl_input_uninit(ctx->input); ufkbd_wl_surface_uninit(ctx->surface); wl_registry_destroy(ctx->registry); wl_display_disconnect(ctx->display); free(ctx); } struct ufkbd_driver ufkbd_wayland = { .init = ufkbd_wayland_init, .uninit = ufkbd_wayland_uninit, .listen_one = ufkbd_wayland_listen_one, .listen_step = ufkbd_wayland_listen_step, .send_key = ufkbd_wayland_send_key, .send_mod = ufkbd_wayland_send_mod, .draw_begin = ufkbd_wayland_draw_begin, .draw_touch = ufkbd_wayland_draw_touch, .draw_end = ufkbd_wayland_draw_end, };