#include "Wayland/xdg-shell-client-protocol.h" #include #include #include #include #include #include #include #include #include #include #include #include "dynarray.h" #include "hashmap.h" #include "log.h" #include "vulkan.h" #include 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; }