unfettered-keyboard/wayland/surface.c
Richard Acayan d43f61ec76 wayland: release unused buffers after buffer swap
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.
2024-04-22 19:29:37 -04:00

299 lines
6.8 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
/*
* Wayland buffer manager and surface.
*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <wayland-client-protocol.h>
#include "graphics.h"
#include "ufkbd.h"
#include "layout.h"
#include "wayland.h"
#include "wayland/viewporter.h"
#include "wayland/wlr-layer-shell-unstable-v1.h"
extern struct ufkbd_driver ufkbd_wayland;
static int ufkbd_wl_surface_resize_buffers(struct ufkbd_wl_surface *ctx,
uint32_t width, uint32_t height);
static void on_surface_configure(void *data,
struct zwlr_layer_surface_v1 *surface,
uint32_t serial,
uint32_t width,
uint32_t height)
{
struct ufkbd_wl_surface *ctx = data;
struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl;
struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd;
zwlr_layer_surface_v1_ack_configure(surface, serial);
wp_viewport_set_source(ctx->vp, 0, 0, width * 256 * ctx->scale,
height * 256 * ctx->scale);
wp_viewport_set_destination(ctx->vp, width, height);
ufkbd_wl_surface_resize_buffers(ctx, width, height);
ufkbd_layout_resize(ufkbd->layout, width, height);
ufkbd_graphics_draw_layout(ufkbd, ufkbd_wl->ufkbd->layout,
width, height);
}
static void on_surface_closed(void *data,
struct zwlr_layer_surface_v1 *toplev)
{
struct ufkbd_wl_surface *ctx = data;
struct ufkbd_input_wayland *ufkbd_wl = ctx->ufkbd_wl;
struct ufkbd_ctx *ufkbd = ufkbd_wl->ufkbd;
ufkbd_terminate(ufkbd);
}
static const struct zwlr_layer_surface_v1_listener surface_listener = {
.configure = on_surface_configure,
.closed = on_surface_closed,
};
static int try_create_layer_surface(struct ufkbd_wl_surface *ctx)
{
if (ctx->wl == NULL)
return -EINVAL;
if (ctx->shell == NULL)
return -EINVAL;
if (ctx->vper == NULL)
return -EINVAL;
ctx->vp = wp_viewporter_get_viewport(ctx->vper, ctx->wl);
if (ctx->vp == NULL)
return -ENOMEM;
ctx->wlr = zwlr_layer_shell_v1_get_layer_surface(ctx->shell, ctx->wl, NULL, ZWLR_LAYER_SHELL_V1_LAYER_TOP, "ufkbd-osk");
if (ctx->wlr == NULL)
return -ENOMEM;
zwlr_layer_surface_v1_set_anchor(ctx->wlr,
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
zwlr_layer_surface_v1_set_size(ctx->wlr, 0, 185);
zwlr_layer_surface_v1_set_exclusive_zone(ctx->wlr, 185);
zwlr_layer_surface_v1_add_listener(ctx->wlr, &surface_listener, ctx);
// Commit the role given to the surface
wl_surface_commit(ctx->wl);
return 0;
}
static void on_global_compositor(void *data,
struct wl_registry *reg,
uint32_t name,
uint32_t version)
{
struct ufkbd_wl_surface *ctx = data;
if (version < WL_COMPOSITOR_CREATE_SURFACE_SINCE_VERSION)
return;
ctx->compositor = wl_registry_bind(reg,
name,
&wl_compositor_interface,
version);
if (ctx->compositor == NULL) {
fprintf(stderr, "warn: could not bind to compositor\n");
return;
}
ctx->wl = wl_compositor_create_surface(ctx->compositor);
if (ctx->wl == NULL) {
fprintf(stderr, "warn: no valid surface\n");
return;
}
try_create_layer_surface(ctx);
}
static void on_global_shell(void *data,
struct wl_registry *reg,
uint32_t name,
uint32_t version)
{
struct ufkbd_wl_surface *ctx = data;
ctx->shell = wl_registry_bind(reg,
name,
&zwlr_layer_shell_v1_interface,
version);
if (ctx->shell == NULL) {
fprintf(stderr, "wl: warn: could not bind to window manager\n");
return;
}
try_create_layer_surface(ctx);
}
static void on_global_viewporter(void *data,
struct wl_registry *reg,
uint32_t name,
uint32_t version)
{
struct ufkbd_wl_surface *ctx = data;
ctx->vper = wl_registry_bind(reg,
name,
&wp_viewporter_interface,
version);
if (ctx->vper == NULL) {
fprintf(stderr, "wl: warn: could not bind to viewporter\n");
return;
}
try_create_layer_surface(ctx);
}
int ufkbd_wl_surface_consume_buffer(struct ufkbd_wl_surface *ctx,
struct wl_buffer **buf, void **ptr)
{
int ret, i;
if (buf == NULL || ptr == NULL)
return -EINVAL;
for (i = 0; i < 2; i++) {
ret = ufkbd_wl_buffer_consume(&ctx->bufs[i], buf, ptr);
if (!ret) {
wl_surface_attach(ctx->wl, *buf, 0, 0);
ctx->used_buf = i;
break;
}
if (ret != -EBUSY)
break;
}
return ret;
}
static int ufkbd_wl_surface_resize_buffers(struct ufkbd_wl_surface *ctx,
uint32_t width, uint32_t height)
{
int ret;
ctx->stride = (size_t) width * 4;
ret = ufkbd_wl_buffer_resize(&ctx->bufs[0],
width * ctx->scale,
height * ctx->scale);
if (ret)
return ret;
return ufkbd_wl_buffer_resize(&ctx->bufs[1],
width * ctx->scale,
height * ctx->scale);
}
void ufkbd_wl_surface_release_unused(struct ufkbd_wl_surface *ctx)
{
int i;
for (i = 0; i < 2; i++) {
if (i != ctx->used_buf)
ufkbd_wl_buffer_release(&ctx->bufs[i]);
}
}
int ufkbd_wl_surface_init(struct ufkbd_input_wayland *ufkbd_wl,
struct ufkbd_wl_surface **out)
{
struct ufkbd_wl_surface *ctx;
int ret;
ctx = calloc(1, sizeof(*ctx));
if (ctx == NULL)
return -errno;
ctx->ufkbd_wl = ufkbd_wl;
ctx->scale = 1;
ctx->used_buf = -1;
ret = ufkbd_wl_buffer_init(&ctx->bufs[0], ufkbd_wl, 0);
if (ret)
goto err_free_ctx;
ret = ufkbd_wl_buffer_init(&ctx->bufs[1], ufkbd_wl, 1);
if (ret)
goto err_uninit_buf0;
ctx->listener_comp.iface = &wl_compositor_interface;
ctx->listener_comp.cb = on_global_compositor;
ctx->listener_comp.data = ctx;
ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_comp);
if (ret)
goto err_uninit_buf1;
ctx->listener_shell.iface = &zwlr_layer_shell_v1_interface;
ctx->listener_shell.cb = on_global_shell;
ctx->listener_shell.data = ctx;
ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_shell);
if (ret)
goto err_uninit_buf1;
ctx->listener_viewporter.iface = &wp_viewporter_interface;
ctx->listener_viewporter.cb = on_global_viewporter;
ctx->listener_viewporter.data = ctx;
ret = ufkbd_wl_global_listener_add(ufkbd_wl, &ctx->listener_viewporter);
if (ret)
goto err_uninit_buf1;
ufkbd_graphics_set_scale(ctx->ufkbd_wl->ufkbd->graphics, ctx->scale);
*out = ctx;
return 0;
err_uninit_buf1:
ufkbd_wl_buffer_uninit(&ctx->bufs[1]);
err_uninit_buf0:
ufkbd_wl_buffer_uninit(&ctx->bufs[0]);
err_free_ctx:
free(ctx);
return ret;
}
void ufkbd_wl_surface_uninit(struct ufkbd_wl_surface *ctx)
{
if (ctx->vper != NULL)
wp_viewporter_destroy(ctx->vper);
if (ctx->vp != NULL)
wp_viewport_destroy(ctx->vp);
if (ctx->wlr != NULL)
zwlr_layer_surface_v1_destroy(ctx->wlr);
if (ctx->wl != NULL)
wl_surface_destroy(ctx->wl);
if (ctx->shell != NULL)
zwlr_layer_shell_v1_destroy(ctx->shell);
if (ctx->compositor != NULL)
wl_compositor_destroy(ctx->compositor);
ufkbd_wl_buffer_uninit(&ctx->bufs[1]);
ufkbd_wl_buffer_uninit(&ctx->bufs[0]);
free(ctx);
}