pawengine/wayland.c
2025-02-09 18:48:25 +01:00

361 lines
12 KiB
C

#include "Wayland/xdg-shell-client-protocol.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client-core.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon.h>
#include "dynarray.h"
#include "hashmap.h"
#include "log.h"
#include "vulkan.h"
#include <vulkan/vulkan_wayland.h>
struct cat_Wl {
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct xdg_wm_base *wm_base;
struct wl_seat *seat;
struct wl_surface *wl_surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct wl_keyboard *keyboard;
struct wl_pointer *pointer;
struct xkb_context *xkb_context;
struct xkb_keymap *keymap;
struct xkb_state *xkb_state;
int width, heigh;
int new_width, new_heigh;
bool resize_coming;
bool resize_ready;
bool should_close;
struct Vk *vk;
struct Hashmap_ui32 *keys;
struct da_uint32_t pressed;
struct da_uint32_t released;
};
static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) {
/* Sent by the compositor when it's no longer using this buffer */
wl_buffer_destroy(wl_buffer);
}
static const struct wl_buffer_listener wl_buffer_listener = {
.release = wl_buffer_release,
};
static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
uint32_t serial) {
struct cat_Wl *state = data;
xdg_surface_ack_configure(xdg_surface, serial);
if (state->resize_coming) {
state->resize_ready = true;
}
}
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure,
};
static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base,
uint32_t serial) {
xdg_wm_base_pong(xdg_wm_base, serial);
}
static const struct xdg_wm_base_listener wm_base_listener = {
.ping = xdg_wm_base_ping,
};
static void keyboard_event_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size) {
struct cat_Wl *state = data;
if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
meow("there is xkb");
void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
const char *keymap = data;
state->keymap = xkb_keymap_new_from_string(state->xkb_context, keymap,
XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(data, size);
close(fd);
state->xkb_state = xkb_state_new(state->keymap);
} else if (format == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
meow("keymap is none");
}
}
static void keyboard_event_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface,
struct wl_array *keys) {}
static void keyboard_event_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {}
static void keyboard_event_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key,
uint32_t key_state) {
key += 8;
struct cat_Wl *state = data;
if (state->xkb_state == NULL) {
meow("there is no xkb");
return;
}
xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb_state, key);
char buffer[128];
xkb_state_key_get_utf8(state->xkb_state, key, buffer, sizeof(buffer));
if (key_state == 1) {
insert_hashmap_ui32(state->keys, sym);
dyn_array_append(&state->pressed, sym);
} else if (key_state == 0) {
remove_hashmap_ui32(state->keys, sym);
dyn_array_append(&state->released, sym);
} else {
meow("bad wayland, a key can either go up or down, not %d", key_state);
}
}
static void keyboard_event_modifiers(void *data,
struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
struct cat_Wl *client_state = data;
xkb_state_update_mask(client_state->xkb_state, mods_depressed, mods_latched,
mods_locked, 0, 0, group);
}
static void keyboard_event_repeat_info(void *data,
struct wl_keyboard *wl_keyboard,
int32_t rate, int32_t delay) {}
static const struct wl_keyboard_listener keyboard_listener = {
.keymap = keyboard_event_keymap,
.enter = keyboard_event_enter,
.leave = keyboard_event_leave,
.key = keyboard_event_key,
.modifiers = keyboard_event_modifiers,
.repeat_info = keyboard_event_repeat_info,
};
static void seat_capabilities_listener(void *data, struct wl_seat *wl_seat,
uint32_t capabilities) {
struct cat_Wl *state = data;
if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
state->keyboard = wl_seat_get_keyboard(wl_seat);
wl_keyboard_add_listener(state->keyboard, &keyboard_listener, state);
} else {
meow("uuuh there is no keyboard");
if (state->keyboard != NULL) {
wl_keyboard_destroy(state->keyboard);
}
}
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
state->pointer = wl_seat_get_pointer(wl_seat);
} else {
meow("uuuh there is no mouse");
if (state->pointer != NULL) {
wl_pointer_destroy(state->pointer);
}
}
}
static void seat_name_listener(void *data, struct wl_seat *wl_seat,
const char *name) {
meow("the seat is named %s", name);
}
static const struct wl_seat_listener seat_listener = {
.capabilities = seat_capabilities_listener,
.name = seat_name_listener,
};
void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height,
struct wl_array *states) {
struct cat_Wl *state = data;
if (width != 0 && height != 0) {
state->resize_coming = true;
state->new_width = width;
state->new_heigh = height;
}
}
void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) {
struct cat_Wl *state = data;
state->should_close = true;
}
static const struct xdg_toplevel_listener toplevel_listener = {
.configure = xdg_toplevel_configure,
.close = xdg_toplevel_close,
};
static void registry_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface,
uint32_t version) {
struct cat_Wl *state = data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
state->compositor =
wl_registry_bind(registry, name, &wl_compositor_interface, 6);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
state->wm_base =
wl_registry_bind(registry, name, &xdg_wm_base_interface, 3);
xdg_wm_base_add_listener(state->wm_base, &wm_base_listener, state);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
state->seat = wl_registry_bind(registry, name, &wl_seat_interface, 8);
wl_seat_add_listener(state->seat, &seat_listener, state);
}
meow("interface %s: version %d", interface, version);
}
static void registry_global_remove(void *data, struct wl_registry *wl_registry,
uint32_t name) {
/* This space deliberately left blank */
}
static const struct wl_registry_listener registry_listener = {
.global = registry_global,
.global_remove = registry_global_remove,
};
VkSurfaceKHR create_wayland_surface(void *data, VkInstance instance) {
struct cat_Wl *state = data;
VkSurfaceKHR surface;
VkWaylandSurfaceCreateInfoKHR surface_info = {0};
surface_info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR;
surface_info.display = state->display;
surface_info.surface = state->wl_surface;
vkCreateWaylandSurfaceKHR(instance, &surface_info, NULL, &surface);
return surface;
}
struct cat_Wl *cat_init_wl(const char *name, int width, int heigh,
struct Vk **vk) {
struct cat_Wl *state = malloc(sizeof(struct cat_Wl));
memset(state, 0, sizeof(struct cat_Wl));
state->keys = create_hashmap_ui32();
dyn_array_create_inplace(&state->pressed);
dyn_array_create_inplace(&state->released);
state->should_close = state->resize_ready = state->resize_coming = false;
state->width = width;
state->heigh = heigh;
state->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
state->display = wl_display_connect(NULL);
state->registry = wl_display_get_registry(state->display);
wl_registry_add_listener(state->registry, &registry_listener, state);
wl_display_roundtrip(state->display);
state->wl_surface = wl_compositor_create_surface(state->compositor);
state->xdg_surface =
xdg_wm_base_get_xdg_surface(state->wm_base, state->wl_surface);
xdg_surface_add_listener(state->xdg_surface, &xdg_surface_listener, state);
state->xdg_toplevel = xdg_surface_get_toplevel(state->xdg_surface);
xdg_toplevel_add_listener(state->xdg_toplevel, &toplevel_listener, state);
xdg_toplevel_set_title(state->xdg_toplevel, name);
xdg_toplevel_set_app_id(state->xdg_toplevel, name);
wl_surface_commit(state->wl_surface);
wl_display_roundtrip(state->display);
wl_surface_commit(state->wl_surface);
state->vk = init_vk(state, width, heigh, create_wayland_surface);
*vk = state->vk;
return state;
}
void cat_wl_draw(struct cat_Wl *state) {
dyn_array_reset(&state->pressed);
dyn_array_reset(&state->released);
if (state->resize_ready && state->resize_coming) {
meow("resizing to %d x %d", state->new_width, state->new_heigh);
state->resize_coming = false;
state->resize_ready = false;
state->width = state->new_width;
state->heigh = state->new_heigh;
vk_resize(state->vk, state->width, state->heigh);
wl_surface_commit(state->wl_surface);
}
vk_draw(state->vk);
wl_surface_damage_buffer(state->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(state->wl_surface);
wl_display_roundtrip(state->display);
}
void cat_uninit_wl(struct cat_Wl *state) {
uninit_vk(state->vk);
destroy_hashmap_ui32(state->keys);
dyn_array_destroy(&state->pressed);
dyn_array_destroy(&state->released);
if (state->xkb_state != NULL) {
xkb_state_unref(state->xkb_state);
xkb_keymap_unref(state->keymap);
xkb_context_unref(state->xkb_context);
}
if (state->keyboard != NULL) {
wl_keyboard_release(state->keyboard);
}
if (state->pointer != NULL) {
wl_pointer_release(state->pointer);
}
if (state->seat != NULL) {
wl_seat_release(state->seat);
}
xdg_toplevel_destroy(state->xdg_toplevel);
xdg_surface_destroy(state->xdg_surface);
wl_surface_destroy(state->wl_surface);
xdg_wm_base_destroy(state->wm_base);
wl_compositor_destroy(state->compositor);
wl_registry_destroy(state->registry);
wl_display_disconnect(state->display);
memset(state, 0, sizeof(struct cat_Wl));
free(state);
meow("unmade a wl, nini :3");
}
bool cat_wl_should_close(struct cat_Wl *state) { return state->should_close; }
bool cat_wl_is_key_pressed(struct cat_Wl *state, uint32_t key) {
return get_hashmap_ui32(state->keys, key);
}
bool cat_wl_was_key_just_pressed(struct cat_Wl *state, uint32_t key) {
for (int i = 0; i < state->pressed.count; i++) {
if (state->pressed.items[i] == key) {
return true;
}
}
return false;
}
bool cat_wl_was_key_just_released(struct cat_Wl *state, uint32_t key) {
for (int i = 0; i < state->released.count; i++) {
if (state->released.items[i] == key) {
return true;
}
}
return false;
}