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.
299 lines
6.8 KiB
C
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);
|
|
}
|