362 lines
12 KiB
C
362 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, ®istry_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;
|
||
|
}
|