diff --git a/src/platform/hid/keyboard.x11.go b/src/platform/hid/keyboard.linux.go similarity index 99% rename from src/platform/hid/keyboard.x11.go rename to src/platform/hid/keyboard.linux.go index 6c41ecda2..707e3d6a4 100644 --- a/src/platform/hid/keyboard.x11.go +++ b/src/platform/hid/keyboard.linux.go @@ -1,7 +1,7 @@ //go:build linux && !android /******************************************************************************/ -/* keyboard.x11.go */ +/* keyboard.linux.go */ /******************************************************************************/ /* This file is part of */ /* KAIJU ENGINE */ diff --git a/src/platform/windowing/linux.h b/src/platform/windowing/linux.h new file mode 100644 index 000000000..df9cb9c7b --- /dev/null +++ b/src/platform/windowing/linux.h @@ -0,0 +1,47 @@ +/******************************************************************************/ +/* linux.h */ +/******************************************************************************/ +/* This file is part of */ +/* KAIJU ENGINE */ +/* https://kaijuengine.com/ */ +/******************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023-present Kaiju Engine authors (AUTHORS.md). */ +/* Copyright (c) 2015-present Brent Farris. */ +/* */ +/* May all those that this source may reach be blessed by the LORD and find */ +/* peace and joy in life. */ +/* Everyone who drinks of this water will be thirsty again; but whoever */ +/* drinks of the water that I will give him shall never thirst; John 4:13-14 */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a */ +/* copy of this software and associated documentation files (the "Software"), */ +/* to deal in the Software without restriction, including without limitation */ +/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ +/* and/or sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright, blessing, biblical verse, notice and */ +/* this permission notice shall be included in all copies or */ +/* substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS */ +/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT */ +/* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE */ +/* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/******************************************************************************/ + +#ifndef WINDOWING_LINUX_H +#define WINDOWING_LINUX_H + +#ifdef USE_WAYLAND +#include "wayland.h" +#else +#include "x11.h" +#endif + +#endif diff --git a/src/platform/windowing/x11.vk.go b/src/platform/windowing/linux.vk.go similarity index 96% rename from src/platform/windowing/x11.vk.go rename to src/platform/windowing/linux.vk.go index a673fa9d9..ecbc52798 100644 --- a/src/platform/windowing/x11.vk.go +++ b/src/platform/windowing/linux.vk.go @@ -1,7 +1,7 @@ -//go:build (linux || darwin) && !android +//go:build (linux || darwin) && !android && !wayland /******************************************************************************/ -/* x11.vk.go */ +/* linux.vk.go */ /******************************************************************************/ /* This file is part of */ /* KAIJU ENGINE */ diff --git a/src/platform/windowing/wayland.c b/src/platform/windowing/wayland.c new file mode 100644 index 000000000..9641560f9 --- /dev/null +++ b/src/platform/windowing/wayland.c @@ -0,0 +1,869 @@ +//go:build linux && !android && wayland + +/******************************************************************************/ +/* wayland.c */ +/******************************************************************************/ +/* This file is part of */ +/* KAIJU ENGINE */ +/* https://kaijuengine.com/ */ +/******************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023-present Kaiju Engine authors (AUTHORS.md). */ +/* Copyright (c) 2015-present Brent Farris. */ +/* */ +/* May all those that this source may reach be blessed by the LORD and find */ +/* peace and joy in life. */ +/* Everyone who drinks of this water will be thirsty again; but whoever */ +/* drinks of the water that I will give him shall never thirst; John 4:13-14 */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a */ +/* copy of this software and associated documentation files (the "Software"), */ +/* to deal in the Software without restriction, including without limitation */ +/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ +/* and/or sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright, blessing, biblical verse, notice and */ +/* this permission notice shall be included in all copies or */ +/* substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS */ +/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT */ +/* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE */ +/* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/******************************************************************************/ + +#if defined(__linux__) && defined(USE_WAYLAND) && !defined(__ANDROID__) + +#include "wayland.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xdg-shell-client-protocol.h" +#include "xdg-decoration-client-protocol.h" + +// Wayland docs: +// https://wayland-book.com/ +// https://wayland.freedesktop.org/docs/html/ + +static WaylandState* global_state = NULL; + +// XDG WM Base listener +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 xdg_wm_base_listener = { + .ping = xdg_wm_base_ping, +}; + +// XDG Surface listener +static void xdg_surface_configure(void* data, struct xdg_surface* xdg_surface, + uint32_t serial) +{ + WaylandState* s = data; + xdg_surface_ack_configure(xdg_surface, serial); + s->configured = true; +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_configure, +}; + +// XDG Toplevel listener +static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel, + int32_t width, int32_t height, struct wl_array* states) +{ + WaylandState* s = data; + if (width > 0 && height > 0) { + if (s->sm.windowWidth != width || s->sm.windowHeight != height) { + s->sm.windowWidth = width; + s->sm.windowHeight = height; + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_RESIZE, + .windowResize = { + .width = width, + .height = height, + .left = s->sm.x, + .top = s->sm.y, + .right = s->sm.x + width, + .bottom = s->sm.y + height, + } + }); + } + } +} + +static void xdg_toplevel_close(void* data, struct xdg_toplevel* toplevel) { + WaylandState* s = data; + s->closed = true; + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_ACTIVITY, + .windowActivity = { + .activityType = WINDOW_EVENT_ACTIVITY_TYPE_CLOSE, + } + }); + shared_mem_flush_events(&s->sm); +} + +static void xdg_toplevel_configure_bounds(void* data, + struct xdg_toplevel* toplevel, int32_t width, int32_t height) +{ + // Optional: can be used to constrain window size +} + +static void xdg_toplevel_wm_capabilities(void* data, + struct xdg_toplevel* toplevel, struct wl_array* capabilities) +{ + // Optional: indicates which capabilities the compositor supports +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_configure, + .close = xdg_toplevel_close, + .configure_bounds = xdg_toplevel_configure_bounds, + .wm_capabilities = xdg_toplevel_wm_capabilities, +}; + +// Keyboard listener +static void keyboard_keymap(void* data, struct wl_keyboard* keyboard, + uint32_t format, int fd, uint32_t size) +{ + WaylandState* s = data; + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + return; + } + char* map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (map_str == MAP_FAILED) { + close(fd); + return; + } + if (s->xkb_keymap) { + xkb_keymap_unref(s->xkb_keymap); + } + if (s->xkb_state) { + xkb_state_unref(s->xkb_state); + } + s->xkb_keymap = xkb_keymap_new_from_string(s->xkb_context, map_str, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_str, size); + close(fd); + if (s->xkb_keymap) { + s->xkb_state = xkb_state_new(s->xkb_keymap); + } +} + +static void keyboard_enter(void* data, struct wl_keyboard* keyboard, + uint32_t serial, struct wl_surface* surface, struct wl_array* keys) +{ + WaylandState* s = data; + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_ACTIVITY, + .windowActivity = { + .activityType = WINDOW_EVENT_ACTIVITY_TYPE_FOCUS, + } + }); +} + +static void keyboard_leave(void* data, struct wl_keyboard* keyboard, + uint32_t serial, struct wl_surface* surface) +{ + WaylandState* s = data; + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_ACTIVITY, + .windowActivity = { + .activityType = WINDOW_EVENT_ACTIVITY_TYPE_BLUR, + } + }); +} + +static void keyboard_key(void* data, struct wl_keyboard* keyboard, + uint32_t serial, uint32_t time, uint32_t key, uint32_t state) +{ + WaylandState* s = data; + if (!s->xkb_state) return; + // Convert evdev keycode to XKB keycode (add 8) + xkb_keysym_t sym = xkb_state_key_get_one_sym(s->xkb_state, key + 8); + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_KEYBOARD_BUTTON, + .keyboardButton = { + .action = state == WL_KEYBOARD_KEY_STATE_PRESSED + ? WINDOW_EVENT_BUTTON_TYPE_DOWN : WINDOW_EVENT_BUTTON_TYPE_UP, + .buttonId = sym, + } + }); +} + +static void keyboard_modifiers(void* data, struct wl_keyboard* keyboard, + uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + WaylandState* s = data; + if (s->xkb_state) { + xkb_state_update_mask(s->xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, group); + } +} + +static void keyboard_repeat_info(void* data, struct wl_keyboard* keyboard, + int32_t rate, int32_t delay) +{ + // Optional: key repeat configuration +} + +static const struct wl_keyboard_listener keyboard_listener = { + .keymap = keyboard_keymap, + .enter = keyboard_enter, + .leave = keyboard_leave, + .key = keyboard_key, + .modifiers = keyboard_modifiers, + .repeat_info = keyboard_repeat_info, +}; + +// Pointer listener +static void pointer_enter(void* data, struct wl_pointer* pointer, + uint32_t serial, struct wl_surface* surface, + wl_fixed_t sx, wl_fixed_t sy) +{ + WaylandState* s = data; + // Set cursor when entering surface + if (s->current_cursor && s->cursor_surface) { + struct wl_cursor_image* image = s->current_cursor->images[0]; + wl_pointer_set_cursor(pointer, serial, s->cursor_surface, + image->hotspot_x, image->hotspot_y); + } +} + +static void pointer_leave(void* data, struct wl_pointer* pointer, + uint32_t serial, struct wl_surface* surface) +{ + // Pointer left the surface +} + +static void pointer_motion(void* data, struct wl_pointer* pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + WaylandState* s = data; + int x = wl_fixed_to_int(sx); + int y = wl_fixed_to_int(sy); + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_MOUSE_MOVE, + .mouseMove = { + .x = x, + .y = y, + } + }); + s->sm.mouseX = x; + s->sm.mouseY = y; +} + +static void pointer_button(void* data, struct wl_pointer* pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +{ + WaylandState* s = data; + WindowEvent evt = { + .type = WINDOW_EVENT_TYPE_MOUSE_BUTTON, + .mouseButton.action = state == WL_POINTER_BUTTON_STATE_PRESSED + ? WINDOW_EVENT_BUTTON_TYPE_DOWN : WINDOW_EVENT_BUTTON_TYPE_UP, + .mouseButton.x = s->sm.mouseX, + .mouseButton.y = s->sm.mouseY, + }; + // Linux input event codes: BTN_LEFT=272, BTN_RIGHT=273, BTN_MIDDLE=274 + switch (button) { + case 272: // BTN_LEFT + evt.mouseButton.buttonId = MOUSE_BUTTON_LEFT; + break; + case 273: // BTN_RIGHT + evt.mouseButton.buttonId = MOUSE_BUTTON_RIGHT; + break; + case 274: // BTN_MIDDLE + evt.mouseButton.buttonId = MOUSE_BUTTON_MIDDLE; + break; + case 275: // BTN_SIDE + evt.mouseButton.buttonId = MOUSE_BUTTON_X1; + break; + case 276: // BTN_EXTRA + evt.mouseButton.buttonId = MOUSE_BUTTON_X2; + break; + default: + return; // Unknown button, ignore + } + shared_mem_add_event(&s->sm, evt); +} + +static void pointer_axis(void* data, struct wl_pointer* pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + WaylandState* s = data; + int delta = wl_fixed_to_int(value); + int deltaX = 0; + int deltaY = 0; + // Axis 0 = vertical, 1 = horizontal + // Wayland uses opposite sign convention from X11 + if (axis == 0) { + deltaY = -delta; + } else { + deltaX = -delta; + } + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_MOUSE_SCROLL, + .mouseScroll = { + .deltaX = deltaX, + .deltaY = deltaY, + .x = s->sm.mouseX, + .y = s->sm.mouseY, + } + }); +} + +static void pointer_frame(void* data, struct wl_pointer* pointer) { + // Optional: indicates end of pointer event sequence +} + +static void pointer_axis_source(void* data, struct wl_pointer* pointer, + uint32_t axis_source) +{ + // Optional: indicates axis source (wheel, finger, etc.) +} + +static void pointer_axis_stop(void* data, struct wl_pointer* pointer, + uint32_t time, uint32_t axis) +{ + // Optional: indicates axis movement stopped +} + +static void pointer_axis_discrete(void* data, struct wl_pointer* pointer, + uint32_t axis, int32_t discrete) +{ + // Optional: discrete scroll events +} + +static void pointer_axis_value120(void* data, struct wl_pointer* pointer, + uint32_t axis, int32_t value120) +{ + // Optional: high-resolution scroll events +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = pointer_enter, + .leave = pointer_leave, + .motion = pointer_motion, + .button = pointer_button, + .axis = pointer_axis, + .frame = pointer_frame, + .axis_source = pointer_axis_source, + .axis_stop = pointer_axis_stop, + .axis_discrete = pointer_axis_discrete, + .axis_value120 = pointer_axis_value120, +}; + +// Seat listener +static void seat_capabilities(void* data, struct wl_seat* seat, + uint32_t capabilities) +{ + WaylandState* s = data; + bool have_keyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD; + bool have_pointer = capabilities & WL_SEAT_CAPABILITY_POINTER; + + if (have_keyboard && !s->keyboard) { + s->keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(s->keyboard, &keyboard_listener, s); + } else if (!have_keyboard && s->keyboard) { + wl_keyboard_release(s->keyboard); + s->keyboard = NULL; + } + + if (have_pointer && !s->pointer) { + s->pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(s->pointer, &pointer_listener, s); + } else if (!have_pointer && s->pointer) { + wl_pointer_release(s->pointer); + s->pointer = NULL; + } +} + +static void seat_name(void* data, struct wl_seat* seat, const char* name) { + // Optional: seat name +} + +static const struct wl_seat_listener seat_listener = { + .capabilities = seat_capabilities, + .name = seat_name, +}; + +// Output listener +static void output_geometry(void* data, struct wl_output* output, + int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, + int32_t subpixel, const char* make, const char* model, int32_t transform) +{ + WaylandState* s = data; + s->output_width_mm = physical_width; + s->output_height_mm = physical_height; +} + +static void output_mode(void* data, struct wl_output* output, + uint32_t flags, int32_t width, int32_t height, int32_t refresh) +{ + WaylandState* s = data; + // WL_OUTPUT_MODE_CURRENT means this is the current mode + if (flags & WL_OUTPUT_MODE_CURRENT) { + s->output_width = width; + s->output_height = height; + } +} + +static void output_done(void* data, struct wl_output* output) { + // Output configuration complete +} + +static void output_scale(void* data, struct wl_output* output, int32_t factor) { + WaylandState* s = data; + s->output_scale = factor; +} + +static void output_name(void* data, struct wl_output* output, + const char* name) +{ + // Output name +} + +static void output_description(void* data, struct wl_output* output, + const char* description) +{ + // Output description +} + +static const struct wl_output_listener output_listener = { + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, + .name = output_name, + .description = output_description, +}; + +// Decoration listener +static void toplevel_decoration_configure(void* data, + struct zxdg_toplevel_decoration_v1* decoration, uint32_t mode) +{ + // Mode received from compositor - we requested server-side +} + +static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = { + .configure = toplevel_decoration_configure, +}; + +// Registry listener +static void registry_global(void* data, struct wl_registry* registry, + uint32_t name, const char* interface, uint32_t version) +{ + WaylandState* s = data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + s->compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 4); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + s->seat = wl_registry_bind(registry, name, &wl_seat_interface, 5); + wl_seat_add_listener(s->seat, &seat_listener, s); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + s->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + s->output = wl_registry_bind(registry, name, &wl_output_interface, 4); + wl_output_add_listener(s->output, &output_listener, s); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + s->xdg_wm_base = wl_registry_bind(registry, name, + &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(s->xdg_wm_base, &xdg_wm_base_listener, s); + } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { + s->decoration_manager = wl_registry_bind(registry, name, + &zxdg_decoration_manager_v1_interface, 1); + } +} + +static void registry_global_remove(void* data, struct wl_registry* registry, + uint32_t name) +{ + // Object removed from registry +} + +static const struct wl_registry_listener registry_listener = { + .global = registry_global, + .global_remove = registry_global_remove, +}; + +static void set_cursor(WaylandState* s, const char* cursor_name) { + if (!s->cursor_theme) { + s->cursor_theme = wl_cursor_theme_load(NULL, 24, s->shm); + if (!s->cursor_theme) return; + } + if (!s->cursor_surface) { + s->cursor_surface = wl_compositor_create_surface(s->compositor); + if (!s->cursor_surface) return; + } + struct wl_cursor* cursor = wl_cursor_theme_get_cursor(s->cursor_theme, + cursor_name); + if (!cursor) { + cursor = wl_cursor_theme_get_cursor(s->cursor_theme, "left_ptr"); + } + if (cursor && cursor->image_count > 0) { + s->current_cursor = cursor; + struct wl_cursor_image* image = cursor->images[0]; + struct wl_buffer* buffer = wl_cursor_image_get_buffer(image); + wl_surface_attach(s->cursor_surface, buffer, 0, 0); + wl_surface_damage_buffer(s->cursor_surface, 0, 0, + image->width, image->height); + wl_surface_commit(s->cursor_surface); + } +} + +unsigned int get_toggle_key_state() { + // Wayland doesn't provide direct access to LED state like X11 + // Return 0 for now - toggle key state must be tracked via key events + if (global_state && global_state->xkb_state) { + unsigned int state = 0; + xkb_mod_index_t caps = xkb_keymap_mod_get_index(global_state->xkb_keymap, + XKB_MOD_NAME_CAPS); + xkb_mod_index_t num = xkb_keymap_mod_get_index(global_state->xkb_keymap, + XKB_MOD_NAME_NUM); + if (caps != XKB_MOD_INVALID && + xkb_state_mod_index_is_active(global_state->xkb_state, caps, + XKB_STATE_MODS_LOCKED)) { + state |= 1; + } + if (num != XKB_MOD_INVALID && + xkb_state_mod_index_is_active(global_state->xkb_state, num, + XKB_STATE_MODS_LOCKED)) { + state |= 2; + } + return state; + } + return 0; +} + +void window_main(const char* windowTitle, + int width, int height, int x, int y, uint64_t goWindow) +{ + WaylandState* s = calloc(1, sizeof(WaylandState)); + global_state = s; + s->sm.goWindow = (void*)goWindow; + s->sm.x = x; + s->sm.y = y; + s->sm.windowWidth = width; + s->sm.windowHeight = height; + s->output_scale = 1; + + s->display = wl_display_connect(NULL); + if (!s->display) { + printf("Failed to connect to Wayland display\n"); fflush(stdout); + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_FATAL, + }); + shared_mem_flush_events(&s->sm); + free(s); + return; + } + + s->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!s->xkb_context) { + printf("Failed to create XKB context\n"); fflush(stdout); + wl_display_disconnect(s->display); + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_FATAL, + }); + shared_mem_flush_events(&s->sm); + free(s); + return; + } + + s->registry = wl_display_get_registry(s->display); + wl_registry_add_listener(s->registry, ®istry_listener, s); + wl_display_roundtrip(s->display); + + if (!s->compositor || !s->xdg_wm_base) { + printf("Failed to get required Wayland interfaces\n"); fflush(stdout); + wl_display_disconnect(s->display); + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_FATAL, + }); + shared_mem_flush_events(&s->sm); + free(s); + return; + } + + s->surface = wl_compositor_create_surface(s->compositor); + s->xdg_surface = xdg_wm_base_get_xdg_surface(s->xdg_wm_base, s->surface); + xdg_surface_add_listener(s->xdg_surface, &xdg_surface_listener, s); + + s->xdg_toplevel = xdg_surface_get_toplevel(s->xdg_surface); + xdg_toplevel_add_listener(s->xdg_toplevel, &xdg_toplevel_listener, s); + xdg_toplevel_set_title(s->xdg_toplevel, windowTitle); + xdg_toplevel_set_app_id(s->xdg_toplevel, "kaiju"); + + // Request server-side decorations (title bar, buttons) if available + if (s->decoration_manager) { + s->toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( + s->decoration_manager, s->xdg_toplevel); + zxdg_toplevel_decoration_v1_add_listener(s->toplevel_decoration, + &decoration_listener, s); + zxdg_toplevel_decoration_v1_set_mode(s->toplevel_decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + + wl_surface_commit(s->surface); + wl_display_roundtrip(s->display); + + // Wait for initial configure + while (!s->configured && !s->closed) { + wl_display_dispatch(s->display); + } + + set_cursor(s, "left_ptr"); + + shared_mem_add_event(&s->sm, (WindowEvent) { + .type = WINDOW_EVENT_TYPE_SET_HANDLE, + .setHandle = { + .hwnd = s, + } + }); + shared_mem_flush_events(&s->sm); +} + +void window_show(void* waylandState) { + // Wayland surfaces are implicitly shown when committed +} + +void window_poll_controller(void* waylandState) { + // TODO: Implement controller support for Wayland +} + +void window_poll(void* waylandState) { + WaylandState* s = waylandState; + if (s->closed) return; + + // Non-blocking dispatch + wl_display_dispatch_pending(s->display); + wl_display_flush(s->display); + + // Handle cursor locking + if (s->sm.lockCursor.active) { + // Wayland doesn't support cursor warping in the same way as X11 + // Pointer constraints protocol would be needed for proper implementation + } + + shared_mem_flush_events(&s->sm); +} + +void window_destroy(void* waylandState) { + WaylandState* s = waylandState; + if (s->cursor_surface) { + wl_surface_destroy(s->cursor_surface); + } + if (s->cursor_theme) { + wl_cursor_theme_destroy(s->cursor_theme); + } + if (s->xkb_state) { + xkb_state_unref(s->xkb_state); + } + if (s->xkb_keymap) { + xkb_keymap_unref(s->xkb_keymap); + } + if (s->xkb_context) { + xkb_context_unref(s->xkb_context); + } + if (s->keyboard) { + wl_keyboard_release(s->keyboard); + } + if (s->pointer) { + wl_pointer_release(s->pointer); + } + if (s->toplevel_decoration) { + zxdg_toplevel_decoration_v1_destroy(s->toplevel_decoration); + } + if (s->xdg_toplevel) { + xdg_toplevel_destroy(s->xdg_toplevel); + } + if (s->xdg_surface) { + xdg_surface_destroy(s->xdg_surface); + } + if (s->surface) { + wl_surface_destroy(s->surface); + } + if (s->decoration_manager) { + zxdg_decoration_manager_v1_destroy(s->decoration_manager); + } + if (s->xdg_wm_base) { + xdg_wm_base_destroy(s->xdg_wm_base); + } + if (s->seat) { + wl_seat_release(s->seat); + } + if (s->output) { + wl_output_release(s->output); + } + if (s->shm) { + wl_shm_destroy(s->shm); + } + if (s->compositor) { + wl_compositor_destroy(s->compositor); + } + if (s->registry) { + wl_registry_destroy(s->registry); + } + if (s->display) { + wl_display_disconnect(s->display); + } + if (global_state == s) { + global_state = NULL; + } + free(s); +} + +void* display(void* waylandState) { + return ((WaylandState*)waylandState)->display; +} + +void* window(void* waylandState) { + return ((WaylandState*)waylandState)->surface; +} + +void window_focus(void* state) { + // Wayland doesn't allow clients to focus themselves + // This is a compositor-only operation +} + +void window_position(void* state, int* x, int* y) { + WaylandState* s = state; + // Wayland doesn't expose window position to clients + *x = s->sm.x; + *y = s->sm.y; +} + +void window_set_position(void* state, int x, int y) { + // Wayland doesn't allow clients to set their own position + // This is handled by the compositor +} + +void window_set_size(void* state, int width, int height) { + WaylandState* s = state; + // Request a specific size - compositor may or may not honor it + s->sm.windowWidth = width; + s->sm.windowHeight = height; + wl_surface_commit(s->surface); +} + +int window_width_mm(void* state) { + WaylandState* s = state; + // Return the physical width, or estimate based on ~96 DPI if unknown + if (s->output_width_mm > 0) { + return s->output_width_mm; + } + // Fallback: assume 96 DPI = ~3.78 pixels per mm + return s->output_width > 0 ? s->output_width * 254 / 960 : 508; +} + +int window_height_mm(void* state) { + WaylandState* s = state; + // Return the physical height, or estimate based on ~96 DPI if unknown + if (s->output_height_mm > 0) { + return s->output_height_mm; + } + // Fallback: assume 96 DPI = ~3.78 pixels per mm + return s->output_height > 0 ? s->output_height * 254 / 960 : 285; +} + +void window_cursor_standard(void* state) { + set_cursor(state, "left_ptr"); +} + +void window_cursor_ibeam(void* state) { + set_cursor(state, "xterm"); +} + +void window_cursor_size_all(void* state) { + set_cursor(state, "all-scroll"); +} + +void window_cursor_size_ns(void* state) { + set_cursor(state, "ns-resize"); +} + +void window_cursor_size_we(void* state) { + set_cursor(state, "ew-resize"); +} + +void window_show_cursor(void* state) { + WaylandState* s = state; + if (s->current_cursor) { + set_cursor(s, "left_ptr"); + } +} + +void window_hide_cursor(void* state) { + WaylandState* s = state; + if (s->pointer && s->cursor_surface) { + wl_surface_attach(s->cursor_surface, NULL, 0, 0); + wl_surface_commit(s->cursor_surface); + // Set cursor to empty surface + // Note: This requires the pointer to enter the surface again + // to properly hide. A better solution would use zwp_pointer_constraints + } +} + +float window_dpi(void* state) { + WaylandState* s = state; + // Wayland uses scale factor instead of DPI directly + // A scale of 2 typically means ~192 DPI (2x 96) + return (float)s->output_scale * 96.0f / 25.4f; +} + +void window_set_title(void* state, const char* windowTitle) { + WaylandState* s = state; + xdg_toplevel_set_title(s->xdg_toplevel, windowTitle); +} + +void window_set_full_screen(void* state) { + WaylandState* s = state; + // Save current state for restoration + s->sm.savedState.rect.left = s->sm.x; + s->sm.savedState.rect.top = s->sm.y; + s->sm.savedState.rect.right = s->sm.x + s->sm.windowWidth; + s->sm.savedState.rect.bottom = s->sm.y + s->sm.windowHeight; + xdg_toplevel_set_fullscreen(s->xdg_toplevel, NULL); +} + +void window_set_windowed(void* state, int width, int height) { + WaylandState* s = state; + xdg_toplevel_unset_fullscreen(s->xdg_toplevel); + // Request preferred size + s->sm.windowWidth = width; + s->sm.windowHeight = height; + wl_surface_commit(s->surface); +} + +void window_lock_cursor(void* state, int x, int y) { + WaylandState* s = state; + s->sm.lockCursor.x = x; + s->sm.lockCursor.y = y; + s->sm.lockCursor.active = true; + // Proper cursor locking requires zwp_pointer_constraints protocol +} + +void window_unlock_cursor(void* state) { + WaylandState* s = state; + s->sm.lockCursor.active = false; +} + +#endif diff --git a/src/platform/windowing/wayland.h b/src/platform/windowing/wayland.h new file mode 100644 index 000000000..02f0e08c5 --- /dev/null +++ b/src/platform/windowing/wayland.h @@ -0,0 +1,105 @@ +/******************************************************************************/ +/* wayland.h */ +/******************************************************************************/ +/* This file is part of */ +/* KAIJU ENGINE */ +/* https://kaijuengine.com/ */ +/******************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023-present Kaiju Engine authors (AUTHORS.md). */ +/* Copyright (c) 2015-present Brent Farris. */ +/* */ +/* May all those that this source may reach be blessed by the LORD and find */ +/* peace and joy in life. */ +/* Everyone who drinks of this water will be thirsty again; but whoever */ +/* drinks of the water that I will give him shall never thirst; John 4:13-14 */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a */ +/* copy of this software and associated documentation files (the "Software"), */ +/* to deal in the Software without restriction, including without limitation */ +/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ +/* and/or sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright, blessing, biblical verse, notice and */ +/* this permission notice shall be included in all copies or */ +/* substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS */ +/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT */ +/* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE */ +/* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/******************************************************************************/ + +#ifndef WINDOWING_WAYLAND_H +#define WINDOWING_WAYLAND_H + +#include +#include +#include "shared_mem.h" + +typedef struct { + SharedMem sm; + struct wl_display* display; + struct wl_registry* registry; + struct wl_compositor* compositor; + struct wl_surface* surface; + struct wl_seat* seat; + struct wl_keyboard* keyboard; + struct wl_pointer* pointer; + struct wl_shm* shm; + struct wl_output* output; + struct xdg_wm_base* xdg_wm_base; + struct xdg_surface* xdg_surface; + struct xdg_toplevel* xdg_toplevel; + struct zxdg_decoration_manager_v1* decoration_manager; + struct zxdg_toplevel_decoration_v1* toplevel_decoration; + struct wl_cursor_theme* cursor_theme; + struct wl_cursor* current_cursor; + struct wl_surface* cursor_surface; + struct xkb_context* xkb_context; + struct xkb_keymap* xkb_keymap; + struct xkb_state* xkb_state; + int32_t output_scale; + int32_t output_width; + int32_t output_height; + int32_t output_width_mm; + int32_t output_height_mm; + bool configured; + bool closed; +} WaylandState; + +unsigned int get_toggle_key_state(); +void window_main(const char* windowTitle, + int width, int height, int x, int y, uint64_t goWindow); +void window_show(void* waylandState); +void window_poll_controller(void* waylandState); +void window_poll(void* waylandState); +void window_destroy(void* waylandState); +void* display(void* waylandState); +void* window(void* waylandState); +void window_focus(void* state); +void window_position(void* state, int* x, int* y); +void window_set_position(void* state, int x, int y); +void window_set_size(void* state, int width, int height); +int window_width_mm(void* state); +int window_height_mm(void* state); +void window_cursor_standard(void* state); +void window_cursor_ibeam(void* state); +void window_cursor_size_all(void* state); +void window_cursor_size_ns(void* state); +void window_cursor_size_we(void* state); +void window_show_cursor(void* state); +void window_hide_cursor(void* state); +float window_dpi(void* state); +void window_set_title(void* state, const char* windowTitle); +void window_set_full_screen(void* state); +void window_set_windowed(void* state, int width, int height); +void window_lock_cursor(void* state, int x, int y); +void window_unlock_cursor(void* state); + +#endif diff --git a/src/platform/windowing/wayland.vk.go b/src/platform/windowing/wayland.vk.go new file mode 100644 index 000000000..39ef07a73 --- /dev/null +++ b/src/platform/windowing/wayland.vk.go @@ -0,0 +1,43 @@ +//go:build linux && !android && wayland + +/******************************************************************************/ +/* wayland.vk.go */ +/******************************************************************************/ +/* This file is part of */ +/* KAIJU ENGINE */ +/* https://kaijuengine.com/ */ +/******************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023-present Kaiju Engine authors (AUTHORS.md). */ +/* Copyright (c) 2015-present Brent Farris. */ +/* */ +/* May all those that this source may reach be blessed by the LORD and find */ +/* peace and joy in life. */ +/* Everyone who drinks of this water will be thirsty again; but whoever */ +/* drinks of the water that I will give him shall never thirst; John 4:13-14 */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a */ +/* copy of this software and associated documentation files (the "Software"), */ +/* to deal in the Software without restriction, including without limitation */ +/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ +/* and/or sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS */ +/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT */ +/* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE */ +/* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/******************************************************************************/ + +package windowing + +func getInstanceExtensions() []string { + return []string{"VK_KHR_surface\x00", "VK_KHR_wayland_surface\x00"} +} diff --git a/src/platform/windowing/window.x11.go b/src/platform/windowing/window.linux.go similarity index 98% rename from src/platform/windowing/window.x11.go rename to src/platform/windowing/window.linux.go index 2f629d83b..271950771 100644 --- a/src/platform/windowing/window.x11.go +++ b/src/platform/windowing/window.linux.go @@ -1,7 +1,7 @@ -//go:build linux && !android +//go:build linux && !android && !wayland /******************************************************************************/ -/* window.x11.go */ +/* window.linux.go */ /******************************************************************************/ /* This file is part of */ /* KAIJU ENGINE */ diff --git a/src/platform/windowing/window.wayland.go b/src/platform/windowing/window.wayland.go new file mode 100644 index 000000000..354e6bc25 --- /dev/null +++ b/src/platform/windowing/window.wayland.go @@ -0,0 +1,230 @@ +//go:build linux && !android && wayland + +/******************************************************************************/ +/* window.wayland.go */ +/******************************************************************************/ +/* This file is part of */ +/* KAIJU ENGINE */ +/* https://kaijuengine.com/ */ +/******************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023-present Kaiju Engine authors (AUTHORS.md). */ +/* Copyright (c) 2015-present Brent Farris. */ +/* */ +/* May all those that this source may reach be blessed by the LORD and find */ +/* peace and joy in life. */ +/* Everyone who drinks of this water will be thirsty again; but whoever */ +/* drinks of the water that I will give him shall never thirst; John 4:13-14 */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a */ +/* copy of this software and associated documentation files (the "Software"), */ +/* to deal in the Software without restriction, including without limitation */ +/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ +/* and/or sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS */ +/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT */ +/* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE */ +/* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/******************************************************************************/ + +package windowing + +/* +#cgo CFLAGS: -I/usr/include -DUSE_WAYLAND +#cgo LDFLAGS: -lwayland-client -lxkbcommon -lwayland-cursor +#cgo noescape get_toggle_key_state +#cgo noescape window_main +#cgo noescape window_poll_controller +#cgo noescape window_poll +#cgo noescape window_show +#cgo noescape window_destroy +#cgo noescape window_focus +#cgo noescape window_position +#cgo noescape window_set_position +#cgo noescape window_set_size +#cgo noescape window_width_mm +#cgo noescape window_height_mm +#cgo noescape window_cursor_standard +#cgo noescape window_cursor_ibeam +#cgo noescape window_cursor_size_all +#cgo noescape window_cursor_size_ns +#cgo noescape window_cursor_size_we +#cgo noescape window_show_cursor +#cgo noescape window_hide_cursor +#cgo noescape window_dpi +#cgo noescape window_set_title +#cgo noescape window_set_full_screen +#cgo noescape window_set_windowed +#cgo noescape window_lock_cursor +#cgo noescape window_unlock_cursor + +#include +#include "windowing.h" +*/ +import "C" +import ( + "errors" + "kaiju/klib" + "kaiju/platform/hid" + "unsafe" + + "golang.design/x/clipboard" +) + +//export goProcessEvents +func goProcessEvents(goWindow C.uint64_t, events unsafe.Pointer, eventCount C.uint32_t) { + goProcessEventsCommon(uint64(goWindow), events, uint32(eventCount)) +} + +func (w *Window) checkToggleKeyState() map[hid.KeyboardKey]bool { + mask := C.get_toggle_key_state() + + caps := (mask & 1) != 0 + num := (mask & 2) != 0 + scroll := (mask & 4) != 0 + + return map[hid.KeyboardKey]bool{ + hid.KeyboardKeyCapsLock: caps, + hid.KeyboardKeyNumLock: num, + hid.KeyboardKeyScrollLock: scroll, + } +} + +func (w *Window) createWindow(windowName string, x, y int, _ any) { + title := C.CString(windowName) + defer C.free(unsafe.Pointer(title)) + w.lookupId = nextLookupId.Add(1) + windowLookup.Store(w.lookupId, w) + C.window_main(title, C.int(w.width), C.int(w.height), + C.int(x), C.int(y), C.uint64_t(w.lookupId)) +} + +func (w *Window) showWindow() { + C.window_show(w.handle) +} + +func destroyWindow(handle unsafe.Pointer) { + C.window_destroy(handle) +} + +func (w *Window) poll() { + C.window_poll_controller(w.handle) + C.window_poll(w.handle) +} + +func (w *Window) cursorStandard() { + C.window_cursor_standard(w.handle) +} + +func (w *Window) cursorIbeam() { + C.window_cursor_ibeam(w.handle) +} + +func (w *Window) cursorSizeAll() { + C.window_cursor_size_all(w.handle) +} + +func (w *Window) cursorSizeNS() { + C.window_cursor_size_ns(w.handle) +} + +func (w *Window) cursorSizeWE() { + C.window_cursor_size_we(w.handle) +} + +func (w *Window) copyToClipboard(text string) { + clipboard.Write(clipboard.FmtText, []byte(text)) +} + +func (w *Window) clipboardContents() string { + return string(clipboard.Read(clipboard.FmtText)) +} + +func (w *Window) sizeMM() (int, int, error) { + width := C.window_width_mm(w.handle) + height := C.window_height_mm(w.handle) + return int(width), int(height), nil +} + +func (w *Window) cHandle() unsafe.Pointer { return C.window(w.handle) } +func (w *Window) cInstance() unsafe.Pointer { return C.display(w.handle) } + +func (w *Window) focus() { + C.window_focus(w.handle) +} + +func (w *Window) position() (x, y int) { + C.window_position(w.handle, (*C.int)(unsafe.Pointer(&x)), (*C.int)(unsafe.Pointer(&y))) + return x, y +} + +func (w *Window) setPosition(x, y int) { + C.window_set_position(w.handle, C.int(x), C.int(y)) +} + +func (w *Window) setSize(width, height int) { + C.window_set_size(w.handle, C.int(width), C.int(height)) +} + +func (w *Window) showCursor() { + C.window_show_cursor(w.handle) +} + +func (w *Window) hideCursor() { + C.window_hide_cursor(w.handle) +} + +func (w *Window) dotsPerMillimeter() float64 { + return float64(C.window_dpi(w.handle)) +} + +func (w *Window) screenSizeMM() (int, int, error) { + mm := float64(C.window_dpi(w.handle)) + return int(float64(w.width) * mm), int(float64(w.height) * mm), nil +} + +func (w Window) setTitle(name string) { + title := C.CString(name) + defer C.free(unsafe.Pointer(title)) + C.window_set_title(w.handle, title) +} + +func (w Window) setFullscreen() { + C.window_set_full_screen(w.handle) +} + +func (w Window) setWindowed(width, height int) { + C.window_set_windowed(w.handle, C.int(width), C.int(height)) +} + +func (w Window) lockCursor(x, y int) { + C.window_lock_cursor(w.handle, C.int(x), C.int(y)) +} + +func (w Window) unlockCursor() { + C.window_unlock_cursor(w.handle) +} + +func (w *Window) removeBorder() { + klib.NotYetImplemented(234) +} + +func (w *Window) addBorder() { + klib.NotYetImplemented(234) +} + +func (w Window) disableRawMouse() { /* Wayland handles this differently */ } +func (w Window) enableRawMouse() { /* Wayland handles this differently */ } + +func (w *Window) readApplicationAsset(path string) ([]byte, error) { + return []byte{}, errors.New("linux doesn't support application assets") +} diff --git a/src/platform/windowing/windowing.h b/src/platform/windowing/windowing.h index 0881d139b..7c1378f5f 100644 --- a/src/platform/windowing/windowing.h +++ b/src/platform/windowing/windowing.h @@ -42,7 +42,9 @@ #include "win32.h" #elif defined(__android__) #include "android.h" -#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) +#elif defined(__linux__) || defined(__unix__) +#include "linux.h" +#elif defined(__APPLE__) #include "x11.h" #endif diff --git a/src/platform/windowing/x11.c b/src/platform/windowing/x11.c index 1cc92088f..dd920af28 100644 --- a/src/platform/windowing/x11.c +++ b/src/platform/windowing/x11.c @@ -37,7 +37,7 @@ /* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /******************************************************************************/ -#if (defined(__linux__) || defined(__unix__)) && !defined(__ANDROID__) +#if (defined(__linux__) || defined(__unix__)) && !defined(__ANDROID__) && !defined(USE_WAYLAND) #include "x11.h" #include diff --git a/src/platform/windowing/xdg-decoration-client-protocol.h b/src/platform/windowing/xdg-decoration-client-protocol.h new file mode 100644 index 000000000..995e58aa9 --- /dev/null +++ b/src/platform/windowing/xdg-decoration-client-protocol.h @@ -0,0 +1,145 @@ +/* Generated by wayland-scanner - xdg-decoration-unstable-v1 protocol */ + +#ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct xdg_toplevel; +struct zxdg_decoration_manager_v1; +struct zxdg_toplevel_decoration_v1; + +#ifndef ZXDG_DECORATION_MANAGER_V1_INTERFACE +#define ZXDG_DECORATION_MANAGER_V1_INTERFACE +extern const struct wl_interface zxdg_decoration_manager_v1_interface; +#endif + +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +#define ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; +#endif + +#define ZXDG_DECORATION_MANAGER_V1_DESTROY 0 +#define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION 1 + +static inline void +zxdg_decoration_manager_v1_set_user_data(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_decoration_manager_v1, user_data); +} + +static inline void * +zxdg_decoration_manager_v1_get_user_data(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_decoration_manager_v1); +} + +static inline uint32_t +zxdg_decoration_manager_v1_get_version(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1); +} + +static inline void +zxdg_decoration_manager_v1_destroy(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_decoration_manager_v1, + ZXDG_DECORATION_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct zxdg_toplevel_decoration_v1 * +zxdg_decoration_manager_v1_get_toplevel_decoration(struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1, struct xdg_toplevel *toplevel) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zxdg_decoration_manager_v1, + ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION, &zxdg_toplevel_decoration_v1_interface, wl_proxy_get_version((struct wl_proxy *) zxdg_decoration_manager_v1), 0, NULL, toplevel); + + return (struct zxdg_toplevel_decoration_v1 *) id; +} + +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM +#define ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ENUM +enum zxdg_toplevel_decoration_v1_error { + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_UNCONFIGURED_BUFFER = 0, + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ALREADY_CONSTRUCTED = 1, + ZXDG_TOPLEVEL_DECORATION_V1_ERROR_ORPHANED = 2, +}; +#endif + +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM +#define ZXDG_TOPLEVEL_DECORATION_V1_MODE_ENUM +enum zxdg_toplevel_decoration_v1_mode { + ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE = 1, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE = 2, +}; +#endif + +struct zxdg_toplevel_decoration_v1_listener { + void (*configure)(void *data, + struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + uint32_t mode); +}; + +static inline int +zxdg_toplevel_decoration_v1_add_listener(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, + const struct zxdg_toplevel_decoration_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zxdg_toplevel_decoration_v1, + (void (**)(void)) listener, data); +} + +#define ZXDG_TOPLEVEL_DECORATION_V1_DESTROY 0 +#define ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE 1 +#define ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE 2 + +static inline void +zxdg_toplevel_decoration_v1_set_user_data(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zxdg_toplevel_decoration_v1, user_data); +} + +static inline void * +zxdg_toplevel_decoration_v1_get_user_data(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zxdg_toplevel_decoration_v1); +} + +static inline uint32_t +zxdg_toplevel_decoration_v1_get_version(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1); +} + +static inline void +zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), WL_MARSHAL_FLAG_DESTROY); +} + +static inline void +zxdg_toplevel_decoration_v1_set_mode(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_SET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), 0, mode); +} + +static inline void +zxdg_toplevel_decoration_v1_unset_mode(struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zxdg_toplevel_decoration_v1, + ZXDG_TOPLEVEL_DECORATION_V1_UNSET_MODE, NULL, wl_proxy_get_version((struct wl_proxy *) zxdg_toplevel_decoration_v1), 0); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/platform/windowing/xdg-decoration-protocol.c b/src/platform/windowing/xdg-decoration-protocol.c new file mode 100644 index 000000000..414dc0331 --- /dev/null +++ b/src/platform/windowing/xdg-decoration-protocol.c @@ -0,0 +1,57 @@ +//go:build linux && !android && wayland + +/* Generated by wayland-scanner - xdg-decoration-unstable-v1 protocol */ + +#if defined(__linux__) && defined(USE_WAYLAND) && !defined(__ANDROID__) + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface xdg_toplevel_interface; +extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; + +static const struct wl_interface *xdg_decoration_types[] = { + NULL, + &zxdg_toplevel_decoration_v1_interface, + &xdg_toplevel_interface, +}; + +static const struct wl_message zxdg_decoration_manager_v1_requests[] = { + { "destroy", "", xdg_decoration_types + 0 }, + { "get_toplevel_decoration", "no", xdg_decoration_types + 1 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = { + "zxdg_decoration_manager_v1", 1, + 2, zxdg_decoration_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = { + { "destroy", "", xdg_decoration_types + 0 }, + { "set_mode", "u", xdg_decoration_types + 0 }, + { "unset_mode", "", xdg_decoration_types + 0 }, +}; + +static const struct wl_message zxdg_toplevel_decoration_v1_events[] = { + { "configure", "u", xdg_decoration_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zxdg_toplevel_decoration_v1_interface = { + "zxdg_toplevel_decoration_v1", 1, + 3, zxdg_toplevel_decoration_v1_requests, + 1, zxdg_toplevel_decoration_v1_events, +}; + +#endif diff --git a/src/platform/windowing/xdg-shell-client-protocol.h b/src/platform/windowing/xdg-shell-client-protocol.h new file mode 100644 index 000000000..e2ffc7cdf --- /dev/null +++ b/src/platform/windowing/xdg-shell-client-protocol.h @@ -0,0 +1,479 @@ +/* Generated by wayland-scanner from xdg-shell.xml */ +/* xdg-shell client protocol - stable */ + +#ifndef XDG_SHELL_CLIENT_PROTOCOL_H +#define XDG_SHELL_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct xdg_wm_base; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_popup; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +extern const struct wl_interface xdg_wm_base_interface; +#endif + +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +extern const struct wl_interface xdg_positioner_interface; +#endif + +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +extern const struct wl_interface xdg_surface_interface; +#endif + +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +extern const struct wl_interface xdg_toplevel_interface; +#endif + +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +extern const struct wl_interface xdg_popup_interface; +#endif + +/* xdg_wm_base */ +enum xdg_wm_base_error { + XDG_WM_BASE_ERROR_ROLE = 0, + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, +}; + +struct xdg_wm_base_listener { + void (*ping)(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial); +}; + +static inline int +xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +#define XDG_WM_BASE_DESTROY 0 +#define XDG_WM_BASE_CREATE_POSITIONER 1 +#define XDG_WM_BASE_GET_XDG_SURFACE 2 +#define XDG_WM_BASE_PONG 3 + +static inline void +xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct xdg_positioner * +xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) +{ + struct wl_proxy *id; + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); + return (struct xdg_positioner *) id; +} + +static inline struct xdg_surface * +xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) +{ + struct wl_proxy *id; + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); + return (struct xdg_surface *) id; +} + +static inline void +xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); +} + +/* xdg_positioner */ +enum xdg_positioner_error { + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; + +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; + +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; + +enum xdg_positioner_constraint_adjustment { + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; + +#define XDG_POSITIONER_DESTROY 0 +#define XDG_POSITIONER_SET_SIZE 1 +#define XDG_POSITIONER_SET_ANCHOR_RECT 2 +#define XDG_POSITIONER_SET_ANCHOR 3 +#define XDG_POSITIONER_SET_GRAVITY 4 +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 +#define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 + +static inline void +xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); +} + +static inline void +xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); +} + +static inline void +xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); +} + +static inline void +xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); +} + +static inline void +xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); +} + +static inline void +xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); +} + +static inline void +xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); +} + +/* xdg_surface */ +enum xdg_surface_error { + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, +}; + +struct xdg_surface_listener { + void (*configure)(void *data, struct xdg_surface *xdg_surface, uint32_t serial); +}; + +static inline int +xdg_surface_add_listener(struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +#define XDG_SURFACE_DESTROY 0 +#define XDG_SURFACE_GET_TOPLEVEL 1 +#define XDG_SURFACE_GET_POPUP 2 +#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 +#define XDG_SURFACE_ACK_CONFIGURE 4 + +static inline void +xdg_surface_destroy(struct xdg_surface *xdg_surface) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); +} + +static inline struct xdg_toplevel * +xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); + return (struct xdg_toplevel *) id; +} + +static inline struct xdg_popup * +xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) +{ + struct wl_proxy *id; + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); + return (struct xdg_popup *) id; +} + +static inline void +xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); +} + +static inline void +xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); +} + +/* xdg_toplevel */ +enum xdg_toplevel_error { + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, +}; + +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; + +enum xdg_toplevel_state { + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + XDG_TOPLEVEL_STATE_RESIZING = 3, + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, +}; + +enum xdg_toplevel_wm_capabilities { + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, +}; + +struct xdg_toplevel_listener { + void (*configure)(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states); + void (*close)(void *data, struct xdg_toplevel *xdg_toplevel); + void (*configure_bounds)(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height); + void (*wm_capabilities)(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities); +}; + +static inline int +xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +#define XDG_TOPLEVEL_DESTROY 0 +#define XDG_TOPLEVEL_SET_PARENT 1 +#define XDG_TOPLEVEL_SET_TITLE 2 +#define XDG_TOPLEVEL_SET_APP_ID 3 +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 +#define XDG_TOPLEVEL_MOVE 5 +#define XDG_TOPLEVEL_RESIZE 6 +#define XDG_TOPLEVEL_SET_MAX_SIZE 7 +#define XDG_TOPLEVEL_SET_MIN_SIZE 8 +#define XDG_TOPLEVEL_SET_MAXIMIZED 9 +#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 +#define XDG_TOPLEVEL_SET_FULLSCREEN 11 +#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 +#define XDG_TOPLEVEL_SET_MINIMIZED 13 + +static inline void +xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); +} + +static inline void +xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); +} + +static inline void +xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); +} + +static inline void +xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); +} + +static inline void +xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); +} + +static inline void +xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); +} + +static inline void +xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); +} + +static inline void +xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +static inline void +xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +static inline void +xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +static inline void +xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +static inline void +xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); +} + +static inline void +xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +static inline void +xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/* xdg_popup */ +enum xdg_popup_error { + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; + +struct xdg_popup_listener { + void (*configure)(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height); + void (*popup_done)(void *data, struct xdg_popup *xdg_popup); + void (*repositioned)(void *data, struct xdg_popup *xdg_popup, uint32_t token); +}; + +static inline int +xdg_popup_add_listener(struct xdg_popup *xdg_popup, + const struct xdg_popup_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, + (void (**)(void)) listener, data); +} + +#define XDG_POPUP_DESTROY 0 +#define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 + +static inline void +xdg_popup_destroy(struct xdg_popup *xdg_popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); +} + +static inline void +xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); +} + +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/platform/windowing/xdg-shell-protocol.c b/src/platform/windowing/xdg-shell-protocol.c new file mode 100644 index 000000000..87e9dac59 --- /dev/null +++ b/src/platform/windowing/xdg-shell-protocol.c @@ -0,0 +1,150 @@ +//go:build linux && !android && wayland + +/* Generated by wayland-scanner from xdg-shell.xml */ + +#if defined(__linux__) && defined(USE_WAYLAND) && !defined(__ANDROID__) + +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface xdg_positioner_interface; +extern const struct wl_interface xdg_surface_interface; +extern const struct wl_interface xdg_toplevel_interface; + +static const struct wl_interface *xdg_shell_types[] = { + NULL, + NULL, + NULL, + NULL, + &xdg_positioner_interface, + &xdg_surface_interface, + &wl_surface_interface, + &xdg_toplevel_interface, + &xdg_popup_interface, + &xdg_surface_interface, + &xdg_positioner_interface, + &xdg_toplevel_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &xdg_positioner_interface, + NULL, +}; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 5, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 5, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_surface_interface = { + "xdg_surface", 5, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, + { "configure_bounds", "4ii", xdg_shell_types + 0 }, + { "wm_capabilities", "5a", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 5, + 14, xdg_toplevel_requests, + 4, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +const struct wl_interface xdg_popup_interface = { + "xdg_popup", 5, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + +#endif diff --git a/src/rendering/vulkan.x11.go b/src/rendering/vulkan.linux.go similarity index 96% rename from src/rendering/vulkan.x11.go rename to src/rendering/vulkan.linux.go index 458b82a88..ab39ff3aa 100644 --- a/src/rendering/vulkan.x11.go +++ b/src/rendering/vulkan.linux.go @@ -1,7 +1,7 @@ -//go:build linux && !android +//go:build linux && !android && !wayland /******************************************************************************/ -/* vulkan.x11.go */ +/* vulkan.linux.go */ /******************************************************************************/ /* This file is part of */ /* KAIJU ENGINE */ diff --git a/src/rendering/vulkan.wayland.go b/src/rendering/vulkan.wayland.go new file mode 100644 index 000000000..41554df2d --- /dev/null +++ b/src/rendering/vulkan.wayland.go @@ -0,0 +1,56 @@ +//go:build linux && !android && wayland + +/******************************************************************************/ +/* vulkan.wayland.go */ +/******************************************************************************/ +/* This file is part of */ +/* KAIJU ENGINE */ +/* https://kaijuengine.com/ */ +/******************************************************************************/ +/* MIT License */ +/* */ +/* Copyright (c) 2023-present Kaiju Engine authors (AUTHORS.md). */ +/* Copyright (c) 2015-present Brent Farris. */ +/* */ +/* May all those that this source may reach be blessed by the LORD and find */ +/* peace and joy in life. */ +/* Everyone who drinks of this water will be thirsty again; but whoever */ +/* drinks of the water that I will give him shall never thirst; John 4:13-14 */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining a */ +/* copy of this software and associated documentation files (the "Software"), */ +/* to deal in the Software without restriction, including without limitation */ +/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */ +/* and/or sell copies of the Software, and to permit persons to whom the */ +/* Software is furnished to do so, subject to the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be included in */ +/* all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS */ +/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT */ +/* OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE */ +/* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/******************************************************************************/ + +package rendering + +import ( + vk "kaiju/rendering/vulkan" + "kaiju/rendering/vulkan_const" +) + +func (vr *Vulkan) createSurface(window RenderingContainer) bool { + var surface vk.Surface + info := vk.WaylandSurfaceCreateInfo{ + SType: vulkan_const.StructureTypeWaylandSurfaceCreateInfo, + Display: uintptr(window.PlatformInstance()), + Surface: uintptr(window.PlatformWindow()), + } + vk.CreateWaylandSurface(vr.instance, &info, nil, &surface) + vr.surface = surface + return vr.surface != vk.NullSurface +} diff --git a/src/rendering/vulkan/vulkan_linux_wayland.go b/src/rendering/vulkan/vulkan_linux_wayland.go index 886d64719..1d71667eb 100644 --- a/src/rendering/vulkan/vulkan_linux_wayland.go +++ b/src/rendering/vulkan/vulkan_linux_wayland.go @@ -39,7 +39,10 @@ package vulkan -import "unsafe" +import ( + "kaiju/rendering/vulkan_const" + "unsafe" +) /* #cgo LDFLAGS: -ldl @@ -75,7 +78,7 @@ type WaylandSurfaceCreateFlags uint32 // Linux Wayland type struct type WaylandSurfaceCreateInfo struct { - SType StructureType + SType vulkan_const.StructureType PNext unsafe.Pointer Flags WaylandSurfaceCreateFlags Display uintptr