summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-06-23 17:35:34 -0400
committerAndrew Opalach <andrew@akon.city> 2025-06-23 17:35:34 -0400
commit01637283b33d7fe4921e9347ada867e44b29edba (patch)
treebda468becec64b5d2930c80910905c567020851a
parent234e3cb390c7bb149ddd281d695d81a368e1a441 (diff)
downloadstela-01637283b33d7fe4921e9347ada867e44b29edba.tar.gz
stela-01637283b33d7fe4921e9347ada867e44b29edba.tar.bz2
stela-01637283b33d7fe4921e9347ada867e44b29edba.zip
Internally track modifier keys, Wayland Vulkan
- Add set_cursor(). - Various Wayland cleanups. Signed-off-by: Andrew Opalach <andrew@akon.city>
-rw-r--r--src/egl_common.c8
-rw-r--r--src/gl_versions.h25
-rw-r--r--src/keys.h61
-rw-r--r--src/poll_common.c4
-rw-r--r--src/poll_common.h2
-rw-r--r--src/window.c61
-rw-r--r--src/window.h15
-rw-r--r--src/window_glfw.c20
-rw-r--r--src/window_wayland.c306
-rw-r--r--src/window_wayland.h28
-rw-r--r--src/window_win32.c160
-rw-r--r--src/window_win32.h1
-rw-r--r--src/window_x11.c8
-rw-r--r--subprojects/libalabaster.wrap2
-rw-r--r--subprojects/libnaunet.wrap2
-rw-r--r--tests/window.c4
16 files changed, 496 insertions, 211 deletions
diff --git a/src/egl_common.c b/src/egl_common.c
index 7ed6a62..606cc67 100644
--- a/src/egl_common.c
+++ b/src/egl_common.c
@@ -57,7 +57,9 @@ bool stl_load_egl(struct stl_egl_global *global)
void stl_terminate_egl(struct stl_egl_global *global)
{
if (global->display != EGL_NO_DISPLAY) {
- eglTerminate(global->display);
+ if (!eglTerminate(global->display)) {
+ log_warn("Failed to eglTerminate().");
+ }
}
}
@@ -69,9 +71,7 @@ static EGLint match_config_to_visual(EGLDisplay display, EGLint visual_id, EGLCo
if (!eglGetConfigAttrib(display, configs[i], EGL_NATIVE_VISUAL_ID, &id)) {
continue;
}
- if (id == visual_id) {
- return i;
- }
+ if (id == visual_id) return i;
}
return -1;
}
diff --git a/src/gl_versions.h b/src/gl_versions.h
index e6c6ab2..0182317 100644
--- a/src/gl_versions.h
+++ b/src/gl_versions.h
@@ -59,28 +59,3 @@
};
#endif
#endif
-
-/*
-#define PASTE_GL_VERSIONS() static struct { \
- s32 api; \
- s32 type; \
- s32 major, minor; \
- s32 glsl_ver; \
- s32 profile; \
-} stl_gl_vers[] = { \
- { STELA_GL_API, STELA_GL_BIT, 4, 6, 460, STELA_GL_CORE_PROFILE_BIT }, \
- { STELA_GL_API, STELA_GL_BIT, 4, 5, 450, STELA_GL_CORE_PROFILE_BIT }, \
- { STELA_GL_API, STELA_GL_BIT, 4, 4, 440, STELA_GL_CORE_PROFILE_BIT }, \
- { STELA_GL_API, STELA_GL_BIT, 4, 0, 400, STELA_GL_CORE_PROFILE_BIT }, \
- { STELA_GL_API, STELA_GL_BIT, 3, 3, 330, STELA_GL_CORE_PROFILE_BIT }, \
- { STELA_GL_API, STELA_GL_BIT, 3, 2, 150, STELA_GL_CORE_PROFILE_BIT }, \
- { STELA_GLES_API, STELA_GLES3_BIT, 3, 2, 320, 0 }, \
- { STELA_GL_API, STELA_GL_BIT, 3, 1, 140, 0 }, \
- { STELA_GLES_API, STELA_GLES3_BIT, 3, 1, 310, 0 }, \
- { STELA_GL_API, STELA_GL_BIT, 3, 0, 130, 0 }, \
- { STELA_GLES_API, STELA_GLES3_BIT, 3, 0, 300, 0 }, \
- { STELA_GLES_API, STELA_GLES2_BIT, 2, 0, 100, 0 }, \
- { STELA_GL_API, STELA_GL_BIT, 2, 1, 120, 0 }, \
- { STELA_GL_API, STELA_GL_BIT, 2, 0, 110, 0 } \
-};
-*/
diff --git a/src/keys.h b/src/keys.h
index d4d17f0..a5691a5 100644
--- a/src/keys.h
+++ b/src/keys.h
@@ -1,6 +1,39 @@
#pragma once
-// https://github.com/glfw/glfw/blob/e7ea71be039836da3a98cea55ae5569cb5eb885c/include/STELA/glfw3.h#L395C1-L519C50
+#include <al/types.h>
+#include <al/lib.h>
+
+// Expected behavior of modifiers:
+// - Window keeps an internal state of all currently held modifiers.
+// - On window focus enter, if modifier is held, send BUTTON_PRESSED event.
+// - If a modifier was held on enter, it must still receive a BUTTON_RELEASED
+// event when the key is released.
+// - On window focus exit, send BUTTON_RELEASED for every active modifier.
+enum {
+ STELA_MOD_NONE = 0,
+ STELA_MOD_LCONTROL = 1,
+ STELA_MOD_RCONTROL = 1 << 1,
+ STELA_MOD_LSHIFT = 1 << 2,
+ STELA_MOD_RSHIFT = 1 << 3,
+ STELA_MOD_LALT = 1 << 4,
+ STELA_MOD_RALT = 1 << 5
+};
+
+AL_IGNORE_WARNING("-Wunused-variable")
+
+static u32 stl_all_modifiers[] = {
+ STELA_MOD_LCONTROL, STELA_MOD_RCONTROL,
+ STELA_MOD_LSHIFT, STELA_MOD_RSHIFT,
+ STELA_MOD_LALT, STELA_MOD_RALT,
+ STELA_MOD_NONE
+};
+
+AL_IGNORE_WARNING_END
+
+// https://github.com/glfw/glfw/blob/e7ea71be039836da3a98cea55ae5569cb5eb885c/include/GLFW/glfw3.h#L395C1-L519C50
+// See window_glfw.c:key_callback() for ommited keys.
+
+#define STELA_KEY_NONE 0
/* Printable keys */
#define STELA_KEY_0 48
@@ -129,3 +162,29 @@
#define STELA_KEY_MENU 348
#define STELA_KEY_LAST STELA_KEY_MENU
+
+static inline u16 stl_mod_to_key(u32 mod)
+{
+ switch (mod) {
+ case STELA_MOD_LCONTROL: return STELA_KEY_LEFT_CONTROL;
+ case STELA_MOD_RCONTROL: return STELA_KEY_RIGHT_CONTROL;
+ case STELA_MOD_LSHIFT: return STELA_KEY_LEFT_SHIFT;
+ case STELA_MOD_RSHIFT: return STELA_KEY_RIGHT_SHIFT;
+ case STELA_MOD_LALT: return STELA_KEY_LEFT_ALT;
+ case STELA_MOD_RALT: return STELA_KEY_RIGHT_ALT;
+ }
+ al_assert_and_return(STELA_KEY_NONE);
+}
+
+static inline u32 stl_key_to_mod(u16 key)
+{
+ switch (key) {
+ case STELA_KEY_LEFT_CONTROL: return STELA_MOD_LCONTROL;
+ case STELA_KEY_RIGHT_CONTROL: return STELA_MOD_RCONTROL;
+ case STELA_KEY_LEFT_SHIFT: return STELA_MOD_LSHIFT;
+ case STELA_KEY_RIGHT_SHIFT: return STELA_MOD_RSHIFT;
+ case STELA_KEY_LEFT_ALT: return STELA_MOD_LALT;
+ case STELA_KEY_RIGHT_ALT: return STELA_MOD_RALT;
+ }
+ return STELA_MOD_NONE;
+}
diff --git a/src/poll_common.c b/src/poll_common.c
index a79e0de..c5d43c4 100644
--- a/src/poll_common.c
+++ b/src/poll_common.c
@@ -5,7 +5,7 @@
#include "poll_common.h"
#ifdef STELA_POLL_INLINE
-bool stl_poll_common(struct nn_pollfd *fds, bool block)
+bool stl_poll_common(struct stl_window *window, struct nn_pollfd *fds, bool block)
{
fds[0].revents = 0;
#ifdef STELA_PAUSE
@@ -30,7 +30,7 @@ bool stl_poll_common(struct nn_pollfd *fds, bool block)
return false;
}
- return true;
+ return !window->closed;
}
#ifdef STELA_PAUSE
void stl_wake_common(struct stl_window *window, struct nn_pollfd *fds)
diff --git a/src/poll_common.h b/src/poll_common.h
index 0efa500..3ad6d5e 100644
--- a/src/poll_common.h
+++ b/src/poll_common.h
@@ -5,7 +5,7 @@
#include "window.h"
#ifdef STELA_POLL_INLINE
-bool stl_poll_common(struct nn_pollfd *fds, bool block);
+bool stl_poll_common(struct stl_window *window, struct nn_pollfd *fds, bool block);
#ifdef STELA_PAUSE
void stl_wake_common(struct stl_window *window, struct nn_pollfd *fds);
#endif
diff --git a/src/window.c b/src/window.c
index 08dc625..034f401 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2,6 +2,7 @@
#include <al/log.h>
#include "window.h"
+#include "keys.h"
static bool nop_pointer_pos_callback(void *userdata, f64 x, f64 y)
{
@@ -44,6 +45,18 @@ static bool nop_key_callback(void *userdata, u8 state, u16 button)
return false;
}
+static void nop_key_immediate_callback(void *userdata, u8 state, u16 button)
+{
+ (void)userdata;
+ (void)state;
+ (void)button;
+}
+
+static void window_default_close(struct stl_window *window)
+{
+ window->closed = true;
+}
+
void stl_window_init(struct stl_window *window)
{
window->pointer_pos_callback = nop_pointer_pos_callback;
@@ -51,9 +64,13 @@ void stl_window_init(struct stl_window *window)
window->scroll_callback = nop_scroll_callback;
window->mouse_button_callback = nop_mouse_button_callback;
window->key_callback = nop_key_callback;
+ window->key_immediate_callback = nop_key_immediate_callback;
+ window->close = window_default_close;
#ifdef STELA_EVENT_BUFFER
al_ring_buffer_init(&window->events, window->data, STELA_EVENTS_SIZE);
#endif
+ window->closed = false;
+ window->held_modifiers = 0;
#ifdef STELA_PAUSE
window->pending_refresh = true;
#endif
@@ -164,8 +181,26 @@ void stl_window_send_mouse_button_event(struct stl_window *window, u8 state, u8
#endif
}
+#ifdef STELA_WINDOW_WIN32
+// It's possible for a window to behave in a way that disallows
+// consistancy between BUTTON_PRESSED and BUTTON_RELEASED events.
+#define STELA_CORRECT_MODIFIER_INCONSISTENCY
+#endif
+
void stl_window_send_key_event(struct stl_window *window, u8 state, u16 button)
{
+ u32 mod = stl_key_to_mod(button);
+#ifdef STELA_CORRECT_MODIFIER_INCONSISTENCY
+ if (state == STELA_BUTTON_PRESSED) {
+ if (window->held_modifiers & mod) {
+ window->held_modifiers &= ~mod;
+ window->key_callback(window->userdata, STELA_BUTTON_RELEASED, button);
+ }
+ } else {
+ if (!(window->held_modifiers & mod)) return;
+ }
+#endif
+ window->held_modifiers = (window->held_modifiers & ~mod) | (mod * (state == STELA_BUTTON_PRESSED));
#ifdef STELA_EVENT_BUFFER
GET_EVENT_CHUNK(STELA_EVENT_KEY);
ptr[1] = state;
@@ -181,9 +216,20 @@ void stl_window_send_key_event(struct stl_window *window, u8 state, u16 button)
void stl_window_send_key_immediate_event(struct stl_window *window, u8 state, u16 button)
{
- if (window->key_immediate_callback) {
- window->key_immediate_callback(window->userdata, state, button);
+ // Don't send modifiers on key immediate.
+ window->key_immediate_callback(window->userdata, state, button * (stl_key_to_mod(button) == STELA_MOD_NONE));
+}
+
+void stl_window_drop_modifiers(struct stl_window *window)
+{
+ u32 *mods = stl_all_modifiers;
+ while (window->held_modifiers) {
+ u32 mod = *mods++;
+ if (window->held_modifiers & mod) {
+ stl_window_send_key_event(window, STELA_BUTTON_RELEASED, stl_mod_to_key(mod));
+ }
}
+ al_assert(!window->held_modifiers);
}
void stl_window_send_should_close_event(struct stl_window *window)
@@ -230,6 +276,17 @@ bool stl_window_read_events(struct stl_window *window)
window->pointer_pos_callback(window->userdata, x, y);
break;
}
+ case STELA_EVENT_TOUCH: {
+ s32 index = ((s32 *)(&bc[1]))[0];
+ u8 phase = bc[5];
+ f64 x = ((f64 *)(&bc[6]))[0];
+ f64 y = ((f64 *)(&bc[14]))[0];
+#ifdef STELA_PAUSE
+ window->pending_refresh |=
+#endif
+ window->touch_callback(window->userdata, index, phase, x, y);
+ break;
+ }
case STELA_EVENT_SCROLL: {
f64 y = ((f64 *)(&bc[1]))[0];
#ifdef STELA_PAUSE
diff --git a/src/window.h b/src/window.h
index cd4e81f..b1b2425 100644
--- a/src/window.h
+++ b/src/window.h
@@ -22,8 +22,10 @@
#endif
#if defined STELA_API_VULKAN
-#ifdef NAUNET_ON_WINDOWS
+#if defined STELA_WINDOW_WIN32
#define VK_USE_PLATFORM_WIN32_KHR
+#elif defined STELA_WINDOW_WAYLAND
+#define VK_USE_PLATFORM_WAYLAND_KHR
#endif
#include <vulkan/vulkan.h>
#elif defined STELA_API_DX11
@@ -45,6 +47,12 @@ enum {
STELA_WINDOW_WALLPAPER = 1 << 1
};
+enum {
+ STELA_CURSOR_NORMAL = 0,
+ STELA_CURSOR_CROSSHAIR,
+ STELA_CURSOR_HIDDEN
+};
+
#define STELA_MAX_MONITOR_NAME 255u
struct stl_monitor {
@@ -71,6 +79,7 @@ struct stl_window {
void (*resize_internal)(struct stl_window *, u32, u32);
void (*resize)(struct stl_window *, u32, u32);
void (*toggle_fullscreen)(struct stl_window *);
+ void (*set_cursor)(struct stl_window *window, u8);
void (*set_decorations)(struct stl_window *, bool);
#ifdef STELA_POLL_INLINE
bool (*poll)(struct stl_window *, bool);
@@ -82,6 +91,7 @@ struct stl_window {
#ifdef STELA_PAUSE
bool (*should_refresh)(struct stl_window *);
#endif
+ void (*close)(struct stl_window *window);
void (*free)(struct stl_window **);
// Context specific.
#if defined STELA_API_VULKAN
@@ -114,6 +124,8 @@ struct stl_window {
void (*key_immediate_callback)(void *, u8, u16);
void (*should_close_callback)(void *);
void *userdata;
+ bool closed;
+ u32 held_modifiers;
#ifdef STELA_PAUSE
bool pending_refresh;
#endif
@@ -174,6 +186,7 @@ void stl_window_send_scroll_event(struct stl_window *window, f64 y);
void stl_window_send_mouse_button_event(struct stl_window *window, u8 state, u8 button);
void stl_window_send_key_event(struct stl_window *window, u8 state, u16 button);
void stl_window_send_key_immediate_event(struct stl_window *window, u8 state, u16 button);
+void stl_window_drop_modifiers(struct stl_window *window);
void stl_window_send_should_close_event(struct stl_window *window);
#ifdef STELA_EVENT_BUFFER
bool stl_window_read_events(struct stl_window *window);
diff --git a/src/window_glfw.c b/src/window_glfw.c
index b7b3b30..0669bd4 100644
--- a/src/window_glfw.c
+++ b/src/window_glfw.c
@@ -107,6 +107,16 @@ static void cursor_position_callback(GLFWwindow *window, f64 xpos, f64 ypos)
stl_window_send_pointer_pos_event(&glfw->w, xpos, ypos);
}
+// @TODO: Test behavior on x11, Windows?
+static void cursor_enter_callback(GLFWwindow *window, s32 entered)
+{
+ struct stl_window_glfw *glfw = glfwGetWindowUserPointer(window);
+ if (entered) {
+ if (glfwGetKey(glfw->window, GLFW_KEY_LEFT_CONTROL)) {
+ }
+ }
+}
+
static void scroll_callback(GLFWwindow *window, f64 xoffset, f64 yoffset)
{
struct stl_window_glfw *glfw = glfwGetWindowUserPointer(window);
@@ -153,9 +163,9 @@ static void key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action,
log_warn("Unmapped GLFW key pressed (%hu).", (u16)key);
return;
}
- u8 key_state = action == GLFW_PRESS ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
- stl_window_send_key_immediate_event(&glfw->w, key_state, (u16)key);
- stl_window_send_key_event(&glfw->w, key_state, (u16)key);
+ u8 mapped_state = action == GLFW_PRESS ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
+ stl_window_send_key_immediate_event(&glfw->w, mapped_state, (u16)key);
+ stl_window_send_key_event(&glfw->w, mapped_state, (u16)key);
}
static void close_callback(GLFWwindow *window)
@@ -170,8 +180,6 @@ static bool window_glfw_create_window(struct stl_window *window, u32 width, u32
struct stl_window_glfw *glfw = (struct stl_window_glfw *)window;
(void)monitor;
- al_assert(width >= STELA_MIN_WIDTH && height >= STELA_MIN_HEIGHT);
-
#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4)
glfwWindowHintString(GLFW_WAYLAND_APP_ID, name);
#endif
@@ -205,6 +213,7 @@ static bool window_glfw_create_window(struct stl_window *window, u32 width, u32
stl_gl_vers[i].api == STELA_GL_API ? "GL" : "GLES",
stl_gl_vers[i].major, stl_gl_vers[i].minor, stl_gl_vers[i].glsl_ver);
#endif
+ al_assert(width >= STELA_MIN_WIDTH && height >= STELA_MIN_HEIGHT);
glfw->window = glfwCreateWindow(width, height, name, NULL, global.master_window);
#ifdef STELA_API_OPENGL
if (glfw->window) break;
@@ -258,6 +267,7 @@ static bool window_glfw_create_window(struct stl_window *window, u32 width, u32
glfwSetWindowSizeCallback(glfw->window, resize_callback);
glfwSetWindowContentScaleCallback(glfw->window, content_scale_callback);
glfwSetCursorPosCallback(glfw->window, cursor_position_callback);
+ glfwSetCursorEnterCallback(glfw->window, cursor_enter_callback);
glfwSetScrollCallback(glfw->window, scroll_callback);
glfwSetMouseButtonCallback(glfw->window, mouse_button_callback);
glfwSetKeyCallback(glfw->window, key_callback);
diff --git a/src/window_wayland.c b/src/window_wayland.c
index 9479c70..ccf911c 100644
--- a/src/window_wayland.c
+++ b/src/window_wayland.c
@@ -1,12 +1,17 @@
#define AL_LOG_SECTION "wayland"
#include <al/log.h>
#include <al/lib.h>
+#ifdef STELA_API_VULKAN
+#include <dlfcn.h>
+#endif
#include "window_wayland.h"
#include "common.h"
#include "poll_common.h"
#include "keys.h"
+#define FRAME_NO_VALUE -DBL_MAX
+
static struct stl_wayland_global global = { 0 };
static void handle_output_geometry(void *data, struct wl_output *output,
@@ -104,7 +109,7 @@ static void global_add(void *data, struct wl_registry *registry, u32 name,
if (strcmp(interface, wl_output_interface.name) == 0) {
struct stl_monitor_wayland *monitor = al_alloc_object(struct stl_monitor_wayland);
al_array_push(global.monitors, (struct stl_monitor *)monitor);
- monitor->output = wl_registry_bind(registry, name, &wl_output_interface, MIN(4u, version));
+ monitor->output = (struct wl_output *)wl_registry_bind(registry, name, &wl_output_interface, MIN(4u, version));
wl_output_add_listener(monitor->output, &output_listener, monitor);
}
}
@@ -266,7 +271,11 @@ bool stl_global_init(bool skip_graphics)
global.display_fd = wl_display_get_fd(global.display);
if (!skip_graphics) {
-#ifdef STELA_API_OPENGL
+#if defined STELA_API_VULKAN
+ global.vk_extensions = (const char **)al_calloc(2, sizeof(const char *const *));
+ global.vk_extensions[0] = "VK_KHR_surface";
+ global.vk_extensions[1] = "VK_KHR_wayland_surface";
+#elif defined STELA_API_OPENGL
if (!stl_maybe_load_egl()) return false;
global.egl.display = eglGetDisplay((EGLNativeDisplayType)global.display);
if (!global.egl.display) {
@@ -276,14 +285,14 @@ bool stl_global_init(bool skip_graphics)
if (!stl_load_egl(&global.egl)) return false;
#endif
} else {
-#ifdef STELA_API_OPENGL
// So global_close() knows we skipped graphics.
+#if defined STELA_API_VULKAN
+ global.vk_extensions = NULL;
+#elif defined STELA_API_OPENGL
global.egl.display = EGL_NO_DISPLAY;
#endif
}
- global.active_surface = NULL;
-
al_array_init(global.monitors);
global.registry = wl_display_get_registry(global.display);
wl_registry_add_listener(global.registry, &registry_listener, NULL);
@@ -323,7 +332,9 @@ s32 stl_swallow_main(u32 argc, str *argv, s32 (*main)(u32, str *, void *), void
void stl_global_close(void)
{
if (!global.display) return;
-#ifdef STELA_API_OPENGL
+#if defined STELA_API_VULKAN
+ if (global.vk_extensions) al_free(global.vk_extensions);
+#elif defined STELA_API_OPENGL
stl_terminate_egl(&global.egl);
#endif
for (u32 i = 0; i < global.monitors.count; i++) {
@@ -345,28 +356,50 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
.ping = xdg_wm_base_ping
};
+static void set_cursor_internal(struct stl_window_wayland *wl, u8 shape)
+{
+ switch (shape) {
+ case STELA_CURSOR_NORMAL:
+ if (wl->cursor_shape_device) {
+ wp_cursor_shape_device_v1_set_shape(wl->cursor_shape_device,
+ wl->cursor.serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
+ }
+ break;
+ case STELA_CURSOR_CROSSHAIR:
+ if (wl->cursor_shape_device) {
+ wp_cursor_shape_device_v1_set_shape(wl->cursor_shape_device,
+ wl->cursor.serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR);
+ }
+ break;
+ case STELA_CURSOR_HIDDEN:
+ wl_pointer_set_cursor(wl->cursor.pointer, wl->cursor.serial, NULL, 0, 0);
+ break;
+ }
+}
+
static void pointer_enter(void *data, struct wl_pointer *pointer,
u32 serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y)
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
- (void)pointer;
- global.active_surface = surface;
- // @TODO: Hide cursor.
- //wl_pointer_set_cursor(wl->pointer, serial, NULL, 0, 0);
- if (wl->cursor_shape_device) {
- wp_cursor_shape_device_v1_set_shape(wl->cursor_shape_device, serial, WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
- }
- wl->pointer_x = wl_fixed_to_double(surface_x) * wl->w.scale;
- wl->pointer_y = wl_fixed_to_double(surface_y) * wl->w.scale;
+ (void)surface;
+ al_assert(pointer == wl->pointer);
+ wl->cursor.serial = serial;
+ wl->cursor.pointer = pointer;
+ set_cursor_internal(wl, wl->cursor_shape);
+ wl->frame.pointer_x = wl_fixed_to_double(surface_x) * wl->w.scale;
+ wl->frame.pointer_y = wl_fixed_to_double(surface_y) * wl->w.scale;
}
static void pointer_leave(void *data, struct wl_pointer *pointer,
u32 serial, struct wl_surface *surface)
{
- (void)data;
- (void)pointer;
- (void)serial;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
(void)surface;
+ al_assert(pointer == wl->pointer);
+ wl->cursor.serial = serial;
+ wl->cursor.pointer = NULL;
+ wl->frame.pointer_x = FRAME_NO_VALUE;
+ wl->frame.pointer_y = FRAME_NO_VALUE;
}
static void pointer_motion(void *data, struct wl_pointer *pointer,
@@ -375,13 +408,8 @@ static void pointer_motion(void *data, struct wl_pointer *pointer,
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
(void)pointer;
(void)time;
-
- if (wl->surface != global.active_surface) {
- return;
- }
-
- wl->pointer_x += wl_fixed_to_double(surface_x) * wl->w.scale;
- wl->pointer_y += wl_fixed_to_double(surface_y) * wl->w.scale;
+ wl->frame.pointer_x = wl_fixed_to_double(surface_x) * wl->w.scale;
+ wl->frame.pointer_y = wl_fixed_to_double(surface_y) * wl->w.scale;
}
static void pointer_button(void *data, struct wl_pointer *pointer,
@@ -390,11 +418,6 @@ static void pointer_button(void *data, struct wl_pointer *pointer,
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
(void)pointer;
(void)time;
-
- if (wl->surface != global.active_surface) {
- return;
- }
-
#define MOUSE1 0x110
#define MOUSE2 0x111
#define MOUSE3 0x112
@@ -433,15 +456,12 @@ static void pointer_axis(void *data, struct wl_pointer *pointer,
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
(void)pointer;
(void)time;
-
- if (wl->surface != global.active_surface) {
- return;
- }
-
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
- wl->scroll_y += wl_fixed_to_double(value);
+ if (wl->frame.scroll_y == FRAME_NO_VALUE) wl->frame.scroll_y = 0.0;
+ wl->frame.scroll_y += wl_fixed_to_double(value);
} else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
- wl->scroll_x += wl_fixed_to_double(value);
+ if (wl->frame.scroll_x == FRAME_NO_VALUE) wl->frame.scroll_x = 0.0;
+ wl->frame.scroll_x += wl_fixed_to_double(value);
}
}
@@ -449,20 +469,16 @@ static void pointer_frame(void *data, struct wl_pointer *pointer)
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
(void)pointer;
-
- if (wl->surface != global.active_surface) {
- return;
+ if (wl->frame.pointer_x != FRAME_NO_VALUE && wl->frame.pointer_y != FRAME_NO_VALUE) {
+ stl_window_send_pointer_pos_event(&wl->w, wl->frame.pointer_x, wl->frame.pointer_y);
+ wl->frame.pointer_x = FRAME_NO_VALUE;
+ wl->frame.pointer_y = FRAME_NO_VALUE;
}
-
- if (wl->pointer_x || wl->pointer_y) {
- stl_window_send_pointer_pos_event(&wl->w, wl->pointer_x, wl->pointer_y);
- wl->pointer_x = 0.0;
- wl->pointer_y = 0.0;
- }
-
- if (wl->scroll_y) {
- stl_window_send_scroll_event(&wl->w, wl->scroll_y);
- wl->scroll_y = 0.0;
+ // If multiple pointer_axis() events can happen within a frame and
+ // resolve to 0.0, this will send a scroll event with y = 0.0.
+ if (wl->frame.scroll_y != FRAME_NO_VALUE) {
+ stl_window_send_scroll_event(&wl->w, wl->frame.scroll_y);
+ wl->frame.scroll_y = FRAME_NO_VALUE;
}
}
@@ -539,37 +555,43 @@ static void keyboard_keymap(void *data, struct wl_keyboard *keyboard,
static void keyboard_enter(void *data, struct wl_keyboard *keyboard,
u32 serial, struct wl_surface *surface, struct wl_array *keys)
{
- (void)data;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
(void)keyboard;
(void)serial;
- (void)keys;
- global.active_surface = surface;
+ (void)surface;
+ void *key;
+ wl_array_for_each(key, keys) {
+ u16 mapped_key = global.keycodes[*(u32 *)key];
+ u32 mod = stl_key_to_mod(mapped_key);
+ if (mod == STELA_MOD_NONE) continue;
+ stl_window_send_key_event(&wl->w, STELA_BUTTON_PRESSED, mapped_key);
+ }
}
static void keyboard_leave(void *data, struct wl_keyboard *keyboard,
u32 serial, struct wl_surface *surface)
{
- (void)data;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
(void)keyboard;
(void)serial;
(void)surface;
+ stl_window_drop_modifiers(&wl->w);
}
+#define WL_TO_STELA_STATE(state) \
+ ((state == WL_KEYBOARD_KEY_STATE_PRESSED) ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED)
+
static void keyboard_key(void *data, struct wl_keyboard *keyboard,
u32 serial, u32 time, u32 key, u32 state)
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
- (void)serial;
(void)keyboard;
+ (void)serial;
(void)time;
-
- if (wl->surface != global.active_surface) {
- return;
- }
-
- u8 key_state = state == WL_KEYBOARD_KEY_STATE_PRESSED ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
- stl_window_send_key_immediate_event(&wl->w, key_state, global.keycodes[key]);
- stl_window_send_key_event(&wl->w, key_state, global.keycodes[key]);
+ u8 mapped_state = WL_TO_STELA_STATE(state);
+ u16 mapped_key = global.keycodes[key];
+ stl_window_send_key_immediate_event(&wl->w, mapped_state, mapped_key);
+ stl_window_send_key_event(&wl->w, mapped_state, mapped_key);
}
static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard,
@@ -609,8 +631,8 @@ static void seat_capabilities(void *data, struct wl_seat *seat, u32 capabilities
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
if (!wl->pointer) {
wl->pointer = wl_seat_get_pointer(wl->seat);
- if (wl->cursor_shape) {
- wl->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(wl->cursor_shape, wl->pointer);
+ if (wl->cursor_shape_manager) {
+ wl->cursor_shape_device = wp_cursor_shape_manager_v1_get_pointer(wl->cursor_shape_manager, wl->pointer);
}
wl_pointer_add_listener(wl->pointer, &pointer_listener, wl);
}
@@ -651,19 +673,19 @@ static void context_global_add(void *data, struct wl_registry *registry, u32 nam
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
if (strcmp(interface, wl_compositor_interface.name) == 0) {
- wl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, MIN(5u, version));
+ wl->compositor = (struct wl_compositor *)wl_registry_bind(registry, name, &wl_compositor_interface, MIN(5u, version));
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
- wl->seat = wl_registry_bind(registry, name, &wl_seat_interface, MIN(8u, version));
+ wl->seat = (struct wl_seat *)wl_registry_bind(registry, name, &wl_seat_interface, MIN(8u, version));
wl_seat_add_listener(wl->seat, &seat_listener, wl);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
- wl->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, MIN(2u, version));
+ wl->xdg_wm_base = (struct xdg_wm_base *)wl_registry_bind(registry, name, &xdg_wm_base_interface, MIN(2u, version));
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, wl);
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
- wl->zxdg_decoration_manager = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, MIN(1u, version));
+ wl->zxdg_decoration_manager = (struct zxdg_decoration_manager_v1 *)wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, MIN(1u, version));
} else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
- wl->cursor_shape = wl_registry_bind(registry, name, &wp_cursor_shape_manager_v1_interface, MIN(1u, version));
+ wl->cursor_shape_manager = (struct wp_cursor_shape_manager_v1 *)wl_registry_bind(registry, name, &wp_cursor_shape_manager_v1_interface, MIN(1u, version));
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
- wl->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, MIN(4u, version));
+ wl->layer_shell = (struct zwlr_layer_shell_v1 *)wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, MIN(4u, version));
}
}
@@ -733,6 +755,8 @@ static bool check_and_set_dimensions(struct stl_window_wayland *wl, s32 event_wi
wl->w.width = width;
wl->w.height = height;
+ wl->geom_width = event_width;
+ wl->geom_height = event_height;
return true;
}
@@ -745,15 +769,16 @@ static void send_resize_event(struct stl_window_wayland *wl)
static void handle_xdg_surface_configure(void *data, struct xdg_surface *surface, u32 serial)
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
- if (wl->pending) {
+ al_assert(surface == wl->xdg_surface);
+ if (wl->pending_configure) {
send_resize_event(wl);
- wl->pending = false;
+ wl->pending_configure = false;
}
- xdg_surface_ack_configure(surface, serial);
+ xdg_surface_ack_configure(wl->xdg_surface, serial);
}
static const struct xdg_surface_listener xdg_surface_listener = {
- .configure = handle_xdg_surface_configure,
+ .configure = handle_xdg_surface_configure
};
static void handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *toplevel,
@@ -765,8 +790,10 @@ static void handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *tople
// It is expected that the first toplevel_configure() will give 0x0 dimensions.
// We set the window dimensions with wl_egl_window_create() and the subsequent
// toplevel_configure() event will come after the first swap_buffers().
+ al_assert(!wl->pending_configure);
if (check_and_set_dimensions(wl, width, height)) {
- wl->pending = true;
+ xdg_surface_set_window_geometry(wl->xdg_surface, 0, 0, wl->geom_width, wl->geom_height);
+ wl->pending_configure = true;
}
}
@@ -804,10 +831,11 @@ static void handle_layer_surface_configure(void *data, struct zwlr_layer_surface
u32 serial, u32 width, u32 height)
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
+ al_assert(surface == wl->layer_surface);
if (check_and_set_dimensions(wl, width, height)) {
send_resize_event(wl);
}
- zwlr_layer_surface_v1_ack_configure(surface, serial);
+ zwlr_layer_surface_v1_ack_configure(wl->layer_surface, serial);
}
static void handle_layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface)
@@ -880,10 +908,11 @@ static bool create_toplevel(struct stl_window_wayland *wl, s32 scale, const char
{
wl->w.scale = scale;
wl_surface_set_buffer_scale(wl->surface, scale);
- //s32 geom_width = wl->w.width, geom_height = wl->w.height;
+ wl->geom_width = wl->w.width;
+ wl->geom_height = wl->w.height;
wl->w.width *= scale;
wl->w.height *= scale;
- wl->pending = true;
+ wl->pending_configure = false;
wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_wm_base, wl->surface);
if (!wl->xdg_surface) {
@@ -903,8 +932,13 @@ static bool create_toplevel(struct stl_window_wayland *wl, s32 scale, const char
// If xdg_surface_set_window_geometry() is never set, it will be equal to the
// entire surface and update on every commit. If we do set it, we need to manually
// update it on any resize. Also, it's meant to be set to the unscaled width and height.
- //xdg_surface_set_window_geometry(wl->xdg_surface, 0, 0, geom_width, geom_height);
+ // For our application, if using the event buffer, window events and rendering are
+ // on seperate threads. This means we have to manually control xdg surface geometry
+ // for responsiveness. Though there's currently a bug where toggling fullscreen
+ // misaligns the surface position within the window.
+ xdg_surface_set_window_geometry(wl->xdg_surface, 0, 0, wl->geom_width, wl->geom_height);
+ wl->cursor_shape = STELA_CURSOR_NORMAL;
if (wl->zxdg_decoration_manager) {
wl->zxdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
wl->zxdg_decoration_manager, wl->xdg_toplevel);
@@ -935,6 +969,7 @@ static bool create_layer_shell(struct stl_window_wayland *wl, struct stl_monitor
zwlr_layer_surface_v1_set_size(wl->layer_surface, wl->w.width, wl->w.height);
zwlr_layer_surface_v1_set_anchor(wl->layer_surface, anchor);
zwlr_layer_surface_v1_add_listener(wl->layer_surface, &layer_surface_listener, wl);
+ wl->cursor_shape = STELA_CURSOR_HIDDEN;
wl->decorated = false;
return true;
}
@@ -944,19 +979,21 @@ static bool window_wayland_create_window(struct stl_window *window, u32 width, u
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
- al_assert(width >= STELA_MIN_WIDTH && height >= STELA_MIN_HEIGHT);
-
if (!create_context_internal(wl)) {
goto err;
}
wl->surface = wl_compositor_create_surface(wl->compositor);
- if (!wl->surface) goto err;
+ if (!wl->surface) {
+ log_error("Failed to create surface.");
+ goto err;
+ }
wl_surface_add_listener(wl->surface, &surface_listener, wl);
+ al_assert(width >= STELA_MIN_WIDTH && height >= STELA_MIN_HEIGHT);
stl_window_created(&wl->w, width, height, flags);
- // @TODO: Test multi-monitor with 1 scaled.
+ // @TODO: Test multi-monitor with only 1 scaled.
struct stl_monitor_wayland *selected = NULL;
if (!(flags & STELA_WINDOW_WALLPAPER)) {
selected = (struct stl_monitor_wayland *)al_array_at(global.monitors, 0);
@@ -973,22 +1010,23 @@ static bool window_wayland_create_window(struct stl_window *window, u32 width, u
}
#ifdef STELA_API_OPENGL
- if (!wl_egl_init(wl)) goto err;
-#endif
-
- wl_surface_commit(wl->surface);
- wl_display_roundtrip(wl->display);
-
-#ifdef STELA_API_OPENGL
+ if (!wl_egl_init(wl)) {
+ goto err;
+ }
stl_egl_make_current(&wl->egl);
stl_egl_swap_interval(&wl->egl, flags & STELA_WINDOW_VSYNC ? 1 : 0);
stl_egl_release_current(&wl->egl);
#endif
- wl->pointer_x = 0.0;
- wl->pointer_y = 0.0;
- wl->scroll_x = 0.0;
- wl->scroll_y = 0.0;
+ wl->cursor.serial = 0;
+ wl->cursor.pointer = NULL;
+ wl->frame.pointer_x = FRAME_NO_VALUE;
+ wl->frame.pointer_y = FRAME_NO_VALUE;
+ wl->frame.scroll_x = FRAME_NO_VALUE;
+ wl->frame.scroll_y = FRAME_NO_VALUE;
+
+ wl_surface_commit(wl->surface);
+ wl_display_roundtrip(wl->display);
return true;
err:
@@ -1009,11 +1047,6 @@ static void window_wayland_resize_internal(struct stl_window *window, u32 width,
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
#ifdef STELA_API_OPENGL
- //if (wl->xdg_surface) {
- // u32 scale = wl->w.scale;
- // s32 geom_width = wl->w.width / scale, geom_height = wl->w.height / scale;
- // xdg_surface_set_window_geometry(wl->xdg_surface, 0, 0, geom_width, geom_height);
- //}
if (stl_egl_is_ready(&wl->egl)) {
wl_egl_window_resize(wl->egl_window, width, height, 0, 0);
}
@@ -1027,12 +1060,21 @@ static void window_wayland_resize_internal(struct stl_window *window, u32 width,
static void window_wayland_toggle_fullscreen(struct stl_window *window)
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
- if (!wl->w.fullscreen) {
+ wl->w.fullscreen = !wl->w.fullscreen;
+ if (wl->w.fullscreen) {
xdg_toplevel_set_fullscreen(wl->xdg_toplevel, NULL);
} else {
xdg_toplevel_unset_fullscreen(wl->xdg_toplevel);
}
- wl->w.fullscreen = !wl->w.fullscreen;
+}
+
+static void window_wayland_set_cursor(struct stl_window *window, u8 shape)
+{
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
+ wl->cursor_shape = shape;
+ if (wl->cursor.pointer) {
+ set_cursor_internal(wl, wl->cursor_shape);
+ }
}
static void window_wayland_set_decorations(struct stl_window *window, bool enabled)
@@ -1055,7 +1097,7 @@ static bool window_wayland_poll(struct stl_window *window, bool block)
{
struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
display_prepare_read(wl->display);
- return stl_poll_common(wl->fds, block);
+ return stl_poll_common(&wl->w, wl->fds, block);
}
#ifdef STELA_PAUSE
@@ -1110,7 +1152,7 @@ void window_wayland_free(struct stl_window **window)
if (wl->registry) wl_registry_destroy(wl->registry);
if (wl->seat) wl_seat_destroy(wl->seat);
if (wl->pointer) wl_pointer_destroy(wl->pointer);
- if (wl->cursor_shape) wp_cursor_shape_manager_v1_destroy(wl->cursor_shape);
+ if (wl->cursor_shape_manager) wp_cursor_shape_manager_v1_destroy(wl->cursor_shape_manager);
if (wl->cursor_shape_device) wp_cursor_shape_device_v1_destroy(wl->cursor_shape_device);
if (wl->keyboard) wl_keyboard_destroy(wl->keyboard);
destroy_surface_internal(wl);
@@ -1128,34 +1170,53 @@ void window_wayland_free(struct stl_window **window)
#if defined STELA_API_VULKAN
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL get_vk_proc_address(VkInstance inst, const char *name)
{
- void *vk_module = dlopen("libvulkan.so.1", RTLD_LAZY | RTLD_LOCAL);
- if (!vk_module) return NULL;
- PFN_vkVoidFunction vk_get_proc_addr = dlsym(vk_module, "vkGetInstanceProcAddr");
+ const char *vk_lib = "libvulkan.so.1";
+ void *vk_module = dlopen(vk_lib, RTLD_LAZY | RTLD_LOCAL);
+ if (!vk_module) {
+ log_error("Failed to load %s.", vk_lib);
+ return NULL;
+ }
+ PFN_vkGetInstanceProcAddr vk_get_proc_addr = (PFN_vkGetInstanceProcAddr)dlsym(vk_module, "vkGetInstanceProcAddr");
+ dlclose(vk_module);
if (!vk_get_proc_addr) return NULL;
return (PFN_vkVoidFunction)vk_get_proc_addr(inst, name);
}
-static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL window_win32_get_vk_proc_address(VkInstance inst, const char *name)
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL window_wayland_get_vk_proc_address(VkInstance inst, const char *name)
{
if (strcmp(name, "vkGetInstanceProcAddr") == 0) {
- return (PFN_vkVoidFunction)vk_get_proc_addr;
+ return (PFN_vkVoidFunction)get_vk_proc_address;
}
- return vk_get_proc_addr(inst, name);
+ return get_vk_proc_address(inst, name);
}
-static VkResult window_win32_vk_create_surface(void *window, VkInstance inst, VkSurfaceKHR *surface)
+static VkResult window_wayland_vk_create_surface(void *window, VkInstance inst, VkSurfaceKHR *surface)
{
- struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
- (void)w32;
- (void)inst;
- (void)surface;
- return VK_ERROR_EXTENSION_NOT_PRESENT;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
+
+ VkWaylandSurfaceCreateInfoKHR info = {
+ .sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
+ .display = wl->display,
+ .surface = wl->surface
+ };
+
+ PFN_vkCreateWaylandSurfaceKHR CreateWaylandSurfaceKHR;
+ CreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR)get_vk_proc_address(inst, "vkCreateWaylandSurfaceKHR");
+ if (!CreateWaylandSurfaceKHR) {
+ return VK_ERROR_EXTENSION_NOT_PRESENT;
+ }
+
+ VkResult ret = vkCreateWaylandSurfaceKHR(inst, &info, NULL, surface);
+ if (ret != VK_SUCCESS) {
+ log_error("Failed to create Wayland Vulkan surface.");
+ }
+ return ret;
}
-static const char *const *window_win32_vk_get_extensions(u32 *num)
+static const char *const *window_wayland_vk_get_extensions(u32 *num)
{
- (void)num;
- return NULL;
+ *num = 2;
+ return global.vk_extensions;
}
#elif defined STELA_API_OPENGL
static bool window_wayland_gl_loader_load(void *window)
@@ -1193,6 +1254,7 @@ struct stl_window *stl_window_create(void)
wl->w.resize = window_wayland_resize;
wl->w.resize_internal = window_wayland_resize_internal;
wl->w.toggle_fullscreen = window_wayland_toggle_fullscreen;
+ wl->w.set_cursor = window_wayland_set_cursor;
wl->w.set_decorations = window_wayland_set_decorations;
#ifdef STELA_POLL_INLINE
wl->w.poll = window_wayland_poll;
@@ -1205,7 +1267,11 @@ struct stl_window *stl_window_create(void)
wl->w.should_refresh = window_wayland_should_refresh;
#endif
wl->w.free = window_wayland_free;
-#ifdef STELA_API_OPENGL
+#if defined STELA_API_VULKAN
+ wl->w.get_vk_proc_address = window_wayland_get_vk_proc_address;
+ wl->w.vk_create_surface = window_wayland_vk_create_surface;
+ wl->w.vk_get_extensions = window_wayland_vk_get_extensions;
+#elif defined STELA_API_OPENGL
wl->w.get_gl_proc_address = NULL;
wl->w.gl_loader_load = window_wayland_gl_loader_load;
wl->w.gl_make_current = window_wayland_gl_make_current;
diff --git a/src/window_wayland.h b/src/window_wayland.h
index e3b2c32..89643d5 100644
--- a/src/window_wayland.h
+++ b/src/window_wayland.h
@@ -25,9 +25,12 @@ struct stl_wayland_global {
s32 display_fd;
struct wl_registry *registry;
u16 keycodes[STELA_WAYLAND_KEY_MAX];
- struct wl_surface *active_surface;
array(struct stl_monitor *) monitors;
+#if defined STELA_API_VULKAN
+ const char **vk_extensions;
+#elif defined STELA_API_OPENGL
struct stl_egl_global egl;
+#endif
void (*output_discovered)(void *, struct stl_monitor *);
void *userdata;
};
@@ -51,7 +54,7 @@ struct stl_window_wayland {
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_decoration_manager_v1 *zxdg_decoration_manager;
- struct wp_cursor_shape_manager_v1 *cursor_shape;
+ struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
struct wp_cursor_shape_device_v1 *cursor_shape_device;
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration;
struct zwlr_layer_shell_v1 *layer_shell;
@@ -64,12 +67,19 @@ struct stl_window_wayland {
#else
struct nn_pollfd fds[1];
#endif
+ bool pending_configure;
+ u8 cursor_shape;
+ struct {
+ u32 serial;
+ struct wl_pointer *pointer;
+ } cursor;
bool decorated;
- bool pending;
- f64 pointer_x;
- f64 pointer_y;
- f64 last_pointer_x;
- f64 last_pointer_y;
- f64 scroll_x;
- f64 scroll_y;
+ s32 geom_width;
+ s32 geom_height;
+ struct {
+ f64 pointer_x;
+ f64 pointer_y;
+ f64 scroll_x;
+ f64 scroll_y;
+ } frame;
};
diff --git a/src/window_win32.c b/src/window_win32.c
index f4baca8..6bb4945 100644
--- a/src/window_win32.c
+++ b/src/window_win32.c
@@ -58,11 +58,15 @@ struct the_baby {
LPVOID lpParam;
};
+// Service messages.
#define CREATE_DANGEROUS_WINDOW (WM_USER + 0x1337)
#define DESTROY_DANGEROUS_WINDOW (WM_USER + 0x1338)
#define WAKE_WINDOW (WM_USER + 0x1339)
#define EXIT_PROCESS (WM_USER + 0x133a)
+// Window messages.
+#define SET_CURSOR (WM_USER + 0x133b)
+
static DWORD MainThreadID;
static struct stl_win32_global Global = { 0 };
@@ -230,12 +234,12 @@ static void set_keycodes_for_win32(void)
Global.keycodes[VK_OEM_PLUS] = STELA_KEY_KP_EQUAL;
Global.keycodes[VK_RETURN] = STELA_KEY_KP_RETURN;
- Global.keycodes[VK_CONTROL] = STELA_KEY_LEFT_CONTROL;
- Global.keycodes[VK_MENU] = STELA_KEY_LEFT_ALT;
- Global.keycodes[VK_SHIFT] = STELA_KEY_LEFT_SHIFT;
- Global.keycodes[VK_CONTROL] = STELA_KEY_RIGHT_CONTROL;
- Global.keycodes[VK_MENU] = STELA_KEY_RIGHT_ALT;
- Global.keycodes[VK_SHIFT] = STELA_KEY_RIGHT_SHIFT;
+ Global.keycodes[VK_LCONTROL] = STELA_KEY_LEFT_CONTROL;
+ Global.keycodes[VK_LMENU] = STELA_KEY_LEFT_ALT;
+ Global.keycodes[VK_LSHIFT] = STELA_KEY_LEFT_SHIFT;
+ Global.keycodes[VK_RCONTROL] = STELA_KEY_RIGHT_CONTROL;
+ Global.keycodes[VK_RMENU] = STELA_KEY_RIGHT_ALT;
+ Global.keycodes[VK_RSHIFT] = STELA_KEY_RIGHT_SHIFT;
Global.keycodes[VK_LWIN] = STELA_KEY_LEFT_SUPER;
Global.keycodes[VK_RWIN] = STELA_KEY_RIGHT_SUPER;
}
@@ -280,6 +284,9 @@ bool stl_global_init(bool skip_graphics)
al_array_init(Global.monitors);
EnumDisplayMonitors(NULL, NULL, MonitorInfoProc, 0);
+ Global.Cursors[STELA_CURSOR_NORMAL] = LoadCursor(NULL, IDC_ARROW);
+ Global.Cursors[STELA_CURSOR_CROSSHAIR] = LoadCursor(NULL, IDC_CROSS);
+
set_keycodes_for_win32();
return true;
@@ -625,6 +632,9 @@ static const char *WndMessageString(UINT Message)
}
#endif
+#define ASYNC_KEY_IS_DOWN(VK) \
+ ((GetAsyncKeyState(VK) & 0x8000) != 0)
+
#define MESSAGE_TO_KEY_STATE(Message) \
((Message == WM_KEYDOWN || Message == WM_SYSKEYDOWN) ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED)
@@ -660,20 +670,10 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam,
case WM_LBUTTONDOWN: {
// https://learn.microsoft.com/en-us/windows/win32/learnwin32/mouse-movement
SetCapture(w32->Window);
- //SetClassLongPtr(w32->Window, GCLP_HCURSOR, (LONG_PTR)HCursor);
- //SystemParametersInfo(SPI_SETCURSORS, 0, NULL, 0); // Reset system cursor state.
- HCURSOR HCursor = LoadCursor(NULL, IDC_CROSS);
- SetCursor(HCursor);
- ShowCursor(TRUE);
goto intercept;
}
case WM_LBUTTONUP: {
ReleaseCapture();
- HCURSOR HCursor = LoadCursor(NULL, IDC_ARROW);
- SetCursor(HCursor);
- if (w32->w.fullscreen) {
- while (ShowCursor(FALSE) >= 0) {}
- }
goto intercept;
}
case WM_RBUTTONDOWN:
@@ -735,14 +735,56 @@ static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam,
}
case WM_MOUSEWHEEL:
goto intercept;
+ case WM_ACTIVATE: {
+ // Do not use Wine behavior as a reference, it's too broken.
+ switch (LOWORD(WParam)) {
+ case WA_ACTIVE:
+ case WA_CLICKACTIVE: {
+ LParam = STELA_MOD_LCONTROL * ASYNC_KEY_IS_DOWN(VK_LCONTROL) |
+ STELA_MOD_RCONTROL * ASYNC_KEY_IS_DOWN(VK_RCONTROL) |
+ STELA_MOD_LSHIFT * ASYNC_KEY_IS_DOWN(VK_LSHIFT) |
+ STELA_MOD_RSHIFT * ASYNC_KEY_IS_DOWN(VK_RSHIFT) |
+ STELA_MOD_LALT * ASYNC_KEY_IS_DOWN(VK_LMENU) |
+ STELA_MOD_RALT * ASYNC_KEY_IS_DOWN(VK_RMENU);
+ break;
+ }
+ case WA_INACTIVE: {
+ break;
+ }
+ }
+ goto intercept;
+ }
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
- case WM_SYSKEYUP:
+ case WM_SYSKEYUP: {
+ // https://learn.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
+ WORD Flags = HIWORD(LParam);
+ bool PreviouslyDown = (Flags & KF_REPEAT) == KF_REPEAT;
+ if (((Message == WM_KEYDOWN || Message == WM_SYSKEYDOWN) && PreviouslyDown) ||
+ ((Message == WM_KEYUP || Message == WM_SYSKEYUP) && !PreviouslyDown)) {
+ break; // Repeat.
+ }
+ WORD Scancode = LOBYTE(Flags);
+ bool Extended = (LParam & KF_EXTENDED) == KF_EXTENDED;
+ if (Extended) Scancode = MAKEWORD(Scancode, 0xE0);
+ switch (WParam) {
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ WParam = LOWORD(MapVirtualKey(Scancode, MAPVK_VSC_TO_VK_EX));
+ break;
+ default:
+ break;
+ }
stl_window_send_key_immediate_event(&w32->w, MESSAGE_TO_KEY_STATE(Message), Global.keycodes[WParam]);
goto intercept;
+ }
case WM_SIZE:
goto intercept;
+ case SET_CURSOR:
+ SetCursor(Global.Cursors[WParam]);
+ return 0;
default:
break;
}
@@ -970,7 +1012,6 @@ static void window_win32_toggle_fullscreen(struct stl_window *window)
w32->Prev.Width = Prev.right - Prev.left;
w32->Prev.Height = Prev.bottom - Prev.top;
set_window_decorations(w32, false);
- while (ShowCursor(FALSE) >= 0) {}
SetWindowPos(w32->Window, HWND_TOPMOST, Monitor->X, Monitor->Y, (s32)Monitor->M.width, (s32)Monitor->M.height, SWP_FRAMECHANGED);
} else {
struct stl_monitor_win32 *Monitor = w32->Prev.Monitor;
@@ -979,7 +1020,6 @@ static void window_win32_toggle_fullscreen(struct stl_window *window)
if (w32->UserDecorated) {
set_window_decorations(w32, true);
}
- ShowCursor(TRUE);
SetWindowPos(w32->Window, HWND_NOTOPMOST, X, Y, w32->Prev.Width, w32->Prev.Height, SWP_FRAMECHANGED);
}
w32->w.fullscreen = !w32->w.fullscreen;
@@ -996,6 +1036,34 @@ static void window_win32_set_decorations(struct stl_window *window, bool enabled
}
}
+static void SetCursorInternal(struct stl_window_win32 *w32, u8 Shape)
+{
+ //SetClassLongPtr(w32->Window, GCLP_HCURSOR, (LONG_PTR)HCursor);
+ //SystemParametersInfo(SPI_SETCURSORS, 0, NULL, 0); // Reset system cursor state.
+ switch (Shape) {
+ case STELA_CURSOR_NORMAL: {
+ PostMessageW(w32->Window, SET_CURSOR, STELA_CURSOR_NORMAL, 0);
+ ShowCursor(TRUE);
+ break;
+ }
+ case STELA_CURSOR_CROSSHAIR: {
+ PostMessageW(w32->Window, SET_CURSOR, STELA_CURSOR_CROSSHAIR, 0);
+ ShowCursor(TRUE);
+ break;
+ }
+ case STELA_CURSOR_HIDDEN: {
+ while (ShowCursor(FALSE) >= 0) {}
+ break;
+ }
+ }
+}
+
+static void window_win32_set_cursor(struct stl_window *window, u8 shape)
+{
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
+ SetCursorInternal(w32, shape);
+}
+
static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message)
{
switch (Message->message) {
@@ -1064,19 +1132,32 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message)
case WM_MOUSEWHEEL:
stl_window_send_scroll_event(&w32->w, -(f64)GET_WHEEL_DELTA_WPARAM(Message->wParam));
break;
+ case WM_ACTIVATE: {
+ switch (LOWORD(Message->wParam)) {
+ case WA_ACTIVE:
+ case WA_CLICKACTIVE: {
+ u32 *mods = stl_all_modifiers;
+ u32 mod;
+ while ((mod = *mods++) != STELA_MOD_NONE) {
+ if (Message->lParam & mod) {
+ stl_window_send_key_event(&w32->w, STELA_BUTTON_PRESSED, stl_mod_to_key(mod));
+ }
+ }
+ break;
+ }
+ case WA_INACTIVE: {
+ stl_window_drop_modifiers(&w32->w);
+ break;
+ }
+ }
+ break;
+ }
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
- case WM_SYSKEYUP: {
- /* https://github.com/glfw/glfw/blob/e7ea71be039836da3a98cea55ae5569cb5eb885c/src/win32_window.c#L717C11-L721C75
- s32 scancode = HIWORD(Message->lParam) & (KF_EXTENDED | 0xff);
- if (!scancode) {
- scancode = MapVirtualKeyW((UINT)Message->wParam, MAPVK_VK_TO_VSC);
- }
- */
+ case WM_SYSKEYUP:
stl_window_send_key_event(&w32->w, MESSAGE_TO_KEY_STATE(Message->message), Global.keycodes[Message->wParam]);
break;
- }
case WM_SIZE:
w32->w.width = LOWORD(Message->lParam);
w32->w.height = HIWORD(Message->lParam);
@@ -1142,8 +1223,14 @@ static void window_win32_free(struct stl_window **window)
#if defined STELA_API_VULKAN
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetVKProcAddress(VkInstance Inst, const char *Name)
{
- HMODULE VKModule = LoadLibraryA("vulkan-1.dll");
+ const char *VKLib = "vulkan-1.dll";
+ HMODULE VKModule = LoadLibraryA(VKLib);
+ if (!VKModule) {
+ log_error("Failed to load %s (0x%lx).", VKLib, GetLastError());
+ return NULL;
+ }
PFN_vkGetInstanceProcAddr GetInstanceProcAddr = (void *)GetProcAddress(VKModule, "vkGetInstanceProcAddr");
+ if (!GetInstanceProcAddr) return NULL;
return (PFN_vkVoidFunction)GetInstanceProcAddr(Inst, Name);
}
@@ -1166,13 +1253,17 @@ static VkResult window_win32_vk_create_surface(void *window, VkInstance inst, Vk
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
- VkWin32SurfaceCreateInfoKHR info = { 0 };
- info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
- info.hinstance = Global.ModuleHandle;
- info.hwnd = w32->Window;
+ VkWin32SurfaceCreateInfoKHR info = {
+ .sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
+ .hinstance = Global.ModuleHandle,
+ .hwnd = w32->Window
+ };
- VkResult err = CreateWin32SurfaceKHR(inst, &info, NULL, surface);
- return err;
+ VkResult ret = CreateWin32SurfaceKHR(inst, &info, NULL, surface);
+ if (ret != VK_SUCCESS) {
+ log_error("Failed to create Win32 Vulkan surface.");
+ }
+ return ret;
}
static const char *const *window_win32_vk_get_extensions(u32 *num)
@@ -1225,6 +1316,7 @@ struct stl_window *stl_window_create(void)
w32->w.resize = window_win32_resize;
w32->w.toggle_fullscreen = window_win32_toggle_fullscreen;
w32->w.set_decorations = window_win32_set_decorations;
+ w32->w.set_cursor = window_win32_set_cursor;
#ifdef STELA_POLL_INLINE
w32->w.poll = window_win32_poll;
#ifdef STELA_PAUSE
diff --git a/src/window_win32.h b/src/window_win32.h
index 5fbb0f3..317916e 100644
--- a/src/window_win32.h
+++ b/src/window_win32.h
@@ -18,6 +18,7 @@ struct stl_win32_global {
#ifdef STELA_API_VULKAN
const char **VKExtensions;
#endif
+ HCURSOR Cursors[STELA_CURSOR_HIDDEN];
u16 keycodes[STELA_WIN32_KEY_MAX];
array(struct stl_monitor *) monitors;
u32 argc;
diff --git a/src/window_x11.c b/src/window_x11.c
index 413e9bd..2566fda 100644
--- a/src/window_x11.c
+++ b/src/window_x11.c
@@ -288,7 +288,7 @@ static void window_x11_toggle_fullscreen(struct stl_window *window)
static bool window_x11_poll(struct stl_window *window, bool block)
{
struct stl_window_x11 *xw = (struct stl_window_x11 *)window;
- return stl_poll_common(xw->fds, block);
+ return stl_poll_common(&xw->w, xw->fds, block);
}
#ifdef STELA_PAUSE
@@ -318,9 +318,9 @@ static void handle_next_xevent(struct stl_window_x11 *xw)
break;
case KeyPress:
case KeyRelease: {
- u8 key_state = xev.type == KeyPress ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
- stl_window_send_key_immediate_event(&xw->w, key_state, xev.xkey.keycode);
- stl_window_send_key_event(&xw->w, key_state, xev.xkey.keycode);
+ u8 mapped_state = xev.type == KeyPress ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
+ stl_window_send_key_immediate_event(&xw->w, mapped_state, xev.xkey.keycode);
+ stl_window_send_key_event(&xw->w, mapped_state, xev.xkey.keycode);
break;
}
case ButtonPress:
diff --git a/subprojects/libalabaster.wrap b/subprojects/libalabaster.wrap
index 81771ed..9a9cfc1 100644
--- a/subprojects/libalabaster.wrap
+++ b/subprojects/libalabaster.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libalabaster
-revision = b403770e7eb6e0a5a8ca14a24f91dbf214ffe917
+revision = 0470ca3416388e7e58b64f63bfe505d454f150b9
depth = 1
diff --git a/subprojects/libnaunet.wrap b/subprojects/libnaunet.wrap
index 462c1a7..15dd478 100644
--- a/subprojects/libnaunet.wrap
+++ b/subprojects/libnaunet.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libnaunet
-revision = fc8df3fe9cd15fe7b0b86c6584e0b3fb487d6886
+revision = 3ea974dd51b1f01483bad6a4d115189928bbf4db
depth = 1
diff --git a/tests/window.c b/tests/window.c
index d334d36..e13f1fa 100644
--- a/tests/window.c
+++ b/tests/window.c
@@ -279,7 +279,9 @@ s32 window_system_main(u32 argc, str *argv, void *extra)
s32 main(s32 argc, char *argv[])
{
- if (!nn_common_init() || !stl_global_init(false)) {
+ if (!nn_common_init("Stella_main")) return EXIT_FAILURE;
+ if (!stl_global_init(false)) {
+ nn_common_close();
return EXIT_FAILURE;
}