initial commit

This commit is contained in:
Richard Acayan 2024-04-15 22:11:00 -04:00
commit 1c606d0274
No known key found for this signature in database
GPG key ID: 0346F4894879DB73
36 changed files with 9908 additions and 0 deletions

247
wayland/driver.c Normal file
View file

@ -0,0 +1,247 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Wayland driver.
*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#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;
}
wl_surface_attach(ctx->surface->wl, buf, 0, 0);
// 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);
}
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, &reg_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,
};