From 9ea9ea7636d96d8e20a2c08dcba51a270c3ecba6 Mon Sep 17 00:00:00 2001 From: martinfouilleul Date: Tue, 21 Feb 2023 12:35:22 +0100 Subject: [PATCH] [surface] abstracted child window layer from win32 surfaces --- src/{win32_egl_surface.c => egl_surface.c} | 87 ++------------ src/glsl_shaders.h | 2 +- src/milepost.c | 2 +- src/wgl_surface.c | 127 +++++---------------- src/win32_app.c | 88 ++++++++++++++ src/win32_app.h | 7 +- todo.txt | 30 ++--- 7 files changed, 148 insertions(+), 195 deletions(-) rename src/{win32_egl_surface.c => egl_surface.c} (64%) diff --git a/src/win32_egl_surface.c b/src/egl_surface.c similarity index 64% rename from src/win32_egl_surface.c rename to src/egl_surface.c index d7b2fd9..ad94628 100644 --- a/src/win32_egl_surface.c +++ b/src/egl_surface.c @@ -19,8 +19,7 @@ typedef struct mg_egl_surface { mg_surface_data interface; - HWND hWnd; - vec2 contentsScaling; + mp_layer layer; EGLDisplay eglDisplay; EGLConfig eglConfig; @@ -46,7 +45,7 @@ void mg_egl_surface_destroy(mg_surface_data* interface) eglDestroyContext(surface->eglDisplay, surface->eglContext); eglDestroySurface(surface->eglDisplay, surface->eglSurface); - DestroyWindow(surface->hWnd); + mp_layer_cleanup(&surface->layer); free(surface); } @@ -72,75 +71,33 @@ void mg_egl_surface_swap_interval(mg_surface_data* interface, int swap) vec2 mg_egl_surface_contents_scaling(mg_surface_data* interface) { mg_egl_surface* surface = (mg_egl_surface*)interface; - return(surface->contentsScaling); + return(mp_layer_contents_scaling(&surface->layer)); } mp_rect mg_egl_surface_get_frame(mg_surface_data* interface) { - mp_rect res = {0}; mg_egl_surface* surface = (mg_egl_surface*)interface; - if(surface) - { - RECT rect = {0}; - GetClientRect(surface->hWnd, &rect); - - vec2 scale = surface->contentsScaling; - - res = (mp_rect){rect.left/scale.x, - rect.bottom/scale.y, - (rect.right - rect.left)/scale.x, - (rect.bottom - rect.top)/scale.y}; - } - return(res); + return(mp_layer_get_frame(&surface->layer)); } void mg_egl_surface_set_frame(mg_surface_data* interface, mp_rect frame) { mg_egl_surface* surface = (mg_egl_surface*)interface; - if(surface) - { - HWND parent = GetParent(surface->hWnd); - RECT parentContentRect; - GetClientRect(parent, &parentContentRect); - int parentHeight = parentContentRect.bottom - parentContentRect.top; - - SetWindowPos(surface->hWnd, - HWND_TOP, - frame.x * surface->contentsScaling.x, - parentHeight - (frame.y + frame.h) * surface->contentsScaling.y, - frame.w * surface->contentsScaling.x, - frame.h * surface->contentsScaling.y, - SWP_NOACTIVATE | SWP_NOZORDER); - } + mp_layer_set_frame(&surface->layer, frame); } void mg_egl_surface_set_hidden(mg_surface_data* interface, bool hidden) { mg_egl_surface* surface = (mg_egl_surface*)interface; - if(surface) - { - ShowWindow(surface->hWnd, hidden ? SW_HIDE : SW_NORMAL); - } + mp_layer_set_hidden(&surface->layer, hidden); } bool mg_egl_surface_get_hidden(mg_surface_data* interface) { - bool hidden = false; mg_egl_surface* surface = (mg_egl_surface*)interface; - if(surface) - { - hidden = !IsWindowVisible(surface->hWnd); - } - return(hidden); + return(mp_layer_get_hidden(&surface->layer)); } - -LRESULT mg_egl_surface_window_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) -{ - return(DefWindowProc(windowHandle, message, wParam, lParam)); -} - - mg_surface mg_egl_surface_create_for_window(mp_window window) { mg_surface res = mg_surface_nil(); @@ -148,31 +105,6 @@ mg_surface mg_egl_surface_create_for_window(mp_window window) mp_window_data* windowData = mp_window_ptr_from_handle(window); if(windowData) { - //NOTE(martin): create a child window for the surface - WNDCLASS childWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, - .lpfnWndProc = mg_egl_surface_window_proc, - .hInstance = GetModuleHandleW(NULL), - .lpszClassName = "egl_surface_window_class", - .hCursor = LoadCursor(0, IDC_ARROW)}; - - if(!RegisterClass(&childWindowClass)) - { - //TODO: error - } - - mp_rect frame = mp_window_get_content_rect(window); - - u32 dpi = GetDpiForWindow(windowData->win32.hWnd); - vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.}; - - HWND childWindow = CreateWindow("egl_surface_window_class", "Test", - WS_CHILD|WS_VISIBLE, - 0, 0, frame.w*contentsScaling.x, frame.h*contentsScaling.y, - windowData->win32.hWnd, - 0, - childWindowClass.hInstance, - 0); - mg_egl_surface* surface = malloc_type(mg_egl_surface); memset(surface, 0, sizeof(mg_egl_surface)); @@ -187,8 +119,7 @@ mg_surface mg_egl_surface_create_for_window(mp_window window) surface->interface.getHidden = mg_egl_surface_get_hidden; surface->interface.setHidden = mg_egl_surface_set_hidden; - surface->hWnd = childWindow; - surface->contentsScaling = contentsScaling; + mp_layer_init_for_window(&surface->layer, windowData); EGLAttrib displayAttribs[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE, @@ -217,7 +148,7 @@ mg_surface mg_egl_surface_create_for_window(mp_window window) EGLint const surfaceAttributes[] = {EGL_NONE}; surface->eglSurface = eglCreateWindowSurface(surface->eglDisplay, surface->eglConfig, - surface->hWnd, + mp_layer_native_surface(&surface->layer), surfaceAttributes); eglBindAPI(EGL_OPENGL_ES_API); diff --git a/src/glsl_shaders.h b/src/glsl_shaders.h index 8588131..a9dae71 100644 --- a/src/glsl_shaders.h +++ b/src/glsl_shaders.h @@ -2,7 +2,7 @@ * * file: glsl_shaders.h * note: string literals auto-generated by embed_text.py -* date: 20/022023 +* date: 21/022023 * **********************************************************************/ #ifndef __GLSL_SHADERS_H__ diff --git a/src/milepost.c b/src/milepost.c index aa2e44a..b1e7562 100644 --- a/src/milepost.c +++ b/src/milepost.c @@ -65,7 +65,7 @@ #endif #if MG_COMPILE_BACKEND_GLES - #include"win32_egl_surface.c" + #include"egl_surface.c" #endif #elif defined(OS_MACOS) diff --git a/src/wgl_surface.c b/src/wgl_surface.c index e04536c..6060f70 100644 --- a/src/wgl_surface.c +++ b/src/wgl_surface.c @@ -115,10 +115,9 @@ typedef struct mg_wgl_surface { mg_surface_data interface; - HWND hWnd; + mp_layer layer; HDC hDC; HGLRC glContext; - vec2 contentsScaling; //NOTE: this may be a bit wasteful to have one api struct per surface, but win32 docs says that loading procs // from different contexts might select different implementations (eg. depending on context version/pixel format) @@ -135,7 +134,7 @@ void mg_wgl_surface_destroy(mg_surface_data* interface) } wglDeleteContext(surface->glContext); - DestroyWindow(surface->hWnd); + mp_layer_cleanup(&surface->layer); free(surface); } @@ -163,69 +162,33 @@ void mg_wgl_surface_swap_interval(mg_surface_data* interface, int swap) vec2 mg_wgl_surface_contents_scaling(mg_surface_data* interface) { mg_wgl_surface* surface = (mg_wgl_surface*)interface; - return(surface->contentsScaling); + return(mp_layer_contents_scaling(&surface->layer)); } mp_rect mg_wgl_surface_get_frame(mg_surface_data* interface) { - mp_rect res = {0}; mg_wgl_surface* surface = (mg_wgl_surface*)interface; - if(surface) - { - RECT rect = {0}; - GetClientRect(surface->hWnd, &rect); - - vec2 scale = surface->contentsScaling; - - res = (mp_rect){rect.left/scale.x, - rect.bottom/scale.y, - (rect.right - rect.left)/scale.x, - (rect.bottom - rect.top)/scale.y}; - } - return(res); + return(mp_layer_get_frame(&surface->layer)); } void mg_wgl_surface_set_frame(mg_surface_data* interface, mp_rect frame) { mg_wgl_surface* surface = (mg_wgl_surface*)interface; - if(surface) - { - HWND parent = GetParent(surface->hWnd); - RECT parentContentRect; - GetClientRect(parent, &parentContentRect); - int parentHeight = parentContentRect.bottom - parentContentRect.top; - - SetWindowPos(surface->hWnd, - HWND_TOP, - frame.x * surface->contentsScaling.x, - parentHeight - (frame.y + frame.h) * surface->contentsScaling.y, - frame.w * surface->contentsScaling.x, - frame.h * surface->contentsScaling.y, - SWP_NOACTIVATE | SWP_NOZORDER); - } + mp_layer_set_frame(&surface->layer, frame); } void mg_wgl_surface_set_hidden(mg_surface_data* interface, bool hidden) { mg_wgl_surface* surface = (mg_wgl_surface*)interface; - if(surface) - { - ShowWindow(surface->hWnd, hidden ? SW_HIDE : SW_NORMAL); - } + mp_layer_set_hidden(&surface->layer, hidden); } bool mg_wgl_surface_get_hidden(mg_surface_data* interface) { - bool hidden = false; mg_wgl_surface* surface = (mg_wgl_surface*)interface; - if(surface) - { - hidden = !IsWindowVisible(surface->hWnd); - } - return(hidden); + return(mp_layer_get_hidden(&surface->layer)); } - void* mg_wgl_get_proc(const char* name) { void* p = wglGetProcAddress(name); @@ -242,11 +205,6 @@ void* mg_wgl_get_proc(const char* name) return(p); } -LRESULT mg_wgl_surface_window_proc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) -{ - return(DefWindowProc(windowHandle, message, wParam, lParam)); -} - mg_surface mg_wgl_surface_create_for_window(mp_window window) { mg_surface surfaceHandle = mg_surface_nil(); @@ -256,30 +214,22 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window) { wgl_init(); - //NOTE(martin): create a child window for the surface - WNDCLASS childWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, - .lpfnWndProc = mg_wgl_surface_window_proc, - .hInstance = GetModuleHandleW(NULL), - .lpszClassName = "wgl_surface_window_class", - .hCursor = LoadCursor(0, IDC_ARROW)}; + //NOTE: fill surface data and load api + mg_wgl_surface* surface = malloc_type(mg_wgl_surface); - if(!RegisterClass(&childWindowClass)) - { - //TODO: error - } + surface->interface.backend = MG_BACKEND_GL; + surface->interface.destroy = mg_wgl_surface_destroy; + surface->interface.prepare = mg_wgl_surface_prepare; + surface->interface.present = mg_wgl_surface_present; + surface->interface.swapInterval = mg_wgl_surface_swap_interval; + surface->interface.contentsScaling = mg_wgl_surface_contents_scaling; + surface->interface.getFrame = mg_wgl_surface_get_frame; + surface->interface.setFrame = mg_wgl_surface_set_frame; + surface->interface.getHidden = mg_wgl_surface_get_hidden; + surface->interface.setHidden = mg_wgl_surface_set_hidden; - mp_rect frame = mp_window_get_content_rect(window); - - u32 dpi = GetDpiForWindow(windowData->win32.hWnd); - vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.}; - - HWND childWindow = CreateWindow("wgl_surface_window_class", "Test", - WS_CHILD|WS_VISIBLE, - 0, 0, frame.w*contentsScaling.x, frame.h*contentsScaling.y, - windowData->win32.hWnd, - 0, - childWindowClass.hInstance, - 0); + mp_layer_init_for_window(&surface->layer, windowData); + surface->hDC = GetDC(surface->layer.hWnd); //NOTE(martin): create the pixel format and gl context PIXELFORMATDESCRIPTOR pixelFormatDesc = @@ -312,17 +262,16 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window) WGL_STENCIL_BITS_ARB, 8, 0}; - HDC hDC = GetDC(childWindow); u32 numFormats = 0; int pixelFormat = 0; - wglChoosePixelFormatARB(hDC, pixelFormatAttrs, 0, 1, &pixelFormat, &numFormats); + wglChoosePixelFormatARB(surface->hDC, pixelFormatAttrs, 0, 1, &pixelFormat, &numFormats); if(!pixelFormat) { //TODO: error } - SetPixelFormat(hDC, pixelFormat, &pixelFormatDesc); + SetPixelFormat(surface->hDC, pixelFormat, &pixelFormatDesc); int contextAttrs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 4, @@ -330,39 +279,19 @@ mg_surface mg_wgl_surface_create_for_window(mp_window window) WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0}; - HGLRC glContext = wglCreateContextAttribsARB(hDC, __mgWGLDummyContext.glContext, contextAttrs); + surface->glContext = wglCreateContextAttribsARB(surface->hDC, __mgWGLDummyContext.glContext, contextAttrs); - if(!glContext) + if(!surface->glContext) { //TODO error int error = GetLastError(); printf("error: %i\n", error); } - //NOTE: make gl context current - wglMakeCurrent(hDC, glContext); + //NOTE: make gl context current and load api + wglMakeCurrent(surface->hDC, surface->glContext); wglSwapIntervalEXT(1); - - //NOTE: fill surface data and load api - mg_wgl_surface* surface = malloc_type(mg_wgl_surface); - - surface->interface.backend = MG_BACKEND_GL; - surface->interface.destroy = mg_wgl_surface_destroy; - surface->interface.prepare = mg_wgl_surface_prepare; - surface->interface.present = mg_wgl_surface_present; - surface->interface.swapInterval = mg_wgl_surface_swap_interval; - surface->interface.contentsScaling = mg_wgl_surface_contents_scaling; - surface->interface.getFrame = mg_wgl_surface_get_frame; - surface->interface.setFrame = mg_wgl_surface_set_frame; - surface->interface.getHidden = mg_wgl_surface_get_hidden; - surface->interface.setHidden = mg_wgl_surface_set_hidden; - - surface->hWnd = childWindow; - surface->hDC = hDC; - surface->glContext = glContext; - surface->contentsScaling = contentsScaling; - - mg_gl_load_gl43(&surface->api, mg_wgl_get_proc); + mg_gl_load_gl43(&surface->api, mg_wgl_get_proc); surfaceHandle = mg_surface_alloc_handle((mg_surface_data*)surface); } diff --git a/src/win32_app.c b/src/win32_app.c index 27659e3..ef665c6 100644 --- a/src/win32_app.c +++ b/src/win32_app.c @@ -777,6 +777,94 @@ mp_rect mp_window_get_content_rect(mp_window window) return(rect); } +/////////////////////////////////////////// + +void mp_layer_init_for_window(mp_layer* layer, mp_window_data* window) +{ + //NOTE(martin): create a child window for the surface + WNDCLASS layerWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + .lpfnWndProc = DefWindowProc, + .hInstance = GetModuleHandleW(NULL), + .lpszClassName = "layer_window_class", + .hCursor = LoadCursor(0, IDC_ARROW)}; + + RegisterClass(&layerWindowClass); + + RECT parentRect; + GetClientRect(window->win32.hWnd, &parentRect); + int width = parentRect.right - parentRect.left; + int height = parentRect.bottom - parentRect.top; + + layer->hWnd = CreateWindow("layer_window_class", "layer", + WS_CHILD | WS_VISIBLE, + 0, 0, width, height, + window->win32.hWnd, + 0, + layerWindowClass.hInstance, + 0); +} + +void mp_layer_cleanup(mp_layer* layer) +{ + DestroyWindow(layer->hWnd); +} + +void* mp_layer_native_surface(mp_layer* layer) +{ + return((void*)layer->hWnd); +} + +vec2 mp_layer_contents_scaling(mp_layer* layer) +{ + u32 dpi = GetDpiForWindow(layer->hWnd); + vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.}; + return(contentsScaling); +} + +mp_rect mp_layer_get_frame(mp_layer* layer) +{ + RECT rect = {0}; + GetClientRect(layer->hWnd, &rect); + + vec2 scale = mp_layer_contents_scaling(layer); + + mp_rect res = {rect.left/scale.x, + rect.bottom/scale.y, + (rect.right - rect.left)/scale.x, + (rect.bottom - rect.top)/scale.y}; + return(res); +} + +void mp_layer_set_frame(mp_layer* layer, mp_rect frame) +{ + HWND parent = GetParent(layer->hWnd); + RECT parentContentRect; + + GetClientRect(parent, &parentContentRect); + int parentHeight = parentContentRect.bottom - parentContentRect.top; + + vec2 scale = mp_layer_contents_scaling(layer); + + SetWindowPos(layer->hWnd, + HWND_TOP, + frame.x * scale.x, + parentHeight - (frame.y + frame.h) * scale.y, + frame.w * scale.x, + frame.h * scale.y, + SWP_NOACTIVATE | SWP_NOZORDER); +} + +void mp_layer_set_hidden(mp_layer* layer, bool hidden) +{ + ShowWindow(layer->hWnd, hidden ? SW_HIDE : SW_NORMAL); +} + +bool mp_layer_get_hidden(mp_layer* layer) +{ + bool hidden = !IsWindowVisible(layer->hWnd); + return(hidden); +} + /////////////////////////////////////////// WIP /////////////////////////////////////////////// //TODO: this is thrown here for a quick test. We should: // - check for errors diff --git a/src/win32_app.h b/src/win32_app.h index 483d665..1a09bea 100644 --- a/src/win32_app.h +++ b/src/win32_app.h @@ -16,12 +16,17 @@ #define UNICODE #include + typedef struct win32_window_data { HWND hWnd; - } win32_window_data; +typedef struct mp_layer +{ + HWND hWnd; +} mp_layer; + #define MP_PLATFORM_WINDOW_DATA win32_window_data win32; typedef struct win32_app_data diff --git a/todo.txt b/todo.txt index 41e5c31..441b7f3 100644 --- a/todo.txt +++ b/todo.txt @@ -4,27 +4,24 @@ Overview [.] Make backend selection easier [.] option macros to select backend-specific APIs to include when building an app (ie, include gl_api.h when using gl backend) - [ ] error on bad option macro combinations - [ ] write doc about backend option macros + [/] error on bad option macro combinations + [/] write doc about backend option macros -[>] Image API and backend - [ ] Build image atlas on top - [ ] Baked fonts? - -[x] Allow different versions of GL/GLES to co-exist -[/] Allow selecting version of GL/GLES context when creating surface - - pass/set attributes when creating surface? +[?] could have an internal, per-platform mp_layer struct that handles resizing/hiding etc... + -> avoid duplication of frame/hiding and duplication of egl code. + [/] EGL surfaces: See how we can isolate platform-specific stuff and just deal with one egl impl... [ ] Check that we can make GLES and GL surfaces co-exist in the app [ ] Allow controlling surface overlaying -[/] could have an internal, per-platform mp_layer struct that handles resizing/hiding etc... - -> avoid duplication of frame/hiding and duplication of egl code. -[/] EGL surfaces: See how we can isolate platform-specific stuff and just deal with one egl impl... +[/] Allow selecting version of GL/GLES context when creating surface + - pass/set attributes when creating surface? [/] Automatic surface resizing -[/] Keep dummy window/dummy context around for gl context creation, and don't reload wgl functions every time [!] Make linking with libEGL optional, even if EGL backend is compiled in milepost? + - use dead_strip_dylib on osx? + - use /DELAYLOAD:lib on windows? + [!] Bundle examples with their own resources?? (e.g. avoiding clashes in metal libs files in bin directory) [!] Sort out gles contents scaling for high dpi on osx @@ -33,10 +30,13 @@ Overview [!] Fix canvas perf issue on OSX +[>] Image API and backend + [ ] Build image atlas on top + [ ] Baked fonts? + + [ ] Delegated drawing API+Impl - - [ ] Make building apps simpler [ ] single include path [ ] script for embedding dependencies / create app bundle