summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-05-25 18:37:30 -0400
committerAndrew Opalach <andrew@akon.city> 2025-05-25 18:37:30 -0400
commit1f1b60ed59cd3b3d594cf16c51cb835cb912fb12 (patch)
tree53effacd6993262e08f7be4f751129c8133ec224
parent7d4434ea4ceb1fa5c75f372a73ac6dfe5cb05b1d (diff)
downloadstela-1f1b60ed59cd3b3d594cf16c51cb835cb912fb12.tar.gz
stela-1f1b60ed59cd3b3d594cf16c51cb835cb912fb12.tar.bz2
stela-1f1b60ed59cd3b3d594cf16c51cb835cb912fb12.zip
Vastly improve Windows backend
- Add basic OpenGL window test. - Cleanup. Signed-off-by: Andrew Opalach <andrew@akon.city>
-rw-r--r--.gitignore2
-rw-r--r--meson.build12
-rw-r--r--meson_options.txt4
-rw-r--r--src/egl_common.c26
-rw-r--r--src/egl_common.h2
-rw-r--r--src/gl_common.c1
-rw-r--r--src/gl_common.h21
-rw-r--r--src/gl_versions.h26
-rw-r--r--src/window.c4
-rw-r--r--src/window.h13
-rw-r--r--src/window_glfm.c23
-rw-r--r--src/window_glfw.c51
-rw-r--r--src/window_glfw.h20
-rw-r--r--src/window_kms.c23
-rw-r--r--src/window_wayland.c38
-rw-r--r--src/window_win32.c1071
-rw-r--r--src/window_win32.h30
-rw-r--r--src/window_x11.c62
-rw-r--r--subprojects/libalabaster.wrap2
-rw-r--r--subprojects/libnaunet.wrap2
-rw-r--r--tests/meson.build1
-rw-r--r--tests/window.c313
22 files changed, 1429 insertions, 318 deletions
diff --git a/.gitignore b/.gitignore
index 6b7e3ff..2c664dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@ subprojects/c89atomic-*/
subprojects/c89atomic.wrap
subprojects/glad/
subprojects/glfm-*/
-subprojects/glfw-*/
+subprojects/glfw3-*/
subprojects/libalabaster
subprojects/libalabaster/
subprojects/libev-*/
diff --git a/meson.build b/meson.build
index 0154e3e..95e7991 100644
--- a/meson.build
+++ b/meson.build
@@ -14,6 +14,10 @@ is_windows = host_machine.system() == 'windows'
is_macos = host_machine.system() == 'darwin'
is_android = host_machine.system() == 'android'
+if get_option('window') == 'null'
+ error('No window type selected.')
+endif
+
if get_option('window') in ['x11', 'wayland']
if not (is_linux and not is_android)
error('Wayland is only supported on linux desktop.')
@@ -70,12 +74,14 @@ elif get_option('api') in ['gl', 'gles']
egl = dependency('egl')
stela_deps += [egl]
stela_src += ['src/egl_common.c']
+ elif get_option('window') == 'win32'
+ stela_deps += [compiler.find_library('opengl32')]
endif
if use_glad
glad_opts = use_gles ? 'use_gles=true' : 'use_gles=false'
stela_deps += [subproject('glad', default_options: glad_opts).get_variable('glad')]
stela_args += ['-DSTELA_USE_GLAD']
- elif not is_android
+ elif not is_android and not is_windows
stela_deps += [dependency('gl')]
endif
if use_gles
@@ -207,3 +213,7 @@ stela = declare_dependency(include_directories: stela_inc,
if not meson.is_subproject()
stela_lib = shared_library('stela', dependencies: stela, install: false)
endif
+
+if get_option('tests').auto() and not meson.is_subproject()
+ subdir('tests')
+endif
diff --git a/meson_options.txt b/meson_options.txt
index 9206415..f082df8 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,6 +1,8 @@
option('api', type: 'combo', choices: ['null', 'gl', 'gles', 'vulkan', 'dx11'], value: 'null')
-option('window', type: 'combo', choices: ['wayland', 'x11', 'kms', 'win32', 'glfw', 'glfm'], value: 'wayland')
+option('window', type: 'combo', choices: ['null', 'wayland', 'x11', 'kms', 'win32', 'glfw', 'glfm'], value: 'null')
option('egl-brcm', type: 'boolean', value: false)
option('poll', type: 'combo', choices: ['inline', 'fd'], value: 'inline')
option('event-buffer', type: 'boolean', value: false)
option('pause', type: 'boolean', value: true)
+option('win32-compat', type: 'boolean', value: false, yield: true)
+option('tests', type: 'feature', value: 'auto', yield: true)
diff --git a/src/egl_common.c b/src/egl_common.c
index 9bfc31f..ad2bea3 100644
--- a/src/egl_common.c
+++ b/src/egl_common.c
@@ -125,23 +125,31 @@ bool stl_init_egl_context(struct stl_egl *egl, struct stl_egl_global *global, EG
eglBindAPI(stl_gl_vers[i].api);
+#define STELA_INCLUDE_DEPTH_BUFFER
const EGLint window_attr[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 0,
+ EGL_ALPHA_SIZE, 8,
+#ifdef STELA_INCLUDE_DEPTH_BUFFER
+ EGL_DEPTH_SIZE, 24,
+ EGL_STENCIL_SIZE, 8,
+#else
+ EGL_DEPTH_SIZE, 0,
+ EGL_STENCIL_SIZE, 0,
+#endif
EGL_RENDERABLE_TYPE, stl_gl_vers[i].type,
EGL_SAMPLES, 0,
EGL_NONE
};
if (!egl_choose_config(egl->display, window_attr, visual_id, config)) {
- log_error("Failed to get config.");
+ log_error("Failed to choose a display config.");
continue;
}
- log_debug("Trying to create a %s context of version %i.%i (GLSL %i).",
+ log_debug("Attempting to create a %s context of version %d.%d (GLSL %d).",
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);
@@ -150,7 +158,6 @@ bool stl_init_egl_context(struct stl_egl *egl, struct stl_egl_global *global, EG
if (egl->context == EGL_NO_CONTEXT) {
continue;
} else {
- log_debug("Context successfully created.");
break;
}
}
@@ -166,13 +173,14 @@ bool stl_init_egl_context(struct stl_egl *egl, struct stl_egl_global *global, EG
return true;
}
-void stl_egl_make_current(struct stl_egl *egl)
+bool stl_egl_make_current(struct stl_egl *egl)
{
if (egl->context != egl->current) {
- al_assert(egl->current == EGL_NO_CONTEXT); // 1 context limit (for now).
- eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
+ al_assert(egl->current == EGL_NO_CONTEXT);
egl->current = egl->context;
+ return eglMakeCurrent(egl->display, egl->surface, egl->surface, egl->context);
}
+ return true;
}
void stl_egl_release_current(struct stl_egl *egl)
@@ -189,8 +197,8 @@ void stl_egl_release_current(struct stl_egl *egl)
// The frame that is presented will still have the dimensions of the previous backbuffer.
// We only support 1 context at a time, so this shouldn't be an issue.
#if 0
- eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- egl->current = EGL_NO_CONTEXT;
+ eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ egl->current = EGL_NO_CONTEXT;
#endif
}
diff --git a/src/egl_common.h b/src/egl_common.h
index c836519..5ef7812 100644
--- a/src/egl_common.h
+++ b/src/egl_common.h
@@ -25,7 +25,7 @@ bool stl_load_egl(struct stl_egl_global *global);
void stl_terminate_egl(struct stl_egl_global *global);
bool stl_init_egl_context(struct stl_egl *egl, struct stl_egl_global *global, EGLint visual_id, EGLConfig *config);
-void stl_egl_make_current(struct stl_egl *egl);
+bool stl_egl_make_current(struct stl_egl *egl);
void stl_egl_release_current(struct stl_egl *egl);
void stl_egl_swap_interval(struct stl_egl *egl, EGLint interval);
void stl_egl_swap_buffers(struct stl_egl *egl);
diff --git a/src/gl_common.c b/src/gl_common.c
index 0005e5d..34d6427 100644
--- a/src/gl_common.c
+++ b/src/gl_common.c
@@ -12,7 +12,6 @@ static const char *glGetErrorString(GLenum error)
ERROR_CASE_STR(GL_INVALID_ENUM)
ERROR_CASE_STR(GL_INVALID_VALUE)
ERROR_CASE_STR(GL_INVALID_OPERATION)
- ERROR_CASE_STR(GL_INVALID_FRAMEBUFFER_OPERATION)
ERROR_CASE_STR(GL_OUT_OF_MEMORY)
default: return "Unknown";
}
diff --git a/src/gl_common.h b/src/gl_common.h
index 1e2ed3f..cd34d4c 100644
--- a/src/gl_common.h
+++ b/src/gl_common.h
@@ -1,4 +1,8 @@
#include <al/types.h>
+#ifdef NAUNET_ON_WINDOWS
+// classic... https://community.khronos.org/t/wingdiapi-not-defined/17645/2
+#include <nnwt/windows.h>
+#endif
#ifdef STELA_WINDOW_GLFM
#include <glfm.h>
@@ -13,13 +17,28 @@
#ifdef STELA_USE_GLES
#include <GLES2/gl2.h>
#else
-#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
+#ifndef NAUNET_ON_WINDOWS
+#define GL_GLEXT_PROTOTYPES
#include <GL/glext.h>
#include <GL/glcorearb.h>
#endif
#endif
#endif
+#endif
+
+#ifdef NAUNET_ON_WINDOWS
+// https://registry.khronos.org/OpenGL/api/GL/wgl.h
+#define WGL_CONTEXT_FLAGS_ARB 0x2094
+#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
+#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
+#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
+#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
+#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
+#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004
+#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004
+#endif
bool stl_gl_check_error();
bool stl_gl_loader_load(void (*(*get_proc_address)(const char *))(void));
diff --git a/src/gl_versions.h b/src/gl_versions.h
index 0fce4ee..e6c6ab2 100644
--- a/src/gl_versions.h
+++ b/src/gl_versions.h
@@ -1,5 +1,17 @@
#pragma once
+#ifdef NAUNET_WIN32_COMPAT_MODE
+#ifndef STELA_FORCE_COMPATABILITY
+#define STELA_FORCE_COMPATABILITY
+#endif
+#endif
+
+// This is likely ill-defined overall. For example, WGL has confusing behavior defined here.
+// https://registry.khronos.org/OpenGL/extensions/ARB/WGL_ARB_create_context.txt
+// Under "Additions to the WGL specification".
+// This also seems to behave different under Wine vs Native Windows. Requesting GL 3.0 Compatibility
+// on Wine still forces a Core context but works according to the specification on Windows.
+
// https://github.com/haasn/libplacebo/blob/795600a44b03fcd52c055981a403ad60ee5d027a/demos/window_glfw.c#L194
#ifdef STELA_USE_GLES
#define PASTE_GL_VERSIONS() static struct { \
@@ -15,6 +27,18 @@
{ STELA_GLES_API, STELA_GLES2_BIT, 2, 0, 100, 0 } \
};
#else
+#ifdef STELA_FORCE_COMPATABILITY
+#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, 2, 1, 120, 0 }, \
+ { STELA_GL_API, STELA_GL_BIT, 2, 0, 110, 0 } \
+};
+#else
#define PASTE_GL_VERSIONS() static struct { \
s32 api; \
s32 type; \
@@ -34,6 +58,8 @@
{ STELA_GL_API, STELA_GL_BIT, 2, 0, 110, 0 } \
};
#endif
+#endif
+
/*
#define PASTE_GL_VERSIONS() static struct { \
s32 api; \
diff --git a/src/window.c b/src/window.c
index bb54461..6b8f4ec 100644
--- a/src/window.c
+++ b/src/window.c
@@ -83,10 +83,6 @@ void stl_window_send_mouse_button_event(struct stl_window *window, u8 state, u8
void stl_window_send_key_event(struct stl_window *window, u8 state, u16 button)
{
- if (window->key_immediate_callback) {
- window->key_immediate_callback(window->userdata, state, button);
- }
-
#ifdef STELA_EVENT_BUFFER
GET_EVENT_CHUNK(STELA_EVENT_KEY);
ptr[1] = state;
diff --git a/src/window.h b/src/window.h
index 068f433..b3faf17 100644
--- a/src/window.h
+++ b/src/window.h
@@ -17,9 +17,14 @@
#include <al/types.h>
#include <al/lib.h>
#include <al/str.h>
+#ifdef STELA_EVENT_BUFFER
#include <al/ring_buffer.h>
+#endif
#if defined STELA_API_VULKAN
+#ifdef NAUNET_ON_WINDOWS
+#define VK_USE_PLATFORM_WIN32_KHR
+#endif
#include <vulkan/vulkan.h>
#elif defined STELA_API_DX11
#include <nnwt/windows.h>
@@ -55,6 +60,7 @@ struct stl_window {
u32 height;
u32 scale;
bool fullscreen;
+ u32 flags;
#ifdef STELA_EVENT_BUFFER
u8 *data;
// Render thread events.
@@ -97,7 +103,6 @@ struct stl_window {
bool (*gl_make_current)(void *);
void (*gl_release_current)(void *);
void (*gl_swap_buffers)(void *);
-#elif defined STELA_API_DX11
#endif
// Callbacks.
void (*output_discovered_callback)(void *, struct stl_monitor *);
@@ -130,9 +135,9 @@ enum {
};
enum {
- STELA_MOUSE1 = 0,
- STELA_MOUSE2,
- STELA_MOUSE3
+ STELA_MOUSE1 = 1,
+ STELA_MOUSE2 = 1 << 1,
+ STELA_MOUSE3 = 1 << 2
};
#define STELA_EVENT_SIZE 32
diff --git a/src/window_glfm.c b/src/window_glfm.c
index dd16d77..ad9203e 100644
--- a/src/window_glfm.c
+++ b/src/window_glfm.c
@@ -152,28 +152,27 @@ static void window_glfm_free(struct stl_window **window)
(void)window;
}
-#if defined STELA_API_VULKAN
-#elif defined STELA_API_OPENGL
-static bool window_glfm_gl_loader_load(void *data)
+#if defined STELA_API_OPENGL
+static bool window_glfm_gl_loader_load(void *window)
{
- (void)data;
+ (void)window;
return true;
}
-static bool window_glfm_gl_make_current(void *data)
+static bool window_glfm_gl_make_current(void *window)
{
- (void)data;
+ (void)window;
return true;
}
-static void window_glfm_gl_release_current(void *data)
+static void window_glfm_gl_release_current(void *window)
{
- (void)data;
+ (void)window;
}
-static void window_glfm_gl_swap_buffers(void *data)
+static void window_glfm_gl_swap_buffers(void *window)
{
- struct stl_window_glfm *glfm = (struct stl_window_glfm *)data;
+ struct stl_window_glfm *glfm = (struct stl_window_glfm *)window;
(void)glfm;
glfmSwapBuffers(global.display);
}
@@ -200,14 +199,12 @@ struct stl_window *stl_window_create(void)
glfm->w.should_refresh = window_glfm_should_refresh;
#endif
glfm->w.free = window_glfm_free;
-#if defined STELA_API_VULKAN
-#elif defined STELA_API_OPENGL
+#if defined STELA_API_OPENGL
glfm->w.get_gl_proc_address = glfmGetProcAddress;
glfm->w.gl_loader_load = window_glfm_gl_loader_load;
glfm->w.gl_make_current = window_glfm_gl_make_current;
glfm->w.gl_release_current = window_glfm_gl_release_current;
glfm->w.gl_swap_buffers = window_glfm_gl_swap_buffers;
-#elif defined STELA_API_DX11
#endif
return (struct stl_window *)glfm;
}
diff --git a/src/window_glfw.c b/src/window_glfw.c
index eca7e91..ad685bf 100644
--- a/src/window_glfw.c
+++ b/src/window_glfw.c
@@ -24,7 +24,7 @@
PASTE_GL_VERSIONS()
#endif
-static struct stl_glfw_global global;
+static struct stl_glfw_global global = { 0 };
void stl_set_output_discovered_callback(void (*output_discovered)(void *, struct stl_monitor *), void *userdata)
{
@@ -82,11 +82,11 @@ static void key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action,
log_warn("Unmapped GLFW key pressed (%hu).", (u16)key);
return;
}
- if (action == GLFW_PRESS) {
- stl_window_send_key_event(&glfw->w, STELA_BUTTON_PRESSED, (u16)key);
- } else if (action == GLFW_RELEASE) {
- stl_window_send_key_event(&glfw->w, STELA_BUTTON_RELEASED, (u16)key);
+ u8 key_state = action == GLFW_PRESS ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
+ if (glfw->w.key_immediate_callback) {
+ glfw->w.key_immediate_callback(glfw->w.userdata, key_state, (u16)key);
}
+ stl_window_send_key_event(&glfw->w, key_state, (u16)key);
}
static void cursor_position_callback(GLFWwindow *window, f64 xpos, f64 ypos)
@@ -202,26 +202,21 @@ static bool window_glfw_create_window(struct stl_window *window, u32 width, u32
if (stl_gl_vers[i].api == STELA_GL_API) {
glfwWindowHint(GLFW_OPENGL_PROFILE, stl_gl_vers[i].profile);
}
-#ifdef __APPLE__
- if (gl_vers[i].profile == GLFW_OPENGL_CORE_PROFILE) {
+ if (stl_gl_vers[i].major >= 3) {
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
}
-#endif
- log_info("Trying to make a %s context of version %i.%i (GLSL %i).",
+ log_debug("Attempting to create a %s context of version %d.%d (GLSL %d).",
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
glfw->window = glfwCreateWindow(width, height, name, NULL, global.master_window);
#ifdef STELA_API_OPENGL
- if (glfw->window) {
- log_info("Context successfully created.");
- break;
- }
+ if (glfw->window) break;
}
#endif
if (!glfw->window) {
- log_info("Failed to create window.");
+ log_info("Failed to create window/context.");
return false;
}
@@ -255,6 +250,8 @@ static bool window_glfw_create_window(struct stl_window *window, u32 width, u32
glfw->w.height = (u32)buffer_height;
glfwSetWindowSizeLimits(glfw->window, STELA_MIN_WIDTH, STELA_MIN_HEIGHT, GLFW_DONT_CARE, GLFW_DONT_CARE);
+ glfw->scale_x = 1.f;
+ glfw->scale_y = 1.f;
glfw->prev.x = 0;
glfw->prev.y = 0;
glfw->prev.width = 0;
@@ -414,9 +411,9 @@ static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL window_glfw_get_vk_proc_address(
return (PFN_vkVoidFunction)glfwGetInstanceProcAddress(inst, name);
}
-static VkResult window_glfw_vk_create_surface(void *data, VkInstance inst, VkSurfaceKHR *surface)
+static VkResult window_glfw_vk_create_surface(void *window, VkInstance inst, VkSurfaceKHR *surface)
{
- struct stl_window_glfw *glfw = (struct stl_window_glfw *)data;
+ struct stl_window_glfw *glfw = (struct stl_window_glfw *)window;
return glfwCreateWindowSurface(inst, glfw->window, NULL, surface);
}
@@ -425,31 +422,28 @@ static const char *const *window_glfw_vk_get_extensions(u32 *num)
return glfwGetRequiredInstanceExtensions(num);
}
#elif defined STELA_API_OPENGL
-static bool window_glfw_gl_loader_load(void *data)
+static bool window_glfw_gl_loader_load(void *window)
{
- struct stl_window_glfw *glfw = (struct stl_window_glfw *)data;
- if (!stl_gl_loader_load(glfw->w.get_gl_proc_address)) {
- return false;
- }
- return true;
+ struct stl_window_glfw *glfw = (struct stl_window_glfw *)window;
+ return stl_gl_loader_load(glfw->w.get_gl_proc_address);
}
-static bool window_glfw_gl_make_current(void *data)
+static bool window_glfw_gl_make_current(void *window)
{
- struct stl_window_glfw *glfw = (struct stl_window_glfw *)data;
+ struct stl_window_glfw *glfw = (struct stl_window_glfw *)window;
glfwMakeContextCurrent(glfw->window);
return true;
}
-static void window_glfw_gl_release_current(void *data)
+static void window_glfw_gl_release_current(void *window)
{
- (void)data;
+ (void)window;
glfwMakeContextCurrent(NULL);
}
-static void window_glfw_gl_swap_buffers(void *data)
+static void window_glfw_gl_swap_buffers(void *window)
{
- struct stl_window_glfw *glfw = (struct stl_window_glfw *)data;
+ struct stl_window_glfw *glfw = (struct stl_window_glfw *)window;
glfwSwapBuffers(glfw->window);
}
#endif
@@ -485,7 +479,6 @@ struct stl_window *stl_window_create(void)
glfw->w.gl_make_current = window_glfw_gl_make_current;
glfw->w.gl_release_current = window_glfw_gl_release_current;
glfw->w.gl_swap_buffers = window_glfw_gl_swap_buffers;
-#elif defined STELA_API_DX11
#endif
return (struct stl_window *)glfw;
}
diff --git a/src/window_glfw.h b/src/window_glfw.h
index 8f5857c..ad64720 100644
--- a/src/window_glfw.h
+++ b/src/window_glfw.h
@@ -24,14 +24,14 @@ struct stl_glfw_global {
};
struct stl_window_glfw {
- struct stl_window w;
- GLFWwindow *window;
- f32 scale_x;
- f32 scale_y;
- struct {
- s32 x;
- s32 y;
- s32 width;
- s32 height;
- } prev;
+ struct stl_window w;
+ GLFWwindow *window;
+ f32 scale_x;
+ f32 scale_y;
+ struct {
+ s32 x;
+ s32 y;
+ s32 width;
+ s32 height;
+ } prev;
};
diff --git a/src/window_kms.c b/src/window_kms.c
index 8261d75..93192e6 100644
--- a/src/window_kms.c
+++ b/src/window_kms.c
@@ -97,28 +97,27 @@ static void window_kms_free(struct stl_window **window)
(void)window;
}
-#if defined STELA_API_VULKAN
-#elif defined STELA_API_OPENGL
-static bool window_kms_gl_loader_load(void *data)
+#if defined STELA_API_OPENGL
+static bool window_kms_gl_loader_load(void *window)
{
- (void)data;
+ (void)window;
return true;
}
-static bool window_kms_gl_make_current(void *data)
+static bool window_kms_gl_make_current(void *window)
{
- (void)data;
+ (void)window;
return true;
}
-static void window_kms_gl_release_current(void *data)
+static void window_kms_gl_release_current(void *window)
{
- (void)data;
+ (void)window;
}
-static void window_kms_gl_swap_buffers(void *data)
+static void window_kms_gl_swap_buffers(void *window)
{
- struct stl_window_kms *kms = (struct stl_window_kms *)data;
+ struct stl_window_kms *kms = (struct stl_window_kms *)window;
(void)kms;
}
#endif
@@ -143,14 +142,12 @@ struct stl_window *stl_window_create(void)
kms->w.should_refresh = window_kms_should_refresh;
#endif
kms->w.free = window_kms_free;
-#if defined STELA_API_VULKAN
-#elif defined STELA_API_OPENGL
+#if defined STELA_API_OPENGL
kms->w.get_gl_proc_address = NULL;
kms->w.gl_loader_load = window_kms_gl_loader_load;
kms->w.gl_make_current = window_kms_gl_make_current;
kms->w.gl_release_current = window_kms_gl_release_current;
kms->w.gl_swap_buffers = window_kms_gl_swap_buffers;
-#elif defined STELA_API_DX11
#endif
return (struct stl_window *)kms;
}
diff --git a/src/window_wayland.c b/src/window_wayland.c
index 7d37a11..59efb10 100644
--- a/src/window_wayland.c
+++ b/src/window_wayland.c
@@ -7,7 +7,7 @@
#include "poll_common.h"
#include "keys.h"
-static struct stl_wayland_global global;
+static struct stl_wayland_global global = { 0 };
static void handle_output_geometry(void *data, struct wl_output *output,
s32 x, s32 y, s32 width_mm, s32 height_mm, s32 subpixel,
@@ -566,11 +566,11 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard,
return;
}
- if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
- stl_window_send_key_event(&wl->w, STELA_BUTTON_PRESSED, global.keycodes[key]);
- } else if (state == WL_KEYBOARD_KEY_STATE_RELEASED) {
- stl_window_send_key_event(&wl->w, STELA_BUTTON_RELEASED, global.keycodes[key]);
+ u8 key_state = state == WL_KEYBOARD_KEY_STATE_PRESSED ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
+ if (wl->w.key_immediate_callback) {
+ wl->w.key_immediate_callback(wl->w.userdata, key_state, global.keycodes[key]);
}
+ stl_window_send_key_event(&wl->w, key_state, global.keycodes[key]);
}
static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard,
@@ -1098,38 +1098,34 @@ void window_wayland_free(struct stl_window **window)
}
#ifdef STELA_API_OPENGL
-static bool window_wayland_gl_loader_load(void *data)
+static bool window_wayland_gl_loader_load(void *window)
{
- struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
- if (!stl_gl_loader_load(wl->w.get_gl_proc_address)) {
- return false;
- }
- return true;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
+ return stl_gl_loader_load(wl->w.get_gl_proc_address);
}
-static bool window_wayland_gl_make_current(void *data)
+static bool window_wayland_gl_make_current(void *window)
{
- struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
- stl_egl_make_current(&wl->egl);
- return true;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
+ return stl_egl_make_current(&wl->egl);
}
-static void window_wayland_gl_release_current(void *data)
+static void window_wayland_gl_release_current(void *window)
{
- struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
stl_egl_release_current(&wl->egl);
}
-static void window_wayland_gl_swap_buffers(void *data)
+static void window_wayland_gl_swap_buffers(void *window)
{
- struct stl_window_wayland *wl = (struct stl_window_wayland *)data;
+ struct stl_window_wayland *wl = (struct stl_window_wayland *)window;
stl_egl_swap_buffers(&wl->egl);
}
#endif
struct stl_window *stl_window_create(void)
{
- log_info("Creating Wayland (EGL) window.");
+ log_info("Creating Wayland window.");
struct stl_window_wayland *wl = al_alloc_object(struct stl_window_wayland);
stl_window_init(&wl->w);
wl->w.fullscreen = false;
@@ -1154,6 +1150,8 @@ struct stl_window *stl_window_create(void)
wl->w.gl_make_current = window_wayland_gl_make_current;
wl->w.gl_release_current = window_wayland_gl_release_current;
wl->w.gl_swap_buffers = window_wayland_gl_swap_buffers;
+#else
+ log_warn("Window created without graphics backend.");
#endif
return (struct stl_window *)wl;
}
diff --git a/src/window_win32.c b/src/window_win32.c
index 84ecc16..1182a27 100644
--- a/src/window_win32.c
+++ b/src/window_win32.c
@@ -5,6 +5,25 @@
#include "common.h"
#include "keys.h"
+#ifdef STELA_API_OPENGL
+#include "gl_common.h"
+
+#define STELA_GL_API 1
+#define STELA_GLES_API 2
+
+#define STELA_GL_BIT 1
+#define STELA_GLES2_BIT 2
+#define STELA_GLES3_BIT 3
+
+#define STELA_GL_CORE_PROFILE_BIT 1
+
+#include "gl_versions.h"
+
+PASTE_GL_VERSIONS()
+#endif
+
+static LPCWSTR DangerousClass = L"Dangerous Class";
+
void stl_set_output_discovered_callback(void (*output_discovered)(void *, struct stl_monitor *), void *userdata)
{
(void)output_discovered;
@@ -34,7 +53,7 @@ struct the_baby {
#define EXIT_PROCESS (WM_USER + 0x133a)
static DWORD MainThreadID;
-static struct stl_win32_global global;
+static struct stl_win32_global Global = { 0 };
static LRESULT CALLBACK ServiceWndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam)
{
@@ -58,7 +77,10 @@ static LRESULT CALLBACK ServiceWndProc(HWND Window, UINT Message, WPARAM WParam,
break;
}
case DESTROY_DANGEROUS_WINDOW:
- DestroyWindow((HWND)WParam);
+ if (!DestroyWindow((HWND)WParam)) {
+ log_error("Failed to destroy window (0x%lx).", GetLastError());
+ }
+ UnregisterClassW(DangerousClass, Global.ModuleHandle);
break;
case EXIT_PROCESS:
PostQuitMessage(WParam);
@@ -74,143 +96,181 @@ static LRESULT CALLBACK ServiceWndProc(HWND Window, UINT Message, WPARAM WParam,
static DWORD WINAPI MainThread(LPVOID Param)
{
(void)Param;
- s32 ret = global.main(global.argc, global.argv, global.extra);
- SendMessageW(global.ServiceWindow, EXIT_PROCESS, (WPARAM)ret, 0);
+ s32 ret = Global.main(Global.argc, Global.argv, Global.extra);
+ SendMessageW(Global.ServiceWindow, EXIT_PROCESS, (WPARAM)ret, 0);
return 0;
}
static void set_keycodes_for_win32(void)
{
- al_memset(global.keycodes, 0, sizeof(global.keycodes));
+ al_memset(Global.keycodes, 0, sizeof(Global.keycodes));
+
+ Global.keycodes[0x30] = STELA_KEY_0;
+ Global.keycodes[0x31] = STELA_KEY_1;
+ Global.keycodes[0x32] = STELA_KEY_2;
+ Global.keycodes[0x33] = STELA_KEY_3;
+ Global.keycodes[0x34] = STELA_KEY_4;
+ Global.keycodes[0x35] = STELA_KEY_5;
+ Global.keycodes[0x36] = STELA_KEY_6;
+ Global.keycodes[0x37] = STELA_KEY_7;
+ Global.keycodes[0x38] = STELA_KEY_8;
+ Global.keycodes[0x39] = STELA_KEY_9;
+ Global.keycodes[0x41] = STELA_KEY_A;
+ Global.keycodes[0x42] = STELA_KEY_B;
+ Global.keycodes[0x43] = STELA_KEY_C;
+ Global.keycodes[0x44] = STELA_KEY_D;
+ Global.keycodes[0x45] = STELA_KEY_E;
+ Global.keycodes[0x46] = STELA_KEY_F;
+ Global.keycodes[0x47] = STELA_KEY_G;
+ Global.keycodes[0x48] = STELA_KEY_H;
+ Global.keycodes[0x49] = STELA_KEY_I;
+ Global.keycodes[0x4a] = STELA_KEY_J;
+ Global.keycodes[0x4b] = STELA_KEY_K;
+ Global.keycodes[0x4c] = STELA_KEY_L;
+ Global.keycodes[0x4d] = STELA_KEY_M;
+ Global.keycodes[0x4e] = STELA_KEY_N;
+ Global.keycodes[0x4f] = STELA_KEY_O;
+ Global.keycodes[0x50] = STELA_KEY_P;
+ Global.keycodes[0x51] = STELA_KEY_Q;
+ Global.keycodes[0x52] = STELA_KEY_R;
+ Global.keycodes[0x53] = STELA_KEY_S;
+ Global.keycodes[0x54] = STELA_KEY_T;
+ Global.keycodes[0x55] = STELA_KEY_U;
+ Global.keycodes[0x56] = STELA_KEY_V;
+ Global.keycodes[0x57] = STELA_KEY_W;
+ Global.keycodes[0x58] = STELA_KEY_X;
+ Global.keycodes[0x59] = STELA_KEY_Y;
+ Global.keycodes[0x5a] = STELA_KEY_Z;
- global.keycodes[0x30] = STELA_KEY_0;
- global.keycodes[0x31] = STELA_KEY_1;
- global.keycodes[0x32] = STELA_KEY_2;
- global.keycodes[0x33] = STELA_KEY_3;
- global.keycodes[0x34] = STELA_KEY_4;
- global.keycodes[0x35] = STELA_KEY_5;
- global.keycodes[0x36] = STELA_KEY_6;
- global.keycodes[0x37] = STELA_KEY_7;
- global.keycodes[0x38] = STELA_KEY_8;
- global.keycodes[0x39] = STELA_KEY_9;
- global.keycodes[0x41] = STELA_KEY_A;
- global.keycodes[0x42] = STELA_KEY_B;
- global.keycodes[0x43] = STELA_KEY_C;
- global.keycodes[0x44] = STELA_KEY_D;
- global.keycodes[0x45] = STELA_KEY_E;
- global.keycodes[0x46] = STELA_KEY_F;
- global.keycodes[0x47] = STELA_KEY_G;
- global.keycodes[0x48] = STELA_KEY_H;
- global.keycodes[0x49] = STELA_KEY_I;
- global.keycodes[0x4a] = STELA_KEY_J;
- global.keycodes[0x4b] = STELA_KEY_K;
- global.keycodes[0x4c] = STELA_KEY_L;
- global.keycodes[0x4d] = STELA_KEY_M;
- global.keycodes[0x4e] = STELA_KEY_N;
- global.keycodes[0x4f] = STELA_KEY_O;
- global.keycodes[0x50] = STELA_KEY_P;
- global.keycodes[0x51] = STELA_KEY_Q;
- global.keycodes[0x52] = STELA_KEY_R;
- global.keycodes[0x53] = STELA_KEY_S;
- global.keycodes[0x54] = STELA_KEY_T;
- global.keycodes[0x55] = STELA_KEY_U;
- global.keycodes[0x56] = STELA_KEY_V;
- global.keycodes[0x57] = STELA_KEY_W;
- global.keycodes[0x58] = STELA_KEY_X;
- global.keycodes[0x59] = STELA_KEY_Y;
- global.keycodes[0x5a] = STELA_KEY_Z;
+ Global.keycodes[VK_SPACE] = STELA_KEY_SPACE;
+ Global.keycodes[VK_OEM_7] = STELA_KEY_APOSTROPHE;
+ Global.keycodes[VK_OEM_COMMA] = STELA_KEY_COMMA;
+ Global.keycodes[VK_OEM_PERIOD] = STELA_KEY_PERIOD;
+ Global.keycodes[VK_OEM_2] = STELA_KEY_SLASH;
+ Global.keycodes[VK_OEM_5] = STELA_KEY_BACKSLASH;
+ Global.keycodes[VK_OEM_3] = STELA_KEY_GRAVE_ACCENT;
+ Global.keycodes[VK_OEM_MINUS] = STELA_KEY_MINUS;
+ Global.keycodes[VK_OEM_4] = STELA_KEY_LEFT_BRACKET;
+ Global.keycodes[VK_OEM_6] = STELA_KEY_RIGHT_BRACKET;
+ Global.keycodes[VK_OEM_1] = STELA_KEY_SEMICOLON;
+ Global.keycodes[VK_OEM_PLUS] = STELA_KEY_EQUAL;
- global.keycodes[VK_SPACE] = STELA_KEY_SPACE;
- global.keycodes[VK_OEM_7] = STELA_KEY_APOSTROPHE;
- global.keycodes[VK_OEM_COMMA] = STELA_KEY_COMMA;
- global.keycodes[VK_OEM_PERIOD] = STELA_KEY_PERIOD;
- global.keycodes[VK_OEM_2] = STELA_KEY_SLASH;
- global.keycodes[VK_OEM_5] = STELA_KEY_BACKSLASH;
- global.keycodes[VK_OEM_3] = STELA_KEY_GRAVE_ACCENT;
- global.keycodes[VK_OEM_MINUS] = STELA_KEY_MINUS;
- global.keycodes[VK_OEM_4] = STELA_KEY_LEFT_BRACKET;
- global.keycodes[VK_OEM_6] = STELA_KEY_RIGHT_BRACKET;
- global.keycodes[VK_OEM_1] = STELA_KEY_SEMICOLON;
- global.keycodes[VK_OEM_PLUS] = STELA_KEY_EQUAL;
+ Global.keycodes[VK_RETURN] = STELA_KEY_RETURN;
+ Global.keycodes[VK_BACK] = STELA_KEY_BACKSPACE;
+ Global.keycodes[VK_TAB] = STELA_KEY_TAB;
+ Global.keycodes[VK_ESCAPE] = STELA_KEY_ESCAPE;
+ Global.keycodes[VK_INSERT] = STELA_KEY_INSERT;
+ Global.keycodes[VK_DELETE] = STELA_KEY_DELETE;
+ Global.keycodes[VK_HOME] = STELA_KEY_HOME;
+ Global.keycodes[VK_END] = STELA_KEY_END;
+ Global.keycodes[VK_PRIOR] = STELA_KEY_PAGE_UP;
+ Global.keycodes[VK_NEXT] = STELA_KEY_PAGE_DOWN;
+ Global.keycodes[VK_SNAPSHOT] = STELA_KEY_PRINT_SCREEN;
+ Global.keycodes[VK_PAUSE] = STELA_KEY_PAUSE;
+ Global.keycodes[VK_CAPITAL] = STELA_KEY_CAPS_LOCK;
+ Global.keycodes[VK_SCROLL] = STELA_KEY_SCROLL_LOCK;
+ Global.keycodes[VK_NUMLOCK] = STELA_KEY_NUM_LOCK;
- global.keycodes[VK_RETURN] = STELA_KEY_RETURN;
- global.keycodes[VK_BACK] = STELA_KEY_BACKSPACE;
- global.keycodes[VK_TAB] = STELA_KEY_TAB;
- global.keycodes[VK_ESCAPE] = STELA_KEY_ESCAPE;
- global.keycodes[VK_INSERT] = STELA_KEY_INSERT;
- global.keycodes[VK_DELETE] = STELA_KEY_DELETE;
- global.keycodes[VK_HOME] = STELA_KEY_HOME;
- global.keycodes[VK_END] = STELA_KEY_END;
- global.keycodes[VK_PRIOR] = STELA_KEY_PAGE_UP;
- global.keycodes[VK_NEXT] = STELA_KEY_PAGE_DOWN;
- global.keycodes[VK_SNAPSHOT] = STELA_KEY_PRINT_SCREEN;
- global.keycodes[VK_PAUSE] = STELA_KEY_PAUSE;
- global.keycodes[VK_CAPITAL] = STELA_KEY_CAPS_LOCK;
- global.keycodes[VK_SCROLL] = STELA_KEY_SCROLL_LOCK;
- global.keycodes[VK_NUMLOCK] = STELA_KEY_NUM_LOCK;
+ Global.keycodes[VK_LEFT] = STELA_KEY_LEFT;
+ Global.keycodes[VK_RIGHT] = STELA_KEY_RIGHT;
+ Global.keycodes[VK_UP] = STELA_KEY_UP;
+ Global.keycodes[VK_DOWN] = STELA_KEY_DOWN;
- global.keycodes[VK_LEFT] = STELA_KEY_LEFT;
- global.keycodes[VK_RIGHT] = STELA_KEY_RIGHT;
- global.keycodes[VK_UP] = STELA_KEY_UP;
- global.keycodes[VK_DOWN] = STELA_KEY_DOWN;
+ Global.keycodes[VK_F1] = STELA_KEY_F1;
+ Global.keycodes[VK_F2] = STELA_KEY_F2;
+ Global.keycodes[VK_F3] = STELA_KEY_F3;
+ Global.keycodes[VK_F4] = STELA_KEY_F4;
+ Global.keycodes[VK_F5] = STELA_KEY_F5;
+ Global.keycodes[VK_F6] = STELA_KEY_F6;
+ Global.keycodes[VK_F7] = STELA_KEY_F7;
+ Global.keycodes[VK_F8] = STELA_KEY_F8;
+ Global.keycodes[VK_F9] = STELA_KEY_F9;
+ Global.keycodes[VK_F10] = STELA_KEY_F10;
+ Global.keycodes[VK_F11] = STELA_KEY_F11;
+ Global.keycodes[VK_F12] = STELA_KEY_F12;
+ Global.keycodes[VK_F13] = STELA_KEY_F13;
+ Global.keycodes[VK_F14] = STELA_KEY_F14;
+ Global.keycodes[VK_F15] = STELA_KEY_F15;
+ Global.keycodes[VK_F16] = STELA_KEY_F16;
+ Global.keycodes[VK_F17] = STELA_KEY_F17;
+ Global.keycodes[VK_F18] = STELA_KEY_F18;
+ Global.keycodes[VK_F19] = STELA_KEY_F19;
+ Global.keycodes[VK_F20] = STELA_KEY_F20;
+ Global.keycodes[VK_F21] = STELA_KEY_F21;
+ Global.keycodes[VK_F22] = STELA_KEY_F22;
+ Global.keycodes[VK_F23] = STELA_KEY_F23;
+ Global.keycodes[VK_F24] = STELA_KEY_F24;
- global.keycodes[VK_F1] = STELA_KEY_F1;
- global.keycodes[VK_F2] = STELA_KEY_F2;
- global.keycodes[VK_F3] = STELA_KEY_F3;
- global.keycodes[VK_F4] = STELA_KEY_F4;
- global.keycodes[VK_F5] = STELA_KEY_F5;
- global.keycodes[VK_F6] = STELA_KEY_F6;
- global.keycodes[VK_F7] = STELA_KEY_F7;
- global.keycodes[VK_F8] = STELA_KEY_F8;
- global.keycodes[VK_F9] = STELA_KEY_F9;
- global.keycodes[VK_F10] = STELA_KEY_F10;
- global.keycodes[VK_F11] = STELA_KEY_F11;
- global.keycodes[VK_F12] = STELA_KEY_F12;
- global.keycodes[VK_F13] = STELA_KEY_F13;
- global.keycodes[VK_F14] = STELA_KEY_F14;
- global.keycodes[VK_F15] = STELA_KEY_F15;
- global.keycodes[VK_F16] = STELA_KEY_F16;
- global.keycodes[VK_F17] = STELA_KEY_F17;
- global.keycodes[VK_F18] = STELA_KEY_F18;
- global.keycodes[VK_F19] = STELA_KEY_F19;
- global.keycodes[VK_F20] = STELA_KEY_F20;
- global.keycodes[VK_F21] = STELA_KEY_F21;
- global.keycodes[VK_F22] = STELA_KEY_F22;
- global.keycodes[VK_F23] = STELA_KEY_F23;
- global.keycodes[VK_F24] = STELA_KEY_F24;
+ Global.keycodes[VK_NUMPAD0] = STELA_KEY_KP_0;
+ Global.keycodes[VK_NUMPAD1] = STELA_KEY_KP_1;
+ Global.keycodes[VK_NUMPAD2] = STELA_KEY_KP_2;
+ Global.keycodes[VK_NUMPAD3] = STELA_KEY_KP_3;
+ Global.keycodes[VK_NUMPAD4] = STELA_KEY_KP_4;
+ Global.keycodes[VK_NUMPAD5] = STELA_KEY_KP_5;
+ Global.keycodes[VK_NUMPAD6] = STELA_KEY_KP_6;
+ Global.keycodes[VK_NUMPAD7] = STELA_KEY_KP_7;
+ Global.keycodes[VK_NUMPAD8] = STELA_KEY_KP_8;
+ Global.keycodes[VK_NUMPAD9] = STELA_KEY_KP_9;
+ Global.keycodes[VK_ADD] = STELA_KEY_KP_ADD;
+ Global.keycodes[VK_SUBTRACT] = STELA_KEY_KP_SUBTRACT;
+ Global.keycodes[VK_MULTIPLY] = STELA_KEY_KP_MULTIPLY;
+ Global.keycodes[VK_DIVIDE] = STELA_KEY_KP_DIVIDE;
+ Global.keycodes[VK_DECIMAL] = STELA_KEY_KP_DECIMAL;
+ Global.keycodes[VK_OEM_PLUS] = STELA_KEY_KP_EQUAL;
+ Global.keycodes[VK_RETURN] = STELA_KEY_KP_RETURN;
- global.keycodes[VK_NUMPAD0] = STELA_KEY_KP_0;
- global.keycodes[VK_NUMPAD1] = STELA_KEY_KP_1;
- global.keycodes[VK_NUMPAD2] = STELA_KEY_KP_2;
- global.keycodes[VK_NUMPAD3] = STELA_KEY_KP_3;
- global.keycodes[VK_NUMPAD4] = STELA_KEY_KP_4;
- global.keycodes[VK_NUMPAD5] = STELA_KEY_KP_5;
- global.keycodes[VK_NUMPAD6] = STELA_KEY_KP_6;
- global.keycodes[VK_NUMPAD7] = STELA_KEY_KP_7;
- global.keycodes[VK_NUMPAD8] = STELA_KEY_KP_8;
- global.keycodes[VK_NUMPAD9] = STELA_KEY_KP_9;
- global.keycodes[VK_ADD] = STELA_KEY_KP_ADD;
- global.keycodes[VK_SUBTRACT] = STELA_KEY_KP_SUBTRACT;
- global.keycodes[VK_MULTIPLY] = STELA_KEY_KP_MULTIPLY;
- global.keycodes[VK_DIVIDE] = STELA_KEY_KP_DIVIDE;
- global.keycodes[VK_DECIMAL] = STELA_KEY_KP_DECIMAL;
- 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_LWIN] = STELA_KEY_LEFT_SUPER;
+ Global.keycodes[VK_RWIN] = STELA_KEY_RIGHT_SUPER;
+}
- 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_LWIN] = STELA_KEY_LEFT_SUPER;
- global.keycodes[VK_RWIN] = STELA_KEY_RIGHT_SUPER;
+//MONITORENUMPROC MonitorInfoProc;
+BOOL __stdcall MonitorInfoProc(HMONITOR Monitor, HDC DeviceContext, LPRECT Rect, LPARAM UserData)
+{
+ struct stl_monitor_win32 *monitor = al_alloc_object(struct stl_monitor_win32);
+ (void)DeviceContext;
+ (void)UserData;
+ monitor->M.width = Rect->right - Rect->left;
+ monitor->M.height = Rect->bottom - Rect->top;
+ monitor->M.scale = 1;
+ monitor->X = Rect->left;
+ monitor->Y = Rect->top;
+ monitor->Handle = Monitor;
+ al_array_push(Global.monitors, (struct stl_monitor *)monitor);
+ return TRUE;
}
bool stl_global_init(bool skip_graphics)
{
(void)skip_graphics;
+
+ Global.ModuleHandle = GetModuleHandleW(NULL);
+
+ HMODULE NTModule = LoadLibraryA("ntdll.dll");
+ if (!NTModule) return false;
+ if (GetProcAddress(NTModule, "wine_get_version") != NULL) {
+ log_info("Wine detected.");
+ Global.IsWine = TRUE;
+ } else {
+ Global.IsWine = FALSE;
+ }
+
+#ifdef STELA_API_VULKAN
+ Global.VKExtensions = al_calloc(2, sizeof(const char *const *));
+ Global.VKExtensions[0] = "VK_KHR_surface";
+ Global.VKExtensions[1] = "VK_KHR_win32_surface";
+#endif
+
+ al_array_init(Global.monitors);
+ EnumDisplayMonitors(NULL, NULL, MonitorInfoProc, 0);
+
set_keycodes_for_win32();
+
return true;
}
@@ -229,27 +289,36 @@ s32 stl_swallow_main(u32 argc, str *argv, s32 (*main)(u32, str *, void *), void
WNDCLASSEXW WindowClass = { 0 };
WindowClass.cbSize = sizeof(WindowClass);
WindowClass.lpfnWndProc = &ServiceWndProc;
- WindowClass.hInstance = GetModuleHandleW(NULL);
+ WindowClass.hInstance = Global.ModuleHandle;
WindowClass.hIcon = LoadIconA(NULL, IDI_APPLICATION);
WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW);
WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
WindowClass.lpszClassName = L"DTCClass";
RegisterClassExW(&WindowClass);
- global.ServiceWindow = CreateWindowExW(0, WindowClass.lpszClassName, L"DTCService", 0,
+ Global.ServiceWindow = CreateWindowExW(0, WindowClass.lpszClassName, L"DTCService", 0,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, WindowClass.hInstance, 0);
- global.argc = argc;
- global.argv = argv;
- global.extra = extra;
- global.main = main;
+ if (!Global.ServiceWindow) {
+ log_error("Failed to create service window (0x%lx).", GetLastError());
+ UnregisterClassW(WindowClass.lpszClassName, Global.ModuleHandle);
+ return EXIT_FAILURE;
+ }
+ Global.argc = argc;
+ Global.argv = argv;
+ Global.extra = extra;
+ Global.main = main;
- CreateThread(0, 0, MainThread, NULL, 0, &MainThreadID);
+ HANDLE Thread = CreateThread(0, 0, MainThread, NULL, 0, &MainThreadID);
for (;;) {
MSG Message;
GetMessageW(&Message, 0, 0, 0);
TranslateMessage(&Message);
if (Message.message == WM_QUIT) {
+ WaitForSingleObject(Thread, INFINITE);
+ CloseHandle(Thread);
+ DestroyWindow(Global.ServiceWindow);
+ UnregisterClassW(WindowClass.lpszClassName, Global.ModuleHandle);
return Message.wParam;
}
DispatchMessageW(&Message);
@@ -258,68 +327,552 @@ s32 stl_swallow_main(u32 argc, str *argv, s32 (*main)(u32, str *, void *), void
void stl_global_close(void)
{
+#ifdef STELA_API_VULKAN
+ al_free(Global.VKExtensions);
+#endif
+ struct stl_monitor *monitor;
+ al_array_foreach(Global.monitors, i, monitor) {
+ al_free((struct stl_window_win32 *)monitor);
+ }
+ al_array_free(Global.monitors);
+}
+
+#ifdef STELA_API_OPENGL
+// https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions
+static void *GetAnyGLFuncAddress(const char *Name)
+{
+ void *p = (void *)wglGetProcAddress(Name);
+ if (p == 0 || (p == (void *)0x1) || (p == (void *)0x2) || (p == (void *)0x3) || (p == (void *)-1)) {
+ HMODULE GLModule = LoadLibraryA("opengl32.dll");
+ p = (void *)GetProcAddress(GLModule, Name);
+ }
+ return p;
+}
+
+typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const s32 *);
+typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(s32);
+#endif
+
+//#define LOG_ALL_WND_MESSAGES
+#ifdef LOG_ALL_WND_MESSAGES
+// https://en.wikichip.org/wiki/List_of_Windows_Messages_-_Win32
+#define MESSAGE_CASE_STR(value) case value: return #value;
+static const char *WndMessageString(UINT Message)
+{
+ switch (Message) {
+ MESSAGE_CASE_STR(WM_NULL)
+ MESSAGE_CASE_STR(WM_CREATE)
+ MESSAGE_CASE_STR(WM_DESTROY)
+ MESSAGE_CASE_STR(WM_MOVE)
+ MESSAGE_CASE_STR(WM_SIZE)
+ MESSAGE_CASE_STR(WM_ACTIVATE)
+ MESSAGE_CASE_STR(WM_SETFOCUS)
+ MESSAGE_CASE_STR(WM_KILLFOCUS)
+ MESSAGE_CASE_STR(WM_ENABLE)
+ MESSAGE_CASE_STR(WM_SETREDRAW)
+ MESSAGE_CASE_STR(WM_SETTEXT)
+ MESSAGE_CASE_STR(WM_GETTEXT)
+ MESSAGE_CASE_STR(WM_GETTEXTLENGTH)
+ MESSAGE_CASE_STR(WM_PAINT)
+ MESSAGE_CASE_STR(WM_CLOSE)
+ MESSAGE_CASE_STR(WM_QUERYENDSESSION)
+ MESSAGE_CASE_STR(WM_QUERYOPEN)
+ MESSAGE_CASE_STR(WM_ENDSESSION)
+ MESSAGE_CASE_STR(WM_QUIT)
+ MESSAGE_CASE_STR(WM_ERASEBKGND)
+ MESSAGE_CASE_STR(WM_SYSCOLORCHANGE)
+ MESSAGE_CASE_STR(WM_SHOWWINDOW)
+ MESSAGE_CASE_STR(WM_WININICHANGE)
+ //MESSAGE_CASE_STR(WM_SETTINGCHANGE)
+ MESSAGE_CASE_STR(WM_DEVMODECHANGE)
+ MESSAGE_CASE_STR(WM_ACTIVATEAPP)
+ MESSAGE_CASE_STR(WM_FONTCHANGE)
+ MESSAGE_CASE_STR(WM_TIMECHANGE)
+ MESSAGE_CASE_STR(WM_CANCELMODE)
+ MESSAGE_CASE_STR(WM_SETCURSOR)
+ MESSAGE_CASE_STR(WM_MOUSEACTIVATE)
+ MESSAGE_CASE_STR(WM_CHILDACTIVATE)
+ MESSAGE_CASE_STR(WM_QUEUESYNC)
+ MESSAGE_CASE_STR(WM_GETMINMAXINFO)
+ MESSAGE_CASE_STR(WM_PAINTICON)
+ MESSAGE_CASE_STR(WM_ICONERASEBKGND)
+ MESSAGE_CASE_STR(WM_NEXTDLGCTL)
+ MESSAGE_CASE_STR(WM_SPOOLERSTATUS)
+ MESSAGE_CASE_STR(WM_DRAWITEM)
+ MESSAGE_CASE_STR(WM_MEASUREITEM)
+ MESSAGE_CASE_STR(WM_DELETEITEM)
+ MESSAGE_CASE_STR(WM_VKEYTOITEM)
+ MESSAGE_CASE_STR(WM_CHARTOITEM)
+ MESSAGE_CASE_STR(WM_SETFONT)
+ MESSAGE_CASE_STR(WM_GETFONT)
+ MESSAGE_CASE_STR(WM_SETHOTKEY)
+ MESSAGE_CASE_STR(WM_GETHOTKEY)
+ MESSAGE_CASE_STR(WM_QUERYDRAGICON)
+ MESSAGE_CASE_STR(WM_COMPAREITEM)
+ MESSAGE_CASE_STR(WM_GETOBJECT)
+ MESSAGE_CASE_STR(WM_COMPACTING)
+ MESSAGE_CASE_STR(WM_COMMNOTIFY)
+ MESSAGE_CASE_STR(WM_WINDOWPOSCHANGING)
+ MESSAGE_CASE_STR(WM_WINDOWPOSCHANGED)
+ MESSAGE_CASE_STR(WM_POWER)
+ MESSAGE_CASE_STR(WM_COPYDATA)
+ MESSAGE_CASE_STR(WM_CANCELJOURNAL)
+ MESSAGE_CASE_STR(WM_NOTIFY)
+ MESSAGE_CASE_STR(WM_INPUTLANGCHANGEREQUEST)
+ MESSAGE_CASE_STR(WM_INPUTLANGCHANGE)
+ MESSAGE_CASE_STR(WM_TCARD)
+ MESSAGE_CASE_STR(WM_HELP)
+ MESSAGE_CASE_STR(WM_USERCHANGED)
+ MESSAGE_CASE_STR(WM_NOTIFYFORMAT)
+ MESSAGE_CASE_STR(WM_CONTEXTMENU)
+ MESSAGE_CASE_STR(WM_STYLECHANGING)
+ MESSAGE_CASE_STR(WM_STYLECHANGED)
+ MESSAGE_CASE_STR(WM_DISPLAYCHANGE)
+ MESSAGE_CASE_STR(WM_GETICON)
+ MESSAGE_CASE_STR(WM_SETICON)
+ MESSAGE_CASE_STR(WM_NCCREATE)
+ MESSAGE_CASE_STR(WM_NCDESTROY)
+ MESSAGE_CASE_STR(WM_NCCALCSIZE)
+ MESSAGE_CASE_STR(WM_NCHITTEST)
+ MESSAGE_CASE_STR(WM_NCPAINT)
+ MESSAGE_CASE_STR(WM_NCACTIVATE)
+ MESSAGE_CASE_STR(WM_GETDLGCODE)
+ MESSAGE_CASE_STR(WM_SYNCPAINT)
+ MESSAGE_CASE_STR(WM_NCMOUSEMOVE)
+ MESSAGE_CASE_STR(WM_NCLBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_NCLBUTTONUP)
+ MESSAGE_CASE_STR(WM_NCLBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_NCRBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_NCRBUTTONUP)
+ MESSAGE_CASE_STR(WM_NCRBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_NCMBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_NCMBUTTONUP)
+ MESSAGE_CASE_STR(WM_NCMBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_NCXBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_NCXBUTTONUP)
+ MESSAGE_CASE_STR(WM_NCXBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_INPUT_DEVICE_CHANGE)
+ MESSAGE_CASE_STR(WM_INPUT)
+ //MESSAGE_CASE_STR(WM_KEYFIRST)
+ MESSAGE_CASE_STR(WM_KEYDOWN)
+ MESSAGE_CASE_STR(WM_KEYUP)
+ MESSAGE_CASE_STR(WM_CHAR)
+ MESSAGE_CASE_STR(WM_DEADCHAR)
+ MESSAGE_CASE_STR(WM_SYSKEYDOWN)
+ MESSAGE_CASE_STR(WM_SYSKEYUP)
+ MESSAGE_CASE_STR(WM_SYSCHAR)
+ MESSAGE_CASE_STR(WM_SYSDEADCHAR)
+ MESSAGE_CASE_STR(WM_UNICHAR)
+ //MESSAGE_CASE_STR(WM_KEYLAST)
+ MESSAGE_CASE_STR(WM_IME_STARTCOMPOSITION)
+ MESSAGE_CASE_STR(WM_IME_ENDCOMPOSITION)
+ MESSAGE_CASE_STR(WM_IME_COMPOSITION)
+ //MESSAGE_CASE_STR(WM_IME_KEYLAST)
+ MESSAGE_CASE_STR(WM_INITDIALOG)
+ MESSAGE_CASE_STR(WM_COMMAND)
+ MESSAGE_CASE_STR(WM_SYSCOMMAND)
+ MESSAGE_CASE_STR(WM_TIMER)
+ MESSAGE_CASE_STR(WM_HSCROLL)
+ MESSAGE_CASE_STR(WM_VSCROLL)
+ MESSAGE_CASE_STR(WM_INITMENU)
+ MESSAGE_CASE_STR(WM_INITMENUPOPUP)
+ MESSAGE_CASE_STR(WM_GESTURE)
+ MESSAGE_CASE_STR(WM_GESTURENOTIFY)
+ MESSAGE_CASE_STR(WM_MENUSELECT)
+ MESSAGE_CASE_STR(WM_MENUCHAR)
+ MESSAGE_CASE_STR(WM_ENTERIDLE)
+ MESSAGE_CASE_STR(WM_MENURBUTTONUP)
+ MESSAGE_CASE_STR(WM_MENUDRAG)
+ MESSAGE_CASE_STR(WM_MENUGETOBJECT)
+ MESSAGE_CASE_STR(WM_UNINITMENUPOPUP)
+ MESSAGE_CASE_STR(WM_MENUCOMMAND)
+ MESSAGE_CASE_STR(WM_CHANGEUISTATE)
+ MESSAGE_CASE_STR(WM_UPDATEUISTATE)
+ MESSAGE_CASE_STR(WM_QUERYUISTATE)
+ MESSAGE_CASE_STR(WM_CTLCOLORMSGBOX)
+ MESSAGE_CASE_STR(WM_CTLCOLOREDIT)
+ MESSAGE_CASE_STR(WM_CTLCOLORLISTBOX)
+ MESSAGE_CASE_STR(WM_CTLCOLORBTN)
+ MESSAGE_CASE_STR(WM_CTLCOLORDLG)
+ MESSAGE_CASE_STR(WM_CTLCOLORSCROLLBAR)
+ MESSAGE_CASE_STR(WM_CTLCOLORSTATIC)
+ //MESSAGE_CASE_STR(WM_MOUSEFIRST)
+ MESSAGE_CASE_STR(WM_MOUSEMOVE)
+ MESSAGE_CASE_STR(WM_LBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_LBUTTONUP)
+ MESSAGE_CASE_STR(WM_LBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_RBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_RBUTTONUP)
+ MESSAGE_CASE_STR(WM_RBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_MBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_MBUTTONUP)
+ MESSAGE_CASE_STR(WM_MBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_MOUSEWHEEL)
+ MESSAGE_CASE_STR(WM_XBUTTONDOWN)
+ MESSAGE_CASE_STR(WM_XBUTTONUP)
+ MESSAGE_CASE_STR(WM_XBUTTONDBLCLK)
+ MESSAGE_CASE_STR(WM_MOUSEHWHEEL)
+ //MESSAGE_CASE_STR(WM_MOUSELAST)
+ MESSAGE_CASE_STR(WM_PARENTNOTIFY)
+ MESSAGE_CASE_STR(WM_ENTERMENULOOP)
+ MESSAGE_CASE_STR(WM_EXITMENULOOP)
+ MESSAGE_CASE_STR(WM_NEXTMENU)
+ MESSAGE_CASE_STR(WM_SIZING)
+ MESSAGE_CASE_STR(WM_CAPTURECHANGED)
+ MESSAGE_CASE_STR(WM_MOVING)
+ MESSAGE_CASE_STR(WM_POWERBROADCAST)
+ MESSAGE_CASE_STR(WM_DEVICECHANGE)
+ MESSAGE_CASE_STR(WM_MDICREATE)
+ MESSAGE_CASE_STR(WM_MDIDESTROY)
+ MESSAGE_CASE_STR(WM_MDIACTIVATE)
+ MESSAGE_CASE_STR(WM_MDIRESTORE)
+ MESSAGE_CASE_STR(WM_MDINEXT)
+ MESSAGE_CASE_STR(WM_MDIMAXIMIZE)
+ MESSAGE_CASE_STR(WM_MDITILE)
+ MESSAGE_CASE_STR(WM_MDICASCADE)
+ MESSAGE_CASE_STR(WM_MDIICONARRANGE)
+ MESSAGE_CASE_STR(WM_MDIGETACTIVE)
+ MESSAGE_CASE_STR(WM_MDISETMENU)
+ MESSAGE_CASE_STR(WM_ENTERSIZEMOVE)
+ MESSAGE_CASE_STR(WM_EXITSIZEMOVE)
+ MESSAGE_CASE_STR(WM_DROPFILES)
+ MESSAGE_CASE_STR(WM_MDIREFRESHMENU)
+ MESSAGE_CASE_STR(WM_POINTERDEVICECHANGE)
+ MESSAGE_CASE_STR(WM_POINTERDEVICEINRANGE)
+ MESSAGE_CASE_STR(WM_POINTERDEVICEOUTOFRANGE)
+ MESSAGE_CASE_STR(WM_TOUCH)
+ MESSAGE_CASE_STR(WM_NCPOINTERUPDATE)
+ MESSAGE_CASE_STR(WM_NCPOINTERDOWN)
+ MESSAGE_CASE_STR(WM_NCPOINTERUP)
+ MESSAGE_CASE_STR(WM_POINTERUPDATE)
+ MESSAGE_CASE_STR(WM_POINTERDOWN)
+ MESSAGE_CASE_STR(WM_POINTERUP)
+ MESSAGE_CASE_STR(WM_POINTERENTER)
+ MESSAGE_CASE_STR(WM_POINTERLEAVE)
+ MESSAGE_CASE_STR(WM_POINTERACTIVATE)
+ MESSAGE_CASE_STR(WM_POINTERCAPTURECHANGED)
+ MESSAGE_CASE_STR(WM_TOUCHHITTESTING)
+ MESSAGE_CASE_STR(WM_POINTERWHEEL)
+ MESSAGE_CASE_STR(WM_POINTERHWHEEL)
+ MESSAGE_CASE_STR(WM_IME_SETCONTEXT)
+ MESSAGE_CASE_STR(WM_IME_NOTIFY)
+ MESSAGE_CASE_STR(WM_IME_CONTROL)
+ MESSAGE_CASE_STR(WM_IME_COMPOSITIONFULL)
+ MESSAGE_CASE_STR(WM_IME_SELECT)
+ MESSAGE_CASE_STR(WM_IME_CHAR)
+ MESSAGE_CASE_STR(WM_IME_REQUEST)
+ MESSAGE_CASE_STR(WM_IME_KEYDOWN)
+ MESSAGE_CASE_STR(WM_IME_KEYUP)
+ MESSAGE_CASE_STR(WM_MOUSEHOVER)
+ MESSAGE_CASE_STR(WM_MOUSELEAVE)
+ MESSAGE_CASE_STR(WM_NCMOUSEHOVER)
+ MESSAGE_CASE_STR(WM_NCMOUSELEAVE)
+ MESSAGE_CASE_STR(WM_WTSSESSION_CHANGE)
+ MESSAGE_CASE_STR(WM_TABLET_FIRST)
+ MESSAGE_CASE_STR(WM_TABLET_LAST)
+ MESSAGE_CASE_STR(WM_CUT)
+ MESSAGE_CASE_STR(WM_COPY)
+ MESSAGE_CASE_STR(WM_PASTE)
+ MESSAGE_CASE_STR(WM_CLEAR)
+ MESSAGE_CASE_STR(WM_UNDO)
+ MESSAGE_CASE_STR(WM_RENDERFORMAT)
+ MESSAGE_CASE_STR(WM_RENDERALLFORMATS)
+ MESSAGE_CASE_STR(WM_DESTROYCLIPBOARD)
+ MESSAGE_CASE_STR(WM_DRAWCLIPBOARD)
+ MESSAGE_CASE_STR(WM_PAINTCLIPBOARD)
+ MESSAGE_CASE_STR(WM_VSCROLLCLIPBOARD)
+ MESSAGE_CASE_STR(WM_SIZECLIPBOARD)
+ MESSAGE_CASE_STR(WM_ASKCBFORMATNAME)
+ MESSAGE_CASE_STR(WM_CHANGECBCHAIN)
+ MESSAGE_CASE_STR(WM_HSCROLLCLIPBOARD)
+ MESSAGE_CASE_STR(WM_QUERYNEWPALETTE)
+ MESSAGE_CASE_STR(WM_PALETTEISCHANGING)
+ MESSAGE_CASE_STR(WM_PALETTECHANGED)
+ MESSAGE_CASE_STR(WM_HOTKEY)
+ MESSAGE_CASE_STR(WM_PRINT)
+ MESSAGE_CASE_STR(WM_PRINTCLIENT)
+ MESSAGE_CASE_STR(WM_APPCOMMAND)
+ MESSAGE_CASE_STR(WM_THEMECHANGED)
+ MESSAGE_CASE_STR(WM_CLIPBOARDUPDATE)
+ MESSAGE_CASE_STR(WM_DWMCOMPOSITIONCHANGED)
+ MESSAGE_CASE_STR(WM_DWMNCRENDERINGCHANGED)
+ MESSAGE_CASE_STR(WM_DWMCOLORIZATIONCOLORCHANGED)
+ MESSAGE_CASE_STR(WM_DWMWINDOWMAXIMIZEDCHANGE)
+ MESSAGE_CASE_STR(WM_DWMSENDICONICTHUMBNAIL)
+ MESSAGE_CASE_STR(WM_DWMSENDICONICLIVEPREVIEWBITMAP)
+ MESSAGE_CASE_STR(WM_GETTITLEBARINFOEX)
+ MESSAGE_CASE_STR(WM_HANDHELDFIRST)
+ MESSAGE_CASE_STR(WM_HANDHELDLAST)
+ MESSAGE_CASE_STR(WM_AFXFIRST)
+ MESSAGE_CASE_STR(WM_AFXLAST)
+ MESSAGE_CASE_STR(WM_PENWINFIRST)
+ MESSAGE_CASE_STR(WM_PENWINLAST)
+ MESSAGE_CASE_STR(WM_APP)
+ MESSAGE_CASE_STR(WM_USER)
+ default: return "Unknown";
+ }
}
+#endif
+
+#define MESSAGE_TO_KEY_STATE(Message) \
+ ((Message == WM_KEYDOWN || Message == WM_SYSKEYDOWN) ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED)
static LRESULT CALLBACK DisplayWndProc(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam)
{
LRESULT Result = 0;
- /*
if (Message == WM_CREATE) {
SetWindowLongPtrA(Window, GWLP_USERDATA, (LONG_PTR)((LPCREATESTRUCT)LParam)->lpCreateParams);
- return Result;
}
struct stl_window_win32 *w32 = (struct stl_window_win32 *)GetWindowLongPtrA(Window, GWLP_USERDATA);
- */
+
+#ifdef LOG_ALL_WND_MESSAGES
+ al_printf("%s 0x%lx 0x%lx\n", WndMessageString(Message), WParam, LParam);
+#endif
+
+ // break = return DefWindowProc().
switch (Message) {
+ case WM_CREATE:
case WM_CLOSE:
+ // Special case with Window as WParam.
PostThreadMessageW(MainThreadID, Message, (WPARAM)Window, LParam);
break;
+ case WM_MOUSEACTIVATE: {
+ LRESULT HitTest = LOWORD(LParam);
+ UINT MouseButton = HIWORD(LParam);
+ if (MouseButton == WM_LBUTTONDOWN && HitTest == HTCLIENT) {
+ return MA_ACTIVATEANDEAT;
+ }
+ break;
+ }
+ 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:
+ case WM_NCRBUTTONDOWN: {
+ if (!Global.IsWine) {
+ POINT Pos;
+ Pos.x = GET_X_LPARAM(LParam);
+ Pos.y = GET_Y_LPARAM(LParam);
+ if (Message == WM_RBUTTONDOWN) {
+ ClientToScreen(w32->Window, &Pos);
+ }
+ w32->LastPos.x = Pos.x;
+ w32->LastPos.y = Pos.y;
+ w32->WindowMoving = TRUE;
+ SetCapture(w32->Window);
+ return Result;
+ } else {
+ /*
+ Message = WM_NCLBUTTONDOWN;
+ WParam = HTCAPTION;
+ */
+ // https://stackoverflow.com/a/35522584, incredible...
+ // https://gitlab.winehq.org/agusev/wine/-/blob/wine-971221/windows/nonclient.c#L1773
+ // This works on Wine but not Windows. Everything works the same except on Windows
+ // there must be a special case for LBUTTON.
+ SendMessageW(w32->Window, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, LParam);
+ }
+ break;
+ }
+ case WM_RBUTTONUP: {
+ if (!Global.IsWine) {
+ w32->WindowMoving = FALSE;
+ ReleaseCapture();
+ return Result;
+ }
+ break;
+ }
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ goto intercept;
+ case WM_MOUSELEAVE:
+ goto intercept;
case WM_MOUSEMOVE:
+ case WM_NCMOUSEMOVE: {
+ if ((WParam & MK_RBUTTON) && w32->WindowMoving) {
+ POINT Current;
+ GetCursorPos(&Current);
+ RECT Window;
+ GetWindowRect(w32->Window, &Window);
+ SetWindowPos(w32->Window, NULL,
+ Window.left + (Current.x - w32->LastPos.x),
+ Window.top + (Current.y - w32->LastPos.y),
+ 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
+ w32->LastPos.x = Current.x;
+ w32->LastPos.y = Current.y;
+ return Result;
+ }
+ goto intercept;
+ }
case WM_MOUSEWHEEL:
+ goto intercept;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
- case WM_LBUTTONDOWN:
- case WM_LBUTTONUP:
- case WM_SETFOCUS:
+ if (w32->w.key_immediate_callback) {
+ w32->w.key_immediate_callback(w32->w.userdata, MESSAGE_TO_KEY_STATE(Message), Global.keycodes[WParam]);
+ }
+ goto intercept;
case WM_SIZE:
- PostThreadMessageW(MainThreadID, Message, WParam, LParam);
- break;
- case WM_MOUSEACTIVATE:
- return MA_ACTIVATEANDEAT;
+ goto intercept;
default:
- Result = DefWindowProcW(Window, Message, WParam, LParam);
break;
}
+ return DefWindowProcW(Window, Message, WParam, LParam);
+intercept:
+ PostThreadMessageW(MainThreadID, Message, WParam, LParam);
return Result;
}
+#ifdef STELA_API_OPENGL
+static bool CreateWGLContext(struct stl_window_win32 *w32)
+{
+#define STELA_INCLUDE_DEPTH_BUFFER
+ PIXELFORMATDESCRIPTOR pfd = {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+#ifdef STELA_INCLUDE_DEPTH_BUFFER
+ PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+#else
+ PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_DEPTH_DONTCARE,
+#endif
+ PFD_TYPE_RGBA,
+ 32, // Colordepth of the framebuffer.
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0,
+ 0,
+ 0, 0, 0, 0,
+#ifdef STELA_INCLUDE_DEPTH_BUFFER
+ 24, // Bits for the depthbuffer.
+ 8, // Bits for the stencilbuffer.
+#else
+ 0,
+ 0,
+#endif
+ 0, // Number of "Aux" buffers.
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ w32->DeviceContext = GetDC(w32->Window);
+
+ s32 letWindowsChooseThisPixelFormat;
+ letWindowsChooseThisPixelFormat = ChoosePixelFormat(w32->DeviceContext, &pfd);
+ SetPixelFormat(w32->DeviceContext, letWindowsChooseThisPixelFormat, &pfd);
+
+ HGLRC FakeContext = wglCreateContext(w32->DeviceContext);
+ if (!FakeContext) {
+ log_error("Failed to create dummy OpenGL context (0x%lx).", GetLastError());
+ return false;
+ }
+
+ wglMakeCurrent(w32->DeviceContext, FakeContext);
+
+ PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
+ CreateContextAttribsARB = (void *)wglGetProcAddress("wglCreateContextAttribsARB");
+
+ PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT;
+ SwapIntervalEXT = (void *)wglGetProcAddress("wglSwapIntervalEXT");
+
+ w32->GLContext = NULL;
+ if (CreateContextAttribsARB) {
+ for (u32 i = 0; i < ARRAY_SIZE(stl_gl_vers); i++) {
+ s32 flags = stl_gl_vers[i].major >= 3 ? WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB : 0;
+ s32 mask = 0;
+ if (stl_gl_vers[i].api == STELA_GLES_API) {
+ mask |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT;
+ } else {
+ mask |= stl_gl_vers[i].profile == STELA_GL_CORE_PROFILE_BIT ?
+ WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
+ }
+ s32 attribs[] = {
+ WGL_CONTEXT_FLAGS_ARB, flags,
+ WGL_CONTEXT_PROFILE_MASK_ARB, mask,
+ WGL_CONTEXT_MAJOR_VERSION_ARB, stl_gl_vers[i].major,
+ WGL_CONTEXT_MINOR_VERSION_ARB, stl_gl_vers[i].minor,
+ 0, 0
+ };
+
+ log_info("Attempting to create a %s context of version %d.%d (GLSL %d).",
+ 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);
+
+ w32->GLContext = CreateContextAttribsARB(w32->DeviceContext, NULL, attribs);
+ if (w32->GLContext) break;
+
+ log_warn("Failed to create context (0x%lx).", GetLastError());
+ }
+
+ wglDeleteContext(FakeContext);
+ wglMakeCurrent(w32->DeviceContext, w32->GLContext);
+ } else {
+#ifndef STELA_USE_GLES
+ w32->GLContext = FakeContext;
+#endif
+ }
+
+ if (!w32->GLContext) {
+ log_error("Couldn't create an OpenGL context.");
+ return false;
+ }
+
+ if (SwapIntervalEXT) {
+ SwapIntervalEXT((w32->w.flags & STELA_WINDOW_VSYNC) ? 1 : 0);
+ }
+ wglMakeCurrent(w32->DeviceContext, NULL);
+
+ return true;
+}
+#endif
+
static bool window_win32_create_window(struct stl_window *window, u32 width, u32 height,
const char *monitor, const char *name, u32 flags)
{
struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
(void)monitor;
- (void)flags;
+ // @TODO: Make a function for this redundant stuff (include fullscreen).
w32->w.width = width;
- w32->w.width = height;
+ w32->w.height = height;
+ w32->w.flags = flags;
WNDCLASSEXW WindowClass = { 0 };
WindowClass.cbSize = sizeof(WindowClass);
WindowClass.lpfnWndProc = &DisplayWndProc;
- WindowClass.hInstance = GetModuleHandleW(NULL);
+ WindowClass.hInstance = Global.ModuleHandle;
WindowClass.hIcon = LoadIconA(NULL, IDI_APPLICATION);
WindowClass.hCursor = LoadCursorA(NULL, IDC_ARROW);
WindowClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
- WindowClass.lpszClassName = L"Dangerous Class";
+ WindowClass.lpszClassName = DangerousClass;
+ // https://learn.microsoft.com/en-us/windows/win32/winmsg/window-class-styles
+ WindowClass.style = CS_OWNDC;
RegisterClassExW(&WindowClass);
- RECT Rect = { 0, 0, width, height };
- AdjustWindowRect(&Rect, WS_OVERLAPPEDWINDOW, false);
+ // Add in window decorations so width and height reference the client area.
+ RECT Over = { 0, 0, width, height };
+ AdjustWindowRect(&Over, WS_OVERLAPPEDWINDOW, FALSE);
struct the_baby Baby = { 0 };
Baby.dwExStyle = 0;
@@ -329,14 +882,24 @@ static bool window_win32_create_window(struct stl_window *window, u32 width, u32
Baby.dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
Baby.X = CW_USEDEFAULT;
Baby.Y = CW_USEDEFAULT;
- Baby.nWidth = Rect.right - Rect.left;
- Baby.nHeight = Rect.bottom - Rect.top;
+ Baby.nWidth = Over.right - Over.left;
+ Baby.nHeight = Over.bottom - Over.top;
Baby.hInstance = WindowClass.hInstance;
Baby.lpParam = w32;
- w32->Window = (HWND)SendMessageW(global.ServiceWindow, CREATE_DANGEROUS_WINDOW, (WPARAM)&Baby, 0);
- w32->w.win32_window = w32->Window;
+ w32->Window = (HWND)SendMessageW(Global.ServiceWindow, CREATE_DANGEROUS_WINDOW, (WPARAM)&Baby, 0);
+ if (!w32->Window) {
+ log_error("Failed to create window (0x%lx).", GetLastError());
+ return false;
+ }
+#if defined STELA_API_OPENGL
+ return CreateWGLContext(w32);
+#elif defined STELA_API_VULKAN
return true;
+#elif defined STELA_API_DX11
+ w32->w.win32_window = w32->Window;
+ return true;
+#endif
}
static void window_win32_resize_internal(struct stl_window *window, u32 width, u32 height)
@@ -353,20 +916,114 @@ static void window_win32_resize(struct stl_window *window, u32 width, u32 height
(void)height;
}
+static struct stl_monitor_win32 *get_monitor_from_handle(HMONITOR Handle)
+{
+ struct stl_monitor *monitor;
+ al_array_foreach(Global.monitors, i, monitor) {
+ if (((struct stl_monitor_win32 *)monitor)->Handle == Handle) {
+ return (struct stl_monitor_win32 *)monitor;
+ }
+ }
+ return NULL;
+}
+
static void window_win32_toggle_fullscreen(struct stl_window *window)
{
- (void)window;
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
+ if (!w32->w.fullscreen) {
+ RECT Prev;
+ GetWindowRect(w32->Window, &Prev);
+ // https://learn.microsoft.com/en-us/windows/win32/gdi/positioning-objects-on-a-multiple-display-setup
+ HMONITOR Nearest = MonitorFromRect(&Prev, MONITOR_DEFAULTTONEAREST);
+ struct stl_monitor_win32 *Monitor = get_monitor_from_handle(Nearest);
+ w32->Prev.Monitor = Monitor;
+ w32->Prev.X = Prev.left;
+ w32->Prev.Y = Prev.top;
+ w32->Prev.Width = Prev.right - Prev.left;
+ w32->Prev.Height = Prev.bottom - Prev.top;
+ LONG Style = GetWindowLong(w32->Window, GWL_STYLE);
+ w32->Prev.Style = Style;
+ // https://stackoverflow.com/a/2400467
+ Style &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
+ SetWindowLong(w32->Window, GWL_STYLE, Style);
+ while (ShowCursor(FALSE) >= 0) {}
+ SetWindowPos(w32->Window, HWND_TOPMOST, Monitor->X, Monitor->Y, (s32)Monitor->M.width, (s32)Monitor->M.height, SWP_FRAMECHANGED);
+ } else {
+ SetWindowLong(w32->Window, GWL_STYLE, w32->Prev.Style);
+ struct stl_monitor_win32 *Monitor = w32->Prev.Monitor;
+ s32 X = w32->Prev.X - Monitor->X;
+ s32 Y = w32->Prev.Y - Monitor->Y;
+ ShowCursor(TRUE);
+ SetWindowPos(w32->Window, HWND_NOTOPMOST, X, Y, w32->Prev.Width, w32->Prev.Height, SWP_FRAMECHANGED);
+ }
+ w32->w.fullscreen = !w32->w.fullscreen;
}
static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message)
{
switch (Message->message) {
+ case WM_CREATE:
+ break;
case WM_CLOSE:
+#ifdef STELA_API_OPENGL
+ wglDeleteContext(w32->GLContext); // Makes context not current.
+#endif
stl_window_send_should_close_event(&w32->w);
break;
+ case WM_LBUTTONDOWN: {
+ /*
+ TRACKMOUSEEVENT TrackMouse = { 0 };
+ TrackMouse.cbSize = sizeof(TRACKMOUSEEVENT);
+ TrackMouse.dwFlags = TME_LEAVE;
+ TrackMouse.hwndTrack = w32->Window;
+ TrackMouse.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&TrackMouse);
+ */
+ w32->MouseButtonMask |= STELA_MOUSE1;
+ stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE1);
+ break;
+ }
+ case WM_LBUTTONUP: {
+ /*
+ TRACKMOUSEEVENT TrackMouse = { 0 };
+ TrackMouse.cbSize = sizeof(TRACKMOUSEEVENT);
+ TrackMouse.dwFlags = TME_CANCEL | TME_LEAVE;
+ TrackMouse.hwndTrack = w32->Window;
+ TrackMouse.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&TrackMouse);
+ */
+ w32->MouseButtonMask &= ~STELA_MOUSE1;
+ stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE1);
+ break;
+ }
+ case WM_MBUTTONDOWN:
+ stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE3);
+ break;
+ case WM_MBUTTONUP:
+ stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE3);
+ break;
+ case WM_MOUSELEAVE: {
+ /*
+ if (w32->MouseButtonMask & STELA_MOUSE1) {
+ w32->MouseButtonMask &= ~STELA_MOUSE1;
+ stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE1);
+ }
+ */
+ break;
+ }
case WM_MOUSEMOVE:
- stl_window_send_pointer_pos_event(&w32->w, (f64)GET_X_LPARAM(Message->lParam), (f64)GET_Y_LPARAM(Message->lParam));
+ case WM_NCMOUSEMOVE: {
+ POINT Pos;
+ Pos.x = GET_X_LPARAM(Message->lParam);
+ Pos.y = GET_Y_LPARAM(Message->lParam);
+ /*
+ if (!(w32->MouseButtonMask & STELA_MOUSE1)) {
+ ScreenToClient(w32->Window, &Pos);
+ }
+ */
+ stl_window_send_pointer_pos_event(&w32->w, (f64)Pos.x, (f64)Pos.y);
break;
+ }
case WM_MOUSEWHEEL:
stl_window_send_scroll_event(&w32->w, -(f64)GET_WHEEL_DELTA_WPARAM(Message->wParam));
break;
@@ -374,25 +1031,15 @@ static void win32_handle_message(struct stl_window_win32 *w32, MSG *Message)
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP: {
- //s32 scancode = (HIWORD(Message->lParam) & (KF_EXTENDED | 0xff));
/* 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);
}
*/
- //al_printf("0x%x\n", Message->wParam);
- u8 state = (Message->message == WM_KEYDOWN || Message->message == WM_SYSKEYDOWN) ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
- stl_window_send_key_event(&w32->w, state, global.keycodes[Message->wParam]);
+ stl_window_send_key_event(&w32->w, MESSAGE_TO_KEY_STATE(Message->message), Global.keycodes[Message->wParam]);
break;
}
- case WM_LBUTTONDOWN:
- stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_PRESSED, STELA_MOUSE1);
- break;
- case WM_LBUTTONUP:
- stl_window_send_mouse_button_event(&w32->w, STELA_BUTTON_RELEASED, STELA_MOUSE1);
- break;
- case WM_SETFOCUS:
- break;
case WM_SIZE:
w32->w.width = LOWORD(Message->lParam);
w32->w.height = HIWORD(Message->lParam);
@@ -445,17 +1092,98 @@ static bool window_win32_should_refresh(struct stl_window *window)
static void window_win32_free(struct stl_window **window)
{
- struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
- SendMessageW(global.ServiceWindow, DESTROY_DANGEROUS_WINDOW, (WPARAM)w32->Window, 0);
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)*window;
+ SendMessageW(Global.ServiceWindow, DESTROY_DANGEROUS_WINDOW, (WPARAM)w32->Window, 0);
al_wstr_free(&w32->WindowName);
+#ifdef STELA_EVENT_BUFFER
+ al_free(wl->w.data);
+#endif
+ al_free(w32);
+ *window = NULL;
+}
+
+#if defined STELA_API_VULKAN
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetVKProcAddress(VkInstance Inst, const char *Name)
+{
+ HMODULE VKModule = LoadLibraryA("vulkan-1.dll");
+ PFN_vkGetInstanceProcAddr GetInstanceProcAddr = (void *)GetProcAddress(VKModule, "vkGetInstanceProcAddr");
+ PFN_vkVoidFunction proc = (PFN_vkVoidFunction)GetInstanceProcAddr(Inst, Name);
+ return proc;
+}
+
+static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL window_win32_get_vk_proc_address(VkInstance inst, const char *name)
+{
+ // https://github.com/glfw/glfw/blob/e7ea71be039836da3a98cea55ae5569cb5eb885c/src/vulkan.c#L259
+ if (strcmp(name, "vkGetInstanceProcAddr") == 0) {
+ return (PFN_vkVoidFunction)GetVKProcAddress;
+ }
+ return GetVKProcAddress(inst, name);
+}
+
+static VkResult window_win32_vk_create_surface(void *window, VkInstance inst, VkSurfaceKHR *surface)
+{
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
+
+ PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR;
+ CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)GetVKProcAddress(inst, "vkCreateWin32SurfaceKHR");
+ if (!CreateWin32SurfaceKHR) {
+ 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;
+
+ VkResult err = CreateWin32SurfaceKHR(inst, &info, NULL, surface);
+ return err;
+}
+
+static const char *const *window_win32_vk_get_extensions(u32 *num)
+{
+ *num = 2;
+ return Global.VKExtensions;
}
+#elif defined STELA_API_OPENGL
+static bool window_win32_gl_loader_load(void *window)
+{
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
+ return stl_gl_loader_load(w32->w.get_gl_proc_address);
+}
+
+static bool window_win32_gl_make_current(void *window)
+{
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
+ if (w32->GLContext != w32->CurrentContext) {
+ w32->CurrentContext = w32->GLContext;
+ return wglMakeCurrent(w32->DeviceContext, w32->GLContext);
+ }
+ return true;
+}
+
+static void window_win32_gl_release_current(void *window)
+{
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
+ al_assert(w32->GLContext == w32->CurrentContext);
+#if 1
+ w32->CurrentContext = NULL;
+ wglMakeCurrent(w32->DeviceContext, NULL);
+#endif
+}
+
+static void window_win32_gl_swap_buffers(void *window)
+{
+ struct stl_window_win32 *w32 = (struct stl_window_win32 *)window;
+ SwapBuffers(w32->DeviceContext);
+}
+#endif
struct stl_window *stl_window_create(void)
{
log_info("Creating Win32 window.");
struct stl_window_win32 *w32 = al_alloc_object(struct stl_window_win32);
stl_window_init(&w32->w);
- w32->w.fullscreen = true;
+ w32->w.fullscreen = false;
w32->w.create_window = window_win32_create_window;
w32->w.resize_internal = window_win32_resize_internal;
w32->w.resize = window_win32_resize;
@@ -471,5 +1199,16 @@ struct stl_window *stl_window_create(void)
w32->w.should_refresh = window_win32_should_refresh;
#endif
w32->w.free = window_win32_free;
+#if defined STELA_API_VULKAN
+ w32->w.get_vk_proc_address = window_win32_get_vk_proc_address;
+ w32->w.vk_create_surface = window_win32_vk_create_surface;
+ w32->w.vk_get_extensions = window_win32_vk_get_extensions;
+#elif defined STELA_API_OPENGL
+ w32->w.get_gl_proc_address = (void (*(*)(const char *))(void))GetAnyGLFuncAddress;
+ w32->w.gl_loader_load = window_win32_gl_loader_load;
+ w32->w.gl_make_current = window_win32_gl_make_current;
+ w32->w.gl_release_current = window_win32_gl_release_current;
+ w32->w.gl_swap_buffers = window_win32_gl_swap_buffers;
+#endif
return (struct stl_window *)w32;
}
diff --git a/src/window_win32.h b/src/window_win32.h
index f6f917d..d29a468 100644
--- a/src/window_win32.h
+++ b/src/window_win32.h
@@ -1,6 +1,7 @@
#pragma once
#include <al/wstr.h>
+#include <al/array.h>
#include <nnwt/windows.h>
// All extra Windows headers come after <nnwt/windows.h>.
#include <windowsx.h>
@@ -11,16 +12,45 @@
#define STELA_WIN32_KEY_MAX (VK_OEM_7 + 1)
struct stl_win32_global {
+ HINSTANCE ModuleHandle;
+ BOOL IsWine;
HWND ServiceWindow;
+#ifdef STELA_API_VULKAN
+ const char **VKExtensions;
+#endif
u16 keycodes[STELA_WIN32_KEY_MAX];
+ array(struct stl_monitor *) monitors;
u32 argc;
str *argv;
void *extra;
s32 (*main)(u32, str *, void *);
};
+struct stl_monitor_win32 {
+ struct stl_monitor M;
+ s32 X;
+ s32 Y;
+ HMONITOR Handle;
+};
+
struct stl_window_win32 {
struct stl_window w;
HWND Window;
wstr WindowName;
+ struct {
+ struct stl_monitor_win32 *Monitor;
+ s32 X;
+ s32 Y;
+ s32 Width;
+ s32 Height;
+ LONG Style;
+ } Prev;
+ BOOL WindowMoving;
+ POINTS LastPos;
+ u32 MouseButtonMask;
+#ifdef STELA_API_OPENGL
+ HDC DeviceContext;
+ HGLRC GLContext;
+ HGLRC CurrentContext;
+#endif
};
diff --git a/src/window_x11.c b/src/window_x11.c
index 8d6fe91..25c6cf2 100644
--- a/src/window_x11.c
+++ b/src/window_x11.c
@@ -14,7 +14,7 @@ static Atom ATOM_WM_DELETE_WINDOW;
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
-static struct stl_x11_global global;
+static struct stl_x11_global global = { 0 };
void stl_set_output_discovered_callback(void (*output_discovered)(void *, struct stl_monitor *), void *userdata)
{
@@ -252,11 +252,14 @@ static void handle_next_xevent(struct stl_window_x11 *xw)
stl_window_send_resize_event(&xw->w, xw->w.width, xw->w.height);
break;
case KeyPress:
- stl_window_send_key_event(&xw->w, STELA_BUTTON_PRESSED, xev.xkey.keycode);
- break;
- case KeyRelease:
- stl_window_send_key_event(&xw->w, STELA_BUTTON_RELEASED, xev.xkey.keycode);
+ case KeyRelease: {
+ u8 key_state = xev.type == KeyPress ? STELA_BUTTON_PRESSED : STELA_BUTTON_RELEASED;
+ if (xw->w.key_immediate_callback) {
+ xw->w.key_immediate_callback(xw->w.userdata, key_state, xev.xkey.keycode);
+ }
+ stl_window_send_key_event(&xw->w, key_state, xev.xkey.keycode);
break;
+ }
case ButtonPress:
if (xev.xbutton.button == Button1) {
stl_window_send_mouse_button_event(&xw->w, STELA_BUTTON_PRESSED, STELA_MOUSE1);
@@ -312,48 +315,28 @@ static void window_x11_free(struct stl_window **window)
*window = NULL;
}
-#if defined STELA_API_VULKAN
-static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL window_x11_get_vk_proc_address(VkInstance inst, const char *name)
-{
- return NULL;
-}
-
-static VkResult window_x11_vk_create_surface(void *data, VkInstance inst, VkSurfaceKHR *surface)
-{
- struct stl_window_x11 *xw = (struct stl_window_x11 *)data;
- return VK_SUCCESS;
-}
-
-static const char *const *window_x11_vk_get_extensions(u32 *num)
-{
- return NULL;
-}
-#elif defined STELA_API_OPENGL
-static bool window_x11_gl_loader_load(void *data)
+#if defined STELA_API_OPENGL
+static bool window_x11_gl_loader_load(void *window)
{
- struct stl_window_x11 *xw = (struct stl_window_x11 *)data;
- if (!stl_gl_loader_load(xw->w.get_gl_proc_address)) {
- return false;
- }
- return true;
+ struct stl_window_x11 *xw = (struct stl_window_x11 *)window;
+ return stl_gl_loader_load(xw->w.get_gl_proc_address);
}
-static bool window_x11_gl_make_current(void *data)
+static bool window_x11_gl_make_current(void *window)
{
- struct stl_window_x11 *xw = (struct stl_window_x11 *)data;
- stl_egl_make_current(&xw->egl);
- return true;
+ struct stl_window_x11 *xw = (struct stl_window_x11 *)window;
+ return stl_egl_make_current(&xw->egl);
}
-static void window_x11_gl_release_current(void *data)
+static void window_x11_gl_release_current(void *window)
{
- struct stl_window_x11 *xw = (struct stl_window_x11 *)data;
+ struct stl_window_x11 *xw = (struct stl_window_x11 *)window;
stl_egl_release_current(&xw->egl);
}
-static void window_x11_gl_swap_buffers(void *data)
+static void window_x11_gl_swap_buffers(void *window)
{
- struct stl_window_x11 *xw = (struct stl_window_x11 *)data;
+ struct stl_window_x11 *xw = (struct stl_window_x11 *)window;
stl_egl_swap_buffers(&xw->egl);
}
#endif
@@ -379,17 +362,12 @@ struct stl_window *stl_window_create(void)
xw->w.should_refresh = window_x11_should_refresh;
#endif
xw->w.free = window_x11_free;
-#if defined STELA_API_VULKAN
- xw->w.get_vk_proc_address = window_x11_get_vk_proc_address;
- xw->w.vk_create_surface = window_x11_vk_create_surface;
- xw->w.vk_get_extensions = window_x11_vk_get_extensions;
-#elif defined STELA_API_OPENGL
+#if defined STELA_API_OPENGL
xw->w.get_gl_proc_address = NULL;
xw->w.gl_loader_load = window_x11_gl_loader_load;
xw->w.gl_make_current = window_x11_gl_make_current;
xw->w.gl_release_current = window_x11_gl_release_current;
xw->w.gl_swap_buffers = window_x11_gl_swap_buffers;
-#elif defined STELA_API_DX11
#endif
return (struct stl_window *)xw;
}
diff --git a/subprojects/libalabaster.wrap b/subprojects/libalabaster.wrap
index 6ab8612..2a0e3f4 100644
--- a/subprojects/libalabaster.wrap
+++ b/subprojects/libalabaster.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libalabaster
-revision = 34a5e136b6c79092eee73857bbad25892fe8047a
+revision = 92d5840e1440fe45a150079fab5a39877589c20f
depth = 1
diff --git a/subprojects/libnaunet.wrap b/subprojects/libnaunet.wrap
index c6bd17a..f068c57 100644
--- a/subprojects/libnaunet.wrap
+++ b/subprojects/libnaunet.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libnaunet
-revision = ef86574ec5d859727695da65f683f0774a3b4460
+revision = 9fb3646511998310fc8c967c239803f38b2bdc5b
depth = 1
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 0000000..c0da2d1
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1 @@
+executable('window', ['window.c'], dependencies: stela, c_args: ['-DSTELA_FORCE_COMPATABILITY'])
diff --git a/tests/window.c b/tests/window.c
new file mode 100644
index 0000000..b185e6b
--- /dev/null
+++ b/tests/window.c
@@ -0,0 +1,313 @@
+#ifdef STELA_API_NULL
+#undef STELA_API_NULL
+#endif
+#ifndef STELA_API_OPENGL
+#define STELA_API_OPENGL
+#endif
+#define AL_LOG_SECTION "test_window"
+#include <al/log.h>
+#include <al/types.h>
+#include <al/array.h>
+#include <al/str.h>
+#include <stl/window.h>
+#include <stl/gl.h>
+#include <nnwt/common.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+#define DEFAULT_WIDTH 720
+#define DEFAULT_HEIGHT 480
+
+static struct stl_window *window;
+static s32 quit = 0;
+
+static bool dragging = false;
+static f64 last_mouse_x = 0.0;
+static f64 last_mouse_y = 0.0;
+
+static const GLfloat init_rotation[] = { -25.f, 35.f, 0.f };
+static GLfloat rotation[] = { init_rotation[0], init_rotation[1], init_rotation[2] };
+static GLfloat zoom = 0.5f;
+
+AL_IGNORE_WARNING("-Wunused-function")
+static inline GLfloat degrees_to_radians(GLfloat degrees)
+{
+ return degrees * M_PI / 180.f;
+}
+AL_IGNORE_WARNING_END
+
+static bool pointer_pos_callback(void *userdata, f64 x, f64 y)
+{
+ (void)userdata;
+
+ // There's probably a "standard" way to handle dragging here
+ // but this works well enough.
+ if (dragging) {
+ GLfloat dx = (GLfloat)x - (GLfloat)last_mouse_x;
+ GLfloat dy = (GLfloat)y - (GLfloat)last_mouse_y;
+ dx /= 2.f;
+ dy /= 2.f;
+ rotation[0] += dy;
+ rotation[1] += rotation[0] > 140.f && rotation[0] < 290.f ? -dx : dx;
+ if (rotation[0] > 360.f) rotation[0] -= 360.f;
+ else if (rotation[0] < 0.f) rotation[0] += 360.f;
+ if (rotation[1] > 360.f) rotation[1] -= 360.f;
+ else if (rotation[1] < 0.f) rotation[1] += 360.f;
+ if (rotation[2] > 360.f) rotation[2] -= 360.f;
+ else if (rotation[2] < 0.f) rotation[2] += 360.f;
+ }
+
+ last_mouse_x = x;
+ last_mouse_y = y;
+
+ return true;
+}
+
+static bool scroll_callback(void *userdata, f64 y)
+{
+ (void)userdata;
+ zoom += y / -2500.f;
+ if (zoom < 0.1f) zoom = 0.1f;
+ return true;
+}
+
+static bool mouse_button_callback(void *userdata, u8 state, u8 button)
+{
+ (void)userdata;
+ switch (button) {
+ case STELA_MOUSE1:
+ switch (state) {
+ case STELA_BUTTON_PRESSED:
+ dragging = true;
+ break;
+ case STELA_BUTTON_RELEASED:
+ dragging = false;
+ break;
+ }
+ break;
+ }
+ return false;
+}
+
+static bool key_callback(void *userdata, u8 state, u16 button)
+{
+ (void)userdata;
+ switch (state) {
+ case STELA_BUTTON_PRESSED:
+ switch (button) {
+ case STELA_KEY_Q:
+ quit = 1;
+ break;
+ case STELA_KEY_R:
+ rotation[0] = init_rotation[0];
+ rotation[1] = init_rotation[1];
+ rotation[2] = init_rotation[2];
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static void key_immediate_callback(void *userdata, u8 state, u16 button)
+{
+ (void)userdata;
+ switch (state) {
+ case STELA_BUTTON_PRESSED:
+ switch (button) {
+ case STELA_KEY_F:
+ window->toggle_fullscreen(window);
+ break;
+ }
+ break;
+ }
+}
+
+static void should_close_callback(void *userdata)
+{
+ (void)userdata;
+ quit = 1;
+}
+
+#ifdef STELA_API_OPENGL
+static GLfloat vertices[] = {
+ -1.f, -1.f, -1.f,
+ 1.f, -1.f, -1.f,
+ 1.f, 1.f, -1.f,
+ -1.f, 1.f, -1.f,
+ -1.f, -1.f, 1.f,
+ 1.f, -1.f, 1.f,
+ 1.f, 1.f, 1.f,
+ -1.f, 1.f, 1.f
+};
+
+#define EXPAND_RGB(c) \
+ ((c >> 16) & 0xff) / 255.f, \
+ ((c >> 8) & 0xff) / 255.f, \
+ (c & 0xff) / 255.f
+
+static const u32 c0 = 0x2F3C7A;
+static const GLfloat bg[3] = { EXPAND_RGB(c0) };
+
+static const u32 c1 = 0x0F1B26;
+static const GLfloat color1[3] = { EXPAND_RGB(c1) };
+
+static const u32 c2 = 0xC3A46F;
+static const GLfloat color2[3] = { EXPAND_RGB(c2) };
+
+//static const u32 c3 = 0x111419;
+static const GLfloat color3[3] = { EXPAND_RGB(c1) };
+
+static GLfloat colors[] = {
+ color1[0], color1[1], color1[2],
+ color1[0], color1[1], color1[2],
+ color1[0], color1[1], color1[2],
+ color1[0], color1[1], color1[2],
+ color2[0], color2[1], color2[2],
+ color3[0], color3[1], color3[2],
+ color3[0], color3[1], color3[2],
+ color3[0], color3[1], color3[2]
+};
+
+static GLubyte indices[] = {
+ 0, 3, 2, 1,
+ 2, 3, 7, 6,
+ 0, 4, 7, 3,
+ 1, 2, 6, 5,
+ 4, 5, 6, 7,
+ 0, 1, 5, 4
+};
+#endif
+
+s32 window_system_main(u32 argc, str *argv, void *extra)
+{
+ (void)extra;
+
+ window = stl_window_create();
+ window->pointer_pos_callback = pointer_pos_callback;
+ window->scroll_callback = scroll_callback;
+ window->mouse_button_callback = mouse_button_callback;
+ window->key_callback = key_callback;
+ window->key_immediate_callback = key_immediate_callback;
+ window->should_close_callback = should_close_callback;
+ window->userdata = NULL;
+
+ u32 flags = STELA_WINDOW_VSYNC;
+ if (!window->create_window(window, DEFAULT_WIDTH, DEFAULT_HEIGHT, "", "Stella", flags)) {
+ return EXIT_FAILURE;
+ }
+
+#ifdef STELA_API_OPENGL
+ if (!window->gl_loader_load(window)) {
+ log_error("Failed to load OpenGL.");
+ return EXIT_FAILURE;
+ }
+ if (!window->gl_make_current(window)) {
+ log_error("Failed to make thread current in OpenGL.");
+ }
+ const char *gl_version = (const char *)glGetString(GL_VERSION);
+ if (gl_version) {
+ log_info("GL context created: %s.", gl_version);
+ }
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glClearColor(1.f, 1.f, 1.f, 1.f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ window->gl_swap_buffers(window);
+ if (argc > 1 && al_str_cmp(&argv[1], &al_str_c("w"), 0, 1) == 0) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ }
+#endif
+
+ while (!quit) {
+ window->poll(window, true);
+ window->process_events(window);
+ if (window->should_refresh(window)) {
+#ifdef STELA_API_OPENGL
+ glViewport(0, 0, window->width, window->height);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ f32 w = window->width;
+ f32 h = window->height;
+ const f32 frame_ratio = 4.f / 3.f;
+ const f32 ratio = w / h;
+ if (frame_ratio > ratio) {
+ h = w / frame_ratio;
+ } else {
+ w = h * frame_ratio;
+ }
+ u32 x_offset = (window->width - w) / 2u;
+ u32 y_offset = (window->height - h) / 2u;
+
+ glScissor(x_offset, y_offset, (GLsizei)w, (GLsizei)h);
+ glEnable(GL_SCISSOR_TEST);
+ glClearColor(bg[0], bg[1], bg[2], 1.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glClearColor(1.f, 1.f, 1.f, 1.f);
+ glDisable(GL_SCISSOR_TEST);
+
+ glViewport(x_offset, y_offset, (GLsizei)w, (GLsizei)h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(-1.0, 1.0, -1.0, 1.0, -100.0, 100.0);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ if (frame_ratio > 1.f) {
+ glScalef(zoom / frame_ratio, zoom, zoom);
+ } else {
+ glScalef(zoom, zoom * frame_ratio, zoom);
+ }
+ glRotatef(rotation[0], 1.f, 0.f, 0.f);
+ glRotatef(rotation[1], 0.f, 1.f, 0.f);
+ glRotatef(rotation[2], 0.f, 0.f, 1.f);
+
+ glEnableClientState(GL_COLOR_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, vertices);
+ glColorPointer(3, GL_FLOAT, 0, colors);
+ glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, indices);
+
+ window->gl_swap_buffers(window);
+#endif
+ }
+ }
+
+#ifdef STELA_API_OPENGL
+ window->gl_release_current(window);
+#endif
+
+ window->free(&window);
+ al_assert(!window);
+
+ return EXIT_SUCCESS;
+}
+
+s32 main(s32 argc, char *argv[])
+{
+ if (!nn_common_init() || !stl_global_init(false)) {
+ return EXIT_FAILURE;
+ }
+
+ array(str) args;
+ al_array_init(args);
+ al_array_reserve(args, argc);
+ for (s32 i = 0; i < argc; i++) {
+ str arg;
+ al_str_from(&arg, argv[i]);
+ al_array_push(args, arg);
+ }
+
+ s32 ret = stl_swallow_main(args.count, args.data, window_system_main, NULL);
+ stl_global_close();
+
+ str *arg;
+ al_array_foreach_ptr(args, i, arg) {
+ al_str_free(arg);
+ }
+ al_array_free(args);
+
+ nn_common_close();
+
+ return ret;
+}