diff options
| author | 2025-05-25 18:37:30 -0400 | |
|---|---|---|
| committer | 2025-05-25 18:37:30 -0400 | |
| commit | 1f1b60ed59cd3b3d594cf16c51cb835cb912fb12 (patch) | |
| tree | 53effacd6993262e08f7be4f751129c8133ec224 | |
| parent | 7d4434ea4ceb1fa5c75f372a73ac6dfe5cb05b1d (diff) | |
| download | stela-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-- | .gitignore | 2 | ||||
| -rw-r--r-- | meson.build | 12 | ||||
| -rw-r--r-- | meson_options.txt | 4 | ||||
| -rw-r--r-- | src/egl_common.c | 26 | ||||
| -rw-r--r-- | src/egl_common.h | 2 | ||||
| -rw-r--r-- | src/gl_common.c | 1 | ||||
| -rw-r--r-- | src/gl_common.h | 21 | ||||
| -rw-r--r-- | src/gl_versions.h | 26 | ||||
| -rw-r--r-- | src/window.c | 4 | ||||
| -rw-r--r-- | src/window.h | 13 | ||||
| -rw-r--r-- | src/window_glfm.c | 23 | ||||
| -rw-r--r-- | src/window_glfw.c | 51 | ||||
| -rw-r--r-- | src/window_glfw.h | 20 | ||||
| -rw-r--r-- | src/window_kms.c | 23 | ||||
| -rw-r--r-- | src/window_wayland.c | 38 | ||||
| -rw-r--r-- | src/window_win32.c | 1071 | ||||
| -rw-r--r-- | src/window_win32.h | 30 | ||||
| -rw-r--r-- | src/window_x11.c | 62 | ||||
| -rw-r--r-- | subprojects/libalabaster.wrap | 2 | ||||
| -rw-r--r-- | subprojects/libnaunet.wrap | 2 | ||||
| -rw-r--r-- | tests/meson.build | 1 | ||||
| -rw-r--r-- | tests/window.c | 313 |
22 files changed, 1429 insertions, 318 deletions
@@ -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; +} |