From 52538248d9db32022711e2917ea8c2726482b67a Mon Sep 17 00:00:00 2001 From: martinfouilleul Date: Fri, 12 May 2023 16:46:13 +0200 Subject: [PATCH] [win32, wip] compile and run simple window example --- examples/simpleWindow/build.bat | 4 +- examples/simpleWindow/main.c | 224 ++-- ext/angle_install_notes.md | 161 +-- src/gl_canvas.c | 1003 +++++++------- src/glsl_shaders.h | 469 ------- src/graphics_surface.c | 814 ++++++------ src/platform/platform_math.h | 53 +- src/win32_app.c | 2164 +++++++++++++++---------------- 8 files changed, 2218 insertions(+), 2674 deletions(-) delete mode 100644 src/glsl_shaders.h diff --git a/examples/simpleWindow/build.bat b/examples/simpleWindow/build.bat index 3e918f6..971b6e5 100644 --- a/examples/simpleWindow/build.bat +++ b/examples/simpleWindow/build.bat @@ -1,2 +1,2 @@ -set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext -cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.lib user32.lib opengl32.lib gdi32.lib /out:test.exe +set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext +cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib user32.lib opengl32.lib gdi32.lib /out:../../bin/example_window.exe diff --git a/examples/simpleWindow/main.c b/examples/simpleWindow/main.c index a80d837..c659847 100644 --- a/examples/simpleWindow/main.c +++ b/examples/simpleWindow/main.c @@ -1,113 +1,111 @@ -/************************************************************//** -* -* @file: main.cpp -* @author: Martin Fouilleul -* @date: 30/07/2022 -* @revision: -* -*****************************************************************/ -#include -#include - -#include"milepost.h" - -#define LOG_SUBSYSTEM "Main" - -int main() -{ - LogLevel(LOG_LEVEL_DEBUG); - - mp_init(); - - mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600}; - mp_window window = mp_window_create(rect, "test", 0); - - mp_window_bring_to_front(window); - mp_window_focus(window); - - while(!mp_should_quit()) - { - mp_pump_events(0); - mp_event event = {0}; - while(mp_next_event(&event)) - { - switch(event.type) - { - case MP_EVENT_WINDOW_CLOSE: - { - mp_request_quit(); - } break; - - case MP_EVENT_WINDOW_RESIZE: - { - printf("resized, rect = {%f, %f, %f, %f}\n", - event.frame.rect.x, - event.frame.rect.y, - event.frame.rect.w, - event.frame.rect.h); - } break; - - case MP_EVENT_WINDOW_MOVE: - { - printf("moved, rect = {%f, %f, %f, %f}\n", - event.frame.rect.x, - event.frame.rect.y, - event.frame.rect.w, - event.frame.rect.h); - } break; - - case MP_EVENT_MOUSE_MOVE: - { - printf("mouse moved, pos = {%f, %f}, delta = {%f, %f}\n", - event.move.x, - event.move.y, - event.move.deltaX, - event.move.deltaY); - } break; - - case MP_EVENT_MOUSE_WHEEL: - { - printf("mouse wheel, delta = {%f, %f}\n", - event.move.deltaX, - event.move.deltaY); - } break; - - case MP_EVENT_MOUSE_ENTER: - { - printf("mouse enter\n"); - } break; - - case MP_EVENT_MOUSE_LEAVE: - { - printf("mouse leave\n"); - } break; - - case MP_EVENT_MOUSE_BUTTON: - { - printf("mouse button %i: %i\n", - event.key.code, - event.key.action == MP_KEY_PRESS ? 1 : 0); - } break; - - case MP_EVENT_KEYBOARD_KEY: - { - printf("key %i: %s\n", - event.key.code, - event.key.action == MP_KEY_PRESS ? "press" : (event.key.action == MP_KEY_RELEASE ? "release" : "repeat")); - } break; - - case MP_EVENT_KEYBOARD_CHAR: - { - printf("entered char %s\n", event.character.sequence); - } break; - - default: - break; - } - } - } - - mp_terminate(); - - return(0); -} +/************************************************************//** +* +* @file: main.cpp +* @author: Martin Fouilleul +* @date: 30/07/2022 +* @revision: +* +*****************************************************************/ +#include +#include +#include + +#include"milepost.h" + +int main() +{ + mp_init(); + + mp_rect rect = {.x = 100, .y = 100, .w = 800, .h = 600}; + mp_window window = mp_window_create(rect, "test", 0); + + mp_window_bring_to_front(window); + mp_window_focus(window); + + while(!mp_should_quit()) + { + mp_pump_events(0); + mp_event *event = 0; + while((event = mp_next_event(mem_scratch())) != 0) + { + switch(event->type) + { + case MP_EVENT_WINDOW_CLOSE: + { + mp_request_quit(); + } break; + + case MP_EVENT_WINDOW_RESIZE: + { + printf("resized, rect = {%f, %f, %f, %f}\n", + event->frame.rect.x, + event->frame.rect.y, + event->frame.rect.w, + event->frame.rect.h); + } break; + + case MP_EVENT_WINDOW_MOVE: + { + printf("moved, rect = {%f, %f, %f, %f}\n", + event->frame.rect.x, + event->frame.rect.y, + event->frame.rect.w, + event->frame.rect.h); + } break; + + case MP_EVENT_MOUSE_MOVE: + { + printf("mouse moved, pos = {%f, %f}, delta = {%f, %f}\n", + event->move.x, + event->move.y, + event->move.deltaX, + event->move.deltaY); + } break; + + case MP_EVENT_MOUSE_WHEEL: + { + printf("mouse wheel, delta = {%f, %f}\n", + event->move.deltaX, + event->move.deltaY); + } break; + + case MP_EVENT_MOUSE_ENTER: + { + printf("mouse enter\n"); + } break; + + case MP_EVENT_MOUSE_LEAVE: + { + printf("mouse leave\n"); + } break; + + case MP_EVENT_MOUSE_BUTTON: + { + printf("mouse button %i: %i\n", + event->key.code, + event->key.action == MP_KEY_PRESS ? 1 : 0); + } break; + + case MP_EVENT_KEYBOARD_KEY: + { + printf("key %i: %s\n", + event->key.code, + event->key.action == MP_KEY_PRESS ? "press" : (event->key.action == MP_KEY_RELEASE ? "release" : "repeat")); + } break; + + case MP_EVENT_KEYBOARD_CHAR: + { + printf("entered char %s\n", event->character.sequence); + } break; + + default: + break; + } + } + mem_arena_clear(mem_scratch()); + } + + mp_terminate(); + + return(0); +} diff --git a/ext/angle_install_notes.md b/ext/angle_install_notes.md index fae4821..c49a07c 100644 --- a/ext/angle_install_notes.md +++ b/ext/angle_install_notes.md @@ -1,77 +1,84 @@ -## Angle install on macOS - -* Get ninja if needed: `brew install ninja` -* Get the `depot_tools`repo: `git clone https://chromium.googlesource.com/* chromium/tools/depot_tools.git` -* Set path: `export PATH=/path/to/depot_tools:$PATH` - -* Maybe necessary to fiddle with certificates here, otherwise `fetch angle` fails in the subsequent steps. - -``` -cd /Applications/Python\ 3.6 -sudo ./Install\ Certificates.command -``` -* Fetch angle: - -``` -mkdir angle -cd angle -fetch angle -``` -* Generate build config: `gn gen out/Debug` - - * To see available arguments: `gn args out/Debug --list` - * To change arguments: `gn args out/Debug` - -For example, to generate dwarf dsyms files, set: - -``` -enable_dsyms=true -use_debug_fission=true -symbol_level=2 -``` - -We also need to set `is_component_build=false` in order to have self-contained librarries. - -Then, build with `autoninja -C out/Debug`and wait until you pass out. - -## Angle install on windows - -* need Python3 (can install through win app store) -* need Windows SDK -* clone `depot_tools`: `git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git` -or download and unzip bundle at [https://storage.googleapis.com/chrome-infra/depot_tools.zip](https://storage.googleapis.com/chrome-infra/depot_tools.zip) -* set `depot_tools` in path env variable through control panel>System and security>system>advanced system settings -* run `gclient` in a cmd shell -* set `DEPOT_TOOLS_WIN_TOOLCHAIN=0` -* `mkdir angle` -* `cd angle` -* `fetch angle` -* wait a million years - -* if it fails when running `python3 third_party/depot_tools/download_from_google_storage.py ...` - -> open `DEPS` and change `third_party/depot_tools` to `../depot/tools` -* run `gclient sync` to complete previous step - -* `gn gen out/Debug` -* `gn args out/Debug` and edit arguments: - * `angle_enable_vulkan = false` - * `angle_build_tests = false` - * `is_component_build = false` - -* link with `libEGL.dll.lib` and `libGLESv2.dll.lib` -* put `libEGL.dll` and `libGLESv2.dll` in same directory as executable - -## To get debugging kinda working with renderdoc: - -Run `gn args out/Debug` and set - * `angle_enable_trace = true` - * `angle_enable_annotator_run_time_checks = true` - -* `autoninja -C out/Debug` -* wait a while - -In renderdoc, set env variables -`RENDERDOC_HOOK_EGL 0` (if you want to trace underlying native API) -`RENDERDOC_HOOK_EGL 1` (if you want to trace EGL calls. You also need to put `libEGL` in the renderdoc folder so it's found when capturing stuff. Unfortunately though, that seems to provoke crashes...) - -`ANGLE_ENABLE_DEBUG_MARKERS 1` (to turn on debug markers) +## Angle install on macOS + +* Get ninja if needed: `brew install ninja` +* Get the `depot_tools`repo: `git clone https://chromium.googlesource.com/* chromium/tools/depot_tools.git` +* Set path: `export PATH=/path/to/depot_tools:$PATH` + +* Maybe necessary to fiddle with certificates here, otherwise `fetch angle` fails in the subsequent steps. + +``` +cd /Applications/Python\ 3.6 +sudo ./Install\ Certificates.command +``` +* Fetch angle: + +``` +mkdir angle +cd angle +fetch angle +``` +* Generate build config: `gn gen out/Debug` + + * To see available arguments: `gn args out/Debug --list` + * To change arguments: `gn args out/Debug` + +For example, to generate dwarf dsyms files, set: + +``` +enable_dsyms=true +use_debug_fission=true +symbol_level=2 +``` + +We also need to set `is_component_build=false` in order to have self-contained librarries. + +Then, build with `autoninja -C out/Debug`and wait until you pass out. + +## Angle install on windows + +* need Python3 (can install through win app store) +* need Windows SDK +* clone `depot_tools`: `git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git` +or download and unzip bundle at [https://storage.googleapis.com/chrome-infra/depot_tools.zip](https://storage.googleapis.com/chrome-infra/depot_tools.zip) +* set `depot_tools` in path env variable through control panel>System and security>system>advanced system settings +* run `gclient` in a cmd shell +* set `DEPOT_TOOLS_WIN_TOOLCHAIN=0` +* `mkdir angle` +* `cd angle` +* `fetch angle` +* wait a million years + +* if it fails when running `python3 third_party/depot_tools/download_from_google_storage.py ...` + -> open `DEPS` and change `third_party/depot_tools` to `../depot_tools` +* run `gclient sync` to complete previous step + +* `gn gen out/Debug` +* `gn args out/Debug` and edit arguments: +``` +is_component_build = false +angle_build_tests = false +angle_enable_metal = false +angle_enable_d3d9 = false +angle_enable_gl = false +angle_enable_vulkan = false +``` + + +* `ninja -C out/Debug` +* link with `libEGL.dll.lib` and `libGLESv2.dll.lib` +* put `libEGL.dll` and `libGLESv2.dll` in same directory as executable + +## To get debugging kinda working with renderdoc: + +Run `gn args out/Debug` and set + * `angle_enable_trace = true` + * `angle_enable_annotator_run_time_checks = true` + +* `autoninja -C out/Debug` +* wait a while + +In renderdoc, set env variables +`RENDERDOC_HOOK_EGL 0` (if you want to trace underlying native API) +`RENDERDOC_HOOK_EGL 1` (if you want to trace EGL calls. You also need to put `libEGL` in the renderdoc folder so it's found when capturing stuff. Unfortunately though, that seems to provoke crashes...) + +`ANGLE_ENABLE_DEBUG_MARKERS 1` (to turn on debug markers) diff --git a/src/gl_canvas.c b/src/gl_canvas.c index 8f5d985..908c08a 100644 --- a/src/gl_canvas.c +++ b/src/gl_canvas.c @@ -1,498 +1,505 @@ -/************************************************************//** -* -* @file: gl_canvas.c -* @author: Martin Fouilleul -* @date: 29/01/2023 -* @revision: -* -*****************************************************************/ -#include"graphics_surface.h" -#include"macro_helpers.h" -#include"glsl_shaders.h" -#include"gl_api.h" - -typedef struct mg_gl_canvas_backend -{ - mg_canvas_backend interface; - mg_surface surface; - - GLuint vao; - GLuint dummyVertexBuffer; - GLuint vertexBuffer; - GLuint shapeBuffer; - GLuint indexBuffer; - GLuint tileCounterBuffer; - GLuint tileArrayBuffer; - GLuint clearCounterProgram; - GLuint tileProgram; - GLuint sortProgram; - GLuint drawProgram; - GLuint blitProgram; - - GLuint outTexture; - - char* indexMapping; - char* vertexMapping; - char* shapeMapping; - -} mg_gl_canvas_backend; - -typedef struct mg_gl_image -{ - mg_image_data interface; - - GLuint textureID; -} mg_gl_image; - -//NOTE: debugger -typedef struct debug_vertex -{ - vec4 cubic; - vec2 pos; - int shapeIndex; - u8 pad[4]; -} debug_vertex; - -typedef struct debug_shape -{ - vec4 color; - vec4 clip; - vec2 uv; - u8 pad[8]; -} debug_shape; - -#define LayoutNext(prevName, prevType, nextType) \ - AlignUpOnPow2(_cat3_(LAYOUT_, prevName, _OFFSET)+_cat3_(LAYOUT_, prevType, _SIZE), _cat3_(LAYOUT_, nextType, _ALIGN)) - -enum { - LAYOUT_VEC2_SIZE = 8, - LAYOUT_VEC2_ALIGN = 8, - LAYOUT_VEC4_SIZE = 16, - LAYOUT_VEC4_ALIGN = 16, - LAYOUT_INT_SIZE = 4, - LAYOUT_INT_ALIGN = 4, - LAYOUT_MAT2x3_SIZE = sizeof(float)*6, - LAYOUT_MAT2x3_ALIGN = 4, - - LAYOUT_CUBIC_OFFSET = 0, - LAYOUT_POS_OFFSET = LayoutNext(CUBIC, VEC4, VEC2), - LAYOUT_ZINDEX_OFFSET = LayoutNext(POS, VEC2, INT), - LAYOUT_VERTEX_ALIGN = 16, - LAYOUT_VERTEX_SIZE = LayoutNext(ZINDEX, INT, VERTEX), - - LAYOUT_COLOR_OFFSET = 0, - LAYOUT_CLIP_OFFSET = LayoutNext(COLOR, VEC4, VEC4), - LAYOUT_UV_TRANSFORM_OFFSET = LayoutNext(CLIP, VEC4, MAT2x3), - LAYOUT_SHAPE_ALIGN = 16, - LAYOUT_SHAPE_SIZE = LayoutNext(UV_TRANSFORM, MAT2x3, SHAPE), - - MG_GL_CANVAS_MAX_BUFFER_LENGTH = 1<<20, - MG_GL_CANVAS_MAX_SHAPE_BUFFER_SIZE = LAYOUT_SHAPE_SIZE * MG_GL_CANVAS_MAX_BUFFER_LENGTH, - MG_GL_CANVAS_MAX_VERTEX_BUFFER_SIZE = LAYOUT_VERTEX_SIZE * MG_GL_CANVAS_MAX_BUFFER_LENGTH, - MG_GL_CANVAS_MAX_INDEX_BUFFER_SIZE = LAYOUT_INT_SIZE * MG_GL_CANVAS_MAX_BUFFER_LENGTH, - - //TODO: actually size this dynamically - MG_GL_CANVAS_MAX_TILE_COUNT = 65536, //NOTE: this allows for 256*256 tiles (e.g. 4096*4096 pixels) - MG_GL_CANVAS_TILE_COUNTER_BUFFER_SIZE = LAYOUT_INT_SIZE * MG_GL_CANVAS_MAX_TILE_COUNT, - - MG_GL_CANVAS_TILE_ARRAY_LENGTH = 1<<10, // max overlapping triangles per tiles - MG_GL_CANVAS_TILE_ARRAY_BUFFER_SIZE = LAYOUT_INT_SIZE * MG_GL_CANVAS_MAX_TILE_COUNT * MG_GL_CANVAS_TILE_ARRAY_LENGTH, -}; - -void mg_gl_canvas_update_vertex_layout(mg_gl_canvas_backend* backend) -{ - backend->interface.vertexLayout = (mg_vertex_layout){ - .maxVertexCount = MG_GL_CANVAS_MAX_BUFFER_LENGTH, - .maxIndexCount = MG_GL_CANVAS_MAX_BUFFER_LENGTH, - .posBuffer = backend->vertexMapping + LAYOUT_POS_OFFSET, - .posStride = LAYOUT_VERTEX_SIZE, - .cubicBuffer = backend->vertexMapping + LAYOUT_CUBIC_OFFSET, - .cubicStride = LAYOUT_VERTEX_SIZE, - .shapeIndexBuffer = backend->vertexMapping + LAYOUT_ZINDEX_OFFSET, - .shapeIndexStride = LAYOUT_VERTEX_SIZE, - - .colorBuffer = backend->shapeMapping + LAYOUT_COLOR_OFFSET, - .colorStride = LAYOUT_SHAPE_SIZE, - .clipBuffer = backend->shapeMapping + LAYOUT_CLIP_OFFSET, - .clipStride = LAYOUT_SHAPE_SIZE, - .uvTransformBuffer = backend->shapeMapping + LAYOUT_UV_TRANSFORM_OFFSET, - .uvTransformStride = LAYOUT_SHAPE_SIZE, - - .indexBuffer = backend->indexMapping, - .indexStride = LAYOUT_INT_SIZE}; -} - -void mg_gl_send_buffers(mg_gl_canvas_backend* backend, int shapeCount, int vertexCount, int indexCount) -{ - glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->vertexBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_VERTEX_SIZE*vertexCount, backend->vertexMapping, GL_STREAM_DRAW); - - glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->shapeBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_SHAPE_SIZE*shapeCount, backend->shapeMapping, GL_STREAM_DRAW); - - glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->indexBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_INT_SIZE*indexCount, backend->indexMapping, GL_STREAM_DRAW); -} - -void mg_gl_canvas_begin(mg_canvas_backend* interface) -{ - mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -} - -void mg_gl_canvas_end(mg_canvas_backend* interface) -{ - //NOTE: nothing to do here... -} - -void mg_gl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor) -{ - mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; - - glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); - glClear(GL_COLOR_BUFFER_BIT); -} - -void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* imageInterface, u32 shapeCount, u32 vertexCount, u32 indexCount) -{ - mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; - -/*NOTE: if we want debug_vertex while debugging, the following ensures the struct def doesn't get stripped away - debug_vertex vertex; - debug_shape shape; - printf("foo %p, bar %p\n", &vertex, &shape); -//*/ - mg_gl_send_buffers(backend, shapeCount, vertexCount, indexCount); - - mp_rect frame = mg_surface_get_frame(backend->surface); - vec2 contentsScaling = mg_surface_contents_scaling(backend->surface); - - const int tileSize = 16; - const int tileCountX = (frame.w*contentsScaling.x + tileSize - 1)/tileSize; - const int tileCountY = (frame.h*contentsScaling.y + tileSize - 1)/tileSize; - const int tileArrayLength = MG_GL_CANVAS_TILE_ARRAY_LENGTH; - - //TODO: ensure there's enough space in tile buffer - - //NOTE: first clear counters - glUseProgram(backend->clearCounterProgram); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->tileCounterBuffer); - glDispatchCompute(tileCountX*tileCountY, 1, 1); - - //NOTE: we first distribute triangles into tiles: - - glUseProgram(backend->tileProgram); - - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->vertexBuffer); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->shapeBuffer); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, backend->indexBuffer); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, backend->tileCounterBuffer); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, backend->tileArrayBuffer); - - glUniform1ui(0, indexCount); - glUniform2ui(1, tileCountX, tileCountY); - glUniform1ui(2, tileSize); - glUniform1ui(3, tileArrayLength); - glUniform2f(4, contentsScaling.x, contentsScaling.y); - - u32 threadCount = indexCount/3; - glDispatchCompute((threadCount + 255)/256, 1, 1); - - //NOTE: next we sort triangles in each tile - glUseProgram(backend->sortProgram); - - glUniform1ui(0, indexCount); - glUniform2ui(1, tileCountX, tileCountY); - glUniform1ui(2, tileSize); - glUniform1ui(3, tileArrayLength); - - glDispatchCompute(tileCountX * tileCountY, 1, 1); - - //NOTE: then we fire the drawing shader that will select only triangles in its tile - glUseProgram(backend->drawProgram); - - glBindImageTexture(0, backend->outTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); - - glUniform1ui(0, indexCount); - glUniform2ui(1, tileCountX, tileCountY); - glUniform1ui(2, tileSize); - glUniform1ui(3, tileArrayLength); - glUniform2f(4, contentsScaling.x, contentsScaling.y); - - if(imageInterface) - { - //TODO: make sure this image belongs to that context - mg_gl_image* image = (mg_gl_image*)imageInterface; - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, image->textureID); - glUniform1ui(5, 1); - } - else - { - glUniform1ui(5, 0); - } - - glDispatchCompute(tileCountX, tileCountY, 1); - - //NOTE: now blit out texture to surface - glUseProgram(backend->blitProgram); - glBindBuffer(GL_ARRAY_BUFFER, backend->dummyVertexBuffer); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, backend->outTexture); - glUniform1i(0, 0); - - glDrawArrays(GL_TRIANGLES, 0, 6); - - mg_gl_canvas_update_vertex_layout(backend); -} - -void mg_gl_canvas_destroy(mg_canvas_backend* interface) -{ - mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; - - glDeleteTextures(1, &backend->outTexture); - - glDeleteBuffers(1, &backend->dummyVertexBuffer); - glDeleteBuffers(1, &backend->vertexBuffer); - glDeleteBuffers(1, &backend->shapeBuffer); - glDeleteBuffers(1, &backend->indexBuffer); - glDeleteBuffers(1, &backend->tileCounterBuffer); - glDeleteBuffers(1, &backend->tileArrayBuffer); - - glDeleteVertexArrays(1, &backend->vao); - - free(backend->shapeMapping); - free(backend->vertexMapping); - free(backend->indexMapping); - free(backend); -} - -mg_image_data* mg_gl_canvas_image_create(mg_canvas_backend* interface, vec2 size) -{ - mg_gl_image* image = 0; - - image = malloc_type(mg_gl_image); - if(image) - { - glGenTextures(1, &image->textureID); - glBindTexture(GL_TEXTURE_2D, image->textureID); -// glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, size.x, size.y); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - image->interface.size = size; - } - return((mg_image_data*)image); -} - -void mg_gl_canvas_image_destroy(mg_canvas_backend* interface, mg_image_data* imageInterface) -{ - //TODO: check that this image belongs to this context - mg_gl_image* image = (mg_gl_image*)imageInterface; - glDeleteTextures(1, &image->textureID); - free(image); -} - -void mg_gl_canvas_image_upload_region(mg_canvas_backend* interface, - mg_image_data* imageInterface, - mp_rect region, - u8* pixels) -{ - //TODO: check that this image belongs to this context - mg_gl_image* image = (mg_gl_image*)imageInterface; - glBindTexture(GL_TEXTURE_2D, image->textureID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, region.w, region.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); -} - -static int mg_gl_compile_shader(const char* name, GLuint shader, const char* source) -{ - int res = 0; - - const char* sources[3] = {"#version 430", glsl_common, source}; - - glShaderSource(shader, 3, sources, 0); - glCompileShader(shader); - - int status = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - if(!status) - { - char buffer[256]; - int size = 0; - glGetShaderInfoLog(shader, 256, &size, buffer); - printf("Shader compile error (%s): %.*s\n", name, size, buffer); - res = -1; - } - return(res); -} - -static int mg_gl_canvas_compile_compute_program_named(const char* name, const char* source, GLuint* outProgram) -{ - int res = 0; - *outProgram = 0; - - GLuint shader = glCreateShader(GL_COMPUTE_SHADER); - GLuint program = glCreateProgram(); - - res |= mg_gl_compile_shader(name, shader, source); - - if(!res) - { - glAttachShader(program, shader); - glLinkProgram(program); - - int status = 0; - glGetProgramiv(program, GL_LINK_STATUS, &status); - if(!status) - { - char buffer[256]; - int size = 0; - glGetProgramInfoLog(program, 256, &size, buffer); - log_error("Shader link error (%s): %.*s\n", name, size, buffer); - - res = -1; - } - else - { - *outProgram = program; - } - } - return(res); -} - -int mg_gl_canvas_compile_render_program_named(const char* progName, - const char* vertexName, - const char* fragmentName, - const char* vertexSrc, - const char* fragmentSrc, - GLuint* outProgram) -{ - int res = 0; - *outProgram = 0; - - GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); - GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - GLuint program = glCreateProgram(); - - res |= mg_gl_compile_shader(vertexName, vertexShader, vertexSrc); - res |= mg_gl_compile_shader(fragmentName, fragmentShader, fragmentSrc); - - if(!res) - { - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); - glLinkProgram(program); - - int status = 0; - glGetProgramiv(program, GL_LINK_STATUS, &status); - if(!status) - { - char buffer[256]; - int size = 0; - glGetProgramInfoLog(program, 256, &size, buffer); - log_error("Shader link error (%s): %.*s\n", progName, size, buffer); - res = -1; - } - else - { - *outProgram = program; - } - } - return(res); -} - -#define mg_gl_canvas_compile_compute_program(src, out) \ - mg_gl_canvas_compile_compute_program_named(#src, src, out) - -#define mg_gl_canvas_compile_render_program(progName, shaderSrc, vertexSrc, out) \ - mg_gl_canvas_compile_render_program_named(progName, #shaderSrc, #vertexSrc, shaderSrc, vertexSrc, out) - -mg_canvas_backend* mg_gl_canvas_create(mg_surface surface) -{ - mg_gl_canvas_backend* backend = 0; - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - - int err = 0; - - if(surfaceData && surfaceData->backend == MG_BACKEND_GL) - { - backend = malloc_type(mg_gl_canvas_backend); - memset(backend, 0, sizeof(mg_gl_canvas_backend)); - backend->surface = surface; - - //NOTE(martin): setup interface functions - backend->interface.destroy = mg_gl_canvas_destroy; - backend->interface.begin = mg_gl_canvas_begin; - backend->interface.end = mg_gl_canvas_end; - backend->interface.clear = mg_gl_canvas_clear; - backend->interface.drawBatch = mg_gl_canvas_draw_batch; - backend->interface.imageCreate = mg_gl_canvas_image_create; - backend->interface.imageDestroy = mg_gl_canvas_image_destroy; - backend->interface.imageUploadRegion = mg_gl_canvas_image_upload_region; - - mg_surface_prepare(surface); - - glGenVertexArrays(1, &backend->vao); - glBindVertexArray(backend->vao); - - glGenBuffers(1, &backend->dummyVertexBuffer); - glBindBuffer(GL_ARRAY_BUFFER, backend->dummyVertexBuffer); - - glGenBuffers(1, &backend->vertexBuffer); - glGenBuffers(1, &backend->shapeBuffer); - glGenBuffers(1, &backend->indexBuffer); - - glGenBuffers(1, &backend->tileCounterBuffer); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->tileCounterBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, MG_GL_CANVAS_TILE_COUNTER_BUFFER_SIZE, 0, GL_DYNAMIC_COPY); - - glGenBuffers(1, &backend->tileArrayBuffer); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->tileArrayBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, MG_GL_CANVAS_TILE_ARRAY_BUFFER_SIZE, 0, GL_DYNAMIC_COPY); - - mp_rect frame = mg_surface_get_frame(backend->surface); - vec2 contentsScaling = mg_surface_contents_scaling(backend->surface); - - glGenTextures(1, &backend->outTexture); - glBindTexture(GL_TEXTURE_2D, backend->outTexture); - glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, frame.w*contentsScaling.x, frame.h*contentsScaling.y); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - //NOTE: create programs - err |= mg_gl_canvas_compile_compute_program(glsl_clear_counters, &backend->clearCounterProgram); - err |= mg_gl_canvas_compile_compute_program(glsl_tile, &backend->tileProgram); - err |= mg_gl_canvas_compile_compute_program(glsl_sort, &backend->sortProgram); - err |= mg_gl_canvas_compile_compute_program(glsl_draw, &backend->drawProgram); - err |= mg_gl_canvas_compile_render_program("blit", glsl_blit_vertex, glsl_blit_fragment, &backend->blitProgram); - - if(glGetError() != GL_NO_ERROR) - { - err |= -1; - } - - backend->shapeMapping = malloc_array(char, MG_GL_CANVAS_MAX_SHAPE_BUFFER_SIZE); - backend->vertexMapping = malloc_array(char, MG_GL_CANVAS_MAX_VERTEX_BUFFER_SIZE); - backend->indexMapping = malloc_array(char, MG_GL_CANVAS_MAX_INDEX_BUFFER_SIZE); - - if( !backend->shapeMapping - || !backend->shapeMapping - || !backend->shapeMapping) - { - err |= -1; - } - - if(err) - { - mg_gl_canvas_destroy((mg_canvas_backend*)backend); - backend = 0; - } - else - { - mg_gl_canvas_update_vertex_layout(backend); - } - } - - return((mg_canvas_backend*)backend); -} +/************************************************************//** +* +* @file: gl_canvas.c +* @author: Martin Fouilleul +* @date: 29/01/2023 +* @revision: +* +*****************************************************************/ +#include"graphics_surface.h" +#include"macro_helpers.h" +#include"glsl_shaders.h" +#include"gl_api.h" + +typedef struct mg_gl_canvas_backend +{ + mg_canvas_backend interface; + mg_surface surface; + + GLuint vao; + GLuint dummyVertexBuffer; + GLuint vertexBuffer; + GLuint shapeBuffer; + GLuint indexBuffer; + GLuint tileCounterBuffer; + GLuint tileArrayBuffer; + GLuint clearCounterProgram; + GLuint tileProgram; + GLuint sortProgram; + GLuint drawProgram; + GLuint blitProgram; + + GLuint outTexture; + + char* indexMapping; + char* vertexMapping; + char* shapeMapping; + +} mg_gl_canvas_backend; + +typedef struct mg_gl_image +{ + mg_image_data interface; + + GLuint textureID; +} mg_gl_image; + +//NOTE: debugger +typedef struct debug_vertex +{ + vec4 cubic; + vec2 pos; + int shapeIndex; + u8 pad[4]; +} debug_vertex; + +typedef struct debug_shape +{ + vec4 color; + vec4 clip; + vec2 uv; + u8 pad[8]; +} debug_shape; + +#define LayoutNext(prevName, prevType, nextType) \ + AlignUpOnPow2(_cat3_(LAYOUT_, prevName, _OFFSET)+_cat3_(LAYOUT_, prevType, _SIZE), _cat3_(LAYOUT_, nextType, _ALIGN)) + +enum { + LAYOUT_VEC2_SIZE = 8, + LAYOUT_VEC2_ALIGN = 8, + LAYOUT_VEC4_SIZE = 16, + LAYOUT_VEC4_ALIGN = 16, + LAYOUT_INT_SIZE = 4, + LAYOUT_INT_ALIGN = 4, + LAYOUT_MAT2x3_SIZE = sizeof(float)*6, + LAYOUT_MAT2x3_ALIGN = 4, + + LAYOUT_CUBIC_OFFSET = 0, + LAYOUT_POS_OFFSET = LayoutNext(CUBIC, VEC4, VEC2), + LAYOUT_ZINDEX_OFFSET = LayoutNext(POS, VEC2, INT), + LAYOUT_VERTEX_ALIGN = 16, + LAYOUT_VERTEX_SIZE = LayoutNext(ZINDEX, INT, VERTEX), + + LAYOUT_COLOR_OFFSET = 0, + LAYOUT_CLIP_OFFSET = LayoutNext(COLOR, VEC4, VEC4), + LAYOUT_UV_TRANSFORM_OFFSET = LayoutNext(CLIP, VEC4, MAT2x3), + LAYOUT_SHAPE_ALIGN = 16, + LAYOUT_SHAPE_SIZE = LayoutNext(UV_TRANSFORM, MAT2x3, SHAPE), + + MG_GL_CANVAS_MAX_BUFFER_LENGTH = 1<<20, + MG_GL_CANVAS_MAX_SHAPE_BUFFER_SIZE = LAYOUT_SHAPE_SIZE * MG_GL_CANVAS_MAX_BUFFER_LENGTH, + MG_GL_CANVAS_MAX_VERTEX_BUFFER_SIZE = LAYOUT_VERTEX_SIZE * MG_GL_CANVAS_MAX_BUFFER_LENGTH, + MG_GL_CANVAS_MAX_INDEX_BUFFER_SIZE = LAYOUT_INT_SIZE * MG_GL_CANVAS_MAX_BUFFER_LENGTH, + + //TODO: actually size this dynamically + MG_GL_CANVAS_MAX_TILE_COUNT = 65536, //NOTE: this allows for 256*256 tiles (e.g. 4096*4096 pixels) + MG_GL_CANVAS_TILE_COUNTER_BUFFER_SIZE = LAYOUT_INT_SIZE * MG_GL_CANVAS_MAX_TILE_COUNT, + + MG_GL_CANVAS_TILE_ARRAY_LENGTH = 1<<10, // max overlapping triangles per tiles + MG_GL_CANVAS_TILE_ARRAY_BUFFER_SIZE = LAYOUT_INT_SIZE * MG_GL_CANVAS_MAX_TILE_COUNT * MG_GL_CANVAS_TILE_ARRAY_LENGTH, +}; + +void mg_gl_canvas_update_vertex_layout(mg_gl_canvas_backend* backend) +{ + backend->interface.vertexLayout = (mg_vertex_layout){ + .maxVertexCount = MG_GL_CANVAS_MAX_BUFFER_LENGTH, + .maxIndexCount = MG_GL_CANVAS_MAX_BUFFER_LENGTH, + .posBuffer = backend->vertexMapping + LAYOUT_POS_OFFSET, + .posStride = LAYOUT_VERTEX_SIZE, + .cubicBuffer = backend->vertexMapping + LAYOUT_CUBIC_OFFSET, + .cubicStride = LAYOUT_VERTEX_SIZE, + .shapeIndexBuffer = backend->vertexMapping + LAYOUT_ZINDEX_OFFSET, + .shapeIndexStride = LAYOUT_VERTEX_SIZE, + + .colorBuffer = backend->shapeMapping + LAYOUT_COLOR_OFFSET, + .colorStride = LAYOUT_SHAPE_SIZE, + .clipBuffer = backend->shapeMapping + LAYOUT_CLIP_OFFSET, + .clipStride = LAYOUT_SHAPE_SIZE, + .uvTransformBuffer = backend->shapeMapping + LAYOUT_UV_TRANSFORM_OFFSET, + .uvTransformStride = LAYOUT_SHAPE_SIZE, + + .indexBuffer = backend->indexMapping, + .indexStride = LAYOUT_INT_SIZE}; +} + +void mg_gl_send_buffers(mg_gl_canvas_backend* backend, int shapeCount, int vertexCount, int indexCount) +{ + glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->vertexBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_VERTEX_SIZE*vertexCount, backend->vertexMapping, GL_STREAM_DRAW); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->shapeBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_SHAPE_SIZE*shapeCount, backend->shapeMapping, GL_STREAM_DRAW); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->indexBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, LAYOUT_INT_SIZE*indexCount, backend->indexMapping, GL_STREAM_DRAW); +} + +void mg_gl_canvas_begin(mg_canvas_backend* interface) +{ + mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); +} + +void mg_gl_canvas_end(mg_canvas_backend* interface) +{ + //NOTE: nothing to do here... +} + +void mg_gl_canvas_clear(mg_canvas_backend* interface, mg_color clearColor) +{ + mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; + + glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a); + glClear(GL_COLOR_BUFFER_BIT); +} + +void mg_gl_canvas_draw_batch(mg_canvas_backend* interface, mg_image_data* imageInterface, u32 shapeCount, u32 vertexCount, u32 indexCount) +{ + mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; + +/*NOTE: if we want debug_vertex while debugging, the following ensures the struct def doesn't get stripped away + debug_vertex vertex; + debug_shape shape; + printf("foo %p, bar %p\n", &vertex, &shape); +//*/ + mg_gl_send_buffers(backend, shapeCount, vertexCount, indexCount); + + mp_rect frame = mg_surface_get_frame(backend->surface); + vec2 contentsScaling = mg_surface_contents_scaling(backend->surface); + + const int tileSize = 16; + const int tileCountX = (frame.w*contentsScaling.x + tileSize - 1)/tileSize; + const int tileCountY = (frame.h*contentsScaling.y + tileSize - 1)/tileSize; + const int tileArrayLength = MG_GL_CANVAS_TILE_ARRAY_LENGTH; + + //TODO: ensure there's enough space in tile buffer + + //NOTE: first clear counters + glUseProgram(backend->clearCounterProgram); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->tileCounterBuffer); + glDispatchCompute(tileCountX*tileCountY, 1, 1); + + //NOTE: we first distribute triangles into tiles: + + glUseProgram(backend->tileProgram); + + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, backend->vertexBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, backend->shapeBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, backend->indexBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, backend->tileCounterBuffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, backend->tileArrayBuffer); + + glUniform1ui(0, indexCount); + glUniform2ui(1, tileCountX, tileCountY); + glUniform1ui(2, tileSize); + glUniform1ui(3, tileArrayLength); + glUniform2f(4, contentsScaling.x, contentsScaling.y); + + u32 threadCount = indexCount/3; + glDispatchCompute((threadCount + 255)/256, 1, 1); + + //NOTE: next we sort triangles in each tile + glUseProgram(backend->sortProgram); + + glUniform1ui(0, indexCount); + glUniform2ui(1, tileCountX, tileCountY); + glUniform1ui(2, tileSize); + glUniform1ui(3, tileArrayLength); + + glDispatchCompute(tileCountX * tileCountY, 1, 1); + + //NOTE: then we fire the drawing shader that will select only triangles in its tile + glUseProgram(backend->drawProgram); + + glBindImageTexture(0, backend->outTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8); + + glUniform1ui(0, indexCount); + glUniform2ui(1, tileCountX, tileCountY); + glUniform1ui(2, tileSize); + glUniform1ui(3, tileArrayLength); + glUniform2f(4, contentsScaling.x, contentsScaling.y); + + if(imageInterface) + { + //TODO: make sure this image belongs to that context + mg_gl_image* image = (mg_gl_image*)imageInterface; + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, image->textureID); + glUniform1ui(5, 1); + } + else + { + glUniform1ui(5, 0); + } + + glDispatchCompute(tileCountX, tileCountY, 1); + + //NOTE: now blit out texture to surface + glUseProgram(backend->blitProgram); + glBindBuffer(GL_ARRAY_BUFFER, backend->dummyVertexBuffer); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, backend->outTexture); + glUniform1i(0, 0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + mg_gl_canvas_update_vertex_layout(backend); +} + +void mg_gl_canvas_destroy(mg_canvas_backend* interface) +{ + mg_gl_canvas_backend* backend = (mg_gl_canvas_backend*)interface; + + glDeleteTextures(1, &backend->outTexture); + + glDeleteBuffers(1, &backend->dummyVertexBuffer); + glDeleteBuffers(1, &backend->vertexBuffer); + glDeleteBuffers(1, &backend->shapeBuffer); + glDeleteBuffers(1, &backend->indexBuffer); + glDeleteBuffers(1, &backend->tileCounterBuffer); + glDeleteBuffers(1, &backend->tileArrayBuffer); + + glDeleteVertexArrays(1, &backend->vao); + + free(backend->shapeMapping); + free(backend->vertexMapping); + free(backend->indexMapping); + free(backend); +} + +mg_image_data* mg_gl_canvas_image_create(mg_canvas_backend* interface, vec2 size) +{ + mg_gl_image* image = 0; + + image = malloc_type(mg_gl_image); + if(image) + { + glGenTextures(1, &image->textureID); + glBindTexture(GL_TEXTURE_2D, image->textureID); +// glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, size.x, size.y); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + image->interface.size = size; + } + return((mg_image_data*)image); +} + +void mg_gl_canvas_image_destroy(mg_canvas_backend* interface, mg_image_data* imageInterface) +{ + //TODO: check that this image belongs to this context + mg_gl_image* image = (mg_gl_image*)imageInterface; + glDeleteTextures(1, &image->textureID); + free(image); +} + +void mg_gl_canvas_image_upload_region(mg_canvas_backend* interface, + mg_image_data* imageInterface, + mp_rect region, + u8* pixels) +{ + //TODO: check that this image belongs to this context + mg_gl_image* image = (mg_gl_image*)imageInterface; + glBindTexture(GL_TEXTURE_2D, image->textureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, region.w, region.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); +} + +static int mg_gl_compile_shader(const char* name, GLuint shader, const char* source) +{ + int res = 0; + + const char* sources[3] = {"#version 430", glsl_common, source}; + + glShaderSource(shader, 3, sources, 0); + glCompileShader(shader); + + int status = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if(!status) + { + char buffer[256]; + int size = 0; + glGetShaderInfoLog(shader, 256, &size, buffer); + printf("Shader compile error (%s): %.*s\n", name, size, buffer); + res = -1; + } + return(res); +} + +static int mg_gl_canvas_compile_compute_program_named(const char* name, const char* source, GLuint* outProgram) +{ + int res = 0; + *outProgram = 0; + + GLuint shader = glCreateShader(GL_COMPUTE_SHADER); + GLuint program = glCreateProgram(); + + res |= mg_gl_compile_shader(name, shader, source); + + if(!res) + { + glAttachShader(program, shader); + glLinkProgram(program); + + int status = 0; + glGetProgramiv(program, GL_LINK_STATUS, &status); + if(!status) + { + char buffer[256]; + int size = 0; + glGetProgramInfoLog(program, 256, &size, buffer); + log_error("Shader link error (%s): %.*s\n", name, size, buffer); + + res = -1; + } + else + { + *outProgram = program; + } + } + return(res); +} + +int mg_gl_canvas_compile_render_program_named(const char* progName, + const char* vertexName, + const char* fragmentName, + const char* vertexSrc, + const char* fragmentSrc, + GLuint* outProgram) +{ + int res = 0; + *outProgram = 0; + + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + GLuint program = glCreateProgram(); + + res |= mg_gl_compile_shader(vertexName, vertexShader, vertexSrc); + res |= mg_gl_compile_shader(fragmentName, fragmentShader, fragmentSrc); + + if(!res) + { + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glLinkProgram(program); + + int status = 0; + glGetProgramiv(program, GL_LINK_STATUS, &status); + if(!status) + { + char buffer[256]; + int size = 0; + glGetProgramInfoLog(program, 256, &size, buffer); + log_error("Shader link error (%s): %.*s\n", progName, size, buffer); + res = -1; + } + else + { + *outProgram = program; + } + } + return(res); +} + +#define mg_gl_canvas_compile_compute_program(src, out) \ + mg_gl_canvas_compile_compute_program_named(#src, src, out) + +#define mg_gl_canvas_compile_render_program(progName, shaderSrc, vertexSrc, out) \ + mg_gl_canvas_compile_render_program_named(progName, #shaderSrc, #vertexSrc, shaderSrc, vertexSrc, out) + +mg_surface_data* gl_canvas_surface_create_for_window(mp_window window) +{ + mg_wgl_surface* surface = (mg_wgl_surface*)mg_wgl_surface_create_for_window(window); + + +/* + mg_gl_canvas_backend* backend = 0; + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + + int err = 0; + + if(surfaceData && surfaceData->api == MG_GL) + { + backend = malloc_type(mg_gl_canvas_backend); + memset(backend, 0, sizeof(mg_gl_canvas_backend)); + backend->surface = surface; + + //NOTE(martin): setup interface functions + backend->interface.destroy = mg_gl_canvas_destroy; + backend->interface.begin = mg_gl_canvas_begin; + backend->interface.end = mg_gl_canvas_end; + backend->interface.clear = mg_gl_canvas_clear; + backend->interface.drawBatch = mg_gl_canvas_draw_batch; + backend->interface.imageCreate = mg_gl_canvas_image_create; + backend->interface.imageDestroy = mg_gl_canvas_image_destroy; + backend->interface.imageUploadRegion = mg_gl_canvas_image_upload_region; + + mg_surface_prepare(surface); + + glGenVertexArrays(1, &backend->vao); + glBindVertexArray(backend->vao); + + glGenBuffers(1, &backend->dummyVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, backend->dummyVertexBuffer); + + glGenBuffers(1, &backend->vertexBuffer); + glGenBuffers(1, &backend->shapeBuffer); + glGenBuffers(1, &backend->indexBuffer); + + glGenBuffers(1, &backend->tileCounterBuffer); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->tileCounterBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, MG_GL_CANVAS_TILE_COUNTER_BUFFER_SIZE, 0, GL_DYNAMIC_COPY); + + glGenBuffers(1, &backend->tileArrayBuffer); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, backend->tileArrayBuffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, MG_GL_CANVAS_TILE_ARRAY_BUFFER_SIZE, 0, GL_DYNAMIC_COPY); + + mp_rect frame = mg_surface_get_frame(backend->surface); + vec2 contentsScaling = mg_surface_contents_scaling(backend->surface); + + glGenTextures(1, &backend->outTexture); + glBindTexture(GL_TEXTURE_2D, backend->outTexture); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, frame.w*contentsScaling.x, frame.h*contentsScaling.y); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + //NOTE: create programs + err |= mg_gl_canvas_compile_compute_program(glsl_clear_counters, &backend->clearCounterProgram); + err |= mg_gl_canvas_compile_compute_program(glsl_tile, &backend->tileProgram); + err |= mg_gl_canvas_compile_compute_program(glsl_sort, &backend->sortProgram); + err |= mg_gl_canvas_compile_compute_program(glsl_draw, &backend->drawProgram); + err |= mg_gl_canvas_compile_render_program("blit", glsl_blit_vertex, glsl_blit_fragment, &backend->blitProgram); + + if(glGetError() != GL_NO_ERROR) + { + err |= -1; + } + + backend->shapeMapping = malloc_array(char, MG_GL_CANVAS_MAX_SHAPE_BUFFER_SIZE); + backend->vertexMapping = malloc_array(char, MG_GL_CANVAS_MAX_VERTEX_BUFFER_SIZE); + backend->indexMapping = malloc_array(char, MG_GL_CANVAS_MAX_INDEX_BUFFER_SIZE); + + if( !backend->shapeMapping + || !backend->shapeMapping + || !backend->shapeMapping) + { + err |= -1; + } + + if(err) + { + mg_gl_canvas_destroy((mg_canvas_backend*)backend); + backend = 0; + } + else + { + mg_gl_canvas_update_vertex_layout(backend); + } + } + + return((mg_canvas_backend*)backend); +*/ + + return((mg_surface_data*)surface); +} diff --git a/src/glsl_shaders.h b/src/glsl_shaders.h deleted file mode 100644 index 6745a33..0000000 --- a/src/glsl_shaders.h +++ /dev/null @@ -1,469 +0,0 @@ -/********************************************************************* -* -* file: glsl_shaders.h -* note: string literals auto-generated by embed_text.py -* date: 09/032023 -* -**********************************************************************/ -#ifndef __GLSL_SHADERS_H__ -#define __GLSL_SHADERS_H__ - - -//NOTE: string imported from src\glsl_shaders\common.glsl -const char* glsl_common = -"\n" -"layout(std430) buffer;\n" -"\n" -"struct vertex {\n" -" vec4 cubic;\n" -" vec2 pos;\n" -" int shapeIndex;\n" -"};\n" -"\n" -"struct shape {\n" -" vec4 color;\n" -" vec4 clip;\n" -" float uvTransform[6];\n" -"};\n"; - -//NOTE: string imported from src\glsl_shaders\blit_vertex.glsl -const char* glsl_blit_vertex = -"\n" -"precision mediump float;\n" -"\n" -"out vec2 uv;\n" -"\n" -"void main()\n" -"{\n" -" /* generate (0, 0) (1, 0) (1, 1) (1, 1) (0, 1) (0, 0)*/\n" -"\n" -" float x = float(((uint(gl_VertexID) + 2u) / 3u)%2u);\n" -" float y = float(((uint(gl_VertexID) + 1u) / 3u)%2u);\n" -"\n" -" gl_Position = vec4(-1.0f + x*2.0f, -1.0f+y*2.0f, 0.0f, 1.0f);\n" -" uv = vec2(x, 1-y);\n" -"}\n"; - -//NOTE: string imported from src\glsl_shaders\blit_fragment.glsl -const char* glsl_blit_fragment = -"\n" -"precision mediump float;\n" -"\n" -"in vec2 uv;\n" -"out vec4 fragColor;\n" -"\n" -"layout(location=0) uniform sampler2D tex;\n" -"\n" -"void main()\n" -"{\n" -" fragColor = texture(tex, uv);\n" -"}\n"; - -//NOTE: string imported from src\glsl_shaders\clear_counters.glsl -const char* glsl_clear_counters = -"\n" -"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" -"\n" -"precision mediump float;\n" -"layout(std430) buffer;\n" -"\n" -"layout(binding = 0) coherent restrict writeonly buffer tileCounterBufferSSBO {\n" -" uint elements[];\n" -"} tileCounterBuffer ;\n" -"\n" -"void main()\n" -"{\n" -" uint tileIndex = gl_WorkGroupID.x;\n" -" tileCounterBuffer.elements[tileIndex] = 0u;\n" -"}\n"; - -//NOTE: string imported from src\glsl_shaders\tile.glsl -const char* glsl_tile = -"\n" -"layout(local_size_x = 512, local_size_y = 1, local_size_z = 1) in;\n" -"\n" -"precision mediump float;\n" -"\n" -"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n" -" vertex elements[];\n" -"} vertexBuffer ;\n" -"\n" -"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n" -" shape elements[];\n" -"} shapeBuffer ;\n" -"\n" -"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n" -" uint elements[];\n" -"} indexBuffer ;\n" -"\n" -"layout(binding = 3) coherent restrict buffer tileCounterBufferSSBO {\n" -" uint elements[];\n" -"} tileCounterBuffer ;\n" -"\n" -"layout(binding = 4) coherent restrict writeonly buffer tileArrayBufferSSBO {\n" -" uint elements[];\n" -"} tileArrayBuffer ;\n" -"\n" -"layout(location = 0) uniform uint indexCount;\n" -"layout(location = 1) uniform uvec2 tileCount;\n" -"layout(location = 2) uniform uint tileSize;\n" -"layout(location = 3) uniform uint tileArraySize;\n" -"layout(location = 4) uniform vec2 scaling;\n" -"\n" -"void main()\n" -"{\n" -" uint triangleIndex = (gl_WorkGroupID.x*gl_WorkGroupSize.x + gl_LocalInvocationIndex) * 3u;\n" -" if(triangleIndex >= indexCount)\n" -" {\n" -" return;\n" -" }\n" -"\n" -" uint i0 = indexBuffer.elements[triangleIndex];\n" -" uint i1 = indexBuffer.elements[triangleIndex+1u];\n" -" uint i2 = indexBuffer.elements[triangleIndex+2u];\n" -"\n" -" vec2 p0 = vertexBuffer.elements[i0].pos * scaling;\n" -" vec2 p1 = vertexBuffer.elements[i1].pos * scaling;\n" -" vec2 p2 = vertexBuffer.elements[i2].pos * scaling;\n" -"\n" -" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n" -" vec4 clip = shapeBuffer.elements[shapeIndex].clip * vec4(scaling, scaling);\n" -"\n" -" vec4 fbox = vec4(max(min(min(p0.x, p1.x), p2.x), clip.x),\n" -" max(min(min(p0.y, p1.y), p2.y), clip.y),\n" -" min(max(max(p0.x, p1.x), p2.x), clip.z),\n" -" min(max(max(p0.y, p1.y), p2.y), clip.w));\n" -"\n" -" ivec4 box = ivec4(floor(fbox))/int(tileSize);\n" -"\n" -" //NOTE(martin): it's importat to do the computation with signed int, so that we can have negative xMax/yMax\n" -" // otherwise all triangles on the left or below the x/y axis are attributed to tiles on row/column 0.\n" -" int xMin = max(0, box.x);\n" -" int yMin = max(0, box.y);\n" -" int xMax = min(box.z, int(tileCount.x) - 1);\n" -" int yMax = min(box.w, int(tileCount.y) - 1);\n" -"\n" -" for(int y = yMin; y <= yMax; y++)\n" -" {\n" -" for(int x = xMin ; x <= xMax; x++)\n" -" {\n" -" uint tileIndex = uint(y)*tileCount.x + uint(x);\n" -" uint tileCounter = atomicAdd(tileCounterBuffer.elements[tileIndex], 1u);\n" -" if(tileCounter < tileArraySize)\n" -" {\n" -" tileArrayBuffer.elements[tileArraySize*tileIndex + tileCounter] = triangleIndex;\n" -" }\n" -" }\n" -" }\n" -"}\n"; - -//NOTE: string imported from src\glsl_shaders\sort.glsl -const char* glsl_sort = -"\n" -"layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n" -"\n" -"precision mediump float;\n" -"\n" -"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n" -" vertex elements[];\n" -"} vertexBuffer ;\n" -"\n" -"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n" -" shape elements[];\n" -"} shapeBuffer ;\n" -"\n" -"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n" -" uint elements[];\n" -"} indexBuffer ;\n" -"\n" -"layout(binding = 3) coherent readonly restrict buffer tileCounterBufferSSBO {\n" -" uint elements[];\n" -"} tileCounterBuffer ;\n" -"\n" -"layout(binding = 4) coherent restrict buffer tileArrayBufferSSBO {\n" -" uint elements[];\n" -"} tileArrayBuffer ;\n" -"\n" -"layout(location = 0) uniform uint indexCount;\n" -"layout(location = 1) uniform uvec2 tileCount;\n" -"layout(location = 2) uniform uint tileSize;\n" -"layout(location = 3) uniform uint tileArraySize;\n" -"\n" -"int get_shape_index(uint tileArrayOffset, uint tileArrayIndex)\n" -"{\n" -" uint triangleIndex = tileArrayBuffer.elements[tileArrayOffset + tileArrayIndex];\n" -" uint i0 = indexBuffer.elements[triangleIndex];\n" -" int shapeIndex = vertexBuffer.elements[i0].shapeIndex;\n" -" return(shapeIndex);\n" -"}\n" -"\n" -"void main()\n" -"{\n" -" uint tileIndex = gl_WorkGroupID.x;\n" -" uint tileArrayOffset = tileArraySize * tileIndex;\n" -" uint tileArrayCount = min(tileCounterBuffer.elements[tileIndex], tileArraySize);\n" -"\n" -" for(uint tileArrayIndex=1u; tileArrayIndex < tileArrayCount; tileArrayIndex++)\n" -" {\n" -" for(uint sortIndex = tileArrayIndex; sortIndex > 0u; sortIndex--)\n" -" {\n" -" int shapeIndex = get_shape_index(tileArrayOffset, sortIndex);\n" -" int prevShapeIndex = get_shape_index(tileArrayOffset, sortIndex-1u);\n" -"\n" -" if(shapeIndex >= prevShapeIndex)\n" -" {\n" -" break;\n" -" }\n" -" uint tmp = tileArrayBuffer.elements[tileArrayOffset + sortIndex];\n" -" tileArrayBuffer.elements[tileArrayOffset + sortIndex] = tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u];\n" -" tileArrayBuffer.elements[tileArrayOffset + sortIndex - 1u] = tmp;\n" -" }\n" -" }\n" -"}\n"; - -//NOTE: string imported from src\glsl_shaders\draw.glsl -const char* glsl_draw = -"\n" -"#extension GL_ARB_gpu_shader_int64 : require\n" -"layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;\n" -"\n" -"precision mediump float;\n" -"//precision mediump image2D;\n" -"\n" -"layout(binding = 0) restrict readonly buffer vertexBufferSSBO {\n" -" vertex elements[];\n" -"} vertexBuffer ;\n" -"\n" -"layout(binding = 1) restrict readonly buffer shapeBufferSSBO {\n" -" shape elements[];\n" -"} shapeBuffer ;\n" -"\n" -"layout(binding = 2) restrict readonly buffer indexBufferSSBO {\n" -" uint elements[];\n" -"} indexBuffer ;\n" -"\n" -"layout(binding = 3) restrict readonly buffer tileCounterBufferSSBO {\n" -" uint elements[];\n" -"} tileCounterBuffer ;\n" -"\n" -"layout(binding = 4) restrict readonly buffer tileArrayBufferSSBO {\n" -" uint elements[];\n" -"} tileArrayBuffer ;\n" -"\n" -"layout(location = 0) uniform uint indexCount;\n" -"layout(location = 1) uniform uvec2 tileCount;\n" -"layout(location = 2) uniform uint tileSize;\n" -"layout(location = 3) uniform uint tileArraySize;\n" -"layout(location = 4) uniform vec2 scaling;\n" -"layout(location = 5) uniform uint useTexture;\n" -"\n" -"layout(rgba8, binding = 0) uniform restrict writeonly image2D outTexture;\n" -"\n" -"layout(binding = 1) uniform sampler2D srcTexture;\n" -"\n" -"\n" -"bool is_top_left(ivec2 a, ivec2 b)\n" -"{\n" -" return( (a.y == b.y && b.x < a.x)\n" -" ||(b.y < a.y));\n" -"}\n" -"\n" -"//////////////////////////////////////////////////////////////////////////////\n" -"//TODO: we should do these computations on 64bits, because otherwise\n" -"// we might overflow for values > 2048.\n" -"// Unfortunately this is costly.\n" -"// Another way is to precompute triangle edges (b - a) in full precision\n" -"// once to avoid doing it all the time...\n" -"//////////////////////////////////////////////////////////////////////////////\n" -"int orient2d(ivec2 a, ivec2 b, ivec2 p)\n" -"{\n" -" return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));\n" -"}\n" -"\n" -"int is_clockwise(ivec2 p0, ivec2 p1, ivec2 p2)\n" -"{\n" -" return((p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x);\n" -"}\n" -"\n" -"void main()\n" -"{\n" -" ivec2 pixelCoord = ivec2(gl_WorkGroupID.xy*uvec2(16, 16) + gl_LocalInvocationID.xy);\n" -" uvec2 tileCoord = uvec2(pixelCoord) / tileSize;\n" -" uint tileIndex = tileCoord.y * tileCount.x + tileCoord.x;\n" -" uint tileCounter = min(tileCounterBuffer.elements[tileIndex], tileArraySize);\n" -"\n" -" const float subPixelFactor = 16.;\n" -" ivec2 centerPoint = ivec2((vec2(pixelCoord) + vec2(0.5, 0.5)) * subPixelFactor);\n" -"\n" -"//*\n" -" const int sampleCount = 8;\n" -" ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(1, 3),\n" -" centerPoint + ivec2(-1, -3),\n" -" centerPoint + ivec2(5, -1),\n" -" centerPoint + ivec2(-3, 5),\n" -" centerPoint + ivec2(-5, -5),\n" -" centerPoint + ivec2(-7, 1),\n" -" centerPoint + ivec2(3, -7),\n" -" centerPoint + ivec2(7, 7));\n" -"/*/\n" -" const int sampleCount = 4;\n" -" ivec2 samplePoints[sampleCount] = ivec2[sampleCount](centerPoint + ivec2(-2, 6),\n" -" centerPoint + ivec2(6, 2),\n" -" centerPoint + ivec2(-6, -2),\n" -" centerPoint + ivec2(2, -6));\n" -"//*/\n" -" //DEBUG\n" -"/*\n" -" {\n" -" vec4 fragColor = vec4(0);\n" -"\n" -" if( pixelCoord.x % 16 == 0\n" -" ||pixelCoord.y % 16 == 0)\n" -" {\n" -" fragColor = vec4(0, 0, 0, 1);\n" -" }\n" -" else if(tileCounterBuffer.elements[tileIndex] == 0xffffu)\n" -" {\n" -" fragColor = vec4(1, 0, 1, 1);\n" -" }\n" -" else if(tileCounter != 0u)\n" -" {\n" -" fragColor = vec4(0, 1, 0, 1);\n" -" }\n" -" else\n" -" {\n" -" fragColor = vec4(1, 0, 0, 1);\n" -" }\n" -" imageStore(outTexture, pixelCoord, fragColor);\n" -" return;\n" -" }\n" -"//*/\n" -" //----\n" -"\n" -" vec4 sampleColor[sampleCount];\n" -" vec4 currentColor[sampleCount];\n" -" int currentShapeIndex[sampleCount];\n" -" int flipCount[sampleCount];\n" -"\n" -" for(int i=0; i clip.z\n" -" || samplePoint.y < clip.y\n" -" || samplePoint.y > clip.w)\n" -" {\n" -" continue;\n" -" }\n" -"\n" -" int w0 = orient2d(p1, p2, samplePoint);\n" -" int w1 = orient2d(p2, p0, samplePoint);\n" -" int w2 = orient2d(p0, p1, samplePoint);\n" -"\n" -" if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)\n" -" {\n" -" vec4 cubic = (cubic0*float(w0) + cubic1*float(w1) + cubic2*float(w2))/(float(w0)+float(w1)+float(w2));\n" -"\n" -" float eps = 0.0001;\n" -" if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)\n" -" {\n" -" if(shapeIndex == currentShapeIndex[sampleIndex])\n" -" {\n" -" flipCount[sampleIndex]++;\n" -" }\n" -" else\n" -" {\n" -" if((flipCount[sampleIndex] & 0x01) != 0)\n" -" {\n" -" sampleColor[sampleIndex] = currentColor[sampleIndex];\n" -" }\n" -"\n" -" vec4 nextColor = color;\n" -" if(useTexture)\n" -" {\n" -" vec3 sampleFP = vec3(vec2(samplePoint).xy/(subPixelFactor*2.), 1);\n" -" vec2 uv = (uvTransform * sampleFP).xy;\n" -" vec4 texColor = texture(srcTexture, uv);\n" -" texColor.rgb *= texColor.a;\n" -" nextColor *= texColor;\n" -" }\n" -" currentColor[sampleIndex] = sampleColor[sampleIndex]*(1.-nextColor.a) + nextColor;\n" -" currentShapeIndex[sampleIndex] = shapeIndex;\n" -" flipCount[sampleIndex] = 1;\n" -" }\n" -" }\n" -" }\n" -" }\n" -" }\n" -" vec4 pixelColor = vec4(0);\n" -" for(int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)\n" -" {\n" -" if((flipCount[sampleIndex] & 0x01) != 0)\n" -" {\n" -" sampleColor[sampleIndex] = currentColor[sampleIndex];\n" -" }\n" -" pixelColor += sampleColor[sampleIndex];\n" -" }\n" -"\n" -" imageStore(outTexture, pixelCoord, pixelColor/float(sampleCount));\n" -"}\n"; - -#endif // __GLSL_SHADERS_H__ diff --git a/src/graphics_surface.c b/src/graphics_surface.c index 3c39420..20eec15 100644 --- a/src/graphics_surface.c +++ b/src/graphics_surface.c @@ -1,407 +1,407 @@ -/************************************************************//** -* -* @file: graphics_surface.c -* @author: Martin Fouilleul -* @date: 25/04/2023 -* -*****************************************************************/ - -#include"graphics_surface.h" - -//--------------------------------------------------------------- -// typed handles functions -//--------------------------------------------------------------- - -mg_surface mg_surface_handle_alloc(mg_surface_data* surface) -{ - mg_surface handle = {.h = mg_handle_alloc(MG_HANDLE_SURFACE, (void*)surface) }; - return(handle); -} - -mg_surface_data* mg_surface_data_from_handle(mg_surface handle) -{ - mg_surface_data* data = mg_data_from_handle(MG_HANDLE_SURFACE, handle.h); - return(data); -} - -mg_image mg_image_handle_alloc(mg_image_data* image) -{ - mg_image handle = {.h = mg_handle_alloc(MG_HANDLE_IMAGE, (void*)image) }; - return(handle); -} - -mg_image_data* mg_image_data_from_handle(mg_image handle) -{ - mg_image_data* data = mg_data_from_handle(MG_HANDLE_IMAGE, handle.h); - return(data); -} - -//--------------------------------------------------------------- -// surface API -//--------------------------------------------------------------- - -#if MG_COMPILE_GL - #if PLATFORM_WINDOWS - #include"wgl_surface.h" - #define gl_surface_create_for_window mg_wgl_surface_create_for_window - #endif -#endif - -#if MG_COMPILE_GLES - #include"egl_surface.h" -#endif - -#if MG_COMPILE_METAL - #include"mtl_surface.h" -#endif - -#if MG_COMPILE_CANVAS - #if PLATFORM_MACOS - mg_surface_data* mtl_canvas_surface_create_for_window(mp_window window); - #elif PLATFORM_WINDOWS - //TODO - #endif -#endif - -bool mg_is_surface_backend_available(mg_surface_api api) -{ - bool result = false; - switch(api) - { - #if MG_COMPILE_METAL - case MG_METAL: - #endif - - #if MG_COMPILE_GL - case MG_GL: - #endif - - #if MG_COMPILE_GLES - case MG_GLES: - #endif - - #if MG_COMPILE_CANVAS - case MG_CANVAS: - #endif - result = true; - break; - - default: - break; - } - return(result); -} - -mg_surface mg_surface_nil() { return((mg_surface){.h = 0}); } -bool mg_surface_is_nil(mg_surface surface) { return(surface.h == 0); } - -mg_surface mg_surface_create_for_window(mp_window window, mg_surface_api api) -{ - if(__mgData.init) - { - mg_init(); - } - mg_surface surfaceHandle = mg_surface_nil(); - mg_surface_data* surface = 0; - - switch(api) - { - #if MG_COMPILE_GL - case MG_GL: - surface = gl_surface_create_for_window(window); - break; - #endif - - #if MG_COMPILE_GLES - case MG_GLES: - surface = mg_egl_surface_create_for_window(window); - break; - #endif - - #if MG_COMPILE_METAL - case MG_METAL: - surface = mg_mtl_surface_create_for_window(window); - break; - #endif - - #if MG_COMPILE_CANVAS - case MG_CANVAS: - - #if PLATFORM_MACOS - surface = mtl_canvas_surface_create_for_window(window); - #elif PLATFORM_WINDOWS - surface = gl_canvas_surface_create_for_window(window); - #endif - break; - #endif - - default: - break; - } - if(surface) - { - surfaceHandle = mg_surface_handle_alloc(surface); - } - return(surfaceHandle); -} - -mg_surface mg_surface_create_remote(u32 width, u32 height, mg_surface_api api) -{ - if(__mgData.init) - { - mg_init(); - } - mg_surface surfaceHandle = mg_surface_nil(); - mg_surface_data* surface = 0; - - switch(api) - { - #if MG_COMPILE_GLES - case MG_GLES: - surface = mg_egl_surface_create_remote(width, height); - break; - #endif - - default: - break; - } - if(surface) - { - surfaceHandle = mg_surface_handle_alloc(surface); - } - return(surfaceHandle); -} - -mg_surface mg_surface_create_host(mp_window window) -{ - if(__mgData.init) - { - mg_init(); - } - mg_surface handle = mg_surface_nil(); - mg_surface_data* surface = 0; - #if PLATFORM_MACOS - surface = mg_osx_surface_create_host(window); - #elif PLATFORM_WINDOWS - surface = mg_win32_surface_create_host(window); - #endif - - if(surface) - { - handle = mg_surface_handle_alloc(surface); - } - return(handle); -} - -void mg_surface_destroy(mg_surface handle) -{ - DEBUG_ASSERT(__mgData.init); - mg_surface_data* surface = mg_surface_data_from_handle(handle); - if(surface) - { - if(surface->backend && surface->backend->destroy) - { - surface->backend->destroy(surface->backend); - } - surface->destroy(surface); - mg_handle_recycle(handle.h); - } -} - -void mg_surface_prepare(mg_surface surface) -{ - DEBUG_ASSERT(__mgData.init); - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->prepare) - { - surfaceData->prepare(surfaceData); - } -} - -void mg_surface_present(mg_surface surface) -{ - DEBUG_ASSERT(__mgData.init); - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->present) - { - surfaceData->present(surfaceData); - } -} - -void mg_surface_swap_interval(mg_surface surface, int swap) -{ - DEBUG_ASSERT(__mgData.init); - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->swapInterval) - { - surfaceData->swapInterval(surfaceData, swap); - } -} - -vec2 mg_surface_contents_scaling(mg_surface surface) -{ - DEBUG_ASSERT(__mgData.init); - vec2 scaling = {1, 1}; - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->contentsScaling) - { - scaling = surfaceData->contentsScaling(surfaceData); - } - return(scaling); -} - - -void mg_surface_set_frame(mg_surface surface, mp_rect frame) -{ - DEBUG_ASSERT(__mgData.init); - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->setFrame) - { - surfaceData->setFrame(surfaceData, frame); - } -} - -mp_rect mg_surface_get_frame(mg_surface surface) -{ - DEBUG_ASSERT(__mgData.init); - mp_rect res = {0}; - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->getFrame) - { - res = surfaceData->getFrame(surfaceData); - } - return(res); -} - -void mg_surface_set_hidden(mg_surface surface, bool hidden) -{ - DEBUG_ASSERT(__mgData.init); - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->setHidden) - { - surfaceData->setHidden(surfaceData, hidden); - } -} - -bool mg_surface_get_hidden(mg_surface surface) -{ - DEBUG_ASSERT(__mgData.init); - bool res = false; - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->getHidden) - { - res = surfaceData->getHidden(surfaceData); - } - return(res); -} - -void* mg_surface_native_layer(mg_surface surface) -{ - void* res = 0; - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->nativeLayer) - { - res = surfaceData->nativeLayer(surfaceData); - } - return(res); -} - -mg_surface_id mg_surface_remote_id(mg_surface handle) -{ - mg_surface_id remoteId = 0; - mg_surface_data* surface = mg_surface_data_from_handle(handle); - if(surface && surface->remoteID) - { - remoteId = surface->remoteID(surface); - } - return(remoteId); -} - -void mg_surface_host_connect(mg_surface handle, mg_surface_id remoteID) -{ - mg_surface_data* surface = mg_surface_data_from_handle(handle); - if(surface && surface->hostConnect) - { - surface->hostConnect(surface, remoteID); - } -} - -void mg_surface_render_commands(mg_surface surface, - mg_color clearColor, - u32 primitiveCount, - mg_primitive* primitives, - u32 eltCount, - mg_path_elt* elements) -{ - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->backend) - { - surfaceData->backend->render(surfaceData->backend, - clearColor, - primitiveCount, - primitives, - eltCount, - elements); - } -} - -//------------------------------------------------------------------------------------------ -//NOTE(martin): images -//------------------------------------------------------------------------------------------ - -vec2 mg_image_size(mg_image image) -{ - vec2 res = {0}; - mg_image_data* imageData = mg_image_data_from_handle(image); - if(imageData) - { - res = imageData->size; - } - return(res); -} - -mg_image mg_image_create(mg_surface surface, u32 width, u32 height) -{ - mg_image image = mg_image_nil(); - mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); - if(surfaceData && surfaceData->backend) - { - DEBUG_ASSERT(surfaceData->api == MG_CANVAS); - - mg_image_data* imageData = surfaceData->backend->imageCreate(surfaceData->backend, (vec2){width, height}); - if(imageData) - { - imageData->surface = surface; - image = mg_image_handle_alloc(imageData); - } - } - return(image); -} - -void mg_image_destroy(mg_image image) -{ - mg_image_data* imageData = mg_image_data_from_handle(image); - if(imageData) - { - mg_surface_data* surface = mg_surface_data_from_handle(imageData->surface); - if(surface && surface->backend) - { - surface->backend->imageDestroy(surface->backend, imageData); - mg_handle_recycle(image.h); - } - } -} - -void mg_image_upload_region_rgba8(mg_image image, mp_rect region, u8* pixels) -{ - mg_image_data* imageData = mg_image_data_from_handle(image); - if(imageData) - { - mg_surface_data* surfaceData = mg_surface_data_from_handle(imageData->surface); - if(surfaceData) - { - DEBUG_ASSERT(surfaceData->backend); - surfaceData->backend->imageUploadRegion(surfaceData->backend, imageData, region, pixels); - } - } -} +/************************************************************//** +* +* @file: graphics_surface.c +* @author: Martin Fouilleul +* @date: 25/04/2023 +* +*****************************************************************/ + +#include"graphics_surface.h" + +//--------------------------------------------------------------- +// typed handles functions +//--------------------------------------------------------------- + +mg_surface mg_surface_handle_alloc(mg_surface_data* surface) +{ + mg_surface handle = {.h = mg_handle_alloc(MG_HANDLE_SURFACE, (void*)surface) }; + return(handle); +} + +mg_surface_data* mg_surface_data_from_handle(mg_surface handle) +{ + mg_surface_data* data = mg_data_from_handle(MG_HANDLE_SURFACE, handle.h); + return(data); +} + +mg_image mg_image_handle_alloc(mg_image_data* image) +{ + mg_image handle = {.h = mg_handle_alloc(MG_HANDLE_IMAGE, (void*)image) }; + return(handle); +} + +mg_image_data* mg_image_data_from_handle(mg_image handle) +{ + mg_image_data* data = mg_data_from_handle(MG_HANDLE_IMAGE, handle.h); + return(data); +} + +//--------------------------------------------------------------- +// surface API +//--------------------------------------------------------------- + +#if MG_COMPILE_GL + #if PLATFORM_WINDOWS + #include"wgl_surface.h" + #define gl_surface_create_for_window mg_wgl_surface_create_for_window + #endif +#endif + +#if MG_COMPILE_GLES + #include"egl_surface.h" +#endif + +#if MG_COMPILE_METAL + #include"mtl_surface.h" +#endif + +#if MG_COMPILE_CANVAS + #if PLATFORM_MACOS + mg_surface_data* mtl_canvas_surface_create_for_window(mp_window window); + #elif PLATFORM_WINDOWS + mg_surface_data* gl_canvas_surface_create_for_window(mp_window window); + #endif +#endif + +bool mg_is_surface_backend_available(mg_surface_api api) +{ + bool result = false; + switch(api) + { + #if MG_COMPILE_METAL + case MG_METAL: + #endif + + #if MG_COMPILE_GL + case MG_GL: + #endif + + #if MG_COMPILE_GLES + case MG_GLES: + #endif + + #if MG_COMPILE_CANVAS + case MG_CANVAS: + #endif + result = true; + break; + + default: + break; + } + return(result); +} + +mg_surface mg_surface_nil() { return((mg_surface){.h = 0}); } +bool mg_surface_is_nil(mg_surface surface) { return(surface.h == 0); } + +mg_surface mg_surface_create_for_window(mp_window window, mg_surface_api api) +{ + if(__mgData.init) + { + mg_init(); + } + mg_surface surfaceHandle = mg_surface_nil(); + mg_surface_data* surface = 0; + + switch(api) + { + #if MG_COMPILE_GL + case MG_GL: + surface = gl_surface_create_for_window(window); + break; + #endif + + #if MG_COMPILE_GLES + case MG_GLES: + surface = mg_egl_surface_create_for_window(window); + break; + #endif + + #if MG_COMPILE_METAL + case MG_METAL: + surface = mg_mtl_surface_create_for_window(window); + break; + #endif + + #if MG_COMPILE_CANVAS + case MG_CANVAS: + + #if PLATFORM_MACOS + surface = mtl_canvas_surface_create_for_window(window); + #elif PLATFORM_WINDOWS + surface = gl_canvas_surface_create_for_window(window); + #endif + break; + #endif + + default: + break; + } + if(surface) + { + surfaceHandle = mg_surface_handle_alloc(surface); + } + return(surfaceHandle); +} + +mg_surface mg_surface_create_remote(u32 width, u32 height, mg_surface_api api) +{ + if(__mgData.init) + { + mg_init(); + } + mg_surface surfaceHandle = mg_surface_nil(); + mg_surface_data* surface = 0; + + switch(api) + { + #if MG_COMPILE_GLES + case MG_GLES: + surface = mg_egl_surface_create_remote(width, height); + break; + #endif + + default: + break; + } + if(surface) + { + surfaceHandle = mg_surface_handle_alloc(surface); + } + return(surfaceHandle); +} + +mg_surface mg_surface_create_host(mp_window window) +{ + if(__mgData.init) + { + mg_init(); + } + mg_surface handle = mg_surface_nil(); + mg_surface_data* surface = 0; + #if PLATFORM_MACOS + surface = mg_osx_surface_create_host(window); + #elif PLATFORM_WINDOWS + surface = mg_win32_surface_create_host(window); + #endif + + if(surface) + { + handle = mg_surface_handle_alloc(surface); + } + return(handle); +} + +void mg_surface_destroy(mg_surface handle) +{ + DEBUG_ASSERT(__mgData.init); + mg_surface_data* surface = mg_surface_data_from_handle(handle); + if(surface) + { + if(surface->backend && surface->backend->destroy) + { + surface->backend->destroy(surface->backend); + } + surface->destroy(surface); + mg_handle_recycle(handle.h); + } +} + +void mg_surface_prepare(mg_surface surface) +{ + DEBUG_ASSERT(__mgData.init); + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->prepare) + { + surfaceData->prepare(surfaceData); + } +} + +void mg_surface_present(mg_surface surface) +{ + DEBUG_ASSERT(__mgData.init); + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->present) + { + surfaceData->present(surfaceData); + } +} + +void mg_surface_swap_interval(mg_surface surface, int swap) +{ + DEBUG_ASSERT(__mgData.init); + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->swapInterval) + { + surfaceData->swapInterval(surfaceData, swap); + } +} + +vec2 mg_surface_contents_scaling(mg_surface surface) +{ + DEBUG_ASSERT(__mgData.init); + vec2 scaling = {1, 1}; + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->contentsScaling) + { + scaling = surfaceData->contentsScaling(surfaceData); + } + return(scaling); +} + + +void mg_surface_set_frame(mg_surface surface, mp_rect frame) +{ + DEBUG_ASSERT(__mgData.init); + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->setFrame) + { + surfaceData->setFrame(surfaceData, frame); + } +} + +mp_rect mg_surface_get_frame(mg_surface surface) +{ + DEBUG_ASSERT(__mgData.init); + mp_rect res = {0}; + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->getFrame) + { + res = surfaceData->getFrame(surfaceData); + } + return(res); +} + +void mg_surface_set_hidden(mg_surface surface, bool hidden) +{ + DEBUG_ASSERT(__mgData.init); + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->setHidden) + { + surfaceData->setHidden(surfaceData, hidden); + } +} + +bool mg_surface_get_hidden(mg_surface surface) +{ + DEBUG_ASSERT(__mgData.init); + bool res = false; + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->getHidden) + { + res = surfaceData->getHidden(surfaceData); + } + return(res); +} + +void* mg_surface_native_layer(mg_surface surface) +{ + void* res = 0; + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->nativeLayer) + { + res = surfaceData->nativeLayer(surfaceData); + } + return(res); +} + +mg_surface_id mg_surface_remote_id(mg_surface handle) +{ + mg_surface_id remoteId = 0; + mg_surface_data* surface = mg_surface_data_from_handle(handle); + if(surface && surface->remoteID) + { + remoteId = surface->remoteID(surface); + } + return(remoteId); +} + +void mg_surface_host_connect(mg_surface handle, mg_surface_id remoteID) +{ + mg_surface_data* surface = mg_surface_data_from_handle(handle); + if(surface && surface->hostConnect) + { + surface->hostConnect(surface, remoteID); + } +} + +void mg_surface_render_commands(mg_surface surface, + mg_color clearColor, + u32 primitiveCount, + mg_primitive* primitives, + u32 eltCount, + mg_path_elt* elements) +{ + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->backend) + { + surfaceData->backend->render(surfaceData->backend, + clearColor, + primitiveCount, + primitives, + eltCount, + elements); + } +} + +//------------------------------------------------------------------------------------------ +//NOTE(martin): images +//------------------------------------------------------------------------------------------ + +vec2 mg_image_size(mg_image image) +{ + vec2 res = {0}; + mg_image_data* imageData = mg_image_data_from_handle(image); + if(imageData) + { + res = imageData->size; + } + return(res); +} + +mg_image mg_image_create(mg_surface surface, u32 width, u32 height) +{ + mg_image image = mg_image_nil(); + mg_surface_data* surfaceData = mg_surface_data_from_handle(surface); + if(surfaceData && surfaceData->backend) + { + DEBUG_ASSERT(surfaceData->api == MG_CANVAS); + + mg_image_data* imageData = surfaceData->backend->imageCreate(surfaceData->backend, (vec2){width, height}); + if(imageData) + { + imageData->surface = surface; + image = mg_image_handle_alloc(imageData); + } + } + return(image); +} + +void mg_image_destroy(mg_image image) +{ + mg_image_data* imageData = mg_image_data_from_handle(image); + if(imageData) + { + mg_surface_data* surface = mg_surface_data_from_handle(imageData->surface); + if(surface && surface->backend) + { + surface->backend->imageDestroy(surface->backend, imageData); + mg_handle_recycle(image.h); + } + } +} + +void mg_image_upload_region_rgba8(mg_image image, mp_rect region, u8* pixels) +{ + mg_image_data* imageData = mg_image_data_from_handle(image); + if(imageData) + { + mg_surface_data* surfaceData = mg_surface_data_from_handle(imageData->surface); + if(surfaceData) + { + DEBUG_ASSERT(surfaceData->backend); + surfaceData->backend->imageUploadRegion(surfaceData->backend, imageData, region, pixels); + } + } +} diff --git a/src/platform/platform_math.h b/src/platform/platform_math.h index f54d02c..442fd48 100644 --- a/src/platform/platform_math.h +++ b/src/platform/platform_math.h @@ -1,26 +1,27 @@ -/************************************************************//** -* -* @file: platform_math.h -* @author: Martin Fouilleul -* @date: 26/04/2023 -* -*****************************************************************/ -#ifndef __PLATFORM_MATH_H_ -#define __PLATFORM_MATH_H_ - -#include"platform.h" - -#if !PLATFORM_ORCA - #include -#else - -#define M_PI 3.14159265358979323846 - -double fabs(double x); -double sqrt(double sqrt); -double cos(double x); -double sin(double x); - -#endif - -#endif //__PLATFORM_MATH_H_ +/************************************************************//** +* +* @file: platform_math.h +* @author: Martin Fouilleul +* @date: 26/04/2023 +* +*****************************************************************/ +#ifndef __PLATFORM_MATH_H_ +#define __PLATFORM_MATH_H_ + +#include"platform.h" + +#if !PLATFORM_ORCA + #define _USE_MATH_DEFINES //NOTE: necessary for MSVC + #include +#else + +#define M_PI 3.14159265358979323846 + +double fabs(double x); +double sqrt(double sqrt); +double cos(double x); +double sin(double x); + +#endif + +#endif //__PLATFORM_MATH_H_ diff --git a/src/win32_app.c b/src/win32_app.c index 2baf620..776387a 100644 --- a/src/win32_app.c +++ b/src/win32_app.c @@ -1,1082 +1,1082 @@ -/************************************************************//** -* -* @file: win32_app.c -* @author: Martin Fouilleul -* @date: 16/12/2022 -* @revision: -* -*****************************************************************/ - -#include"mp_app.c" - -void mp_init_keys() -{ - memset(__mpApp.keyCodes, MP_KEY_UNKNOWN, 256*sizeof(int)); - - __mpApp.keyCodes[0x00B] = MP_KEY_0; - __mpApp.keyCodes[0x002] = MP_KEY_1; - __mpApp.keyCodes[0x003] = MP_KEY_2; - __mpApp.keyCodes[0x004] = MP_KEY_3; - __mpApp.keyCodes[0x005] = MP_KEY_4; - __mpApp.keyCodes[0x006] = MP_KEY_5; - __mpApp.keyCodes[0x007] = MP_KEY_6; - __mpApp.keyCodes[0x008] = MP_KEY_7; - __mpApp.keyCodes[0x009] = MP_KEY_8; - __mpApp.keyCodes[0x00A] = MP_KEY_9; - __mpApp.keyCodes[0x01E] = MP_KEY_A; - __mpApp.keyCodes[0x030] = MP_KEY_B; - __mpApp.keyCodes[0x02E] = MP_KEY_C; - __mpApp.keyCodes[0x020] = MP_KEY_D; - __mpApp.keyCodes[0x012] = MP_KEY_E; - __mpApp.keyCodes[0x021] = MP_KEY_F; - __mpApp.keyCodes[0x022] = MP_KEY_G; - __mpApp.keyCodes[0x023] = MP_KEY_H; - __mpApp.keyCodes[0x017] = MP_KEY_I; - __mpApp.keyCodes[0x024] = MP_KEY_J; - __mpApp.keyCodes[0x025] = MP_KEY_K; - __mpApp.keyCodes[0x026] = MP_KEY_L; - __mpApp.keyCodes[0x032] = MP_KEY_M; - __mpApp.keyCodes[0x031] = MP_KEY_N; - __mpApp.keyCodes[0x018] = MP_KEY_O; - __mpApp.keyCodes[0x019] = MP_KEY_P; - __mpApp.keyCodes[0x010] = MP_KEY_Q; - __mpApp.keyCodes[0x013] = MP_KEY_R; - __mpApp.keyCodes[0x01F] = MP_KEY_S; - __mpApp.keyCodes[0x014] = MP_KEY_T; - __mpApp.keyCodes[0x016] = MP_KEY_U; - __mpApp.keyCodes[0x02F] = MP_KEY_V; - __mpApp.keyCodes[0x011] = MP_KEY_W; - __mpApp.keyCodes[0x02D] = MP_KEY_X; - __mpApp.keyCodes[0x015] = MP_KEY_Y; - __mpApp.keyCodes[0x02C] = MP_KEY_Z; - __mpApp.keyCodes[0x028] = MP_KEY_APOSTROPHE; - __mpApp.keyCodes[0x02B] = MP_KEY_BACKSLASH; - __mpApp.keyCodes[0x033] = MP_KEY_COMMA; - __mpApp.keyCodes[0x00D] = MP_KEY_EQUAL; - __mpApp.keyCodes[0x029] = MP_KEY_GRAVE_ACCENT; - __mpApp.keyCodes[0x01A] = MP_KEY_LEFT_BRACKET; - __mpApp.keyCodes[0x00C] = MP_KEY_MINUS; - __mpApp.keyCodes[0x034] = MP_KEY_PERIOD; - __mpApp.keyCodes[0x01B] = MP_KEY_RIGHT_BRACKET; - __mpApp.keyCodes[0x027] = MP_KEY_SEMICOLON; - __mpApp.keyCodes[0x035] = MP_KEY_SLASH; - __mpApp.keyCodes[0x056] = MP_KEY_WORLD_2; - __mpApp.keyCodes[0x00E] = MP_KEY_BACKSPACE; - __mpApp.keyCodes[0x153] = MP_KEY_DELETE; - __mpApp.keyCodes[0x14F] = MP_KEY_END; - __mpApp.keyCodes[0x01C] = MP_KEY_ENTER; - __mpApp.keyCodes[0x001] = MP_KEY_ESCAPE; - __mpApp.keyCodes[0x147] = MP_KEY_HOME; - __mpApp.keyCodes[0x152] = MP_KEY_INSERT; - __mpApp.keyCodes[0x15D] = MP_KEY_MENU; - __mpApp.keyCodes[0x151] = MP_KEY_PAGE_DOWN; - __mpApp.keyCodes[0x149] = MP_KEY_PAGE_UP; - __mpApp.keyCodes[0x045] = MP_KEY_PAUSE; - __mpApp.keyCodes[0x146] = MP_KEY_PAUSE; - __mpApp.keyCodes[0x039] = MP_KEY_SPACE; - __mpApp.keyCodes[0x00F] = MP_KEY_TAB; - __mpApp.keyCodes[0x03A] = MP_KEY_CAPS_LOCK; - __mpApp.keyCodes[0x145] = MP_KEY_NUM_LOCK; - __mpApp.keyCodes[0x046] = MP_KEY_SCROLL_LOCK; - __mpApp.keyCodes[0x03B] = MP_KEY_F1; - __mpApp.keyCodes[0x03C] = MP_KEY_F2; - __mpApp.keyCodes[0x03D] = MP_KEY_F3; - __mpApp.keyCodes[0x03E] = MP_KEY_F4; - __mpApp.keyCodes[0x03F] = MP_KEY_F5; - __mpApp.keyCodes[0x040] = MP_KEY_F6; - __mpApp.keyCodes[0x041] = MP_KEY_F7; - __mpApp.keyCodes[0x042] = MP_KEY_F8; - __mpApp.keyCodes[0x043] = MP_KEY_F9; - __mpApp.keyCodes[0x044] = MP_KEY_F10; - __mpApp.keyCodes[0x057] = MP_KEY_F11; - __mpApp.keyCodes[0x058] = MP_KEY_F12; - __mpApp.keyCodes[0x064] = MP_KEY_F13; - __mpApp.keyCodes[0x065] = MP_KEY_F14; - __mpApp.keyCodes[0x066] = MP_KEY_F15; - __mpApp.keyCodes[0x067] = MP_KEY_F16; - __mpApp.keyCodes[0x068] = MP_KEY_F17; - __mpApp.keyCodes[0x069] = MP_KEY_F18; - __mpApp.keyCodes[0x06A] = MP_KEY_F19; - __mpApp.keyCodes[0x06B] = MP_KEY_F20; - __mpApp.keyCodes[0x06C] = MP_KEY_F21; - __mpApp.keyCodes[0x06D] = MP_KEY_F22; - __mpApp.keyCodes[0x06E] = MP_KEY_F23; - __mpApp.keyCodes[0x076] = MP_KEY_F24; - __mpApp.keyCodes[0x038] = MP_KEY_LEFT_ALT; - __mpApp.keyCodes[0x01D] = MP_KEY_LEFT_CONTROL; - __mpApp.keyCodes[0x02A] = MP_KEY_LEFT_SHIFT; - __mpApp.keyCodes[0x15B] = MP_KEY_LEFT_SUPER; - __mpApp.keyCodes[0x137] = MP_KEY_PRINT_SCREEN; - __mpApp.keyCodes[0x138] = MP_KEY_RIGHT_ALT; - __mpApp.keyCodes[0x11D] = MP_KEY_RIGHT_CONTROL; - __mpApp.keyCodes[0x036] = MP_KEY_RIGHT_SHIFT; - __mpApp.keyCodes[0x15C] = MP_KEY_RIGHT_SUPER; - __mpApp.keyCodes[0x150] = MP_KEY_DOWN; - __mpApp.keyCodes[0x14B] = MP_KEY_LEFT; - __mpApp.keyCodes[0x14D] = MP_KEY_RIGHT; - __mpApp.keyCodes[0x148] = MP_KEY_UP; - __mpApp.keyCodes[0x052] = MP_KEY_KP_0; - __mpApp.keyCodes[0x04F] = MP_KEY_KP_1; - __mpApp.keyCodes[0x050] = MP_KEY_KP_2; - __mpApp.keyCodes[0x051] = MP_KEY_KP_3; - __mpApp.keyCodes[0x04B] = MP_KEY_KP_4; - __mpApp.keyCodes[0x04C] = MP_KEY_KP_5; - __mpApp.keyCodes[0x04D] = MP_KEY_KP_6; - __mpApp.keyCodes[0x047] = MP_KEY_KP_7; - __mpApp.keyCodes[0x048] = MP_KEY_KP_8; - __mpApp.keyCodes[0x049] = MP_KEY_KP_9; - __mpApp.keyCodes[0x04E] = MP_KEY_KP_ADD; - __mpApp.keyCodes[0x053] = MP_KEY_KP_DECIMAL; - __mpApp.keyCodes[0x135] = MP_KEY_KP_DIVIDE; - __mpApp.keyCodes[0x11C] = MP_KEY_KP_ENTER; - __mpApp.keyCodes[0x037] = MP_KEY_KP_MULTIPLY; - __mpApp.keyCodes[0x04A] = MP_KEY_KP_SUBTRACT; - - memset(__mpApp.nativeKeys, 0, sizeof(int)*MP_KEY_COUNT); - for(int nativeKey=0; nativeKey<256; nativeKey++) - { - mp_key_code mpKey = __mpApp.keyCodes[nativeKey]; - if(mpKey) - { - __mpApp.nativeKeys[mpKey] = nativeKey; - } - } -} - -void mp_init() -{ - if(!__mpApp.init) - { - memset(&__mpApp, 0, sizeof(__mpApp)); - - mp_init_common(); - mp_init_keys(); - - __mpApp.win32.savedConsoleCodePage = GetConsoleOutputCP(); - SetConsoleOutputCP(CP_UTF8); - - SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); - } -} - -void mp_terminate() -{ - if(__mpApp.init) - { - SetConsoleOutputCP(__mpApp.win32.savedConsoleCodePage); - - mp_terminate_common(); - __mpApp = (mp_app){0}; - } -} - -static mp_key_code mp_convert_win32_key(int code) -{ - return(__mpApp.keyCodes[code]); -} - -static mp_keymod_flags mp_get_mod_keys() -{ - mp_keymod_flags mods = 0; - if(GetKeyState(VK_SHIFT) & 0x8000) - { - mods |= MP_KEYMOD_SHIFT; - } - if(GetKeyState(VK_CONTROL) & 0x8000) - { - mods |= MP_KEYMOD_CTRL; - } - if(GetKeyState(VK_MENU) & 0x8000) - { - mods |= MP_KEYMOD_ALT; - } - if((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) - { - mods |= MP_KEYMOD_CMD; - } - return(mods); -} - -static void process_mouse_event(mp_window_data* window, mp_key_action action, mp_key_code button) -{ - if(action == MP_KEY_PRESS) - { - if(!__mpApp.win32.mouseCaptureMask) - { - SetCapture(window->win32.hWnd); - } - __mpApp.win32.mouseCaptureMask |= (1<win32.hWnd, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOZORDER); - - //TODO: send a message - - } break; - - //TODO: enter/exit size & move - - case WM_SIZING: - { - //TODO: take dpi into account - - RECT* rect = (RECT*)lParam; - - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_RESIZE; - event.frame.rect = (mp_rect){rect->bottom, rect->left, rect->top - rect->bottom, rect->right - rect->left}; - mp_queue_event(&event); - } break; - - case WM_MOVING: - { - //TODO: take dpi into account - - RECT* rect = (RECT*)lParam; - - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_MOVE; - event.frame.rect = (mp_rect){rect->bottom, rect->left, rect->top - rect->bottom, rect->right - rect->left}; - mp_queue_event(&event); - } break; - - case WM_SETFOCUS: - { - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_FOCUS; - mp_queue_event(&event); - } break; - - case WM_KILLFOCUS: - { - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_UNFOCUS; - mp_queue_event(&event); - } break; - - case WM_SIZE: - { - bool minimized = (wParam == SIZE_MINIMIZED); - if(minimized != mpWindow->minimized) - { - mpWindow->minimized = minimized; - - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - - if(minimized) - { - event.type = MP_EVENT_WINDOW_HIDE; - } - else if(mpWindow->minimized) - { - event.type = MP_EVENT_WINDOW_SHOW; - } - mp_queue_event(&event); - } - } break; - - case WM_LBUTTONDOWN: - { - process_mouse_event(mpWindow, MP_KEY_PRESS, MP_MOUSE_LEFT); - } break; - - case WM_RBUTTONDOWN: - { - process_mouse_event(mpWindow, MP_KEY_PRESS, MP_MOUSE_RIGHT); - } break; - - case WM_MBUTTONDOWN: - { - process_mouse_event(mpWindow, MP_KEY_PRESS, MP_MOUSE_MIDDLE); - } break; - - case WM_LBUTTONUP: - { - process_mouse_event(mpWindow, MP_KEY_RELEASE, MP_MOUSE_LEFT); - } break; - - case WM_RBUTTONUP: - { - process_mouse_event(mpWindow, MP_KEY_RELEASE, MP_MOUSE_RIGHT); - } break; - - case WM_MBUTTONUP: - { - process_mouse_event(mpWindow, MP_KEY_RELEASE, MP_MOUSE_MIDDLE); - } break; - - case WM_MOUSEMOVE: - { - RECT rect; - GetClientRect(mpWindow->win32.hWnd, &rect); - - u32 dpi = GetDpiForWindow(mpWindow->win32.hWnd); - f32 scaling = (f32)dpi/96.; - - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_MOUSE_MOVE; - event.move.x = LOWORD(lParam) / scaling; - event.move.y = HIWORD(lParam) / scaling; - - if(__mpApp.win32.mouseTracked || __mpApp.win32.mouseCaptureMask) - { - event.move.deltaX = event.move.x - __mpApp.win32.lastMousePos.x; - event.move.deltaY = event.move.y - __mpApp.win32.lastMousePos.y; - } - __mpApp.win32.lastMousePos = (vec2){event.move.x, event.move.y}; - - if(!__mpApp.win32.mouseTracked) - { - __mpApp.win32.mouseTracked = true; - - TRACKMOUSEEVENT track; - memset(&track, 0, sizeof(track)); - track.cbSize = sizeof(track); - track.dwFlags = TME_LEAVE; - track.hwndTrack = mpWindow->win32.hWnd; - TrackMouseEvent(&track); - - mp_event enter = {.window = event.window, - .type = MP_EVENT_MOUSE_ENTER, - .move.x = event.move.x, - .move.y = event.move.y}; - mp_queue_event(&enter); - } - - mp_queue_event(&event); - } break; - - case WM_MOUSELEAVE: - { - __mpApp.win32.mouseTracked = false; - - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_MOUSE_LEAVE; - mp_queue_event(&event); - } break; - - case WM_MOUSEWHEEL: - { - process_wheel_event(mpWindow, 0, (float)((i16)HIWORD(wParam))); - } break; - - case WM_MOUSEHWHEEL: - { - process_wheel_event(mpWindow, (float)((i16)HIWORD(wParam)), 0); - } break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - { - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_KEYBOARD_KEY; - event.key.action = (lParam & 0x40000000) ? MP_KEY_REPEAT : MP_KEY_PRESS; - event.key.code = mp_convert_win32_key(HIWORD(lParam) & 0x1ff); - event.key.mods = mp_get_mod_keys(); - mp_queue_event(&event); - } break; - - case WM_KEYUP: - case WM_SYSKEYUP: - { - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_KEYBOARD_KEY; - event.key.action = MP_KEY_RELEASE; - event.key.code = mp_convert_win32_key(HIWORD(lParam) & 0x1ff); - event.key.mods = mp_get_mod_keys(); - mp_queue_event(&event); - } break; - - case WM_CHAR: - { - if((u32)wParam >= 32) - { - mp_event event = {0}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_KEYBOARD_CHAR; - event.character.codepoint = (utf32)wParam; - str8 seq = utf8_encode(event.character.sequence, event.character.codepoint); - event.character.seqLen = seq.len; - mp_queue_event(&event); - } - } break; - - case WM_DROPFILES: - { - //TODO - } break; - - default: - { - result = DefWindowProc(windowHandle, message, wParam, lParam); - } break; - } - - return(result); -} - -//-------------------------------------------------------------------- -// app management -//-------------------------------------------------------------------- - -bool mp_should_quit() -{ - return(__mpApp.shouldQuit); -} - -void mp_cancel_quit() -{ - __mpApp.shouldQuit = false; -} - -void mp_request_quit() -{ - __mpApp.shouldQuit = true; -} - -void mp_pump_events(f64 timeout) -{ - MSG message; - while(PeekMessage(&message, 0, 0, 0, PM_REMOVE)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } -} - -//-------------------------------------------------------------------- -// window management -//-------------------------------------------------------------------- - -//WARN: the following header pulls in objbase.h (even with WIN32_LEAN_AND_MEAN), which -// #defines interface to struct... so make sure to #undef interface since it's a -// name we want to be able to use throughout the codebase -#include -#undef interface - -mp_window mp_window_create(mp_rect rect, const char* title, mp_window_style style) -{ - WNDCLASS windowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, - .lpfnWndProc = WinProc, - .hInstance = GetModuleHandleW(NULL), - .lpszClassName = "ApplicationWindowClass", - .hCursor = LoadCursor(0, IDC_ARROW)}; - - if(!RegisterClass(&windowClass)) - { - //TODO: error - goto quit; - } - - u32 dpiX, dpiY; - HMONITOR monitor = MonitorFromPoint((POINT){rect.x, rect.y}, MONITOR_DEFAULTTOPRIMARY); - GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - - f32 dpiScalingX = (f32)dpiX/96.; - f32 dpiScalingY = (f32)dpiY/96.; - - HWND windowHandle = CreateWindow("ApplicationWindowClass", "Test Window", - WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - rect.w * dpiScalingX, rect.h * dpiScalingY, - 0, 0, windowClass.hInstance, 0); - - if(!windowHandle) - { - //TODO: error - goto quit; - } - - UpdateWindow(windowHandle); - - //TODO: return wrapped window - quit:; - mp_window_data* window = mp_window_alloc(); - window->win32.hWnd = windowHandle; - - SetPropW(windowHandle, L"MilePost", window); - - return(mp_window_handle_from_ptr(window)); -} - -void mp_window_destroy(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - DestroyWindow(windowData->win32.hWnd); - //TODO: check when to unregister class - - mp_window_recycle_ptr(windowData); - } -} - -void* mp_window_native_pointer(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(windowData->win32.hWnd); - } - else - { - return(0); - } -} - -bool mp_window_should_close(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(windowData->shouldClose); - } - else - { - return(false); - } -} - -void mp_window_request_close(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - windowData->shouldClose = true; - PostMessage(windowData->win32.hWnd, WM_CLOSE, 0, 0); - } -} - -void mp_window_cancel_close(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - windowData->shouldClose = false; - } -} - - -bool mp_window_is_hidden(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(IsWindowVisible(windowData->win32.hWnd)); - } - else - { - return(false); - } -} - -void mp_window_hide(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - ShowWindow(windowData->win32.hWnd, SW_HIDE); - } -} - -void mp_window_show(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - ShowWindow(windowData->win32.hWnd, SW_NORMAL); - } -} - -bool mp_window_is_minimized(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(windowData->minimized); - } - else - { - return(false); - } -} - -void mp_window_minimize(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - ShowWindow(windowData->win32.hWnd, SW_MINIMIZE); - } -} - -void mp_window_maximize(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - ShowWindow(windowData->win32.hWnd, SW_MAXIMIZE); - } -} - -void mp_window_restore(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - ShowWindow(windowData->win32.hWnd, SW_RESTORE); - } -} - -bool mp_window_has_focus(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(GetActiveWindow() == windowData->win32.hWnd); - } - else - { - return(false); - } -} - -void mp_window_focus(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - SetFocus(windowData->win32.hWnd); - } -} - -void mp_window_unfocus(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - SetFocus(0); - } -} - -void mp_window_send_to_back(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - SetWindowPos(windowData->win32.hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } -} - -void mp_window_bring_to_front(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - if(!IsWindowVisible(windowData->win32.hWnd)) - { - ShowWindow(windowData->win32.hWnd, SW_NORMAL); - } - SetWindowPos(windowData->win32.hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); - } -} - -mp_rect mp_window_get_content_rect(mp_window window) -{ - mp_rect rect = {0}; - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - RECT winRect; - if(GetClientRect(windowData->win32.hWnd, &winRect)) - { - u32 dpi = GetDpiForWindow(windowData->win32.hWnd); - f32 scale = (float)dpi/96.; - rect = (mp_rect){0, 0, (winRect.right - winRect.left)/scale, (winRect.bottom - winRect.top)/scale}; - } - } - return(rect); -} - -//-------------------------------------------------------------------------------- -// clipboard functions -//-------------------------------------------------------------------------------- - -MP_API void mp_clipboard_clear(void) -{ - if(OpenClipboard(NULL)) - { - EmptyClipboard(); - CloseClipboard(); - } -} - -MP_API void mp_clipboard_set_string(str8 string) -{ - if(OpenClipboard(NULL)) - { - EmptyClipboard(); - - int wideCount = MultiByteToWideChar(CP_UTF8, 0, string.ptr, string.len, 0, 0); - HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, (wideCount+1)*sizeof(wchar_t)); - if(handle) - { - char* memory = GlobalLock(handle); - if(memory) - { - MultiByteToWideChar(CP_UTF8, 0, string.ptr, string.len, (wchar_t*)memory, wideCount); - ((wchar_t*)memory)[wideCount] = '\0'; - - GlobalUnlock(handle); - SetClipboardData(CF_UNICODETEXT, handle); - } - } - CloseClipboard(); - } -} - -MP_API str8 mp_clipboard_get_string(mem_arena* arena) -{ - str8 string = {0}; - - if(OpenClipboard(NULL)) - { - HANDLE handle = GetClipboardData(CF_UNICODETEXT); - if(handle) - { - char* memory = GlobalLock(handle); - if(memory) - { - u64 size = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)memory, -1, 0, 0, 0, 0); - if(size) - { - string.ptr = mem_arena_alloc(arena, size); - string.len = size - 1; - WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)memory, -1, string.ptr, size, 0, 0); - GlobalUnlock(handle); - } - } - } - CloseClipboard(); - } - return(string); -} - -MP_API str8 mp_clipboard_copy_string(str8 backing) -{ - //TODO - return((str8){0}); -} - - -//-------------------------------------------------------------------------------- -// win32 surfaces -//-------------------------------------------------------------------------------- - -#include"graphics_surface.h" - -vec2 mg_win32_surface_contents_scaling(mg_surface_data* surface) -{ - u32 dpi = GetDpiForWindow(surface->layer.hWnd); - vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.}; - return(contentsScaling); -} - -mp_rect mg_win32_surface_get_frame(mg_surface_data* surface) -{ - RECT rect = {0}; - GetClientRect(surface->layer.hWnd, &rect); - - vec2 scale = mg_win32_surface_contents_scaling(surface); - - 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 mg_win32_surface_set_frame(mg_surface_data* surface, mp_rect frame) -{ - HWND parent = GetParent(surface->layer.hWnd); - RECT parentContentRect; - - GetClientRect(parent, &parentContentRect); - int parentHeight = parentContentRect.bottom - parentContentRect.top; - - vec2 scale = mg_win32_surface_contents_scaling(surface); - - SetWindowPos(surface->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); -} - -bool mg_win32_surface_get_hidden(mg_surface_data* surface) -{ - bool hidden = !IsWindowVisible(surface->layer.hWnd); - return(hidden); -} - -void mg_win32_surface_set_hidden(mg_surface_data* surface, bool hidden) -{ - ShowWindow(surface->layer.hWnd, hidden ? SW_HIDE : SW_NORMAL); -} - -void* mg_win32_surface_native_layer(mg_surface_data* surface) -{ - return((void*)surface->layer.hWnd); -} - -mg_surface_id mg_win32_surface_remote_id(mg_surface_data* surface) -{ - return((mg_surface_id)surface->layer.hWnd); -} - -void mg_win32_surface_host_connect(mg_surface_data* surface, mg_surface_id remoteID) -{ - HWND dstWnd = surface->layer.hWnd; - HWND srcWnd = (HWND)remoteID; - - RECT dstRect; - GetClientRect(dstWnd, &dstRect); - - SetParent(srcWnd, dstWnd); - ShowWindow(srcWnd, SW_NORMAL); - - SetWindowPos(srcWnd, - HWND_TOP, - 0, - 0, - dstRect.right - dstRect.left, - dstRect.bottom - dstRect.top, - SWP_NOACTIVATE | SWP_NOZORDER); -} - -void mg_surface_cleanup(mg_surface_data* surface) -{ - DestroyWindow(surface->layer.hWnd); -} - -LRESULT LayerWinProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) -{ - if(message == WM_NCHITTEST) - { - return(HTTRANSPARENT); - } - else - { - return(DefWindowProc(windowHandle, message, wParam, lParam)); - } -} - -void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window) -{ - surface->contentsScaling = mg_win32_surface_contents_scaling; - surface->getFrame = mg_win32_surface_get_frame; - surface->setFrame = mg_win32_surface_set_frame; - surface->getHidden = mg_win32_surface_get_hidden; - surface->setHidden = mg_win32_surface_set_hidden; - surface->nativeLayer = mg_win32_surface_native_layer; - - //NOTE(martin): create a child window for the surface - WNDCLASS layerWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, - .lpfnWndProc = LayerWinProc, - .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; - - surface->layer.hWnd = CreateWindow("layer_window_class", "layer", - WS_CHILD | WS_VISIBLE, - 0, 0, width, height, - window->win32.hWnd, - 0, - layerWindowClass.hInstance, - 0); -} - -void mg_surface_init_remote(mg_surface_data* surface, u32 width, u32 height) -{ - surface->contentsScaling = mg_win32_surface_contents_scaling; - surface->getFrame = mg_win32_surface_get_frame; - surface->setFrame = mg_win32_surface_set_frame; - surface->getHidden = mg_win32_surface_get_hidden; - surface->setHidden = mg_win32_surface_set_hidden; - surface->nativeLayer = mg_win32_surface_native_layer; - surface->remoteID = mg_win32_surface_remote_id; - - WNDCLASS layerWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, - .lpfnWndProc = DefWindowProc, - .hInstance = GetModuleHandleW(NULL), - .lpszClassName = "server_layer_window_class", - .hCursor = LoadCursor(0, IDC_ARROW)}; - - RegisterClass(&layerWindowClass); - - //NOTE(martin): create a temporary parent window. This seems like a necessary hack, because if layer window is created as - // a normal window first, and then parented to the client window, it breaks resizing the parent - // window for some reason... - HWND tmpParent = CreateWindow("server_layer_window_class", "layerParent", - WS_OVERLAPPED, - 0, 0, width, height, - 0, - 0, - layerWindowClass.hInstance, - 0); - - //NOTE: create the layer window - surface->layer.hWnd = CreateWindowEx(WS_EX_NOACTIVATE, - "server_layer_window_class", "layer", - WS_CHILD, - 0, 0, width, height, - tmpParent, - 0, - layerWindowClass.hInstance, - 0); - - //NOTE: unparent it and destroy tmp parent - SetParent(surface->layer.hWnd, 0); - DestroyWindow(tmpParent); -} - -mg_surface_data* mg_win32_surface_create_host(mp_window window) -{ - mg_surface_data* surface = 0; - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - surface = malloc_type(mg_surface_data); - if(surface) - { - memset(surface, 0, sizeof(mg_surface_data)); - mg_surface_init_for_window(surface, windowData); - - surface->backend = MG_BACKEND_HOST; - surface->hostConnect = mg_win32_surface_host_connect; - } - } - return(surface); -} - -/////////////////////////////////////////// WIP /////////////////////////////////////////////// -//TODO: this is thrown here for a quick test. We should: -// - check for errors -// - use utf8 version of API -str8 mp_app_get_executable_path(mem_arena* arena) -{ - char* buffer = mem_arena_alloc_array(arena, char, MAX_PATH+1); - int size = GetModuleFileName(NULL, buffer, MAX_PATH+1); - //TODO: check for errors... - - return(str8_from_buffer(size, buffer)); -} - -str8 mp_app_get_resource_path(mem_arena* arena, const char* name) -{ - str8_list list = {0}; - mem_arena* scratch = mem_scratch(); - - str8 executablePath = mp_app_get_executable_path(scratch); - char* executablePathCString = str8_to_cstring(scratch, executablePath); - - char* driveBuffer = mem_arena_alloc_array(scratch, char, MAX_PATH); - char* dirBuffer = mem_arena_alloc_array(scratch, char, MAX_PATH); - - _splitpath_s(executablePathCString, driveBuffer, MAX_PATH, dirBuffer, MAX_PATH, 0, 0, 0, 0); - - str8 drive = STR8(driveBuffer); - str8 dirPath = STR8(dirBuffer); - - str8_list_push(scratch, &list, drive); - str8_list_push(scratch, &list, dirPath); - str8_list_push(scratch, &list, STR8("\\")); - str8_list_push(scratch, &list, str8_push_cstring(scratch, name)); - str8 path = str8_list_join(scratch, list); - char* pathCString = str8_to_cstring(scratch, path); - - char* buffer = mem_arena_alloc_array(arena, char, path.len+1); - char* filePart = 0; - int size = GetFullPathName(pathCString, MAX_PATH, buffer, &filePart); - - str8 result = str8_from_buffer(size, buffer); - return(result); -} -////////////////////////////////////////////////////////////////////////////////////////////////// +/************************************************************//** +* +* @file: win32_app.c +* @author: Martin Fouilleul +* @date: 16/12/2022 +* @revision: +* +*****************************************************************/ + +#include"mp_app.c" + +void mp_init_keys() +{ + memset(__mpApp.keyCodes, MP_KEY_UNKNOWN, 256*sizeof(int)); + + __mpApp.keyCodes[0x00B] = MP_KEY_0; + __mpApp.keyCodes[0x002] = MP_KEY_1; + __mpApp.keyCodes[0x003] = MP_KEY_2; + __mpApp.keyCodes[0x004] = MP_KEY_3; + __mpApp.keyCodes[0x005] = MP_KEY_4; + __mpApp.keyCodes[0x006] = MP_KEY_5; + __mpApp.keyCodes[0x007] = MP_KEY_6; + __mpApp.keyCodes[0x008] = MP_KEY_7; + __mpApp.keyCodes[0x009] = MP_KEY_8; + __mpApp.keyCodes[0x00A] = MP_KEY_9; + __mpApp.keyCodes[0x01E] = MP_KEY_A; + __mpApp.keyCodes[0x030] = MP_KEY_B; + __mpApp.keyCodes[0x02E] = MP_KEY_C; + __mpApp.keyCodes[0x020] = MP_KEY_D; + __mpApp.keyCodes[0x012] = MP_KEY_E; + __mpApp.keyCodes[0x021] = MP_KEY_F; + __mpApp.keyCodes[0x022] = MP_KEY_G; + __mpApp.keyCodes[0x023] = MP_KEY_H; + __mpApp.keyCodes[0x017] = MP_KEY_I; + __mpApp.keyCodes[0x024] = MP_KEY_J; + __mpApp.keyCodes[0x025] = MP_KEY_K; + __mpApp.keyCodes[0x026] = MP_KEY_L; + __mpApp.keyCodes[0x032] = MP_KEY_M; + __mpApp.keyCodes[0x031] = MP_KEY_N; + __mpApp.keyCodes[0x018] = MP_KEY_O; + __mpApp.keyCodes[0x019] = MP_KEY_P; + __mpApp.keyCodes[0x010] = MP_KEY_Q; + __mpApp.keyCodes[0x013] = MP_KEY_R; + __mpApp.keyCodes[0x01F] = MP_KEY_S; + __mpApp.keyCodes[0x014] = MP_KEY_T; + __mpApp.keyCodes[0x016] = MP_KEY_U; + __mpApp.keyCodes[0x02F] = MP_KEY_V; + __mpApp.keyCodes[0x011] = MP_KEY_W; + __mpApp.keyCodes[0x02D] = MP_KEY_X; + __mpApp.keyCodes[0x015] = MP_KEY_Y; + __mpApp.keyCodes[0x02C] = MP_KEY_Z; + __mpApp.keyCodes[0x028] = MP_KEY_APOSTROPHE; + __mpApp.keyCodes[0x02B] = MP_KEY_BACKSLASH; + __mpApp.keyCodes[0x033] = MP_KEY_COMMA; + __mpApp.keyCodes[0x00D] = MP_KEY_EQUAL; + __mpApp.keyCodes[0x029] = MP_KEY_GRAVE_ACCENT; + __mpApp.keyCodes[0x01A] = MP_KEY_LEFT_BRACKET; + __mpApp.keyCodes[0x00C] = MP_KEY_MINUS; + __mpApp.keyCodes[0x034] = MP_KEY_PERIOD; + __mpApp.keyCodes[0x01B] = MP_KEY_RIGHT_BRACKET; + __mpApp.keyCodes[0x027] = MP_KEY_SEMICOLON; + __mpApp.keyCodes[0x035] = MP_KEY_SLASH; + __mpApp.keyCodes[0x056] = MP_KEY_WORLD_2; + __mpApp.keyCodes[0x00E] = MP_KEY_BACKSPACE; + __mpApp.keyCodes[0x153] = MP_KEY_DELETE; + __mpApp.keyCodes[0x14F] = MP_KEY_END; + __mpApp.keyCodes[0x01C] = MP_KEY_ENTER; + __mpApp.keyCodes[0x001] = MP_KEY_ESCAPE; + __mpApp.keyCodes[0x147] = MP_KEY_HOME; + __mpApp.keyCodes[0x152] = MP_KEY_INSERT; + __mpApp.keyCodes[0x15D] = MP_KEY_MENU; + __mpApp.keyCodes[0x151] = MP_KEY_PAGE_DOWN; + __mpApp.keyCodes[0x149] = MP_KEY_PAGE_UP; + __mpApp.keyCodes[0x045] = MP_KEY_PAUSE; + __mpApp.keyCodes[0x146] = MP_KEY_PAUSE; + __mpApp.keyCodes[0x039] = MP_KEY_SPACE; + __mpApp.keyCodes[0x00F] = MP_KEY_TAB; + __mpApp.keyCodes[0x03A] = MP_KEY_CAPS_LOCK; + __mpApp.keyCodes[0x145] = MP_KEY_NUM_LOCK; + __mpApp.keyCodes[0x046] = MP_KEY_SCROLL_LOCK; + __mpApp.keyCodes[0x03B] = MP_KEY_F1; + __mpApp.keyCodes[0x03C] = MP_KEY_F2; + __mpApp.keyCodes[0x03D] = MP_KEY_F3; + __mpApp.keyCodes[0x03E] = MP_KEY_F4; + __mpApp.keyCodes[0x03F] = MP_KEY_F5; + __mpApp.keyCodes[0x040] = MP_KEY_F6; + __mpApp.keyCodes[0x041] = MP_KEY_F7; + __mpApp.keyCodes[0x042] = MP_KEY_F8; + __mpApp.keyCodes[0x043] = MP_KEY_F9; + __mpApp.keyCodes[0x044] = MP_KEY_F10; + __mpApp.keyCodes[0x057] = MP_KEY_F11; + __mpApp.keyCodes[0x058] = MP_KEY_F12; + __mpApp.keyCodes[0x064] = MP_KEY_F13; + __mpApp.keyCodes[0x065] = MP_KEY_F14; + __mpApp.keyCodes[0x066] = MP_KEY_F15; + __mpApp.keyCodes[0x067] = MP_KEY_F16; + __mpApp.keyCodes[0x068] = MP_KEY_F17; + __mpApp.keyCodes[0x069] = MP_KEY_F18; + __mpApp.keyCodes[0x06A] = MP_KEY_F19; + __mpApp.keyCodes[0x06B] = MP_KEY_F20; + __mpApp.keyCodes[0x06C] = MP_KEY_F21; + __mpApp.keyCodes[0x06D] = MP_KEY_F22; + __mpApp.keyCodes[0x06E] = MP_KEY_F23; + __mpApp.keyCodes[0x076] = MP_KEY_F24; + __mpApp.keyCodes[0x038] = MP_KEY_LEFT_ALT; + __mpApp.keyCodes[0x01D] = MP_KEY_LEFT_CONTROL; + __mpApp.keyCodes[0x02A] = MP_KEY_LEFT_SHIFT; + __mpApp.keyCodes[0x15B] = MP_KEY_LEFT_SUPER; + __mpApp.keyCodes[0x137] = MP_KEY_PRINT_SCREEN; + __mpApp.keyCodes[0x138] = MP_KEY_RIGHT_ALT; + __mpApp.keyCodes[0x11D] = MP_KEY_RIGHT_CONTROL; + __mpApp.keyCodes[0x036] = MP_KEY_RIGHT_SHIFT; + __mpApp.keyCodes[0x15C] = MP_KEY_RIGHT_SUPER; + __mpApp.keyCodes[0x150] = MP_KEY_DOWN; + __mpApp.keyCodes[0x14B] = MP_KEY_LEFT; + __mpApp.keyCodes[0x14D] = MP_KEY_RIGHT; + __mpApp.keyCodes[0x148] = MP_KEY_UP; + __mpApp.keyCodes[0x052] = MP_KEY_KP_0; + __mpApp.keyCodes[0x04F] = MP_KEY_KP_1; + __mpApp.keyCodes[0x050] = MP_KEY_KP_2; + __mpApp.keyCodes[0x051] = MP_KEY_KP_3; + __mpApp.keyCodes[0x04B] = MP_KEY_KP_4; + __mpApp.keyCodes[0x04C] = MP_KEY_KP_5; + __mpApp.keyCodes[0x04D] = MP_KEY_KP_6; + __mpApp.keyCodes[0x047] = MP_KEY_KP_7; + __mpApp.keyCodes[0x048] = MP_KEY_KP_8; + __mpApp.keyCodes[0x049] = MP_KEY_KP_9; + __mpApp.keyCodes[0x04E] = MP_KEY_KP_ADD; + __mpApp.keyCodes[0x053] = MP_KEY_KP_DECIMAL; + __mpApp.keyCodes[0x135] = MP_KEY_KP_DIVIDE; + __mpApp.keyCodes[0x11C] = MP_KEY_KP_ENTER; + __mpApp.keyCodes[0x037] = MP_KEY_KP_MULTIPLY; + __mpApp.keyCodes[0x04A] = MP_KEY_KP_SUBTRACT; + + memset(__mpApp.nativeKeys, 0, sizeof(int)*MP_KEY_COUNT); + for(int nativeKey=0; nativeKey<256; nativeKey++) + { + mp_key_code mpKey = __mpApp.keyCodes[nativeKey]; + if(mpKey) + { + __mpApp.nativeKeys[mpKey] = nativeKey; + } + } +} + +void mp_init() +{ + if(!__mpApp.init) + { + memset(&__mpApp, 0, sizeof(__mpApp)); + + mp_init_common(); + mp_init_keys(); + + __mpApp.win32.savedConsoleCodePage = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); + } +} + +void mp_terminate() +{ + if(__mpApp.init) + { + SetConsoleOutputCP(__mpApp.win32.savedConsoleCodePage); + + mp_terminate_common(); + __mpApp = (mp_app){0}; + } +} + +static mp_key_code mp_convert_win32_key(int code) +{ + return(__mpApp.keyCodes[code]); +} + +static mp_keymod_flags mp_get_mod_keys() +{ + mp_keymod_flags mods = 0; + if(GetKeyState(VK_SHIFT) & 0x8000) + { + mods |= MP_KEYMOD_SHIFT; + } + if(GetKeyState(VK_CONTROL) & 0x8000) + { + mods |= MP_KEYMOD_CTRL; + } + if(GetKeyState(VK_MENU) & 0x8000) + { + mods |= MP_KEYMOD_ALT; + } + if((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) + { + mods |= MP_KEYMOD_CMD; + } + return(mods); +} + +static void process_mouse_event(mp_window_data* window, mp_key_action action, mp_key_code button) +{ + if(action == MP_KEY_PRESS) + { + if(!__mpApp.win32.mouseCaptureMask) + { + SetCapture(window->win32.hWnd); + } + __mpApp.win32.mouseCaptureMask |= (1<win32.hWnd, + HWND_TOP, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOZORDER); + + //TODO: send a message + + } break; + + //TODO: enter/exit size & move + + case WM_SIZING: + { + //TODO: take dpi into account + + RECT* rect = (RECT*)lParam; + + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_WINDOW_RESIZE; + event.frame.rect = (mp_rect){rect->bottom, rect->left, rect->top - rect->bottom, rect->right - rect->left}; + mp_queue_event(&event); + } break; + + case WM_MOVING: + { + //TODO: take dpi into account + + RECT* rect = (RECT*)lParam; + + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_WINDOW_MOVE; + event.frame.rect = (mp_rect){rect->bottom, rect->left, rect->top - rect->bottom, rect->right - rect->left}; + mp_queue_event(&event); + } break; + + case WM_SETFOCUS: + { + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_WINDOW_FOCUS; + mp_queue_event(&event); + } break; + + case WM_KILLFOCUS: + { + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_WINDOW_UNFOCUS; + mp_queue_event(&event); + } break; + + case WM_SIZE: + { + bool minimized = (wParam == SIZE_MINIMIZED); + if(minimized != mpWindow->minimized) + { + mpWindow->minimized = minimized; + + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + + if(minimized) + { + event.type = MP_EVENT_WINDOW_HIDE; + } + else if(mpWindow->minimized) + { + event.type = MP_EVENT_WINDOW_SHOW; + } + mp_queue_event(&event); + } + } break; + + case WM_LBUTTONDOWN: + { + process_mouse_event(mpWindow, MP_KEY_PRESS, MP_MOUSE_LEFT); + } break; + + case WM_RBUTTONDOWN: + { + process_mouse_event(mpWindow, MP_KEY_PRESS, MP_MOUSE_RIGHT); + } break; + + case WM_MBUTTONDOWN: + { + process_mouse_event(mpWindow, MP_KEY_PRESS, MP_MOUSE_MIDDLE); + } break; + + case WM_LBUTTONUP: + { + process_mouse_event(mpWindow, MP_KEY_RELEASE, MP_MOUSE_LEFT); + } break; + + case WM_RBUTTONUP: + { + process_mouse_event(mpWindow, MP_KEY_RELEASE, MP_MOUSE_RIGHT); + } break; + + case WM_MBUTTONUP: + { + process_mouse_event(mpWindow, MP_KEY_RELEASE, MP_MOUSE_MIDDLE); + } break; + + case WM_MOUSEMOVE: + { + RECT rect; + GetClientRect(mpWindow->win32.hWnd, &rect); + + u32 dpi = GetDpiForWindow(mpWindow->win32.hWnd); + f32 scaling = (f32)dpi/96.; + + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_MOUSE_MOVE; + event.move.x = LOWORD(lParam) / scaling; + event.move.y = HIWORD(lParam) / scaling; + + if(__mpApp.win32.mouseTracked || __mpApp.win32.mouseCaptureMask) + { + event.move.deltaX = event.move.x - __mpApp.win32.lastMousePos.x; + event.move.deltaY = event.move.y - __mpApp.win32.lastMousePos.y; + } + __mpApp.win32.lastMousePos = (vec2){event.move.x, event.move.y}; + + if(!__mpApp.win32.mouseTracked) + { + __mpApp.win32.mouseTracked = true; + + TRACKMOUSEEVENT track; + memset(&track, 0, sizeof(track)); + track.cbSize = sizeof(track); + track.dwFlags = TME_LEAVE; + track.hwndTrack = mpWindow->win32.hWnd; + TrackMouseEvent(&track); + + mp_event enter = {.window = event.window, + .type = MP_EVENT_MOUSE_ENTER, + .move.x = event.move.x, + .move.y = event.move.y}; + mp_queue_event(&enter); + } + + mp_queue_event(&event); + } break; + + case WM_MOUSELEAVE: + { + __mpApp.win32.mouseTracked = false; + + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_MOUSE_LEAVE; + mp_queue_event(&event); + } break; + + case WM_MOUSEWHEEL: + { + process_wheel_event(mpWindow, 0, (float)((i16)HIWORD(wParam))); + } break; + + case WM_MOUSEHWHEEL: + { + process_wheel_event(mpWindow, (float)((i16)HIWORD(wParam)), 0); + } break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_KEYBOARD_KEY; + event.key.action = (lParam & 0x40000000) ? MP_KEY_REPEAT : MP_KEY_PRESS; + event.key.code = mp_convert_win32_key(HIWORD(lParam) & 0x1ff); + event.key.mods = mp_get_mod_keys(); + mp_queue_event(&event); + } break; + + case WM_KEYUP: + case WM_SYSKEYUP: + { + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_KEYBOARD_KEY; + event.key.action = MP_KEY_RELEASE; + event.key.code = mp_convert_win32_key(HIWORD(lParam) & 0x1ff); + event.key.mods = mp_get_mod_keys(); + mp_queue_event(&event); + } break; + + case WM_CHAR: + { + if((u32)wParam >= 32) + { + mp_event event = {0}; + event.window = mp_window_handle_from_ptr(mpWindow); + event.type = MP_EVENT_KEYBOARD_CHAR; + event.character.codepoint = (utf32)wParam; + str8 seq = utf8_encode(event.character.sequence, event.character.codepoint); + event.character.seqLen = seq.len; + mp_queue_event(&event); + } + } break; + + case WM_DROPFILES: + { + //TODO + } break; + + default: + { + result = DefWindowProc(windowHandle, message, wParam, lParam); + } break; + } + + return(result); +} + +//-------------------------------------------------------------------- +// app management +//-------------------------------------------------------------------- + +bool mp_should_quit() +{ + return(__mpApp.shouldQuit); +} + +void mp_cancel_quit() +{ + __mpApp.shouldQuit = false; +} + +void mp_request_quit() +{ + __mpApp.shouldQuit = true; +} + +void mp_pump_events(f64 timeout) +{ + MSG message; + while(PeekMessage(&message, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } +} + +//-------------------------------------------------------------------- +// window management +//-------------------------------------------------------------------- + +//WARN: the following header pulls in objbase.h (even with WIN32_LEAN_AND_MEAN), which +// #defines interface to struct... so make sure to #undef interface since it's a +// name we want to be able to use throughout the codebase +#include +#undef interface + +mp_window mp_window_create(mp_rect rect, const char* title, mp_window_style style) +{ + WNDCLASS windowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + .lpfnWndProc = WinProc, + .hInstance = GetModuleHandleW(NULL), + .lpszClassName = "ApplicationWindowClass", + .hCursor = LoadCursor(0, IDC_ARROW)}; + + if(!RegisterClass(&windowClass)) + { + //TODO: error + goto quit; + } + + u32 dpiX, dpiY; + HMONITOR monitor = MonitorFromPoint((POINT){rect.x, rect.y}, MONITOR_DEFAULTTOPRIMARY); + GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + + f32 dpiScalingX = (f32)dpiX/96.; + f32 dpiScalingY = (f32)dpiY/96.; + + HWND windowHandle = CreateWindow("ApplicationWindowClass", "Test Window", + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + rect.w * dpiScalingX, rect.h * dpiScalingY, + 0, 0, windowClass.hInstance, 0); + + if(!windowHandle) + { + //TODO: error + goto quit; + } + + UpdateWindow(windowHandle); + + //TODO: return wrapped window + quit:; + mp_window_data* window = mp_window_alloc(); + window->win32.hWnd = windowHandle; + + SetPropW(windowHandle, L"MilePost", window); + + return(mp_window_handle_from_ptr(window)); +} + +void mp_window_destroy(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + DestroyWindow(windowData->win32.hWnd); + //TODO: check when to unregister class + + mp_window_recycle_ptr(windowData); + } +} + +void* mp_window_native_pointer(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + return(windowData->win32.hWnd); + } + else + { + return(0); + } +} + +bool mp_window_should_close(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + return(windowData->shouldClose); + } + else + { + return(false); + } +} + +void mp_window_request_close(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + windowData->shouldClose = true; + PostMessage(windowData->win32.hWnd, WM_CLOSE, 0, 0); + } +} + +void mp_window_cancel_close(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + windowData->shouldClose = false; + } +} + + +bool mp_window_is_hidden(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + return(IsWindowVisible(windowData->win32.hWnd)); + } + else + { + return(false); + } +} + +void mp_window_hide(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + ShowWindow(windowData->win32.hWnd, SW_HIDE); + } +} + +void mp_window_show(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + ShowWindow(windowData->win32.hWnd, SW_NORMAL); + } +} + +bool mp_window_is_minimized(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + return(windowData->minimized); + } + else + { + return(false); + } +} + +void mp_window_minimize(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + ShowWindow(windowData->win32.hWnd, SW_MINIMIZE); + } +} + +void mp_window_maximize(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + ShowWindow(windowData->win32.hWnd, SW_MAXIMIZE); + } +} + +void mp_window_restore(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + ShowWindow(windowData->win32.hWnd, SW_RESTORE); + } +} + +bool mp_window_has_focus(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + return(GetActiveWindow() == windowData->win32.hWnd); + } + else + { + return(false); + } +} + +void mp_window_focus(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + SetFocus(windowData->win32.hWnd); + } +} + +void mp_window_unfocus(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + SetFocus(0); + } +} + +void mp_window_send_to_back(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + SetWindowPos(windowData->win32.hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } +} + +void mp_window_bring_to_front(mp_window window) +{ + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + if(!IsWindowVisible(windowData->win32.hWnd)) + { + ShowWindow(windowData->win32.hWnd, SW_NORMAL); + } + SetWindowPos(windowData->win32.hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } +} + +mp_rect mp_window_get_content_rect(mp_window window) +{ + mp_rect rect = {0}; + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + RECT winRect; + if(GetClientRect(windowData->win32.hWnd, &winRect)) + { + u32 dpi = GetDpiForWindow(windowData->win32.hWnd); + f32 scale = (float)dpi/96.; + rect = (mp_rect){0, 0, (winRect.right - winRect.left)/scale, (winRect.bottom - winRect.top)/scale}; + } + } + return(rect); +} + +//-------------------------------------------------------------------------------- +// clipboard functions +//-------------------------------------------------------------------------------- + +MP_API void mp_clipboard_clear(void) +{ + if(OpenClipboard(NULL)) + { + EmptyClipboard(); + CloseClipboard(); + } +} + +MP_API void mp_clipboard_set_string(str8 string) +{ + if(OpenClipboard(NULL)) + { + EmptyClipboard(); + + int wideCount = MultiByteToWideChar(CP_UTF8, 0, string.ptr, string.len, 0, 0); + HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, (wideCount+1)*sizeof(wchar_t)); + if(handle) + { + char* memory = GlobalLock(handle); + if(memory) + { + MultiByteToWideChar(CP_UTF8, 0, string.ptr, string.len, (wchar_t*)memory, wideCount); + ((wchar_t*)memory)[wideCount] = '\0'; + + GlobalUnlock(handle); + SetClipboardData(CF_UNICODETEXT, handle); + } + } + CloseClipboard(); + } +} + +MP_API str8 mp_clipboard_get_string(mem_arena* arena) +{ + str8 string = {0}; + + if(OpenClipboard(NULL)) + { + HANDLE handle = GetClipboardData(CF_UNICODETEXT); + if(handle) + { + char* memory = GlobalLock(handle); + if(memory) + { + u64 size = WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)memory, -1, 0, 0, 0, 0); + if(size) + { + string.ptr = mem_arena_alloc(arena, size); + string.len = size - 1; + WideCharToMultiByte(CP_UTF8, 0, (wchar_t*)memory, -1, string.ptr, size, 0, 0); + GlobalUnlock(handle); + } + } + } + CloseClipboard(); + } + return(string); +} + +MP_API str8 mp_clipboard_copy_string(str8 backing) +{ + //TODO + return((str8){0}); +} + + +//-------------------------------------------------------------------------------- +// win32 surfaces +//-------------------------------------------------------------------------------- + +#include"graphics_surface.h" + +vec2 mg_win32_surface_contents_scaling(mg_surface_data* surface) +{ + u32 dpi = GetDpiForWindow(surface->layer.hWnd); + vec2 contentsScaling = (vec2){(float)dpi/96., (float)dpi/96.}; + return(contentsScaling); +} + +mp_rect mg_win32_surface_get_frame(mg_surface_data* surface) +{ + RECT rect = {0}; + GetClientRect(surface->layer.hWnd, &rect); + + vec2 scale = mg_win32_surface_contents_scaling(surface); + + 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 mg_win32_surface_set_frame(mg_surface_data* surface, mp_rect frame) +{ + HWND parent = GetParent(surface->layer.hWnd); + RECT parentContentRect; + + GetClientRect(parent, &parentContentRect); + int parentHeight = parentContentRect.bottom - parentContentRect.top; + + vec2 scale = mg_win32_surface_contents_scaling(surface); + + SetWindowPos(surface->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); +} + +bool mg_win32_surface_get_hidden(mg_surface_data* surface) +{ + bool hidden = !IsWindowVisible(surface->layer.hWnd); + return(hidden); +} + +void mg_win32_surface_set_hidden(mg_surface_data* surface, bool hidden) +{ + ShowWindow(surface->layer.hWnd, hidden ? SW_HIDE : SW_NORMAL); +} + +void* mg_win32_surface_native_layer(mg_surface_data* surface) +{ + return((void*)surface->layer.hWnd); +} + +mg_surface_id mg_win32_surface_remote_id(mg_surface_data* surface) +{ + return((mg_surface_id)surface->layer.hWnd); +} + +void mg_win32_surface_host_connect(mg_surface_data* surface, mg_surface_id remoteID) +{ + HWND dstWnd = surface->layer.hWnd; + HWND srcWnd = (HWND)remoteID; + + RECT dstRect; + GetClientRect(dstWnd, &dstRect); + + SetParent(srcWnd, dstWnd); + ShowWindow(srcWnd, SW_NORMAL); + + SetWindowPos(srcWnd, + HWND_TOP, + 0, + 0, + dstRect.right - dstRect.left, + dstRect.bottom - dstRect.top, + SWP_NOACTIVATE | SWP_NOZORDER); +} + +void mg_surface_cleanup(mg_surface_data* surface) +{ + DestroyWindow(surface->layer.hWnd); +} + +LRESULT LayerWinProc(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) +{ + if(message == WM_NCHITTEST) + { + return(HTTRANSPARENT); + } + else + { + return(DefWindowProc(windowHandle, message, wParam, lParam)); + } +} + +void mg_surface_init_for_window(mg_surface_data* surface, mp_window_data* window) +{ + surface->contentsScaling = mg_win32_surface_contents_scaling; + surface->getFrame = mg_win32_surface_get_frame; + surface->setFrame = mg_win32_surface_set_frame; + surface->getHidden = mg_win32_surface_get_hidden; + surface->setHidden = mg_win32_surface_set_hidden; + surface->nativeLayer = mg_win32_surface_native_layer; + + //NOTE(martin): create a child window for the surface + WNDCLASS layerWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + .lpfnWndProc = LayerWinProc, + .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; + + surface->layer.hWnd = CreateWindow("layer_window_class", "layer", + WS_CHILD | WS_VISIBLE, + 0, 0, width, height, + window->win32.hWnd, + 0, + layerWindowClass.hInstance, + 0); +} + +void mg_surface_init_remote(mg_surface_data* surface, u32 width, u32 height) +{ + surface->contentsScaling = mg_win32_surface_contents_scaling; + surface->getFrame = mg_win32_surface_get_frame; + surface->setFrame = mg_win32_surface_set_frame; + surface->getHidden = mg_win32_surface_get_hidden; + surface->setHidden = mg_win32_surface_set_hidden; + surface->nativeLayer = mg_win32_surface_native_layer; + surface->remoteID = mg_win32_surface_remote_id; + + WNDCLASS layerWindowClass = {.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + .lpfnWndProc = DefWindowProc, + .hInstance = GetModuleHandleW(NULL), + .lpszClassName = "server_layer_window_class", + .hCursor = LoadCursor(0, IDC_ARROW)}; + + RegisterClass(&layerWindowClass); + + //NOTE(martin): create a temporary parent window. This seems like a necessary hack, because if layer window is created as + // a normal window first, and then parented to the client window, it breaks resizing the parent + // window for some reason... + HWND tmpParent = CreateWindow("server_layer_window_class", "layerParent", + WS_OVERLAPPED, + 0, 0, width, height, + 0, + 0, + layerWindowClass.hInstance, + 0); + + //NOTE: create the layer window + surface->layer.hWnd = CreateWindowEx(WS_EX_NOACTIVATE, + "server_layer_window_class", "layer", + WS_CHILD, + 0, 0, width, height, + tmpParent, + 0, + layerWindowClass.hInstance, + 0); + + //NOTE: unparent it and destroy tmp parent + SetParent(surface->layer.hWnd, 0); + DestroyWindow(tmpParent); +} + +mg_surface_data* mg_win32_surface_create_host(mp_window window) +{ + mg_surface_data* surface = 0; + mp_window_data* windowData = mp_window_ptr_from_handle(window); + if(windowData) + { + surface = malloc_type(mg_surface_data); + if(surface) + { + memset(surface, 0, sizeof(mg_surface_data)); + mg_surface_init_for_window(surface, windowData); + + surface->api = MG_HOST; + surface->hostConnect = mg_win32_surface_host_connect; + } + } + return(surface); +} + +/////////////////////////////////////////// WIP /////////////////////////////////////////////// +//TODO: this is thrown here for a quick test. We should: +// - check for errors +// - use utf8 version of API +str8 mp_app_get_executable_path(mem_arena* arena) +{ + char* buffer = mem_arena_alloc_array(arena, char, MAX_PATH+1); + int size = GetModuleFileName(NULL, buffer, MAX_PATH+1); + //TODO: check for errors... + + return(str8_from_buffer(size, buffer)); +} + +str8 mp_app_get_resource_path(mem_arena* arena, const char* name) +{ + str8_list list = {0}; + mem_arena* scratch = mem_scratch(); + + str8 executablePath = mp_app_get_executable_path(scratch); + char* executablePathCString = str8_to_cstring(scratch, executablePath); + + char* driveBuffer = mem_arena_alloc_array(scratch, char, MAX_PATH); + char* dirBuffer = mem_arena_alloc_array(scratch, char, MAX_PATH); + + _splitpath_s(executablePathCString, driveBuffer, MAX_PATH, dirBuffer, MAX_PATH, 0, 0, 0, 0); + + str8 drive = STR8(driveBuffer); + str8 dirPath = STR8(dirBuffer); + + str8_list_push(scratch, &list, drive); + str8_list_push(scratch, &list, dirPath); + str8_list_push(scratch, &list, STR8("\\")); + str8_list_push(scratch, &list, str8_push_cstring(scratch, name)); + str8 path = str8_list_join(scratch, list); + char* pathCString = str8_to_cstring(scratch, path); + + char* buffer = mem_arena_alloc_array(arena, char, path.len+1); + char* filePart = 0; + int size = GetFullPathName(pathCString, MAX_PATH, buffer, &filePart); + + str8 result = str8_from_buffer(size, buffer); + return(result); +} +//////////////////////////////////////////////////////////////////////////////////////////////////