diff options
Diffstat (limited to 'src')
| -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 |
15 files changed, 1098 insertions, 313 deletions
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; } |