When the surface attaches to a buffer, all other buffers are available. Export the function used to release a buffer, track the buffer used by the surface, and release unused buffers when a new buffer is attached.
246 lines
5.2 KiB
C
246 lines
5.2 KiB
C
// 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;
|
|
}
|
|
|
|
// 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,
|
|
};
|