From 5dc2716abd43166cd65133625f408ad36a6623b3 Mon Sep 17 00:00:00 2001 From: Vasco Date: Mon, 24 Aug 2026 00:56:24 +0100 Subject: Peak 0.4.0 and Draw --- Draw/draw.h | 224 ++++ Peak/build.c | 39 + Peak/demos/demo | Bin 0 -> 43544 bytes Peak/demos/demo.c | 38 +- Peak/demos/demo.html | 19 + Peak/demos/demo.js | 2370 +++++++++++++++++++++++++++++++++++++ Peak/demos/demo.wasm | Bin 0 -> 30983 bytes Peak/demos/demo_replace_main.c | 67 -- Peak/demos/demo_run | Bin 0 -> 43912 bytes Peak/demos/demo_run.c | 76 ++ Peak/peak.h | 1556 ++++++++++++++---------- Peak/src/p_emscripten.c | 194 ++- Peak/src/p_linux.c | 515 ++++---- Peak/src/p_win32.c | 399 ++++++- Peak/src/peak.c | 300 ++--- Peak/src/peak_header.h | 164 +-- Peak/src/peak_header.h.out | 1556 ++++++++++++++---------- Podium/README.md | 9 - Podium/p_allocators.c | 301 ----- Podium/p_allocators.h | 44 - Podium/p_ds.h | 52 - Podium/p_log.h | 186 --- Podium/p_math.c | 255 ---- Podium/p_math.h | 33 - Podium/p_platform_linux.c | 404 ------- Podium/p_platform_win32.c | 402 ------- Podium/p_string.c | 201 ---- Podium/p_string.h | 38 - Podium/p_types.h | 88 -- Podium/podium.c | 1 - Podium/podium.h | 2269 ----------------------------------- Podium/podium_header.h | 258 ---- README.md | 15 +- archive/Podium/README.md | 9 + archive/Podium/p_allocators.c | 301 +++++ archive/Podium/p_allocators.h | 44 + archive/Podium/p_ds.h | 52 + archive/Podium/p_log.h | 186 +++ archive/Podium/p_math.c | 255 ++++ archive/Podium/p_math.h | 33 + archive/Podium/p_platform_linux.c | 404 +++++++ archive/Podium/p_platform_win32.c | 402 +++++++ archive/Podium/p_string.c | 201 ++++ archive/Podium/p_string.h | 38 + archive/Podium/p_types.h | 88 ++ archive/Podium/podium.c | 1 + archive/Podium/podium.h | 2269 +++++++++++++++++++++++++++++++++++ archive/Podium/podium_header.h | 258 ++++ build.c | 34 +- 49 files changed, 10181 insertions(+), 6467 deletions(-) create mode 100644 Draw/draw.h create mode 100644 Peak/build.c create mode 100755 Peak/demos/demo create mode 100644 Peak/demos/demo.html create mode 100644 Peak/demos/demo.js create mode 100755 Peak/demos/demo.wasm delete mode 100644 Peak/demos/demo_replace_main.c create mode 100755 Peak/demos/demo_run create mode 100644 Peak/demos/demo_run.c delete mode 100644 Podium/README.md delete mode 100644 Podium/p_allocators.c delete mode 100644 Podium/p_allocators.h delete mode 100644 Podium/p_ds.h delete mode 100644 Podium/p_log.h delete mode 100644 Podium/p_math.c delete mode 100644 Podium/p_math.h delete mode 100644 Podium/p_platform_linux.c delete mode 100644 Podium/p_platform_win32.c delete mode 100644 Podium/p_string.c delete mode 100644 Podium/p_string.h delete mode 100644 Podium/p_types.h delete mode 100644 Podium/podium.c delete mode 100644 Podium/podium.h delete mode 100644 Podium/podium_header.h create mode 100644 archive/Podium/README.md create mode 100644 archive/Podium/p_allocators.c create mode 100644 archive/Podium/p_allocators.h create mode 100644 archive/Podium/p_ds.h create mode 100644 archive/Podium/p_log.h create mode 100644 archive/Podium/p_math.c create mode 100644 archive/Podium/p_math.h create mode 100644 archive/Podium/p_platform_linux.c create mode 100644 archive/Podium/p_platform_win32.c create mode 100644 archive/Podium/p_string.c create mode 100644 archive/Podium/p_string.h create mode 100644 archive/Podium/p_types.h create mode 100644 archive/Podium/podium.c create mode 100644 archive/Podium/podium.h create mode 100644 archive/Podium/podium_header.h diff --git a/Draw/draw.h b/Draw/draw.h new file mode 100644 index 0000000..fded875 --- /dev/null +++ b/Draw/draw.h @@ -0,0 +1,224 @@ +/* =========================================================================== + * DRAW - software raster into an RGBA32 buffer - see LICENCE at end of file. + * =========================================================================== */ + +#ifndef DRAW_H +#define DRAW_H + +#include +#include +#include +#include + +typedef struct Draw { + uint32_t *px; + int w, h; + int cx, cy, cw, ch; +} Draw; + +#define DRAW__MIN(a, b) ((a) < (b) ? (a) : (b)) +#define DRAW__MAX(a, b) ((a) > (b) ? (a) : (b)) + +static inline void +draw_begin(Draw *d, uint32_t *px, int w, int h) +{ + d->px = px; + d->w = w; + d->h = h; + d->cx = 0; + d->cy = 0; + d->cw = w; + d->ch = h; +} + +static inline void +draw_clip(Draw *d, int x, int y, int w, int h) +{ + d->cx = x; + d->cy = y; + d->cw = w; + d->ch = h; +} + +static inline void +draw_clip_reset(Draw *d) +{ + d->cx = 0; + d->cy = 0; + d->cw = d->w; + d->ch = d->h; +} + +static inline void +draw_pixel(Draw *d, int x, int y, uint32_t color) +{ + if (x < d->cx || x >= d->cx + d->cw || y < d->cy || y >= d->cy + d->ch) return; + if (x < 0 || x >= d->w || y < 0 || y >= d->h) return; + d->px[y * d->w + x] = color; +} + +static inline uint32_t +draw__lerp(uint32_t c0, uint32_t c1, float t) +{ + if (t <= 0.0f) return c0; + if (t >= 1.0f) return c1; + uint32_t a0 = (c0 >> 24) & 0xFF, a1 = (c1 >> 24) & 0xFF; + uint32_t r0 = (c0 >> 16) & 0xFF, r1 = (c1 >> 16) & 0xFF; + uint32_t g0 = (c0 >> 8) & 0xFF, g1 = (c1 >> 8) & 0xFF; + uint32_t b0 = c0 & 0xFF, b1 = c1 & 0xFF; + uint32_t a = (uint32_t)(a0 + t * ((float)a1 - (float)a0)); + uint32_t r = (uint32_t)(r0 + t * ((float)r1 - (float)r0)); + uint32_t g = (uint32_t)(g0 + t * ((float)g1 - (float)g0)); + uint32_t b = (uint32_t)(b0 + t * ((float)b1 - (float)b0)); + return (a << 24) | (r << 16) | (g << 8) | b; +} + +static inline float +draw__cross(float ax, float ay, float bx, float by, float cx, float cy) +{ + return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax); +} + +static inline void +draw_blit(Draw *d, int x, int y, const uint32_t *src, int sw, int sh) +{ + for (int row = 0; row < sh; ++row) { + int ty = y + row; + if (ty < d->cy || ty >= d->cy + d->ch || ty < 0 || ty >= d->h) continue; + int x0 = DRAW__MAX(x, d->cx); + int x1 = DRAW__MIN(x + sw, d->cx + d->cw); + if (x0 >= x1) continue; + memcpy(&d->px[ty * d->w + x0], &src[row * sw + (x0 - x)], (size_t)(x1 - x0) * sizeof(uint32_t)); + } +} + +static inline void +draw_rect(Draw *d, int x, int y, int w, int h, uint32_t color) +{ + int x0 = DRAW__MAX(x, d->cx); + int y0 = DRAW__MAX(y, d->cy); + int x1 = DRAW__MIN(x + w, d->cx + d->cw); + int y1 = DRAW__MIN(y + h, d->cy + d->ch); + for (int py = y0; py < y1; ++py) { + if (py < 0 || py >= d->h) continue; + for (int px = x0; px < x1; ++px) { + if (px < 0 || px >= d->w) continue; + d->px[py * d->w + px] = color; + } + } +} + +static inline void +draw_rect_gradient(Draw *d, int x, int y, int w, int h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) +{ + for (int ry = 0; ry < h; ++ry) { + float ty = (h > 1) ? (float)ry / (float)(h - 1) : 0.0f; + uint32_t left = draw__lerp(c0, c3, ty); + uint32_t right = draw__lerp(c1, c2, ty); + for (int rx = 0; rx < w; ++rx) { + float tx = (w > 1) ? (float)rx / (float)(w - 1) : 0.0f; + draw_pixel(d, x + rx, y + ry, draw__lerp(left, right, tx)); + } + } +} + +static inline void +draw_tri(Draw *d, int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2) +{ + int min_x = DRAW__MIN(x0, DRAW__MIN(x1, x2)); + int max_x = DRAW__MAX(x0, DRAW__MAX(x1, x2)); + int min_y = DRAW__MIN(y0, DRAW__MIN(y1, y2)); + int max_y = DRAW__MAX(y0, DRAW__MAX(y1, y2)); + float area = draw__cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); + if (area == 0.0f) return; + int solid = (c0 == c1 && c1 == c2); + for (int y = min_y; y <= max_y; ++y) { + for (int x = min_x; x <= max_x; ++x) { + float w0 = draw__cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y); + float w1 = draw__cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y); + float w2 = draw__cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y); + if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) { + uint32_t c = c0; + if (!solid) { + w0 /= area; + w1 /= area; + w2 /= area; + c = draw__lerp(c0, c1, w1 / (w0 + w1 + 1e-6f)); + c = draw__lerp(c, c2, w2); + } + draw_pixel(d, x, y, c); + } + } + } +} + +static inline void +draw_line(Draw *d, int x0, int y0, int x1, int y1, int width, uint32_t c0, uint32_t c1) +{ + float total = 0.0f; + int grad = (c0 != c1); + if (grad) total = sqrtf((float)((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0))); + int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; + int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; + int err = dx + dy; + int ox0 = x0, oy0 = y0; + int hw = width / 2; + for (;;) { + uint32_t c = c0; + if (grad) { + float cd = sqrtf((float)((x0 - ox0) * (x0 - ox0) + (y0 - oy0) * (y0 - oy0))); + c = draw__lerp(c0, c1, total > 0.0f ? cd / total : 0.0f); + } + for (int ox = -hw; ox <= hw; ++ox) + for (int oy = -hw; oy <= hw; ++oy) + draw_pixel(d, x0 + ox, y0 + oy, c); + if (x0 == x1 && y0 == y1) break; + int e2 = 2 * err; + if (e2 >= dy) { err += dy; x0 += sx; } + if (e2 <= dx) { err += dx; y0 += sy; } + } +} + +#endif /* DRAW_H */ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2026 Vasco Alves +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/Peak/build.c b/Peak/build.c new file mode 100644 index 0000000..c3b20b9 --- /dev/null +++ b/Peak/build.c @@ -0,0 +1,39 @@ +#define POOF_IMPLEMENTATION +#include "../Poof/poof.h" + +int main(int argc, char **argv) { + POOF_GO_REBUILD_URSELF(argc, argv); + + Poof_Cmd cmd = {0}; + poof_cmd_append(&cmd, "python", "../bundle_header.py", "src/peak_header.h"); + if (!poof_cmd_run(&cmd)) return 1; + + poof_copy_file("src/peak_header.h.out", "peak.h"); + + Poof_CC cc = {0}; + poof_cc_init(&cc, POOF_CC_GCC | POOF_CC_CLANG, POOF_TARGET_HOST); + cc.debug_mode = true; + cc.optimization = POOF_O0; + cc.output = "demos/demo"; + poof_cc_append(&cc.inputs, "demos/demo.c"); + poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations"); + poof_cc_append(&cc.libs, "m"); + if (!poof_cc_run(&cc)) return 1; + + cc.debug_mode = true; + cc.optimization = POOF_O0; + cc.output = "demos/demo_run"; + poof_cc_append(&cc.inputs, "demos/demo_run.c"); + poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations"); + poof_cc_append(&cc.libs, "m"); + if (!poof_cc_run(&cc)) return 1; + + poof_cmd_clear(&cmd); + poof_cmd_append(&cmd, "emcc", "demos/demo_run.c", "-o", "demos/demo.js", + "-std=c99", "-Wall", "-Wno-deprecated-declarations", + "-sALLOW_MEMORY_GROWTH=1", "-sENVIRONMENT=web"); + if (!poof_cmd_run(&cmd)) return 1; + + return 0; +} + diff --git a/Peak/demos/demo b/Peak/demos/demo new file mode 100755 index 0000000..8f72b0a Binary files /dev/null and b/Peak/demos/demo differ diff --git a/Peak/demos/demo.c b/Peak/demos/demo.c index 7aec9b3..cb5212d 100644 --- a/Peak/demos/demo.c +++ b/Peak/demos/demo.c @@ -1,16 +1,14 @@ #include -#include -#include #define PEAK_IMPLEMENTATION #include "../peak.h" - void print_window_resize(PeakEvent ev) { printf("window: %u %u\n", ev.resize.width, ev.resize.height); } void print_window_close(PeakEvent ev) { + (void)ev; printf("WINDOW CLOSE!!!!!!\nWOWWWOWAAAAAAAAAAAAAAAAAAAAAAAAh\n"); } @@ -34,32 +32,30 @@ print_event_func print_event[PEAK_EVENT_LAST] = { [PEAK_EVENT_KEY_UP] = print_key, }; +int main(int argc, char **argv) { + (void)argc; + (void)argv; -typedef struct { - char *msg; -} AppData; + if (!peak_init()) return 1; -static AppData data; + PeakWindow win = peak_window_open("Demo", 800, 600, 0); + peak_window_clear(&win, 0.5, 0.5, 0, 1); -int main(int argc, char**argv) { - peak_init(); - int running = 1000000; PeakEvent ev; - data.msg = "hello world"; - while (running--) { - while (peak_poll_events(&ev)) { - if (ev.type == PEAK_EVENT_NONE) - continue; + while (win.running) { + while (peak_window_epoll(&win, &ev)) { + if (ev.type == PEAK_EVENT_NONE) continue; print_event[ev.type](ev); - switch (ev.type) { - case PEAK_EVENT_WINDOW_CLOSE: - printf("STOPPPPEEED\n"); - return 1; + if (ev.type == PEAK_EVENT_WINDOW_CLOSE) { + win.running = 0; + break; } } - // printf("frame: %d - %s\n", running, data.msg); + if (!win.running) break; + } + + peak_window_close(&win); peak_quit(); return 0; } - diff --git a/Peak/demos/demo.html b/Peak/demos/demo.html new file mode 100644 index 0000000..a10e85e --- /dev/null +++ b/Peak/demos/demo.html @@ -0,0 +1,19 @@ + + + + + + Peak + + + +
+ +
+ + + diff --git a/Peak/demos/demo.js b/Peak/demos/demo.js new file mode 100644 index 0000000..6ebf1b1 --- /dev/null +++ b/Peak/demos/demo.js @@ -0,0 +1,2370 @@ +// include: shell.js +// include: minimum_runtime_check.js +(function() { + // "30.0.0" -> 300000 + function humanReadableVersionToPacked(str) { + str = str.split('-')[0]; // Remove any trailing part from e.g. "12.53.3-alpha" + var vers = str.split('.').slice(0, 3); + while(vers.length < 3) vers.push('00'); + vers = vers.map((n, i, arr) => n.padStart(2, '0')); + return vers.join(''); + } + // 300000 -> "30.0.0" + var packedVersionToHumanReadable = n => [n / 10000 | 0, (n / 100 | 0) % 100, n % 100].join('.'); + + var TARGET_NOT_SUPPORTED = 2147483647; + + // Note: We use a typeof check here instead of optional chaining using + // globalThis because older browsers might not have globalThis defined. + + // We skip the node version checking when running on Bun/Deno since the node + // version they report doesn't seem to be useful. + if (typeof process !== 'undefined' && !process.versions?.bun && typeof Deno == "undefined") { + var currentNodeVersion = process.versions?.node ? humanReadableVersionToPacked(process.versions.node) : TARGET_NOT_SUPPORTED; + if (currentNodeVersion < TARGET_NOT_SUPPORTED) { + throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + } + if (currentNodeVersion < 2147483647) { + throw new Error(`This emscripten-generated code requires node v${ packedVersionToHumanReadable(2147483647) } (detected v${packedVersionToHumanReadable(currentNodeVersion)})`); + } + } + + var userAgent = typeof navigator !== 'undefined' && navigator.userAgent; + if (!userAgent) { + return; + } + + var currentSafariVersion = userAgent.includes("Safari/") && !userAgent.includes("Chrome/") && userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED; + if (currentSafariVersion < 150000) { + throw new Error(`This emscripten-generated code requires Safari v${ packedVersionToHumanReadable(150000) } (detected v${currentSafariVersion})`); + } + + var currentFirefoxVersion = userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/) ? parseFloat(userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED; + if (currentFirefoxVersion < 79) { + throw new Error(`This emscripten-generated code requires Firefox v79 (detected v${currentFirefoxVersion})`); + } + + var currentChromeVersion = userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/) ? parseFloat(userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED; + if (currentChromeVersion < 85) { + throw new Error(`This emscripten-generated code requires Chrome v85 (detected v${currentChromeVersion})`); + } +})(); + +// end include: minimum_runtime_check.js +// The Module object: Our interface to the outside world. We import +// and export values on it. There are various ways Module can be used: +// 1. Not defined. We create it here +// 2. A function parameter, function(moduleArg) => Promise +// 3. pre-run appended it, var Module = {}; ..generated code.. +// 4. External script tag defines var Module. +// We need to check if Module already exists (e.g. case 3 above). +// Substitution will be replaced with actual code on later stage of the build, +// this way Closure Compiler will not mangle it (e.g. case 4. above). +// Note that if you want to run closure, and also to use Module +// after the generated code, you will need to define var Module = {}; +// before the code. Then that object will be used in the code, and you +// can continue to use Module afterwards as well. +var Module = typeof Module != 'undefined' ? Module : {}; + +// Determine the runtime environment we are in. You can customize this by +// setting the ENVIRONMENT setting at compile time (see settings.js). + +// Attempt to auto-detect the environment +var ENVIRONMENT_IS_WEB = !!globalThis.window; +var ENVIRONMENT_IS_WORKER = !!globalThis.WorkerGlobalScope; +// N.b. Electron.js environment is simultaneously a NODE-environment, but +// also a web environment. +var ENVIRONMENT_IS_NODE = globalThis.process?.versions?.node && globalThis.process?.type != 'renderer'; +var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + +// --pre-jses are emitted after the Module integration code, so that they can +// refer to Module (if they choose; they can also define Module) + + +var programArgs = []; +var thisProgram = './this.program'; +var quit_ = (status, toThrow) => { + throw toThrow; +}; + +// In MODULARIZE mode _scriptName needs to be captured already at the very top of the page immediately when the page is parsed, so it is generated there +// before the page load. In non-MODULARIZE modes generate it here. +var _scriptName = globalThis.document?.currentScript?.src; + +// `/` should be present at the end if `scriptDirectory` is not empty +var scriptDirectory = ''; +function locateFile(path) { + if (Module['locateFile']) { + return Module['locateFile'](path, scriptDirectory); + } + return scriptDirectory + path; +} + +// Hooks that are implemented differently in different runtime environments. +var readAsync, readBinary; + +if (ENVIRONMENT_IS_SHELL) { + +} else + +// Note that this includes Node.js workers when relevant (pthreads is enabled). +// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and +// ENVIRONMENT_IS_NODE. +if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { + try { + scriptDirectory = new URL('.', _scriptName).href; // includes trailing slash + } catch { + // Must be a `blob:` or `data:` URL (e.g. `blob:http://site.com/etc/etc`), we cannot + // infer anything from them. + } + + if (!(globalThis.window || globalThis.WorkerGlobalScope)) throw new Error('not compiled for this environment (did you build to HTML and try to run it not on the web, or set ENVIRONMENT to something - like node - and run it someplace else - like on the web?)'); + + { +// include: web_or_worker_shell_read.js +readAsync = async (url) => { + assert(!isFileURI(url), "readAsync does not work with file:// URLs"); + var response = await fetch(url, { credentials: 'same-origin' }); + if (response.ok) { + return response.arrayBuffer(); + } + throw new Error(response.status + ' : ' + response.url); + }; +// end include: web_or_worker_shell_read.js + } +} else +{ + throw new Error('environment detection error'); +} + +var out = console.log.bind(console); +var err = console.error.bind(console); + +var IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js'; +var PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js'; +var WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js'; +var FETCHFS = 'FETCHFS is no longer included by default; build with -lfetchfs.js'; +var ICASEFS = 'ICASEFS is no longer included by default; build with -licasefs.js'; +var JSFILEFS = 'JSFILEFS is no longer included by default; build with -ljsfilefs.js'; +var OPFS = 'OPFS is no longer included by default; build with -lopfs.js'; + +var NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js'; + +// perform assertions in shell.js after we set up out() and err(), as otherwise +// if an assertion fails it cannot print the message + +assert(!ENVIRONMENT_IS_WORKER, 'worker environment detected but not enabled at build time (add `worker` to `-sENVIRONMENT` to enable)'); + +assert(!ENVIRONMENT_IS_NODE, 'node environment detected but not enabled at build time (add `node` to `-sENVIRONMENT` to enable)'); + +assert(!ENVIRONMENT_IS_SHELL, 'shell environment detected but not enabled at build time (add `shell` to `-sENVIRONMENT` to enable)'); + +// end include: shell.js + +// include: preamble.js +// === Preamble library stuff === + +// Documentation for the public APIs defined in this file must be updated in: +// site/source/docs/api_reference/preamble.js.rst +// A prebuilt local version of the documentation is available at: +// site/build/text/docs/api_reference/preamble.js.txt +// You can also build docs locally as HTML or other formats in site/ +// An online HTML version (which may be of a different version of Emscripten) +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html + +var wasmBinary; + +if (!globalThis.WebAssembly) { + err('no native wasm support detected'); +} + +// Wasm globals + +//======================================== +// Runtime essentials +//======================================== + +// whether we are quitting the application. no code should run after this. +// set in exit() and abort() +var ABORT = false; + +// set by exit() and abort(). Passed to 'onExit' handler. +// NOTE: This is also used as the process return code in shell environments +// but only when noExitRuntime is false. +var EXITSTATUS; + +// In STRICT mode, we only define assert() when ASSERTIONS is set. i.e. we +// don't define it at all in release modes. This matches the behaviour of +// MINIMAL_RUNTIME. +// TODO(sbc): Make this the default even without STRICT enabled. +/** @type {function(*, string=)} */ +function assert(condition, text) { + if (!condition) { + abort('Assertion failed' + (text ? ': ' + text : '')); + } +} + +// We used to include malloc/free by default in the past. Show a helpful error in +// builds with assertions. +function _free() { + // Show a helpful error since we used to include free by default in the past. + abort('free() called but not included in the build - add `_free` to EXPORTED_FUNCTIONS'); +} + +/** + * Indicates whether filename is delivered via file protocol (as opposed to http/https) + * @noinline + */ +var isFileURI = (filename) => filename.startsWith('file://'); + +// include: runtime_common.js +// include: runtime_exceptions.js +// Base Emscripten EH error class +class EmscriptenEH {} + +class EmscriptenSjLj extends EmscriptenEH {} + +// end include: runtime_exceptions.js +// include: runtime_debug.js +var runtimeDebug = true; // Switch to false at runtime to disable logging at the right times + +// Used by XXXXX_DEBUG settings to output debug messages. +function dbg(...args) { + if (!runtimeDebug && typeof runtimeDebug != 'undefined') return; + // TODO(sbc): Make this configurable somehow. Its not always convenient for + // logging to show up as warnings. + console.warn(...args); +} + +// Endianness check +(() => { + var h16 = new Int16Array(1); + var h8 = new Int8Array(h16.buffer); + h16[0] = 0x6373; + if (h8[0] !== 0x73 || h8[1] !== 0x63) abort('Runtime error: expected the system to be little-endian! (Run with -sSUPPORT_BIG_ENDIAN to bypass)'); +})(); + +function consumedModuleProp(prop) { + var value = Module[prop]; + var msg = `Attempt to modify \`Module.${prop}\` after it has already been processed. This can happen, for example, when code is injected via '--post-js' rather than '--pre-js'`; + if (Array.isArray(value)) { + value = new Proxy(value, { + set(target, key, val) { + abort(msg); + return false; + }, + defineProperty(target, key, descriptor) { + abort(msg); + return false; + }, + deleteProperty(target, key) { + abort(msg); + return false; + } + }); + } + Object.defineProperty(Module, prop, { + configurable: true, + get() { return value; }, + set() { + abort(msg); + } + }); +} + +function makeInvalidEarlyAccess(name) { + return () => assert(false, `call to '${name}' via reference taken before Wasm module initialization`); + +} + +function ignoredModuleProp(prop) { + if (Object.getOwnPropertyDescriptor(Module, prop)) { + abort(`\`Module.${prop}\` was supplied but \`${prop}\` not included in INCOMING_MODULE_JS_API`); + } +} + +// forcing the filesystem exports a few things by default +function isExportedByForceFilesystem(name) { + return name === 'FS_createPath' || + name === 'FS_createDataFile' || + name === 'FS_createPreloadedFile' || + name === 'FS_preloadFile' || + name === 'FS_unlink' || + name === 'addRunDependency' || + // The old FS has some functionality that WasmFS lacks. + name === 'FS_createLazyFile' || + name === 'FS_createDevice' || + name === 'removeRunDependency'; +} + +/** + * Intercept access to a symbols in the global symbol. This enables us to give + * informative warnings/errors when folks attempt to use symbols they did not + * include in their build, or no symbols that no longer exist. + * + * We don't define this in MODULARIZE mode since in that mode emscripten symbols + * are never placed in the global scope. + */ +function hookGlobalSymbolAccess(sym, func) { + if (!Object.getOwnPropertyDescriptor(globalThis, sym)) { + Object.defineProperty(globalThis, sym, { + configurable: true, + get() { + func(); + return undefined; + } + }); + } +} + +function missingGlobal(sym, msg) { + hookGlobalSymbolAccess(sym, () => { + warnOnce(`\`${sym}\` is no longer defined by emscripten. ${msg}`); + }); +} + +missingGlobal('buffer', 'Please use HEAP8.buffer or wasmMemory.buffer'); +missingGlobal('asm', 'Please use wasmExports instead'); + +function missingLibrarySymbol(sym) { + hookGlobalSymbolAccess(sym, () => { + // Can't `abort()` here because it would break code that does runtime + // checks. e.g. `if (typeof SDL === 'undefined')`. + var msg = `\`${sym}\` is a library symbol and not included by default; add it to your library.js __deps or to DEFAULT_LIBRARY_FUNCS_TO_INCLUDE on the command line`; + // DEFAULT_LIBRARY_FUNCS_TO_INCLUDE requires the name as it appears in + // library.js, which means $name for a JS name with no prefix, or name + // for a JS name like _name. + var librarySymbol = sym; + if (!librarySymbol.startsWith('_')) { + librarySymbol = '$' + sym; + } + msg += ` (e.g. -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE='${librarySymbol}')`; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + warnOnce(msg); + }); + + // Any symbol that is not included from the JS library is also (by definition) + // not exported on the Module object. + unexportedRuntimeSymbol(sym); +} + +function unexportedRuntimeSymbol(sym) { + if (!Object.getOwnPropertyDescriptor(Module, sym)) { + Object.defineProperty(Module, sym, { + configurable: true, + get() { + var msg = `'${sym}' was not exported. add it to EXPORTED_RUNTIME_METHODS (see the Emscripten FAQ)`; + if (isExportedByForceFilesystem(sym)) { + msg += '. Alternatively, forcing filesystem support (-sFORCE_FILESYSTEM) can export this for you'; + } + abort(msg); + }, + }); + } +} + +// end include: runtime_debug.js +// include: runtime_stack_check.js +const stackCookie1 = 0x02135467; +const stackCookie2 = 0x89BACDFE; + +// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode. +function writeStackCookie() { + var max = _emscripten_stack_get_end(); + assert((max & 3) == 0); + // If the stack ends at address zero we write our cookies 4 bytes into the + // stack. This prevents interference with SAFE_HEAP and ASAN which also + // monitor writes to address zero. + if (max == 0) { + max += 4; + } + // The stack grow downwards towards _emscripten_stack_get_end. + // We write cookies to the final two words in the stack and detect if they are + // ever overwritten. + HEAPU32[((max)>>2)] = stackCookie1; + HEAPU32[(((max)+(4))>>2)] = stackCookie2; + // Also test the global address 0 for integrity. + HEAPU32[((0)>>2)] = 1668509029; +} + +function u32ToHexString(num) { + return '0x' + (num >>> 0).toString(16).padStart(8, '0'); +} + +function checkStackCookie() { + if (ABORT) return; + var max = _emscripten_stack_get_end(); + // See writeStackCookie(). + if (max == 0) { + max += 4; + } + var val1 = HEAPU32[((max)>>2)]; + var val2 = HEAPU32[(((max)+(4))>>2)]; + if (val1 != stackCookie1 || val2 != stackCookie2) { + abort(`Stack overflow! Stack cookie has been overwritten at ${ptrToString(max)}, expected hex dwords ${u32ToHexString(stackCookie2)} and ${u32ToHexString(stackCookie1)}, but received ${u32ToHexString(val2)} ${u32ToHexString(val1)}`); + } + // Also test the global address 0 for integrity. + if (HEAPU32[((0)>>2)] != 0x63736d65 /* 'emsc' */) { + abort('Runtime error: The application has corrupted its heap memory area (address zero)!'); + } +} +// end include: runtime_stack_check.js +// Memory management + +var runtimeInitialized = false; + + + +// When ALLOW_MEMORY_GROWTH is enabled, the conversion from Wasm +// memory to ArrayBuffer requires some additional logic. +function getMemoryBuffer() { + return wasmMemory.buffer; +} + +function updateMemoryViews() { + // If we already have a heap that is resizeable/growable buffer we don't + // need to do anything in updateMemoryViews. + if (HEAP8?.buffer?.resizable) return; + var b = getMemoryBuffer(); + HEAP8 = new Int8Array(b); + HEAP16 = new Int16Array(b); + HEAPU8 = new Uint8Array(b); + + HEAP32 = new Int32Array(b); + HEAPU32 = new Uint32Array(b); + + HEAPF64 = new Float64Array(b); + HEAP64 = new BigInt64Array(b); + +} + +// include: memoryprofiler.js +// end include: memoryprofiler.js +// end include: runtime_common.js +assert(globalThis.Int32Array && globalThis.Float64Array && Int32Array.prototype.subarray && Int32Array.prototype.set, + 'JS engine does not provide full typed array support'); + +function preRun() { + var preRun = Module['preRun']; + if (preRun) { + if (typeof preRun == 'function') preRun = [preRun]; + onPreRuns.push(...preRun); + } + consumedModuleProp('preRun'); + // Begin ATPRERUNS hooks + callRuntimeCallbacks(onPreRuns); + // End ATPRERUNS hooks +} + +function initRuntime() { + assert(!runtimeInitialized); + runtimeInitialized = true; + + checkStackCookie(); + + // No ATINITS hooks + + wasmExports['__wasm_call_ctors'](); + + // No ATPOSTCTORS hooks + + checkStackCookie(); +} + +function postRun() { + checkStackCookie(); + + var postRun = Module['postRun']; + if (postRun) { + if (typeof postRun == 'function') postRun = [postRun]; + onPostRuns.push(...postRun); + } + consumedModuleProp('postRun'); + + // Begin ATPOSTRUNS hooks + callRuntimeCallbacks(onPostRuns); + // End ATPOSTRUNS hooks +} + +/** + * @param {string|number=} what + */ +function abort(what) { + Module['onAbort']?.(what); + + what = `Aborted(${what})`; + // TODO(sbc): Should we remove printing and leave it up to whoever + // catches the exception? + err(what); + + ABORT = true; + + // Use a wasm runtime error, because a JS error might be seen as a foreign + // exception, which means we'd run destructors on it. We need the error to + // simply make the program stop. + // FIXME This approach does not work in Wasm EH because it currently does not assume + // all RuntimeErrors are from traps; it decides whether a RuntimeError is from + // a trap or not based on a hidden field within the object. So at the moment + // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that + // allows this in the wasm spec. + + // Suppress closure compiler warning here. Closure compiler's builtin extern + // definition for WebAssembly.RuntimeError claims it takes no arguments even + // though it can. + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. + /** @suppress {checkTypes} */ + var e = new WebAssembly.RuntimeError(what); + + // Throw the error whether or not MODULARIZE is set because abort is used + // in code paths apart from instantiation where an exception is expected + // to be thrown when abort is called. + throw e; +} + +// show errors on likely calls to FS when it was not included +function fsMissing() { + abort('Filesystem support (FS) was not included. The problem is that you are using files from JS, but files were not used from C/C++, so filesystem support was not auto-included. You can force-include filesystem support with -sFORCE_FILESYSTEM'); +} +var FS = { + init: fsMissing, + createDataFile: fsMissing, + createPreloadedFile: fsMissing, + createLazyFile: fsMissing, + open: fsMissing, + mkdev: fsMissing, + registerDevice: fsMissing, + analyzePath: fsMissing, + ErrnoError: fsMissing, +}; + + +function createExportWrapper(name, func, nargs) { + assert(func); + return (...args) => { + assert(runtimeInitialized, `native function \`${name}\` called before runtime initialization`); + // Only assert for too many arguments. Too few can be valid since the missing arguments will be zero filled. + assert(args.length <= nargs, `native function \`${name}\` called with ${args.length} args but expects ${nargs}`); + return func(...args); + }; +} + +var wasmBinaryFile; + +function findWasmBinary() { + return locateFile('demo.wasm'); +} + +function getBinarySync(file) { + if (readBinary) { + return readBinary(file); + } + // Throwing a plain string here, even though it not normally advisable since + // this gets turning into an `abort` in instantiateArrayBuffer. + throw 'both async and sync fetching of the wasm failed'; +} + +async function getWasmBinary(binaryFile) { + // If we don't have the binary yet, load it asynchronously using readAsync. + if (!wasmBinary) { + // Fetch the binary using readAsync + try { + var response = await readAsync(binaryFile); + return new Uint8Array(response); + } catch { + // Fall back to getBinarySync below; + } + } + + // Otherwise, getBinarySync should be able to get it synchronously + return getBinarySync(binaryFile); +} + +async function instantiateArrayBuffer(binaryFile, imports) { + try { + var binary = await getWasmBinary(binaryFile); + var instance = await WebAssembly.instantiate(binary, imports); + return instance; + } catch (reason) { + err(`failed to asynchronously prepare wasm: ${reason}`); + + // Warn on some common problems. + if (isFileURI(binaryFile)) { + err(`warning: Loading from a file URI (${binaryFile}) is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing`); + } + abort(reason); + } +} + +async function instantiateAsync(binary, binaryFile, imports) { + if (!binary + ) { + try { + var response = fetch(binaryFile, { credentials: 'same-origin' }); + var instantiationResult = await WebAssembly.instantiateStreaming(response, imports); + return instantiationResult; + } catch (reason) { + // We expect the most common failure cause to be a bad MIME type for the binary, + // in which case falling back to ArrayBuffer instantiation should work. + err(`wasm streaming compile failed: ${reason}`); + err('falling back to ArrayBuffer instantiation'); + // fall back of instantiateArrayBuffer below + }; + } + return instantiateArrayBuffer(binaryFile, imports); +} + +function getWasmImports() { + // prepare imports + var imports = { + 'env': wasmImports, + 'wasi_snapshot_preview1': wasmImports, + }; + return imports; +} + +// Create the wasm instance. +// Receives the wasm imports, returns the exports. +async function createWasm() { + // Load the wasm module and create an instance of using native support in the JS engine. + // handle a generated wasm instance, receiving its exports and + // performing other necessary setup + function receiveInstance(instance) { + wasmExports = instance.exports; + + assignWasmExports(wasmExports); + + updateMemoryViews(); + + return wasmExports; + } + + // Prefer streaming instantiation if available. + // Async compilation can be confusing when an error on the page overwrites Module + // (for example, if the order of elements is wrong, and the one defining Module is + // later), so we save Module and check it later. + var trueModule = Module; + function receiveInstantiationResult(result) { + // 'result' is a ResultObject object which has both the module and instance. + // receiveInstance() will swap in the exports (to Module.asm) so they can be called + assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?'); + trueModule = null; + // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. + // When the regression is fixed, can restore the above PTHREADS-enabled path. + return receiveInstance(result['instance']); + } + + var info = getWasmImports(); + + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback + // to manually instantiate the Wasm module themselves. This allows pages to + // run the instantiation parallel to any other async startup actions they are + // performing. + // Also pthreads and wasm workers initialize the wasm instance through this + // path. + var instantiateWasm = Module['instantiateWasm']; + if (instantiateWasm) { + return new Promise((resolve) => { + try { + instantiateWasm(info, (inst) => resolve(receiveInstance(inst))); + } catch(e) { + err(`Module.instantiateWasm callback failed with error: ${e}`); + throw e; + } + }); + } + + wasmBinaryFile ??= findWasmBinary(); + var result = await instantiateAsync(wasmBinary, wasmBinaryFile, info); + var exports = receiveInstantiationResult(result); + return exports; +} + +// end include: preamble.js + +// Begin JS library code + + + class ExitStatus { + name = 'ExitStatus'; + constructor(status) { + this.message = `Program terminated with exit(${status})`; + this.status = status; + } + } + + /** @type {!Int32Array} */ + var HEAP32; + + /** @type {!Int8Array} */ + var HEAP8; + + /** @type {!Uint32Array} */ + var HEAPU32; + + var callRuntimeCallbacks = (callbacks) => { + while (callbacks.length > 0) { + // Pass the module as the first argument. + callbacks.shift()(Module); + } + }; + var onPostRuns = []; + var addOnPostRun = (cb) => onPostRuns.push(cb); + + var onPreRuns = []; + var addOnPreRun = (cb) => onPreRuns.push(cb); + + + var noExitRuntime = true; + + function ptrToString(ptr) { + assert(typeof ptr === 'number', `ptrToString expects a number, got ${typeof ptr}`); + // Convert to 32-bit unsigned value + ptr >>>= 0; + return '0x' + ptr.toString(16).padStart(8, '0'); + } + + var stackRestore = (val) => __emscripten_stack_restore(val); + + var stackSave = () => _emscripten_stack_get_current(); + + var warnOnce = (text) => { + warnOnce.shown ||= {}; + if (!warnOnce.shown[text]) { + warnOnce.shown[text] = 1; + err(text); + } + }; + + + + var UTF8Decoder = globalThis.TextDecoder && new TextDecoder(); + + + /** + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul + * @return {number} + */ + var findStringEnd = (heapOrArray, idx, maxBytesToRead, ignoreNul) => { + var maxIdx = idx + maxBytesToRead; + if (ignoreNul) return maxIdx; + // TextDecoder needs to know the byte length in advance, it doesn't stop on + // null terminator by itself. + // As a tiny code save trick, compare idx against maxIdx using a negation, + // so that maxBytesToRead=undefined/NaN means Infinity. + while (heapOrArray[idx] && !(idx >= maxIdx)) ++idx; + return idx; + }; + + + /** + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given + * array that contains uint8 values, returns a copy of that string as a + * Javascript String object. + * heapOrArray is either a regular array, or a JavaScript typed array view. + * @param {number=} idx + * @param {number=} maxBytesToRead + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead, ignoreNul) => { + + var endPtr = findStringEnd(heapOrArray, idx, maxBytesToRead, ignoreNul); + + // When using conditional TextDecoder, skip it for short strings as the overhead of the native call is not worth it. + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); + } + var str = ''; + while (idx < endPtr) { + // For UTF8 byte structure, see: + // http://en.wikipedia.org/wiki/UTF-8#Description + // https://www.ietf.org/rfc/rfc2279.txt + // https://tools.ietf.org/html/rfc3629 + var u0 = heapOrArray[idx++]; + if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; } + var u1 = heapOrArray[idx++] & 63; + if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; } + var u2 = heapOrArray[idx++] & 63; + if ((u0 & 0xF0) == 0xE0) { + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; + } else { + if ((u0 & 0xF8) != 0xF0) warnOnce(`Invalid UTF-8 leading byte ${ptrToString(u0)} encountered when deserializing a UTF-8 string in wasm memory to a JS string!`); + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); + } + + if (u0 < 0x10000) { + str += String.fromCharCode(u0); + } else { + var ch = u0 - 0x10000; + str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF)); + } + } + return str; + }; + + /** @type {!Uint8Array} */ + var HEAPU8; + + /** + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the + * emscripten HEAP, returns a copy of that string as a Javascript String object. + * + * @param {number} ptr + * @param {number=} maxBytesToRead - An optional length that specifies the + * maximum number of bytes to read. You can omit this parameter to scan the + * string until the first 0 byte. If maxBytesToRead is passed, and the string + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the + * string will cut short at that byte index. + * @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character. + * @return {string} + */ + var UTF8ToString = (ptr, maxBytesToRead, ignoreNul) => { + assert(typeof ptr == 'number', `UTF8ToString expects a number (got ${typeof ptr})`); + return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead, ignoreNul) : ''; + }; + var ___assert_fail = (condition, filename, line, func) => + abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']); + + var __abort_js = () => + abort('native code called abort()'); + + var _emscripten_get_now = () => performance.now(); + + var _emscripten_date_now = () => Date.now(); + + var nowIsMonotonic = 1; + + var checkWasiClock = (clock_id) => clock_id >= 0 && clock_id <= 3; + + var INT53_MAX = 9007199254740992; + + var INT53_MIN = -9007199254740992; + var bigintToI53Checked = (num) => (num < INT53_MIN || num > INT53_MAX) ? NaN : Number(num); + + /** not-@type {!BigInt64Array} */ + var HEAP64; + function _clock_time_get(clk_id, ignored_precision, ptime) { + ignored_precision = bigintToI53Checked(ignored_precision); + + + if (!checkWasiClock(clk_id)) { + return 28; + } + var now; + // all wasi clocks but realtime are monotonic + if (clk_id === 0) { + now = _emscripten_date_now(); + } else if (nowIsMonotonic) { + now = _emscripten_get_now(); + } else { + return 52; + } + // "now" is in ms, and wasi times are in ns. + var nsec = Math.round(now * 1000 * 1000); + HEAP64[((ptime)>>3)] = BigInt(nsec); + return 0; + ; + } + + + var _emscripten_set_main_loop_timing = (mode, value) => { + MainLoop.timingMode = mode; + MainLoop.timingValue = value; + + if (!MainLoop.func) { + err('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.'); + return 1; // Return non-zero on failure, can't set timing mode when there is no main loop. + } + + if (mode == 0) { + MainLoop.scheduler = function MainLoop_scheduler_setTimeout() { + var timeUntilNextTick = Math.max(0, MainLoop.tickStartTime + value - _emscripten_get_now())|0; + setTimeout(MainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop + }; + } else if (mode == 1) { + MainLoop.scheduler = function MainLoop_scheduler_rAF() { + MainLoop.requestAnimationFrame(MainLoop.runner); + }; + } else { + assert(mode == 2); + if (!MainLoop.setImmediate) { + if (globalThis.scheduler) { + // Some modern browsers implement scheduler.postTask, but not all. + MainLoop.setImmediate = scheduler.postTask.bind(scheduler); + } else { + // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed) + var setImmediates = []; + var emscriptenMainLoopMessageId = 'setimmediate'; + /** @param {Event} event */ + var MainLoop_setImmediate_messageHandler = (event) => { + if (event.data === emscriptenMainLoopMessageId) { + event.stopPropagation(); + setImmediates.shift()(); + } + }; + addEventListener('message', MainLoop_setImmediate_messageHandler, true); + MainLoop.setImmediate = /** @type{function(function(): ?, ...?): number} */((func) => { + setImmediates.push(func); + if (ENVIRONMENT_IS_WORKER) { + // The postMessge API in a Worker, sends message to the main + // thread and does not support the `targetOrigin` (*) argument. + postMessage(emscriptenMainLoopMessageId); + } else { + postMessage(emscriptenMainLoopMessageId, '*'); + } + }); + } + } + MainLoop.scheduler = function MainLoop_scheduler_setImmediate() { + MainLoop.setImmediate(MainLoop.runner); + }; + } + return 0; + }; + + + + var runtimeKeepaliveCounter = 0; + var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; + var _proc_exit = (code) => { + EXITSTATUS = code; + if (!keepRuntimeAlive()) { + Module['onExit']?.(code); + ABORT = true; + } + quit_(code, new ExitStatus(code)); + }; + + + /** @param {boolean|number=} implicit */ + var exitJS = (status, implicit) => { + EXITSTATUS = status; + + checkUnflushedContent(); + + // if exit() was called explicitly, warn the user if the runtime isn't actually being shut down + if (keepRuntimeAlive() && !implicit) { + var msg = `program exited (with status: ${status}), but keepRuntimeAlive() is set (counter=${runtimeKeepaliveCounter}) due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)`; + err(msg); + } + + _proc_exit(status); + }; + var _exit = exitJS; + + var handleException = (e) => { + // Certain exception types we do not treat as errors since they are used for + // internal control flow. + // 1. ExitStatus, which is thrown by exit() + // 2. "unwind", which is thrown by emscripten_unwind_to_js_event_loop() and others + // that wish to return to JS event loop. + if (e instanceof ExitStatus || e == 'unwind') { + return EXITSTATUS; + } + checkStackCookie(); + if (e instanceof WebAssembly.RuntimeError) { + if (_emscripten_stack_get_current() <= 0) { + err('Stack overflow detected. You can try increasing -sSTACK_SIZE (currently set to 65536)'); + } + } + quit_(1, e); + }; + + var maybeExit = () => { + if (!keepRuntimeAlive()) { + try { + _exit(EXITSTATUS); + } catch (e) { + handleException(e); + } + } + }; + + /** + * @param {number=} arg + * @param {boolean=} noSetTiming + */ + var setMainLoop = (iterFunc, fps, simulateInfiniteLoop, arg, noSetTiming) => { + assert(!MainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once') + MainLoop.func = iterFunc; + MainLoop.arg = arg; + + var thisMainLoopId = MainLoop.currentlyRunningMainloop; + function checkIsRunning() { + if (thisMainLoopId < MainLoop.currentlyRunningMainloop) { + maybeExit(); + return false; + } + return true; + } + + // We create the loop runner here but it is not actually running until + // _emscripten_set_main_loop_timing is called (which might happen at a + // later time). + MainLoop.runner = function MainLoop_runner() { + if (ABORT) return; + if (MainLoop.queue.length > 0) { + var start = Date.now(); + var blocker = MainLoop.queue.shift(); + blocker.func(blocker.arg); + if (MainLoop.remainingBlockers) { + var remaining = MainLoop.remainingBlockers; + var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining); + if (blocker.counted) { + MainLoop.remainingBlockers = next; + } else { + // not counted, but move the progress along a tiny bit + next = next + 0.5; // do not steal all the next one's progress + MainLoop.remainingBlockers = (8*remaining + next)/9; + } + } + MainLoop.updateStatus(); + + // catches pause/resume main loop from blocker execution + if (!checkIsRunning()) return; + + setTimeout(MainLoop.runner, 0); + return; + } + + // catch pauses from non-main loop sources + if (!checkIsRunning()) return; + + // Implement very basic swap interval control + MainLoop.currentFrameNumber = MainLoop.currentFrameNumber + 1 | 0; + if (MainLoop.timingMode == 1 && MainLoop.timingValue > 1 && MainLoop.currentFrameNumber % MainLoop.timingValue != 0) { + // Not the scheduled time to render this frame - skip. + MainLoop.scheduler(); + return; + } else if (MainLoop.timingMode == 0) { + MainLoop.tickStartTime = _emscripten_get_now(); + if (Module['ctx']) { + warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!'); + } + } + + MainLoop.runIter(iterFunc); + + // catch pauses from the main loop itself + if (!checkIsRunning()) return; + + MainLoop.scheduler(); + } + + if (!noSetTiming) { + if (fps > 0) { + _emscripten_set_main_loop_timing(0, 1000.0 / fps); + } else { + // Do rAF by rendering each frame (no decimating) + _emscripten_set_main_loop_timing(1, 1); + } + + MainLoop.scheduler(); + } + + if (simulateInfiniteLoop) { + throw 'unwind'; + } + }; + + + var callUserCallback = (func) => { + if (ABORT) { + err('user callback triggered after runtime exited or application aborted. Ignoring.'); + return; + } + try { + return func(); + } catch (e) { + handleException(e); + } finally { + maybeExit(); + } + }; + + var MainLoop = { + func:null, + scheduler:null, + currentlyRunningMainloop:0, + arg:0, + timingMode:0, + timingValue:0, + currentFrameNumber:0, + queue:[], + preMainLoop:[], + postMainLoop:[], + pause() { + if (MainLoop.scheduler) { + MainLoop.scheduler = null; + // Incrementing this signals the previous main loop that it's now become old, and it must return. + MainLoop.currentlyRunningMainloop++; + + } + }, + resume() { + MainLoop.currentlyRunningMainloop++; + var timingMode = MainLoop.timingMode; + var timingValue = MainLoop.timingValue; + var func = MainLoop.func; + MainLoop.func = null; + // do not set timing and call scheduler, we will do it on the next lines + setMainLoop(func, 0, false, MainLoop.arg, true); + _emscripten_set_main_loop_timing(timingMode, timingValue); + MainLoop.scheduler(); + }, + updateStatus() { + if (Module['setStatus']) { + var message = Module['statusMessage'] || 'Please wait...'; + var remaining = MainLoop.remainingBlockers ?? 0; + var expected = MainLoop.expectedBlockers ?? 0; + if (remaining) { + if (remaining < expected) { + Module['setStatus'](`{message} ({expected - remaining}/{expected})`); + } else { + Module['setStatus'](message); + } + } else { + Module['setStatus'](''); + } + } + }, + init() { + }, + runIter(func) { + if (ABORT) return; + for (var pre of MainLoop.preMainLoop) { + if (pre() === false) { + return; // |return false| skips a frame + } + } + callUserCallback(func); + for (var post of MainLoop.postMainLoop) { + post(); + } + checkStackCookie(); + }, + nextRAF:0, + fakeRequestAnimationFrame(func) { + // try to keep 60fps between calls to here + var now = Date.now(); + if (!MainLoop.nextRAF) { + MainLoop.nextRAF = now + 1000/60; + } else { + while (now + 2 >= MainLoop.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0 + MainLoop.nextRAF += 1000/60; + } + } + var delay = Math.max(MainLoop.nextRAF - now, 0); + setTimeout(func, delay); + }, + requestAnimationFrame(func) { + if (globalThis.requestAnimationFrame) { + requestAnimationFrame(func); + } else { + MainLoop.fakeRequestAnimationFrame(func); + } + }, + }; + var _emscripten_cancel_main_loop = () => { + MainLoop.pause(); + MainLoop.func = null; + }; + + + var getHeapMax = () => + // Stay one Wasm page short of 4GB: while e.g. Chrome is able to allocate + // full 4GB Wasm memories, the size will wrap back to 0 bytes in Wasm side + // for any code that deals with heap sizes, which would require special + // casing all heap size related code to treat 0 specially. + 2147483648; + + var alignMemory = (size, alignment) => { + assert(alignment, 'alignment argument is required'); + return Math.ceil(size / alignment) * alignment; + }; + + var growMemory = (size) => { + var oldHeapSize = wasmMemory.buffer.byteLength; + var pages = ((size - oldHeapSize + 65535) / 65536) | 0; + try { + // round size grow request up to wasm page size (fixed 64KB per spec) + wasmMemory.grow(pages); // .grow() takes a delta compared to the previous size + updateMemoryViews(); + return 1 /*success*/; + } catch(e) { + err(`growMemory: Attempted to grow heap from ${oldHeapSize} bytes to ${size} bytes, but got error: ${e}`); + } + // implicit 0 return to save code size (caller will cast 'undefined' into 0 + // anyhow) + }; + + var _emscripten_resize_heap = (requestedSize) => { + var oldSize = HEAPU8.length; + // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. + requestedSize >>>= 0; + // With multithreaded builds, races can happen (another thread might increase the size + // in between), so return a failure, and let the caller retry. + assert(requestedSize > oldSize); + + // Memory resize rules: + // 1. Always increase heap size to at least the requested size, rounded up + // to next page multiple. + // 2a. If MEMORY_GROWTH_LINEAR_STEP == -1, excessively resize the heap + // geometrically: increase the heap size according to + // MEMORY_GROWTH_GEOMETRIC_STEP factor (default +20%), At most + // overreserve by MEMORY_GROWTH_GEOMETRIC_CAP bytes (default 96MB). + // 2b. If MEMORY_GROWTH_LINEAR_STEP != -1, excessively resize the heap + // linearly: increase the heap size by at least + // MEMORY_GROWTH_LINEAR_STEP bytes. + // 3. Max size for the heap is capped at 2048MB-WASM_PAGE_SIZE, or by + // MAXIMUM_MEMORY, or by ASAN limit, depending on which is smallest + // 4. If we were unable to allocate as much memory, it may be due to + // over-eager decision to excessively reserve due to (3) above. + // Hence if an allocation fails, cut down on the amount of excess + // growth, in an attempt to succeed to perform a smaller allocation. + + // A limit is set for how much we can grow. We should not exceed that + // (the wasm binary specifies it, so if we tried, we'd fail anyhow). + var maxHeapSize = getHeapMax(); + if (requestedSize > maxHeapSize) { + err(`Cannot enlarge memory, requested ${requestedSize} bytes, but the limit is ${maxHeapSize} bytes!`); + return false; + } + + // Loop through potential heap size increases. If we attempt a too eager + // reservation that fails, cut down on the attempted size and reserve a + // smaller bump instead. (max 3 times, chosen somewhat arbitrarily) + for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { + var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown); // ensure geometric growth + // but limit overreserving (default to capping at +96MB overgrowth at most) + overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 ); + + var newSize = Math.min(maxHeapSize, alignMemory(Math.max(requestedSize, overGrownHeapSize), 65536)); + + var replacement = growMemory(newSize); + if (replacement) { + + return true; + } + } + err(`Failed to grow the heap from ${oldSize} bytes to ${newSize} bytes, not enough memory!`); + return false; + }; + + var onExits = []; + var addOnExit = (cb) => onExits.push(cb); + var JSEvents = { + removeAllEventListeners() { + while (JSEvents.eventHandlers.length) { + JSEvents._removeHandler(JSEvents.eventHandlers.length - 1); + } + JSEvents.deferredCalls = []; + }, + inEventHandler:0, + deferredCalls:[], + deferCall(targetFunction, precedence, argsList) { + function arraysHaveEqualContent(arrA, arrB) { + if (arrA.length != arrB.length) return false; + + for (var i = 0; i < arrA.length; i++) { + if (arrA[i] != arrB[i]) return false; + } + return true; + } + // Test if the given call was already queued, and if so, don't add it again. + for (var call of JSEvents.deferredCalls) { + if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) { + return; + } + } + JSEvents.deferredCalls.push({ + targetFunction, + precedence, + argsList + }); + + JSEvents.deferredCalls.sort((x,y) => x.precedence - y.precedence); + }, + removeDeferredCalls(targetFunction) { + JSEvents.deferredCalls = JSEvents.deferredCalls.filter((call) => call.targetFunction != targetFunction); + }, + canPerformEventHandlerRequests() { + // Browsers that support navigator.userActivation.isActive: https://developer.mozilla.org/en-US/docs/Web/API/UserActivation/isActive + if (navigator.userActivation) { + // Verify against transient activation status from UserActivation API + // whether it is possible to perform a request here without needing to defer. See + // https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation + // and https://caniuse.com/mdn-api_useractivation + return navigator.userActivation.isActive; + } + + return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls; + }, + runDeferredCalls() { + if (!JSEvents.canPerformEventHandlerRequests()) { + return; + } + var deferredCalls = JSEvents.deferredCalls; + JSEvents.deferredCalls = []; + for (var call of deferredCalls) { + call.targetFunction(...call.argsList); + } + }, + eventHandlers:[], + removeAllHandlersOnTarget:(target, eventTypeString) => { + for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == target && + (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) { + JSEvents._removeHandler(i--); + } + } + }, + _removeHandler(i) { + var h = JSEvents.eventHandlers[i]; + h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture); + JSEvents.eventHandlers.splice(i, 1); + }, + registerOrRemoveHandler(eventHandler) { + if (!eventHandler.target) { + err('registerOrRemoveHandler: the target element for event handler registration does not exist, when processing the following event handler registration:'); + console.dir(eventHandler); + return -4; + } + if (eventHandler.callbackfunc) { + eventHandler.eventListenerFunc = function(event) { + // Increment nesting count for the event handler. + ++JSEvents.inEventHandler; + JSEvents.currentEventHandler = eventHandler; + // Process any old deferred calls the user has placed. + JSEvents.runDeferredCalls(); + // Process the actual event, calls back to user C code handler. + eventHandler.handlerFunc(event); + // Process any new deferred calls that were placed right now from this event handler. + JSEvents.runDeferredCalls(); + // Out of event handler - restore nesting count. + --JSEvents.inEventHandler; + }; + + eventHandler.target.addEventListener(eventHandler.eventTypeString, + eventHandler.eventListenerFunc, + eventHandler.useCapture); + JSEvents.eventHandlers.push(eventHandler); + } else { + for (var i = 0; i < JSEvents.eventHandlers.length; ++i) { + if (JSEvents.eventHandlers[i].target == eventHandler.target + && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) { + JSEvents._removeHandler(i--); + } + } + } + return 0; + }, + removeSingleHandler(eventHandler) { + let success = false; + for (let i = 0; i < JSEvents.eventHandlers.length; ++i) { + const handler = JSEvents.eventHandlers[i]; + if (handler.target === eventHandler.target + && handler.eventTypeId === eventHandler.eventTypeId + && handler.callbackfunc === eventHandler.callbackfunc + && handler.userData === eventHandler.userData) { + // in some very rare cases (ex: Safari / fullscreen events), there is more than 1 handler (eventTypeString is different) + JSEvents._removeHandler(i--); + success = true; + } + } + return success ? 0 : -5; + }, + getNodeNameForTarget(target) { + if (target == window) return '#window'; + if (target == screen) return '#screen'; + return target?.nodeName ?? ''; + }, + fullscreenEnabled() { + return document.fullscreenEnabled + // Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitFullscreenEnabled. + // TODO: If Safari at some point ships with unprefixed version, update the version check above. + ?? document.webkitFullscreenEnabled + ; + }, + }; + + var maybeCStringToJsString = (cString) => { + // 'cString > 2' checks if the input is a number, and isn't of the special + // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2). + // In other words, if cString > 2 then it's a pointer to a valid place in + // memory, and points to a C string. + return cString > 2 ? UTF8ToString(cString) : cString; + }; + + /** @type {Object} */ + var specialHTMLTargets = [0, document, window]; + var findEventTarget = (target) => { + target = maybeCStringToJsString(target); + var domElement = specialHTMLTargets[target] || document.querySelector(target); + return domElement; + }; + + var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { + assert(typeof str === 'string', `stringToUTF8Array expects a string (got ${typeof str})`); + // Parameter maxBytesToWrite is not optional. Negative values, 0, null, + // undefined and false each don't write out any bytes. + if (!(maxBytesToWrite > 0)) + return 0; + + var startIdx = outIdx; + var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator. + for (var i = 0; i < str.length; ++i) { + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description + // and https://www.ietf.org/rfc/rfc2279.txt + // and https://tools.ietf.org/html/rfc3629 + var u = str.codePointAt(i); + if (u <= 0x7F) { + if (outIdx >= endIdx) break; + heap[outIdx++] = u; + } else if (u <= 0x7FF) { + if (outIdx + 1 >= endIdx) break; + heap[outIdx++] = 0xC0 | (u >> 6); + heap[outIdx++] = 0x80 | (u & 63); + } else if (u <= 0xFFFF) { + if (outIdx + 2 >= endIdx) break; + heap[outIdx++] = 0xE0 | (u >> 12); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + } else { + if (outIdx + 3 >= endIdx) break; + if (u > 0x10FFFF) warnOnce(`Invalid Unicode code point ${ptrToString(u)} encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x10FFFF).`); + heap[outIdx++] = 0xF0 | (u >> 18); + heap[outIdx++] = 0x80 | ((u >> 12) & 63); + heap[outIdx++] = 0x80 | ((u >> 6) & 63); + heap[outIdx++] = 0x80 | (u & 63); + // Gotcha: if codePoint is over 0xFFFF, it is represented as a surrogate pair in UTF-16. + // We need to manually skip over the second code unit for correct iteration. + i++; + } + } + // Null-terminate the pointer to the buffer. + heap[outIdx] = 0; + return outIdx - startIdx; + }; + + var stringToUTF8 = (str, outPtr, maxBytesToWrite) => { + assert(typeof maxBytesToWrite == 'number', 'stringToUTF8 requires a third parameter that specifies the length of the output buffer'); + return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); + }; + + + var wasmTableMirror = []; + + + var getWasmTableEntry = (funcPtr) => { + var func = wasmTableMirror[funcPtr]; + if (!func) { + /** @suppress {checkTypes} */ + wasmTableMirror[funcPtr] = func = wasmTable.get(funcPtr); + } + /** @suppress {checkTypes} */ + assert(wasmTable.get(funcPtr) == func, 'table mirror is out of date'); + return func; + }; + + + + /** @type {!Float64Array} */ + var HEAPF64; + var registerKeyEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { + var eventSize = 160; + JSEvents.keyEvent ||= _malloc(eventSize); + + var keyEventHandlerFunc = (e) => { + assert(e); + + var keyEventData = JSEvents.keyEvent; + HEAPF64[((keyEventData)>>3)] = e.timeStamp; + + var idx = ((keyEventData)>>2); + + HEAP32[idx + 2] = e.location; + HEAP8[keyEventData + 12] = e.ctrlKey; + HEAP8[keyEventData + 13] = e.shiftKey; + HEAP8[keyEventData + 14] = e.altKey; + HEAP8[keyEventData + 15] = e.metaKey; + HEAP8[keyEventData + 16] = e.repeat; + HEAP32[idx + 5] = e.charCode; + HEAP32[idx + 6] = e.keyCode; + HEAP32[idx + 7] = e.which; + stringToUTF8(e.key ?? '', keyEventData + 32, 32); + stringToUTF8(e.code ?? '', keyEventData + 64, 32); + stringToUTF8(e.char ?? '', keyEventData + 96, 32); + stringToUTF8(e.locale ?? '', keyEventData + 128, 32); + + if (getWasmTableEntry(callbackfunc)(eventTypeId, keyEventData, userData)) e.preventDefault(); + }; + + var eventHandler = { + target: findEventTarget(target), + eventTypeString, + eventTypeId, + userData, + callbackfunc, + handlerFunc: keyEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + var _emscripten_set_keydown_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => + registerKeyEventCallback(target, userData, useCapture, callbackfunc, 2, 'keydown', targetThread); + + var _emscripten_set_keyup_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => + registerKeyEventCallback(target, userData, useCapture, callbackfunc, 3, 'keyup', targetThread); + + + var _emscripten_set_main_loop_arg = (func, arg, fps, simulateInfiniteLoop) => { + var iterFunc = () => getWasmTableEntry(func)(arg); + setMainLoop(iterFunc, fps, simulateInfiniteLoop, arg); + }; + + + var getBoundingClientRect = (e) => specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0}; + + + /** @type {!Int16Array} */ + var HEAP16; + + + var fillMouseEventData = (eventStruct, e, target) => { + assert(eventStruct % 4 == 0); + HEAPF64[((eventStruct)>>3)] = e.timeStamp; + var idx = ((eventStruct)>>2); + HEAP32[idx + 2] = e.screenX; + HEAP32[idx + 3] = e.screenY; + HEAP32[idx + 4] = e.clientX; + HEAP32[idx + 5] = e.clientY; + HEAP8[eventStruct + 24] = e.ctrlKey; + HEAP8[eventStruct + 25] = e.shiftKey; + HEAP8[eventStruct + 26] = e.altKey; + HEAP8[eventStruct + 27] = e.metaKey; + HEAP16[idx*2 + 14] = e.button; + HEAP16[idx*2 + 15] = e.buttons; + HEAP32[idx + 8] = e.movementX; + HEAP32[idx + 9] = e.movementY; + + // Note: rect contains doubles (truncated to placate SAFE_HEAP, which is the same behaviour when writing to HEAP32 anyway) + var rect = getBoundingClientRect(target); + HEAP32[idx + 10] = e.clientX - (rect.left | 0); + HEAP32[idx + 11] = e.clientY - (rect.top | 0); + }; + + + + var registerMouseEventCallback = (target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) => { + var eventSize = 64; + JSEvents.mouseEvent ||= _malloc(eventSize); + target = findEventTarget(target); + + var mouseEventHandlerFunc = (e) => { + // TODO: Make this access thread safe, or this could update live while app is reading it. + fillMouseEventData(JSEvents.mouseEvent, e, target); + + if (getWasmTableEntry(callbackfunc)(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault(); + }; + + var eventHandler = { + target, + allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them! + eventTypeString, + eventTypeId, + userData, + callbackfunc, + handlerFunc: mouseEventHandlerFunc, + useCapture + }; + return JSEvents.registerOrRemoveHandler(eventHandler); + }; + var _emscripten_set_mousedown_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, 5, 'mousedown', targetThread); + + var _emscripten_set_mousemove_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, 8, 'mousemove', targetThread); + + var _emscripten_set_mouseup_callback_on_thread = (target, userData, useCapture, callbackfunc, targetThread) => + registerMouseEventCallback(target, userData, useCapture, callbackfunc, 6, 'mouseup', targetThread); + + var SYSCALLS = { + varargs:undefined, + getStr(ptr) { + var ret = UTF8ToString(ptr); + return ret; + }, + }; + var _fd_close = (fd) => { + abort('fd_close called without SYSCALLS_REQUIRE_FILESYSTEM'); + }; + + function _fd_seek(fd, offset, whence, newOffset) { + offset = bigintToI53Checked(offset); + + + return 70; + ; + } + + var printCharBuffers = [null,[],[]]; + + var printChar = (stream, curr) => { + var buffer = printCharBuffers[stream]; + assert(buffer); + if (!curr || curr === 10) { + (stream === 1 ? out : err)(UTF8ArrayToString(buffer)); + buffer.length = 0; + } else { + buffer.push(curr); + } + }; + + var flush_NO_FILESYSTEM = () => { + // flush anything remaining in the buffers during shutdown + _fflush(0); + if (printCharBuffers[1].length) printChar(1, 10); + if (printCharBuffers[2].length) printChar(2, 10); + }; + + + + + var _fd_write = (fd, iov, iovcnt, pnum) => { + // hack to support printf in SYSCALLS_REQUIRE_FILESYSTEM=0 + var num = 0; + for (var i = 0; i < iovcnt; i++) { + var ptr = HEAPU32[((iov)>>2)]; + var len = HEAPU32[(((iov)+(4))>>2)]; + iov += 8; + for (var j = 0; j < len; j++) { + printChar(fd, HEAPU8[ptr+j]); + } + num += len; + } + HEAPU32[((pnum)>>2)] = num; + return 0; + }; + + + + var lengthBytesUTF8 = (str) => { + var len = 0; + for (var i = 0; i < str.length; ++i) { + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code + // unit, not a Unicode code point of the character! So decode + // UTF16->UTF32->UTF8. + // See http://unicode.org/faq/utf_bom.html#utf16-3 + var c = str.charCodeAt(i); // possibly a lead surrogate + if (c <= 0x7F) { + len++; + } else if (c <= 0x7FF) { + len += 2; + } else if (c >= 0xD800 && c <= 0xDFFF) { + len += 4; ++i; + } else { + len += 3; + } + } + return len; + }; + + + var stackAlloc = (sz) => __emscripten_stack_alloc(sz); + var stringToUTF8OnStack = (str) => { + var size = lengthBytesUTF8(str) + 1; + var ret = stackAlloc(size); + stringToUTF8(str, ret, size); + return ret; + }; + + + + + + Module['requestAnimationFrame'] = MainLoop.requestAnimationFrame; + Module['pauseMainLoop'] = MainLoop.pause; + Module['resumeMainLoop'] = MainLoop.resume; + MainLoop.init();; +// End JS library code + +// include: postlibrary.js +// This file is included after the automatically-generated JS library code +// but before the wasm module is created. + +{ + + // Begin ATMODULES hooks + if (Module['noExitRuntime']) noExitRuntime = Module['noExitRuntime']; +if (Module['print']) out = Module['print']; +if (Module['printErr']) err = Module['printErr']; + +Module['FS_createDataFile'] = FS.createDataFile; +Module['FS_createPreloadedFile'] = FS.createPreloadedFile; + + // End ATMODULES hooks + + checkIncomingModuleAPI(); + + if (Module['arguments']) programArgs = Module['arguments']; + if (Module['thisProgram']) thisProgram = Module['thisProgram']; + + // Assertions on removed incoming Module JS APIs. + assert(typeof Module['memoryInitializerPrefixURL'] == 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead'); + assert(typeof Module['pthreadMainPrefixURL'] == 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead'); + assert(typeof Module['cdInitializerPrefixURL'] == 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead'); + assert(typeof Module['filePackagePrefixURL'] == 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead'); + assert(typeof Module['read'] == 'undefined', 'Module.read option was removed'); + assert(typeof Module['readAsync'] == 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)'); + assert(typeof Module['readBinary'] == 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)'); + assert(typeof Module['setWindowTitle'] == 'undefined', 'Module.setWindowTitle option was removed (modify emscripten_set_window_title in JS)'); + assert(typeof Module['TOTAL_MEMORY'] == 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY'); + assert(typeof Module['ENVIRONMENT'] == 'undefined', 'Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node)'); + assert(typeof Module['STACK_SIZE'] == 'undefined', 'STACK_SIZE can no longer be set at runtime. Use -sSTACK_SIZE at link time') + // If memory is defined in wasm, the user can't provide it, or set INITIAL_MEMORY + assert(typeof Module['wasmMemory'] == 'undefined', 'Use of `wasmMemory` detected. Use -sIMPORTED_MEMORY to define wasmMemory externally'); + assert(typeof Module['INITIAL_MEMORY'] == 'undefined', 'Detected runtime INITIAL_MEMORY setting. Use -sIMPORTED_MEMORY to define wasmMemory dynamically'); + + var preInit = Module['preInit']; + if (preInit) { + if (typeof preInit == 'function') Module['preInit'] = preInit = [preInit]; + // Written as a loop so that preInit functions that themselves add more + // preInit functions. Is this actually needed? + while (preInit.length > 0) { + preInit.shift()(); + } + } + consumedModuleProp('preInit'); +} + +// Begin runtime exports + var missingLibrarySymbols = [ + 'writeI53ToI64', + 'writeI53ToI64Clamped', + 'writeI53ToI64Signaling', + 'writeI53ToU64Clamped', + 'writeI53ToU64Signaling', + 'readI53FromI64', + 'readI53FromU64', + 'convertI32PairToI53', + 'convertI32PairToI53Checked', + 'convertU32PairToI53', + 'getTempRet0', + 'setTempRet0', + 'createNamedFunction', + 'zeroMemory', + 'withStackSave', + 'strError', + 'inetPton4', + 'inetNtop4', + 'inetPton6', + 'inetNtop6', + 'readSockaddr', + 'writeSockaddr', + 'readEmAsmArgs', + 'jstoi_q', + 'getExecutableName', + 'autoResumeAudioContext', + 'getDynCaller', + 'dynCall', + 'runtimeKeepalivePush', + 'runtimeKeepalivePop', + 'asyncLoad', + 'asmjsMangle', + 'mmapAlloc', + 'HandleAllocator', + 'getUniqueRunDependency', + 'addRunDependency', + 'removeRunDependency', + 'addOnInit', + 'addOnPostCtor', + 'addOnPreMain', + 'STACK_SIZE', + 'STACK_ALIGN', + 'POINTER_SIZE', + 'ASSERTIONS', + 'ccall', + 'cwrap', + 'convertJsFunctionToWasm', + 'getEmptyTableSlot', + 'updateTableMap', + 'getFunctionAddress', + 'addFunction', + 'removeFunction', + 'setValue', + 'getValue', + 'intArrayFromString', + 'intArrayToString', + 'AsciiToString', + 'stringToAscii', + 'UTF16ToString', + 'stringToUTF16', + 'lengthBytesUTF16', + 'UTF32ToString', + 'stringToUTF32', + 'lengthBytesUTF32', + 'stringToNewUTF8', + 'writeArrayToMemory', + 'registerWheelEventCallback', + 'registerUiEventCallback', + 'registerFocusEventCallback', + 'fillDeviceOrientationEventData', + 'registerDeviceOrientationEventCallback', + 'fillDeviceMotionEventData', + 'registerDeviceMotionEventCallback', + 'screenOrientation', + 'fillOrientationChangeEventData', + 'registerOrientationChangeEventCallback', + 'fillFullscreenChangeEventData', + 'registerFullscreenChangeEventCallback', + 'callCanvasResizedCallback', + 'JSEvents_requestFullscreen', + 'JSEvents_resizeCanvasForFullscreen', + 'registerRestoreOldStyle', + 'hideEverythingExceptGivenElement', + 'restoreHiddenElements', + 'setLetterbox', + 'currentFullscreenStrategy', + 'softFullscreenResizeWebGLRenderTarget', + 'doRequestFullscreen', + 'fillPointerlockChangeEventData', + 'registerPointerlockChangeEventCallback', + 'registerPointerlockErrorEventCallback', + 'requestPointerLock', + 'fillVisibilityChangeEventData', + 'registerVisibilityChangeEventCallback', + 'registerTouchEventCallback', + 'fillGamepadEventData', + 'registerGamepadEventCallback', + 'registerBeforeUnloadEventCallback', + 'fillBatteryEventData', + 'registerBatteryEventCallback', + 'setCanvasElementSize', + 'getCanvasElementSize', + 'jsStackTrace', + 'getCallstack', + 'convertPCtoSourceLocation', + 'getEnvStrings', + 'wasiRightsToMuslOFlags', + 'wasiOFlagsToMuslOFlags', + 'initRandomFill', + 'randomFill', + 'safeSetTimeout', + 'setImmediateWrapped', + 'safeRequestAnimationFrame', + 'clearImmediateWrapped', + 'registerPostMainLoop', + 'registerPreMainLoop', + 'getPromise', + 'makePromise', + 'addPromise', + 'idsToPromises', + 'makePromiseCallback', + 'Browser_asyncPrepareDataCounter', + 'isLeapYear', + 'ydayFromDate', + 'arraySum', + 'addDays', + 'getSocketFromFD', + 'getSocketAddress', + 'FS_createPreloadedFile', + 'FS_preloadFile', + 'FS_modeStringToFlags', + 'FS_getMode', + 'FS_fileDataToTypedArray', + 'FS_stdin_getChar', + 'FS_mkdirTree', + '_setNetworkCallback', + 'heapObjectForWebGLType', + 'toTypedArrayIndex', + 'webgl_enable_ANGLE_instanced_arrays', + 'webgl_enable_OES_vertex_array_object', + 'webgl_enable_WEBGL_draw_buffers', + 'webgl_enable_WEBGL_multi_draw', + 'webgl_enable_EXT_polygon_offset_clamp', + 'webgl_enable_EXT_clip_control', + 'webgl_enable_WEBGL_polygon_mode', + 'emscriptenWebGLGet', + 'computeUnpackAlignedImageSize', + 'colorChannelsInGlTextureFormat', + 'emscriptenWebGLGetTexPixelData', + 'emscriptenWebGLGetUniform', + 'webglGetProgramUniformLocation', + 'webglGetUniformLocation', + 'webglPrepareUniformLocationsBeforeFirstUse', + 'webglGetLeftBracePos', + 'emscriptenWebGLGetVertexAttrib', + '__glGetActiveAttribOrUniform', + 'writeGLArray', + 'registerWebGlEventCallback', + 'runAndAbortIfError', + 'writeStringToMemory', + 'writeAsciiToMemory', + 'allocateUTF8', + 'allocateUTF8OnStack', + 'stackTrace', + 'getNativeTypeSize', +]; +missingLibrarySymbols.forEach(missingLibrarySymbol) + + var unexportedSymbols = [ + 'run', + 'out', + 'err', + 'callMain', + 'abort', + 'wasmExports', + 'writeStackCookie', + 'checkStackCookie', + 'INT53_MAX', + 'INT53_MIN', + 'bigintToI53Checked', + 'HEAP8', + 'HEAPU8', + 'HEAP16', + 'HEAPU16', + 'HEAP32', + 'HEAPU32', + 'HEAPF32', + 'HEAPF64', + 'HEAP64', + 'HEAPU64', + 'stackSave', + 'stackRestore', + 'stackAlloc', + 'ptrToString', + 'exitJS', + 'getHeapMax', + 'growMemory', + 'ENV', + 'ERRNO_CODES', + 'DNS', + 'Protocols', + 'Sockets', + 'timers', + 'warnOnce', + 'readEmAsmArgsArray', + 'handleException', + 'keepRuntimeAlive', + 'callUserCallback', + 'maybeExit', + 'alignMemory', + 'wasmTable', + 'wasmMemory', + 'noExitRuntime', + 'addOnPreRun', + 'addOnExit', + 'addOnPostRun', + 'freeTableIndexes', + 'functionsInTableMap', + 'PATH', + 'PATH_FS', + 'UTF8Decoder', + 'UTF8ArrayToString', + 'UTF8ToString', + 'stringToUTF8Array', + 'stringToUTF8', + 'lengthBytesUTF8', + 'UTF16Decoder', + 'stringToUTF8OnStack', + 'JSEvents', + 'registerKeyEventCallback', + 'specialHTMLTargets', + 'maybeCStringToJsString', + 'findEventTarget', + 'findCanvasEventTarget', + 'getBoundingClientRect', + 'fillMouseEventData', + 'registerMouseEventCallback', + 'restoreOldWindowedStyle', + 'UNWIND_CACHE', + 'ExitStatus', + 'checkWasiClock', + 'flush_NO_FILESYSTEM', + 'emSetImmediate', + 'emClearImmediate_deps', + 'emClearImmediate', + 'promiseMap', + 'Browser', + 'requestFullscreen', + 'setCanvasSize', + 'getUserMedia', + 'createContext', + 'getPreloadedImageData__data', + 'wget', + 'MONTH_DAYS_REGULAR', + 'MONTH_DAYS_LEAP', + 'MONTH_DAYS_REGULAR_CUMULATIVE', + 'MONTH_DAYS_LEAP_CUMULATIVE', + 'SYSCALLS', + 'preloadPlugins', + 'FS_stdin_getChar_buffer', + 'FS_unlink', + 'FS_createPath', + 'FS_createDevice', + 'FS_readFile', + 'FS', + 'FS_root', + 'FS_mounts', + 'FS_devices', + 'FS_streams', + 'FS_nextInode', + 'FS_nameTable', + 'FS_currentPath', + 'FS_initialized', + 'FS_ignorePermissions', + 'FS_filesystems', + 'FS_syncFSRequests', + 'FS_lookupPath', + 'FS_getPath', + 'FS_hashName', + 'FS_hashAddNode', + 'FS_hashRemoveNode', + 'FS_lookupNode', + 'FS_createNode', + 'FS_destroyNode', + 'FS_isRoot', + 'FS_isMountpoint', + 'FS_isFile', + 'FS_isDir', + 'FS_isLink', + 'FS_isChrdev', + 'FS_isBlkdev', + 'FS_isFIFO', + 'FS_isSocket', + 'FS_flagsToPermissionString', + 'FS_nodePermissions', + 'FS_mayLookup', + 'FS_mayCreate', + 'FS_mayDelete', + 'FS_mayOpen', + 'FS_checkOpExists', + 'FS_nextfd', + 'FS_getStreamChecked', + 'FS_getStream', + 'FS_createStream', + 'FS_closeStream', + 'FS_dupStream', + 'FS_doSetAttr', + 'FS_chrdev_stream_ops', + 'FS_major', + 'FS_minor', + 'FS_makedev', + 'FS_registerDevice', + 'FS_getDevice', + 'FS_getMounts', + 'FS_syncfs', + 'FS_mount', + 'FS_unmount', + 'FS_lookup', + 'FS_mknod', + 'FS_statfs', + 'FS_statfsStream', + 'FS_statfsNode', + 'FS_create', + 'FS_mkdir', + 'FS_mkdev', + 'FS_symlink', + 'FS_link', + 'FS_rename', + 'FS_rmdir', + 'FS_readdir', + 'FS_readlink', + 'FS_stat', + 'FS_fstat', + 'FS_lstat', + 'FS_doChmod', + 'FS_chmod', + 'FS_lchmod', + 'FS_fchmod', + 'FS_doChown', + 'FS_chown', + 'FS_lchown', + 'FS_fchown', + 'FS_doTruncate', + 'FS_truncate', + 'FS_ftruncate', + 'FS_utime', + 'FS_open', + 'FS_close', + 'FS_isClosed', + 'FS_llseek', + 'FS_read', + 'FS_write', + 'FS_mmap', + 'FS_msync', + 'FS_ioctl', + 'FS_writeFile', + 'FS_cwd', + 'FS_chdir', + 'FS_createDefaultDirectories', + 'FS_createDefaultDevices', + 'FS_createSpecialDirectories', + 'FS_createStandardStreams', + 'FS_staticInit', + 'FS_init', + 'FS_quit', + 'FS_findObject', + 'FS_analyzePath', + 'FS_createFile', + 'FS_createDataFile', + 'FS_forceLoadFile', + 'FS_createLazyFile', + 'MEMFS', + 'TTY', + 'PIPEFS', + 'SOCKFS', + 'tempFixedLengthArray', + 'miniTempWebGLFloatBuffers', + 'miniTempWebGLIntBuffers', + 'GL', + 'AL', + 'GLUT', + 'EGL', + 'GLEW', + 'IDBStore', + 'SDL', + 'SDL_gfx', + 'print', + 'printErr', + 'jstoi_s', +]; +unexportedSymbols.forEach(unexportedRuntimeSymbol); + + // End runtime exports + // Begin JS library exports + // End JS library exports + +// end include: postlibrary.js + +function checkIncomingModuleAPI() { + ignoredModuleProp('fetchSettings'); + ignoredModuleProp('logReadFiles'); + ignoredModuleProp('loadSplitModule'); + ignoredModuleProp('onMalloc'); + ignoredModuleProp('onRealloc'); + ignoredModuleProp('onFree'); + ignoredModuleProp('onSbrkGrow'); + ignoredModuleProp('onCOSCacheHit'); + ignoredModuleProp('onCOSCacheMiss'); + ignoredModuleProp('onCOSStore'); + ignoredModuleProp('GL_MAX_TEXTURE_IMAGE_UNITS'); + ignoredModuleProp('SDL_canPlayWithWebAudio'); + ignoredModuleProp('SDL_numSimultaneouslyQueuedBuffers'); + ignoredModuleProp('freePreloadedMediaOnUse'); + ignoredModuleProp('preinitializedWebGLContext'); + ignoredModuleProp('keyboardListeningElement'); + ignoredModuleProp('doNotCaptureKeyboard'); + ignoredModuleProp('extraStackTrace'); + ignoredModuleProp('preloadPlugins'); + ignoredModuleProp('preMainLoop'); + ignoredModuleProp('postMainLoop'); + ignoredModuleProp('forcedAspectRatio'); + ignoredModuleProp('mainScriptUrlOrBlob'); + ignoredModuleProp('onFullScreen'); + ignoredModuleProp('INITIAL_MEMORY'); + ignoredModuleProp('wasmMemory'); + ignoredModuleProp('wasmBinary'); +} +function peak_web_dom_open(id,w,h) { var name = UTF8ToString(id); var c = document.getElementById(name); if (!c) { c = document.createElement('canvas'); c.id = name; document.body.appendChild(c); } c.width = w; c.height = h; c.tabIndex = 0; c.focus(); } +function peak_web_dom_present(id,w,h,pixels) { var c = document.getElementById(UTF8ToString(id)); if (!c) return; var ctx = c.getContext('2d'); if (c.width !== w || c.height !== h) { c.width = w; c.height = h; } var img = ctx.createImageData(w, h); img.data.set(HEAPU8.subarray(pixels, pixels + w * h * 4)); ctx.putImageData(img, 0, 0); } + +// Imports from the Wasm binary. +var _main = Module['_main'] = makeInvalidEarlyAccess('_main'); +var _fflush = makeInvalidEarlyAccess('_fflush'); +var _emscripten_stack_get_end = makeInvalidEarlyAccess('_emscripten_stack_get_end'); +var _emscripten_stack_get_base = makeInvalidEarlyAccess('_emscripten_stack_get_base'); +var _strerror = makeInvalidEarlyAccess('_strerror'); +var _malloc = makeInvalidEarlyAccess('_malloc'); +var _emscripten_stack_init = makeInvalidEarlyAccess('_emscripten_stack_init'); +var _emscripten_stack_get_free = makeInvalidEarlyAccess('_emscripten_stack_get_free'); +var __emscripten_stack_restore = makeInvalidEarlyAccess('__emscripten_stack_restore'); +var __emscripten_stack_alloc = makeInvalidEarlyAccess('__emscripten_stack_alloc'); +var _emscripten_stack_get_current = makeInvalidEarlyAccess('_emscripten_stack_get_current'); +var memory = makeInvalidEarlyAccess('memory'); +var __indirect_function_table = makeInvalidEarlyAccess('__indirect_function_table'); +var wasmMemory = makeInvalidEarlyAccess('wasmMemory'); +var wasmTable = makeInvalidEarlyAccess('wasmTable'); + +function assignWasmExports(wasmExports) { + assert(typeof wasmExports['__main_argc_argv'] != 'undefined', 'missing Wasm export: __main_argc_argv'); + assert(typeof wasmExports['fflush'] != 'undefined', 'missing Wasm export: fflush'); + assert(typeof wasmExports['emscripten_stack_get_end'] != 'undefined', 'missing Wasm export: emscripten_stack_get_end'); + assert(typeof wasmExports['emscripten_stack_get_base'] != 'undefined', 'missing Wasm export: emscripten_stack_get_base'); + assert(typeof wasmExports['strerror'] != 'undefined', 'missing Wasm export: strerror'); + assert(typeof wasmExports['malloc'] != 'undefined', 'missing Wasm export: malloc'); + assert(typeof wasmExports['emscripten_stack_init'] != 'undefined', 'missing Wasm export: emscripten_stack_init'); + assert(typeof wasmExports['emscripten_stack_get_free'] != 'undefined', 'missing Wasm export: emscripten_stack_get_free'); + assert(typeof wasmExports['_emscripten_stack_restore'] != 'undefined', 'missing Wasm export: _emscripten_stack_restore'); + assert(typeof wasmExports['_emscripten_stack_alloc'] != 'undefined', 'missing Wasm export: _emscripten_stack_alloc'); + assert(typeof wasmExports['emscripten_stack_get_current'] != 'undefined', 'missing Wasm export: emscripten_stack_get_current'); + assert(typeof wasmExports['memory'] != 'undefined', 'missing Wasm export: memory'); + assert(typeof wasmExports['__indirect_function_table'] != 'undefined', 'missing Wasm export: __indirect_function_table'); + _main = Module['_main'] = createExportWrapper('__main_argc_argv', wasmExports['__main_argc_argv'], 2); + _fflush = createExportWrapper('fflush', wasmExports['fflush'], 1); + _emscripten_stack_get_end = wasmExports['emscripten_stack_get_end']; + _emscripten_stack_get_base = wasmExports['emscripten_stack_get_base']; + _strerror = createExportWrapper('strerror', wasmExports['strerror'], 1); + _malloc = createExportWrapper('malloc', wasmExports['malloc'], 1); + _emscripten_stack_init = wasmExports['emscripten_stack_init']; + _emscripten_stack_get_free = wasmExports['emscripten_stack_get_free']; + __emscripten_stack_restore = wasmExports['_emscripten_stack_restore']; + __emscripten_stack_alloc = wasmExports['_emscripten_stack_alloc']; + _emscripten_stack_get_current = wasmExports['emscripten_stack_get_current']; + memory = wasmMemory = wasmExports['memory']; + __indirect_function_table = wasmTable = wasmExports['__indirect_function_table']; +} + +var wasmImports = { + /** @export */ + __assert_fail: ___assert_fail, + /** @export */ + _abort_js: __abort_js, + /** @export */ + clock_time_get: _clock_time_get, + /** @export */ + emscripten_cancel_main_loop: _emscripten_cancel_main_loop, + /** @export */ + emscripten_get_now: _emscripten_get_now, + /** @export */ + emscripten_resize_heap: _emscripten_resize_heap, + /** @export */ + emscripten_set_keydown_callback_on_thread: _emscripten_set_keydown_callback_on_thread, + /** @export */ + emscripten_set_keyup_callback_on_thread: _emscripten_set_keyup_callback_on_thread, + /** @export */ + emscripten_set_main_loop_arg: _emscripten_set_main_loop_arg, + /** @export */ + emscripten_set_mousedown_callback_on_thread: _emscripten_set_mousedown_callback_on_thread, + /** @export */ + emscripten_set_mousemove_callback_on_thread: _emscripten_set_mousemove_callback_on_thread, + /** @export */ + emscripten_set_mouseup_callback_on_thread: _emscripten_set_mouseup_callback_on_thread, + /** @export */ + fd_close: _fd_close, + /** @export */ + fd_seek: _fd_seek, + /** @export */ + fd_write: _fd_write, + /** @export */ + peak_web_dom_open +}; + + +// include: postamble.js +// === Auto-generated postamble setup entry stuff === + +var calledRun; + +function callMain(args = []) { + assert(typeof onPreRuns === 'undefined' || onPreRuns.length == 0, 'cannot call main when preRun functions remain to be called'); + + var entryFunction = _main; + + args.unshift(thisProgram); + + var argc = args.length; + var argv = stackAlloc((argc + 1) * 4); + var argv_ptr = argv; + for (var arg of args) { + HEAPU32[((argv_ptr)>>2)] = stringToUTF8OnStack(arg); + argv_ptr += 4; + } + HEAPU32[((argv_ptr)>>2)] = 0; + + try { + + var ret = entryFunction(argc, argv); + + // if we're not running an evented main loop, it's time to exit + exitJS(ret, /* implicit = */ true); + return ret; + } catch (e) { + return handleException(e); + } +} + +function stackCheckInit() { + // This is normally called automatically during __wasm_call_ctors but need to + // get these values before even running any of the ctors so we call it redundantly + // here. + _emscripten_stack_init(); + // TODO(sbc): Move writeStackCookie to native to to avoid this. + writeStackCookie(); +} + +async function run(args = programArgs) { + assert(!calledRun); + calledRun = true; + + stackCheckInit(); + + preRun(); + + var setStatus = Module['setStatus']; + if (setStatus) { + setStatus('Running...'); + // Yield to the event loop to allow the browser to paint "Running..." + await new Promise((resolve) => setTimeout(resolve, 1)); + // Then we want to clear the status text, but only after the rest of this function runs. + setTimeout(setStatus, 1, ''); + } + + if (ABORT) return; + + initRuntime(); + + // No ATMAINS hooks + + Module['onRuntimeInitialized']?.(); + consumedModuleProp('onRuntimeInitialized'); + + var noInitialRun = Module['noInitialRun'] || false; + if (!noInitialRun) callMain(args); + + postRun(); +} + +function checkUnflushedContent() { + // Compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + // Normally we would not even include flush() at all, but in ASSERTIONS + // builds we do so just for this check, and here we see if there is any + // content to flush, that is, we check if there would have been + // something a non-ASSERTIONS build would have not seen. + // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0 + // mode (which has its own special function for this; otherwise, all + // the code is inside libc) + var oldOut = out; + var oldErr = err; + var has = false; + out = err = (x) => { + has = true; + } + try { // it doesn't matter if it fails + flush_NO_FILESYSTEM(); + } catch(e) {} + out = oldOut; + err = oldErr; + if (has) { + warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the Emscripten FAQ), or make sure to emit a newline when you printf etc.'); + warnOnce('(this may also be due to not including full filesystem support - try building with -sFORCE_FILESYSTEM)'); + } +} + +var wasmExports; + +// With async instantation wasmExports is assigned asynchronously when the +// instance is received. +createWasm().then(() => run()); + +// end include: postamble.js + diff --git a/Peak/demos/demo.wasm b/Peak/demos/demo.wasm new file mode 100755 index 0000000..e7e428b Binary files /dev/null and b/Peak/demos/demo.wasm differ diff --git a/Peak/demos/demo_replace_main.c b/Peak/demos/demo_replace_main.c deleted file mode 100644 index 3aedfaa..0000000 --- a/Peak/demos/demo_replace_main.c +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include - -#define PEAK_REPLACE_MAIN -#define PEAK_IMPLEMENTATION -#include "../peak.h" - - -void print_window_resize(PeakEvent ev) { - printf("window: %u %u\n", ev.resize.width, ev.resize.height); -} - -void print_window_close(PeakEvent ev) { - printf("WINDOW CLOSE!!!!!!\nWOWWWOWAAAAAAAAAAAAAAAAAAAAAAAAh\n"); -} - -void print_mouse(PeakEvent ev) { - printf("mouse: state=%d type=%d pos=(%f, %f)\n", ev.pointer.state, ev.pointer.type, ev.pointer.x, ev.pointer.y); -} - -void print_key(PeakEvent ev) { - printf("key: key=%d mod=%d\n", ev.key.key, ev.key.mod); -} - -typedef void (*print_event_func)(PeakEvent ev); -print_event_func print_event[PEAK_EVENT_LAST] = { - [PEAK_EVENT_NONE] = print_mouse, - [PEAK_EVENT_WINDOW_RESIZE] = print_window_resize, - [PEAK_EVENT_WINDOW_CLOSE] = print_window_close, - [PEAK_EVENT_POINTER] = print_mouse, - [PEAK_EVENT_POINTER_CONNECTED] = print_mouse, - [PEAK_EVENT_POINTER_DISCONNECTED] = print_mouse, - [PEAK_EVENT_KEY_DOWN] = print_key, - [PEAK_EVENT_KEY_UP] = print_key, -}; - - -typedef struct { - char *msg; -} AppData; - -static AppData data; - -int main(int argc, char**argv) { - data.msg = "Hello World"; - return PEAK_CONTINUE; -} - -void -peak_events(PeakEvent ev) -{ - print_event[ev.type](ev); - switch (ev.type) { - case PEAK_EVENT_WINDOW_CLOSE: - printf("stoppppeeed\n"); - peak_stop(); - break; - } -} - -void -peak_tick() -{ - printf("%s\n",data.msg); - sleep(1); -} diff --git a/Peak/demos/demo_run b/Peak/demos/demo_run new file mode 100755 index 0000000..7a526c1 Binary files /dev/null and b/Peak/demos/demo_run differ diff --git a/Peak/demos/demo_run.c b/Peak/demos/demo_run.c new file mode 100644 index 0000000..f5c2f7f --- /dev/null +++ b/Peak/demos/demo_run.c @@ -0,0 +1,76 @@ +#include + +#define PEAK_IMPLEMENTATION +#include "../peak.h" + + +void print_window_resize(PeakEvent ev) { + printf("window: %u %u\n", ev.resize.width, ev.resize.height); +} + +void print_window_close(PeakEvent ev) { + printf("WINDOW CLOSE!!!!!!\nWOWWWOWAAAAAAAAAAAAAAAAAAAAAAAAh\n"); +} + +void print_mouse(PeakEvent ev) { + printf("mouse: state=%d type=%d pos=(%f, %f)\n", ev.pointer.state, ev.pointer.type, ev.pointer.x, ev.pointer.y); +} + +void print_key(PeakEvent ev) { + printf("key: key=%d mod=%d\n", ev.key.key, ev.key.mod); +} + +typedef void (*print_event_func)(PeakEvent ev); +print_event_func print_event[PEAK_EVENT_LAST] = { + [PEAK_EVENT_NONE] = print_mouse, + [PEAK_EVENT_WINDOW_RESIZE] = print_window_resize, + [PEAK_EVENT_WINDOW_CLOSE] = print_window_close, + [PEAK_EVENT_POINTER] = print_mouse, + [PEAK_EVENT_POINTER_CONNECTED] = print_mouse, + [PEAK_EVENT_POINTER_DISCONNECTED] = print_mouse, + [PEAK_EVENT_KEY_DOWN] = print_key, + [PEAK_EVENT_KEY_UP] = print_key, +}; + + +typedef struct { + char *msg; +} AppData; + +static AppData data; + +static int +peak_run_func(PeakWindow *win, void *userdata) +{ + AppData d = *(AppData*) userdata; + static PeakEvent ev; + while (peak_window_epoll(win, &ev)) { + switch (ev.type) { + case PEAK_EVENT_NONE: + continue; + case PEAK_EVENT_WINDOW_CLOSE: + return 0; + default: + print_event[ev.type](ev); + continue; + } + } + printf("%s\n", d.msg); + return 1; +} + +int +main(int argc, char**argv) +{ + data.msg = "Hello World"; + + if (!peak_init()) { + return 1; + } + + PeakWindow win = peak_window_open("demo", 400, 400, 0); + peak_window_run(&win, peak_run_func, &data); + peak_window_close(&win); + peak_quit(); + return 0; +} diff --git a/Peak/peak.h b/Peak/peak.h index 581aa74..97bb0a3 100644 --- a/Peak/peak.h +++ b/Peak/peak.h @@ -17,12 +17,11 @@ * 0.0.0 - @vasco - prototyping * 0.1.0 - @vasco - linux x11 that automagically loads X11 DLL * 0.1.1 - @vasco - fixed event handling on linux - * - * TODO: demo that stress tests the whole API + * 0.1.2 - @vasco - better handling of windows, if multiple windows becomes necessary in the future + * 0.2.0 - @vasco - win32 */ #include -#include #include #include #include @@ -52,15 +51,13 @@ #else #define PEAK_MACOS #endif -#elif defined(__linux__) - #define PEAK_LINUX -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) - #define PEAK_BSD #elif defined(__ANDROID__) #define PEAK_ANDROID #define PEAK_LINUX #elif defined(__linux__) #define PEAK_LINUX +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) + #define PEAK_BSD #endif #if defined(PEAK_LINUX) || defined(PEAK_BSD) || defined(PEAK_APPLE) @@ -77,7 +74,8 @@ #endif typedef enum { - PEAK_KEYMOD_ALT = 0, + PEAK_KEYMOD_NONE = 0, + PEAK_KEYMOD_ALT, PEAK_KEYMOD_SHIFT, PEAK_KEYMOD_CTRL, PEAK_KEYMOD_CAPS, @@ -102,7 +100,7 @@ typedef enum { PEAK_EVENT_POINTER_CONNECTED, PEAK_EVENT_POINTER_DISCONNECTED, PEAK_EVENT_LAST -} PeakEvenType; +} PeakEventType; typedef enum { PEAK_POINTER_MOVED = 0, @@ -118,7 +116,7 @@ typedef enum { } PeakPointerType; typedef struct { - PeakEvenType type; + PeakEventType type; union { struct { PeakKeyCode key; PeakKeyMod mod; } key; struct { uint32_t width, height; } resize; @@ -126,727 +124,1085 @@ typedef struct { }; } PeakEvent; +/* Implemented by the platform */ +typedef struct peak_window_internal_t PeakWindowInternal; +typedef struct PeakWindow PeakWindow; + /* Public API */ -PEAK void peak_init(void); // open a window and initialize graphics. called automatically if using peak_setup(). -PEAK void peak_quit(void); // close the window and release resources. called automatically if using peak_setup(). -PEAK bool peak_poll_events(PeakEvent *ev); // Poll the next event from the window queue. Returns true if an event was retrieved. -PEAK void peak_extensions(void); // retrieve gpu window surface extensions for native api access (e.g., vulkan/opengl). -PEAK void peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height); // blit rgba pixels to the screen. -PEAK void peak_clip(int x, int y, size_t w, size_t h); // restrict all subsequent rendering operations to this bounding rectangle. -PEAK void peak_clip_reset(void); // reset clipping plane to window size. -PEAK void peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color); // draw a solid-color filled rectangle. -PEAK void peak_draw_rectangle_gradient(int x, int y, size_t w, size_t h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3); // fill rectangle interpolating four corner colors in clock-wise order. -PEAK void peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color); // draw a solid-color filled triangle. -PEAK void peak_draw_triangle_gradient(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2); // fill triangle with color interpolation clock-wise across its three vertices. -PEAK void peak_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color); // draw a line between two points. -PEAK void peak_draw_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1); // draw a line with a color gradient. +PEAK int peak_init(void); // Initialize platform context and load necessary DLLs. +PEAK void peak_quit(void); // Close platform context. +PEAK PeakWindow peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); // Open a window. +PEAK void peak_window_close(PeakWindow *window); // Close a window. +PEAK void peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata); // Hijacking the main loop makes life easier on platforms like web +PEAK int peak_window_epoll(PeakWindow *win, PeakEvent *ev); // Poll a window for events. +PEAK uint32_t* peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height); // Get the windows backbuffer. +PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); +PEAK void peak_window_present(PeakWindow *win); -/* Implemented by the platform */ -PEAK void peak_platform_window_open(); -PEAK void peak_platform_window_close(); -PEAK uint32_t *peak_platform_window_buffer(size_t *width, size_t *height); -PEAK bool peak_platform_epoll(PeakEvent *ev); +#endif // PEAK_H -/* - * He was wipping up amazing foods, like making happiness. - * Actual happiness? - * Actual happiness and joy. - * Oh the smell, it was so divine. - * He was making happiness!? - * Happiness in the kettle! - */ -#ifdef PEAK_REPLACE_MAIN -enum PeakReturn { PEAK_CONTINUE = 0, PEAK_STOP = 1 }; -extern int peak__main(int argc, char**argv); -extern void peak_events(PeakEvent ev); -extern void peak_tick(); -static bool running = true; -static inline void peak_stop() { running = false; } -#ifndef PEAK_WEB -#define main(...)\ -main(int argc, char **argv) {\ - assert(peak_events != 0 && "Declare peak_events() to handle window events.");\ - assert(peak_tick != 0 && "Declare peak_tick() to handle each frame ticks.");\ - peak_init();\ - int ret = peak__main(argc, argv);\ - if (ret != PEAK_CONTINUE) return ret;\ - PeakEvent ev;\ - while (running) {\ - while (running && peak_poll_events(&ev)) {\ - peak_events(ev);\ - }\ - peak_tick();\ - }\ - peak_quit();\ -}\ -int peak__main(__VA_ARGS__) -#else -#include -/* - * He was wipping up agony in the kettle. - * Actual pain? - * Boiling hatred in the kettle. - * The smell, it's pain. - * Boiling anger!? - * Yes, boiling anger. - */ +#ifdef PEAK_IMPLEMENTATION +#undef PEAK_IMPLEMENTATION -static inline void -peak__emscripten_loop_step(void *arg) +#if defined(PEAK_WIN32) || defined(PEAK_WEB) +#define PEAK_Q 64 + +typedef struct { + unsigned h, n; + PeakEvent e[PEAK_Q]; +} PeakQ; + +static void +peak_q_push(PeakQ *q, PeakEvent ev) { - (void)arg; - PeakEvent ev; - while (peak_poll_events(&ev)) { - peak_events(ev); - } - - if (running) { - peak_tick(); - } else { - emscripten_cancel_main_loop(); - peak_quit(); - } + if (!q || q->n == PEAK_Q) + return; + q->e[(q->h + q->n++) % PEAK_Q] = ev; } -#define main(...)\ - main(int argc, char **argv) {\ - _Static_assert(peak_events != 0, "When using peak_setup, declare peak_events() to handle window events.");\ - _Static_assert(peak_tick != 0, "When using peak_setup, declare peak_tick() to handle frame ticks.");\ - peak_init();\ - int ret = peak__main(argc, argv);\ - if (ret != PEAK_CONTINUE) return ret;\ - emscripten_set_main_loop_arg(peak__emscripten_loop_step, NULL, 0, 1);\ - return 0;\ - }\ - int peak__main(__VA_ARGS__) +static int +peak_q_pop(PeakQ *q, PeakEvent *ev) +{ + if (!q || !q->n) + return 0; + *ev = q->e[q->h]; + q->h = (q->h + 1) % PEAK_Q; + q->n--; + return 1; +} #endif -#endif // PEAK_DONT_REPLACE_MAIN - -#endif // PEAK_H - -#ifdef PEAK_IMPLEMENTATION -#undef PEAK_IMPLEMENTATION #if defined(PEAK_WIN32) /* --- Start of p_win32.c --- */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + #include +#include +#include +#include -void -peak_platform_window_open() +#define PEAK_WIN32_USER32 "user32.dll" +#define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_CLASS "PeakWindow" +#define PEAK_WIN32_PROP "Peak" + +#define PEAK_USER32_API(X) \ + X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ + X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ + X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ + X(DestroyWindow, BOOL, WINAPI, (HWND)) \ + X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ + X(GetDC, HDC, WINAPI, (HWND)) \ + X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ + X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ + X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ + X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ + X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ + X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ + X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ + X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ + X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ + X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ + X(GetKeyState, SHORT, WINAPI, (int)) + +#define PEAK_GDI32_API(X) \ + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_USER32_API(X) +#undef X +} PeakUser32Api; + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_GDI32_API(X) +#undef X +} PeakGdi32Api; + +typedef struct { + HMODULE user32; + HMODULE gdi32; + int class_reg; +} PeakWin32; + +struct peak_win32_win { + HWND hwnd; + HDC hdc; + uint32_t *buffer; + uint32_t width; + uint32_t height; + PeakQ q; +}; + +struct peak_window_internal_t { + struct peak_win32_win *w; +}; + +static PeakWin32 peak_win32; +static PeakUser32Api peak_user32; +static PeakGdi32Api peak_gdi32; + +static int +peak_internal_user32_load(HMODULE handle) { +#define X(name, ret, conv, args) peak_user32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_USER32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_user32.name + if (0 PEAK_USER32_API(X)) + return 0; +#undef X + return 1; } -void -peak_platform_window_close() +static int +peak_internal_gdi32_load(HMODULE handle) { +#define X(name, ret, conv, args) peak_gdi32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_GDI32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_gdi32.name + if (0 PEAK_GDI32_API(X)) + return 0; +#undef X + return 1; } -uint32_t* -peak_platform_window_buffer(size_t *width, size_t *height) +static PeakKeyCode +peak_internal_win32_key_map(WPARAM vk) { + if (vk >= 'A' && vk <= 'Z') + return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A')); + switch (vk) { + case VK_UP: return PEAK_KEY_UP; + case VK_DOWN: return PEAK_KEY_DOWN; + case VK_LEFT: return PEAK_KEY_LEFT; + case VK_RIGHT: return PEAK_KEY_RIGHT; + case VK_SPACE: return PEAK_KEY_SPACE; + case VK_ESCAPE: return PEAK_KEY_ESCAPE; + case VK_RETURN: return PEAK_KEY_ENTER; + default: return PEAK_KEY_UNKNOWN; + } } -bool -peak_platform_epoll(PeakEvent *ev) +static PeakKeyMod +peak_internal_win32_mod_map(void) +{ + if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL; + if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT; + if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT; + if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS; + return (PeakKeyMod)0; +} + +static int +peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) +{ + uint32_t *buffer; + + buffer = calloc((size_t)width * height, sizeof *buffer); + if (!buffer) + return 0; + free(w->buffer); + w->buffer = buffer; + w->width = width; + w->height = height; + return 1; +} + +static LRESULT CALLBACK +peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + struct peak_win32_win *w; + PeakEvent ev; + + if (msg == WM_NCCREATE) { + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); + } + + w = (struct peak_win32_win *)peak_user32.GetPropA(hwnd, PEAK_WIN32_PROP); + if (!w) + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + + switch (msg) { + case WM_CLOSE: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_CLOSE; + peak_q_push(&w->q, ev); + return 0; + case WM_SIZE: { + uint32_t width, height; + if (wparam == SIZE_MINIMIZED) + return 0; + width = (uint32_t)LOWORD(lparam); + height = (uint32_t)HIWORD(lparam); + if (!width || !height || (width == w->width && height == w->height)) + return 0; + if (!peak_win32_buffer(w, width, height)) + return 0; + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_RESIZE; + ev.resize.width = w->width; + ev.resize.height = w->height; + peak_q_push(&w->q, ev); + return 0; + } + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + memset(&ev, 0, sizeof ev); + ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev.key.key = peak_internal_win32_key_map(wparam); + ev.key.mod = peak_internal_win32_mod_map(); + peak_q_push(&w->q, ev); + return 0; + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_POINTER; + ev.pointer.x = (float)(short)LOWORD(lparam); + ev.pointer.y = (float)(short)HIWORD(lparam); + if (msg == WM_MOUSEMOVE) { + ev.pointer.state = PEAK_POINTER_MOVED; + ev.pointer.type = (wparam & MK_RBUTTON) ? PEAK_POINTER_RIGHT : + (wparam & MK_MBUTTON) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) { + ev.pointer.state = PEAK_POINTER_PRESSED; + ev.pointer.type = (msg == WM_RBUTTONDOWN) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONDOWN) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else { + ev.pointer.state = PEAK_POINTER_RELEASED; + ev.pointer.type = (msg == WM_RBUTTONUP) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONUP) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } + peak_q_push(&w->q, ev); + return 0; + case WM_PAINT: { + PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); + peak_user32.EndPaint(hwnd, &ps); + return 0; + } + case WM_ERASEBKGND: + return 1; + case WM_DESTROY: + peak_user32.RemovePropA(hwnd, PEAK_WIN32_PROP); + return 0; + default: + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + } +} + +static int +peak_platform_init(void) { + WNDCLASSEXA wc; + + if (!peak_user32.CreateWindowExA) { + if (!(peak_win32.user32 = LoadLibraryA(PEAK_WIN32_USER32))) { + fputs("Failed to load user32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_user32_load(peak_win32.user32)) { + fputs("Failed to load user32 symbols", stderr); + return 0; + } + } + if (!peak_gdi32.StretchDIBits) { + if (!(peak_win32.gdi32 = LoadLibraryA(PEAK_WIN32_GDI32))) { + fputs("Failed to load gdi32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_gdi32_load(peak_win32.gdi32)) { + fputs("Failed to load gdi32 symbols", stderr); + return 0; + } + } + if (!peak_win32.class_reg) { + memset(&wc, 0, sizeof wc); + wc.cbSize = sizeof wc; + wc.style = CS_OWNDC; + wc.lpfnWndProc = peak_internal_win32_wndproc; + wc.hInstance = GetModuleHandleA(NULL); + wc.hCursor = peak_user32.LoadCursorA(NULL, IDC_ARROW); + wc.lpszClassName = PEAK_WIN32_CLASS; + if (!peak_user32.RegisterClassExA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { + fputs("Failed to register window class", stderr); + return 0; + } + peak_win32.class_reg = 1; + } + return 1; +} + +static void +peak_platform_quit(void) +{ + if (!peak_win32.class_reg) + return; + peak_user32.UnregisterClassA(PEAK_WIN32_CLASS, GetModuleHandleA(NULL)); + peak_win32.class_reg = 0; +} + +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal intern = {0}; + struct peak_win32_win *w; + DWORD style, ex; + RECT r; + HINSTANCE inst; + + (void)flags; + if (!peak_user32.CreateWindowExA && !peak_platform_init()) + return intern; + + w = calloc(1, sizeof *w); + if (!w) + return intern; + if (!peak_win32_buffer(w, width, height)) { + free(w); + return intern; + } + + ex = 0; + style = WS_OVERLAPPEDWINDOW; + r.left = 0; + r.top = 0; + r.right = (LONG)width; + r.bottom = (LONG)height; + peak_user32.AdjustWindowRectEx(&r, style, FALSE, ex); + inst = GetModuleHandleA(NULL); + w->hwnd = peak_user32.CreateWindowExA(ex, PEAK_WIN32_CLASS, name, style, + CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, + NULL, NULL, inst, w); + if (!w->hwnd) { + free(w->buffer); + free(w); + return intern; + } + + w->hdc = peak_user32.GetDC(w->hwnd); + if (!w->hdc) { + peak_user32.DestroyWindow(w->hwnd); + free(w->buffer); + free(w); + return intern; + } + + peak_user32.ShowWindow(w->hwnd, SW_SHOWNORMAL); + intern.w = w; + return intern; +} + +static void +peak_platform_window_close(PeakWindowInternal *intern) +{ + struct peak_win32_win *w; + if (!intern || !intern->w) + return; + w = intern->w; + if (w->hdc && w->hwnd) + peak_user32.ReleaseDC(w->hwnd, w->hdc); + if (w->hwnd) { + peak_user32.RemovePropA(w->hwnd, PEAK_WIN32_PROP); + peak_user32.DestroyWindow(w->hwnd); + } + free(w->buffer); + free(w); + intern->w = NULL; +} + +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + if (!w) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; +} + +static void +peak_platform_window_present(PeakWindowInternal *intern) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + BITMAPINFO bmi; + + if (!w || !w->hwnd || !w->hdc || !w->buffer) + return; + + memset(&bmi, 0, sizeof bmi); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = (LONG)w->width; + bmi.bmiHeader.biHeight = -(LONG)w->height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + peak_gdi32.StretchDIBits(w->hdc, + 0, 0, (int)w->width, (int)w->height, + 0, 0, (int)w->width, (int)w->height, + w->buffer, &bmi, DIB_RGB_COLORS, SRCCOPY); +} + +static bool +peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) +{ + MSG msg; + struct peak_win32_win *w = intern ? intern->w : NULL; + + if (!w || !w->hwnd) + return 0; + + while (peak_user32.PeekMessageA(&msg, w->hwnd, 0, 0, PM_REMOVE)) { + peak_user32.TranslateMessage(&msg); + peak_user32.DispatchMessageA(&msg); + if (peak_q_pop(&w->q, ev)) + return 1; + } + return peak_q_pop(&w->q, ev); } /* --- End of p_win32.c --- */ #elif defined(PEAK_LINUX) /* --- Start of p_linux.c --- */ -#include +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif + #include -#include #include +#include #include -#include +#include +#include #include -#include -#define PEAK_X11_LINUX "/usr/lib/libX11.so.6" - -#define PEAK_LINUX_DEFAULT_WIDTH 800 -#define PEAK_LINUX_DEFAULT_HEIGHT 600 - -typedef Display* (*XOpenDisplay_t)(const char*); -typedef int (*XCloseDisplay_t)(Display*); -typedef Window (*XCreateSimpleWindow_t)(Display*, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned long, unsigned long); -typedef int (*XStoreName_t)(Display*, Window, const char*); -typedef Atom (*XInternAtom_t)(Display*, const char*, Bool); -typedef Status (*XSetWMProtocols_t)(Display*, Window, Atom*, int); -typedef int (*XSelectInput_t)(Display*, Window, long); -typedef GC (*XCreateGC_t)(Display*, Window, unsigned long, XGCValues*); -typedef XImage* (*XCreateImage_t)(Display*, Visual*, unsigned int, int, int, char*, unsigned int, unsigned int, int, int); -typedef int (*XMapRaised_t)(Display*, Window); -typedef int (*XFlush_t)(Display*); -typedef int (*XFreeGC_t)(Display*, GC); -typedef int (*XDestroyWindow_t)(Display*, Window); -typedef int (*XPending_t)(Display*); -typedef int (*XNextEvent_t)(Display*, XEvent*); -typedef KeySym (*XLookupKeysym_t)(XKeyEvent*, int); -typedef int (*XPutImage_t)(Display*, Drawable, GC, XImage*, int, int, int, int, unsigned int, unsigned int); -typedef int (*XDefaultScreen_t)(Display*); -typedef Window (*XRootWindow_t)(Display*, int); -typedef unsigned long (*XBlackPixel_t)(Display*, int); -typedef Visual* (*XDefaultVisual_t)(Display*, int); -typedef int (*XDefaultDepth_t)(Display*, int); +#define PEAK_X11_LINUX "libX11.so.6" + +#define PEAK_X11_API(X) \ + X(XOpenDisplay, Display *, (const char *)) \ + X(XCloseDisplay, int, (Display *)) \ + X(XCreateSimpleWindow, Window, (Display *, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned long, unsigned long)) \ + X(XStoreName, int, (Display *, Window, const char *)) \ + X(XInternAtom, Atom, (Display *, const char *, Bool)) \ + X(XSetWMProtocols, Status, (Display *, Window, Atom *, int)) \ + X(XSelectInput, int, (Display *, Window, long)) \ + X(XCreateGC, GC, (Display *, Window, unsigned long, XGCValues *)) \ + X(XCreateImage, XImage *, (Display *, Visual *, unsigned int, int, int, char *, unsigned int, unsigned int, int, int)) \ + X(XMapRaised, int, (Display *, Window)) \ + X(XFlush, int, (Display *)) \ + X(XFreeGC, int, (Display *, GC)) \ + X(XDestroyWindow, int, (Display *, Window)) \ + X(XCheckIfEvent, Bool, (Display *, XEvent *, Bool (*)(Display *, XEvent *, XPointer), XPointer)) \ + X(XLookupKeysym, KeySym, (XKeyEvent *, int)) \ + X(XPutImage, int, (Display *, Drawable, GC, XImage *, int, int, int, int, unsigned int, unsigned int)) typedef struct { - XOpenDisplay_t XOpenDisplay; - XCloseDisplay_t XCloseDisplay; - XCreateSimpleWindow_t XCreateSimpleWindow; - XStoreName_t XStoreName; - XInternAtom_t XInternAtom; - XSetWMProtocols_t XSetWMProtocols; - XSelectInput_t XSelectInput; - XCreateGC_t XCreateGC; - XCreateImage_t XCreateImage; - XMapRaised_t XMapRaised; - XFlush_t XFlush; - XFreeGC_t XFreeGC; - XDestroyWindow_t XDestroyWindow; - XPending_t XPending; - XNextEvent_t XNextEvent; - XLookupKeysym_t XLookupKeysym; - XPutImage_t XPutImage; - XDefaultScreen_t XDefaultScreen; - XRootWindow_t XRootWindow; - XBlackPixel_t XBlackPixel; - XDefaultVisual_t XDefaultVisual; - XDefaultDepth_t XDefaultDepth; +#define X(name, ret, args) ret (*name) args; + PEAK_X11_API(X) +#undef X } PeakX11Api; -static PeakX11Api peak_x11; - -static int peak_load_x11(void *handle) { - peak_x11.XOpenDisplay = (XOpenDisplay_t)dlsym(handle, "XOpenDisplay"); - peak_x11.XCloseDisplay = (XCloseDisplay_t)dlsym(handle, "XCloseDisplay"); - peak_x11.XCreateSimpleWindow = (XCreateSimpleWindow_t)dlsym(handle, "XCreateSimpleWindow"); - peak_x11.XStoreName = (XStoreName_t)dlsym(handle, "XStoreName"); - peak_x11.XInternAtom = (XInternAtom_t)dlsym(handle, "XInternAtom"); - peak_x11.XSetWMProtocols = (XSetWMProtocols_t)dlsym(handle, "XSetWMProtocols"); - peak_x11.XSelectInput = (XSelectInput_t)dlsym(handle, "XSelectInput"); - peak_x11.XCreateGC = (XCreateGC_t)dlsym(handle, "XCreateGC"); - peak_x11.XCreateImage = (XCreateImage_t)dlsym(handle, "XCreateImage"); - peak_x11.XMapRaised = (XMapRaised_t)dlsym(handle, "XMapRaised"); - peak_x11.XFlush = (XFlush_t)dlsym(handle, "XFlush"); - peak_x11.XFreeGC = (XFreeGC_t)dlsym(handle, "XFreeGC"); - peak_x11.XDestroyWindow = (XDestroyWindow_t)dlsym(handle, "XDestroyWindow"); - peak_x11.XPending = (XPending_t)dlsym(handle, "XPending"); - peak_x11.XNextEvent = (XNextEvent_t)dlsym(handle, "XNextEvent"); - peak_x11.XLookupKeysym = (XLookupKeysym_t)dlsym(handle, "XLookupKeysym"); - peak_x11.XPutImage = (XPutImage_t)dlsym(handle, "XPutImage"); - peak_x11.XDefaultScreen = (XDefaultScreen_t)dlsym(handle, "XDefaultScreen"); - peak_x11.XRootWindow = (XRootWindow_t)dlsym(handle, "XRootWindow"); - peak_x11.XBlackPixel = (XBlackPixel_t)dlsym(handle, "XBlackPixel"); - peak_x11.XDefaultVisual = (XDefaultVisual_t)dlsym(handle, "XDefaultVisual"); - peak_x11.XDefaultDepth = (XDefaultDepth_t)dlsym(handle, "XDefaultDepth"); - - if (!peak_x11.XOpenDisplay || !peak_x11.XCloseDisplay || !peak_x11.XCreateSimpleWindow || - !peak_x11.XStoreName || !peak_x11.XInternAtom || !peak_x11.XSetWMProtocols || - !peak_x11.XSelectInput || !peak_x11.XCreateGC || !peak_x11.XCreateImage || - !peak_x11.XMapRaised || !peak_x11.XFlush || - !peak_x11.XFreeGC || !peak_x11.XDestroyWindow || !peak_x11.XPending || - !peak_x11.XNextEvent || !peak_x11.XLookupKeysym || !peak_x11.XPutImage || - !peak_x11.XDefaultScreen || !peak_x11.XRootWindow || !peak_x11.XBlackPixel || - !peak_x11.XDefaultVisual || !peak_x11.XDefaultDepth) { - return 0; - } - return 1; -} - - typedef struct { - Display *display; - Atom wm_delete_window; - bool initialized; + Display *display; + Atom wm_delete_window; } PeakLinux; -typedef struct { - Window window; - GC gfx_ctx; /* GRAPHICS CONTEXT, not garbage collector lol */ -} PeakLinuxWindow; +struct peak_window_internal_t { + Window window; + GC gfx_ctx; + XImage *ximage; + uint32_t *buffer; + uint32_t width; + uint32_t height; +}; -static PeakLinux peak_linux = {0}; -static PeakLinuxWindow peak_linux_window = {0}; -static XImage *peak_linux_ximage = NULL; -static uint32_t *peak_linux_buffer = NULL; -static size_t peak_linux_width = PEAK_LINUX_DEFAULT_WIDTH; -static size_t peak_linux_height = PEAK_LINUX_DEFAULT_HEIGHT; +static PeakLinux peak_linux; +static PeakX11Api peak_x11; + +static int +peak_internal_x11_load(void *handle) +{ +#define X(name, ret, args) peak_x11.name = (ret (*) args)dlsym(handle, #name); + PEAK_X11_API(X) +#undef X +#define X(name, ret, args) || !peak_x11.name + if (0 PEAK_X11_API(X)) + return 0; +#undef X + return 1; +} static PeakKeyCode -peak__x11_map_key(KeySym sym) -{ - if (sym >= XK_a && sym <= XK_z) return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_a)); - if (sym >= XK_A && sym <= XK_Z) return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_A)); - switch (sym) { - case XK_Up: return PEAK_KEY_UP; - case XK_Down: return PEAK_KEY_DOWN; - case XK_Left: return PEAK_KEY_LEFT; - case XK_Right: return PEAK_KEY_RIGHT; - case XK_space: return PEAK_KEY_SPACE; - case XK_Escape: return PEAK_KEY_ESCAPE; - case XK_Return: return PEAK_KEY_ENTER; - default: return PEAK_KEY_UNKNOWN; - } +peak_internal_x11_key_map(KeySym sym) +{ + if (sym >= XK_a && sym <= XK_z) + return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_a)); + if (sym >= XK_A && sym <= XK_Z) + return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_A)); + switch (sym) { + case XK_Up: return PEAK_KEY_UP; + case XK_Down: return PEAK_KEY_DOWN; + case XK_Left: return PEAK_KEY_LEFT; + case XK_Right: return PEAK_KEY_RIGHT; + case XK_space: return PEAK_KEY_SPACE; + case XK_Escape: return PEAK_KEY_ESCAPE; + case XK_Return: return PEAK_KEY_ENTER; + default: return PEAK_KEY_UNKNOWN; + } } static PeakKeyMod -peak__x11_map_mod(unsigned int state) +peak_internal_x11_mod_map(unsigned int state) { - if (state & ControlMask) return PEAK_KEYMOD_CTRL; - if (state & Mod1Mask) return PEAK_KEYMOD_ALT; - if (state & ShiftMask) return PEAK_KEYMOD_SHIFT; - if (state & LockMask) return PEAK_KEYMOD_CAPS; - return (PeakKeyMod)0; + if (state & ControlMask) return PEAK_KEYMOD_CTRL; + if (state & Mod1Mask) return PEAK_KEYMOD_ALT; + if (state & ShiftMask) return PEAK_KEYMOD_SHIFT; + if (state & LockMask) return PEAK_KEYMOD_CAPS; + return (PeakKeyMod)0; } -void -peak_platform_window_open() +static Bool +peak_internal_x11_window_match(Display *dpy, XEvent *ev, XPointer arg) { - void *x11_handle = dlopen(PEAK_X11_LINUX, RTLD_LOCAL | RTLD_NOW); - assert(x11_handle && "Failed to load X11 library. What system are you fucking using and abusing?"); - - int success = peak_load_x11(x11_handle); - assert(success && "Failed to open X11 display"); - - peak_linux.display = peak_x11.XOpenDisplay(NULL); - assert(peak_linux.display && "Failed to open X11 display"); - - /* Who design this API??? */ - int screen = DefaultScreen(peak_linux.display); - peak_linux_window.window = peak_x11.XCreateSimpleWindow(peak_linux.display, RootWindow(peak_linux.display, screen), 0, 0, peak_linux_width, peak_linux_height, 0, BlackPixel(peak_linux.display, screen), BlackPixel(peak_linux.display, screen)); - peak_x11.XStoreName(peak_linux.display, peak_linux_window.window, "Peak"); - peak_linux.wm_delete_window = peak_x11.XInternAtom(peak_linux.display, "WM_DELETE_WINDOW", False); - peak_x11.XSetWMProtocols(peak_linux.display, peak_linux_window.window, &peak_linux.wm_delete_window, 1); - peak_x11.XSelectInput(peak_linux.display, peak_linux_window.window, ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask); - peak_linux_window.gfx_ctx = peak_x11.XCreateGC(peak_linux.display, peak_linux_window.window, 0, NULL); - peak_linux_buffer = (uint32_t *)calloc(peak_linux_width * peak_linux_height, sizeof(uint32_t)); - assert(peak_linux_buffer && "Failed to allocate framebuffer"); - - /* Wrap the buffer in an XImage for XPutImage blitting */ - Visual *visual = DefaultVisual(peak_linux.display, screen); - int depth = DefaultDepth(peak_linux.display, screen); - - peak_linux_ximage = peak_x11.XCreateImage(peak_linux.display, visual, depth, ZPixmap, 0, (char *)peak_linux_buffer, peak_linux_width, peak_linux_height, 32, 0); - assert(peak_linux_ximage && "Failed to create XImage"); - - peak_x11.XMapRaised(peak_linux.display, peak_linux_window.window); - peak_x11.XFlush(peak_linux.display); + (void)dpy; + return ev->xany.window == *(Window *)arg; +} - peak_linux.initialized = true; +static int +peak_linux_buffer(PeakWindowInternal *w, uint32_t width, uint32_t height) +{ + int screen; + + if (w->ximage) { + w->ximage->data = NULL; + XDestroyImage(w->ximage); + w->ximage = NULL; + } + free(w->buffer); + w->buffer = calloc((size_t)width * height, sizeof *w->buffer); + if (!w->buffer) + return 0; + + w->width = width; + w->height = height; + screen = DefaultScreen(peak_linux.display); + w->ximage = peak_x11.XCreateImage(peak_linux.display, + DefaultVisual(peak_linux.display, screen), + DefaultDepth(peak_linux.display, screen), + ZPixmap, 0, (char *)w->buffer, width, height, 32, 0); + if (!w->ximage) { + free(w->buffer); + w->buffer = NULL; + return 0; + } + return 1; } -void -peak_platform_window_close() +static int +peak_platform_init(void) { - if (!peak_linux.initialized) return; + void *handle; + + if (!peak_x11.XOpenDisplay) { + if (!(handle = dlopen(PEAK_X11_LINUX, RTLD_LOCAL | RTLD_NOW))) { + fputs("Failed to load X11 library. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_x11_load(handle)) { + fputs("Failed to load X11 symbols", stderr); + return 0; + } + } + if (!peak_linux.display) { + if (!(peak_linux.display = peak_x11.XOpenDisplay(NULL))) { + fputs("Failed to open X11 display", stderr); + return 0; + } + peak_linux.wm_delete_window = peak_x11.XInternAtom(peak_linux.display, "WM_DELETE_WINDOW", False); + } + return 1; +} - if (peak_linux_ximage) { - peak_linux_ximage->data = NULL; - XDestroyImage(peak_linux_ximage); - peak_linux_ximage = NULL; - } +static void +peak_platform_quit(void) +{ + if (!peak_linux.display) + return; + peak_x11.XCloseDisplay(peak_linux.display); + peak_linux.display = 0; +} - free(peak_linux_buffer); - peak_linux_buffer = NULL; +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal w = {0}; + int screen; + + (void)flags; + if (!peak_linux.display && !peak_platform_init()) + return w; + + screen = DefaultScreen(peak_linux.display); + w.window = peak_x11.XCreateSimpleWindow(peak_linux.display, + RootWindow(peak_linux.display, screen), 0, 0, width, height, 0, + BlackPixel(peak_linux.display, screen), BlackPixel(peak_linux.display, screen)); + peak_x11.XStoreName(peak_linux.display, w.window, name); + peak_x11.XSetWMProtocols(peak_linux.display, w.window, &peak_linux.wm_delete_window, 1); + peak_x11.XSelectInput(peak_linux.display, w.window, + KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | StructureNotifyMask); + w.gfx_ctx = peak_x11.XCreateGC(peak_linux.display, w.window, 0, NULL); + + if (!peak_linux_buffer(&w, width, height)) { + peak_x11.XFreeGC(peak_linux.display, w.gfx_ctx); + peak_x11.XDestroyWindow(peak_linux.display, w.window); + return (PeakWindowInternal){0}; + } + + peak_x11.XMapRaised(peak_linux.display, w.window); + peak_x11.XFlush(peak_linux.display); + return w; +} - peak_x11.XFreeGC(peak_linux.display, peak_linux_window.gfx_ctx); - peak_x11.XDestroyWindow(peak_linux.display, peak_linux_window.window); - peak_x11.XCloseDisplay(peak_linux.display); +static void +peak_platform_window_close(PeakWindowInternal *w) +{ + if (!w || !w->window || !peak_linux.display) + return; + if (w->ximage) { + w->ximage->data = NULL; + XDestroyImage(w->ximage); + } + free(w->buffer); + if (w->gfx_ctx) + peak_x11.XFreeGC(peak_linux.display, w->gfx_ctx); + peak_x11.XDestroyWindow(peak_linux.display, w->window); + *w = (PeakWindowInternal){0}; +} - peak_linux.initialized = false; +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *w, size_t *width, size_t *height) +{ + if (!w || !w->window) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; } -uint32_t * -peak_platform_window_buffer(size_t *width, size_t *height) -{ - *width = peak_linux_width; - *height = peak_linux_height; - return peak_linux_buffer; -} - -bool -peak_platform_epoll(PeakEvent *ev) -{ - if (!peak_linux.initialized) return false; - - Display *dpy = peak_linux.display; - - while (peak_x11.XPending(dpy) > 0) { - XEvent xev; - peak_x11.XNextEvent(dpy, &xev); - - switch (xev.type) { - - case ClientMessage: - if ((Atom)xev.xclient.data.l[0] == peak_linux.wm_delete_window) { - ev->type = PEAK_EVENT_WINDOW_CLOSE; - printf("CLLOOOOOOOSEEEE"); - return true; - } - continue; - - case KeyPress: - case KeyRelease: { - KeySym sym = peak_x11.XLookupKeysym(&xev.xkey, 0); - ev->type = (xev.type == KeyPress) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; - ev->key.key = peak__x11_map_key(sym); - ev->key.mod = peak__x11_map_mod(xev.xkey.state); - return true; - } - - case ButtonPress: - case ButtonRelease: { - ev->type = PEAK_EVENT_POINTER; - ev->pointer.state = (xev.type == ButtonPress) ? PEAK_POINTER_PRESSED : PEAK_POINTER_RELEASED; - ev->pointer.x = (float)xev.xbutton.x; - ev->pointer.y = (float)xev.xbutton.y; - switch (xev.xbutton.button) { - case Button1: ev->pointer.type = PEAK_POINTER_LEFT; break; - case Button2: ev->pointer.type = PEAK_POINTER_MIDDLE; break; - case Button3: ev->pointer.type = PEAK_POINTER_RIGHT; break; - default: ev->pointer.type = PEAK_POINTER_LEFT; break; - } - return true; - } - - case MotionNotify: - ev->type = PEAK_EVENT_POINTER; - ev->pointer.state = PEAK_POINTER_MOVED; - ev->pointer.x = (float)xev.xmotion.x; - ev->pointer.y = (float)xev.xmotion.y; - if (xev.xmotion.state & Button3Mask) - ev->pointer.type = PEAK_POINTER_RIGHT; - else if (xev.xmotion.state & Button2Mask) - ev->pointer.type = PEAK_POINTER_MIDDLE; - else - ev->pointer.type = PEAK_POINTER_LEFT; - return true; - - case ConfigureNotify: - if ((size_t)xev.xconfigure.width != peak_linux_width || - (size_t)xev.xconfigure.height != peak_linux_height) { - ev->type = PEAK_EVENT_WINDOW_RESIZE; - ev->resize.width = (uint32_t)xev.xconfigure.width; - ev->resize.height = (uint32_t)xev.xconfigure.height; - return true; - } - continue; - - case Expose: - if (xev.xexpose.count == 0) { - peak_x11.XPutImage(dpy, peak_linux_window.window, peak_linux_window.gfx_ctx, peak_linux_ximage, 0, 0, 0, 0, peak_linux_width, peak_linux_height); - } - continue; - - default: - continue; - } - } +static void +peak_platform_window_present(PeakWindowInternal *w) +{ + if (!w || !w->ximage || !peak_linux.display) + return; + peak_x11.XPutImage(peak_linux.display, w->window, w->gfx_ctx, w->ximage, + 0, 0, 0, 0, w->width, w->height); + peak_x11.XFlush(peak_linux.display); +} - peak_x11.XPutImage(dpy, peak_linux_window.window, peak_linux_window.gfx_ctx, peak_linux_ximage, 0, 0, 0, 0, peak_linux_width, peak_linux_height); - peak_x11.XFlush(dpy); - return false; +static bool +peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev) +{ + XEvent xev; + + if (!w || !w->window || !peak_linux.display) + return 0; + + while (peak_x11.XCheckIfEvent(peak_linux.display, &xev, peak_internal_x11_window_match, (XPointer)&w->window)) { + switch (xev.type) { + case ClientMessage: + if ((Atom)xev.xclient.data.l[0] == peak_linux.wm_delete_window) { + ev->type = PEAK_EVENT_WINDOW_CLOSE; + return 1; + } + continue; + case KeyPress: + case KeyRelease: + ev->type = (xev.type == KeyPress) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev->key.key = peak_internal_x11_key_map(peak_x11.XLookupKeysym(&xev.xkey, 0)); + ev->key.mod = peak_internal_x11_mod_map(xev.xkey.state); + return 1; + case ButtonPress: + case ButtonRelease: + ev->type = PEAK_EVENT_POINTER; + ev->pointer.state = (xev.type == ButtonPress) ? PEAK_POINTER_PRESSED : PEAK_POINTER_RELEASED; + ev->pointer.x = (float)xev.xbutton.x; + ev->pointer.y = (float)xev.xbutton.y; + ev->pointer.type = (xev.xbutton.button == Button2) ? PEAK_POINTER_MIDDLE : + (xev.xbutton.button == Button3) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + return 1; + case MotionNotify: + ev->type = PEAK_EVENT_POINTER; + ev->pointer.state = PEAK_POINTER_MOVED; + ev->pointer.x = (float)xev.xmotion.x; + ev->pointer.y = (float)xev.xmotion.y; + ev->pointer.type = (xev.xmotion.state & Button3Mask) ? PEAK_POINTER_RIGHT : + (xev.xmotion.state & Button2Mask) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + return 1; + case ConfigureNotify: { + uint32_t width = (uint32_t)xev.xconfigure.width; + uint32_t height = (uint32_t)xev.xconfigure.height; + if (width == w->width && height == w->height) + continue; + if (!peak_linux_buffer(w, width, height)) + continue; + ev->type = PEAK_EVENT_WINDOW_RESIZE; + ev->resize.width = w->width; + ev->resize.height = w->height; + return 1; + } + default: + continue; + } + } + return 0; } /* --- End of p_linux.c --- */ #elif defined(PEAK_WEB) /* --- Start of p_emscripten.c --- */ +#include +#include #include #include -#include - -#define PEAK_CANVAS "#PeakCanvas" +#include -static PeakKeyCode map_key(const char *code); -static PeakKeyMod map_mod(const EmscriptenKeyboardEvent *keyEvent); -static EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); -static EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); -static EM_BOOL resize_callback(int eventType, const EmscriptenUiEvent *uiEvent, void *userData); +EM_JS(void, peak_web_dom_open, (const char *id, int w, int h), { + var name = UTF8ToString(id); + var c = document.getElementById(name); + if (!c) { + c = document.createElement('canvas'); + c.id = name; + document.body.appendChild(c); + } + c.width = w; + c.height = h; + c.tabIndex = 0; + c.focus(); +}); + +EM_JS(void, peak_web_dom_present, (const char *id, int w, int h, uintptr_t pixels), { + var c = document.getElementById(UTF8ToString(id)); + if (!c) return; + var ctx = c.getContext('2d'); + if (c.width !== w || c.height !== h) { + c.width = w; + c.height = h; + } + var img = ctx.createImageData(w, h); + img.data.set(HEAPU8.subarray(pixels, pixels + w * h * 4)); + ctx.putImageData(img, 0, 0); +}); + +struct peak_web_win { + char name[64]; + uint32_t width, height; + PeakQ q; + uint32_t buffer[]; +}; + +struct peak_window_internal_t { + struct peak_web_win *w; +}; + +static void +peak_web_sel(const char *name, char *out, size_t n) +{ + out[0] = '#'; + strncpy(out + 1, name, n - 2); + out[n - 1] = 0; +} -void -peak_platform_window_open() +static PeakKeyCode +peak_web_key_map(const char *code) { - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, key_callback); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, key_callback); - emscripten_set_mousedown_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_mouseup_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_mousemove_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, resize_callback); + if (code[0] == 'K' && code[1] == 'e' && code[2] == 'y' && code[3] >= 'A' && code[3] <= 'Z' && code[4] == 0) + return (PeakKeyCode)(PEAK_KEY_A + (code[3] - 'A')); + if (!strcmp(code, "ArrowUp")) return PEAK_KEY_UP; + if (!strcmp(code, "ArrowDown")) return PEAK_KEY_DOWN; + if (!strcmp(code, "ArrowLeft")) return PEAK_KEY_LEFT; + if (!strcmp(code, "ArrowRight")) return PEAK_KEY_RIGHT; + if (!strcmp(code, "Space")) return PEAK_KEY_SPACE; + if (!strcmp(code, "Escape")) return PEAK_KEY_ESCAPE; + if (!strcmp(code, "Enter")) return PEAK_KEY_ENTER; + return PEAK_KEY_UNKNOWN; } -void -peak_platform_window_close() +static EM_BOOL +peak_web_key(int type, const EmscriptenKeyboardEvent *e, void *ud) { + PeakEvent ev = {0}; + ev.type = (type == EMSCRIPTEN_EVENT_KEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev.key.key = peak_web_key_map(e->code); + ev.key.mod = e->ctrlKey ? PEAK_KEYMOD_CTRL : e->altKey ? PEAK_KEYMOD_ALT : e->shiftKey ? PEAK_KEYMOD_SHIFT : 0; + peak_q_push(&((struct peak_web_win *)ud)->q, ev); + return EM_TRUE; } -uint32_t* -peak_platform_window_buffer(size_t *width, size_t *height) +static EM_BOOL +peak_web_mouse(int type, const EmscriptenMouseEvent *e, void *ud) { + PeakEvent ev = {0}; + ev.type = PEAK_EVENT_POINTER; + ev.pointer.x = (float)e->targetX; + ev.pointer.y = (float)e->targetY; + if (type == EMSCRIPTEN_EVENT_MOUSEDOWN) + ev.pointer.state = PEAK_POINTER_PRESSED; + else if (type == EMSCRIPTEN_EVENT_MOUSEUP) + ev.pointer.state = PEAK_POINTER_RELEASED; + else + ev.pointer.state = PEAK_POINTER_MOVED; + if (type == EMSCRIPTEN_EVENT_MOUSEMOVE) { + ev.pointer.type = (e->buttons & 4) ? PEAK_POINTER_MIDDLE : + (e->buttons & 2) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + } else { + ev.pointer.type = (e->button == 1) ? PEAK_POINTER_MIDDLE : + (e->button == 2) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + } + peak_q_push(&((struct peak_web_win *)ud)->q, ev); + return EM_TRUE; } -bool -peak_platform_epoll(PeakEvent *ev) +static void +peak_web_listen(struct peak_web_win *w, int on) { - return 0; + char sel[66]; + peak_web_sel(w->name, sel, sizeof sel); + emscripten_set_keydown_callback(sel, w, EM_TRUE, on ? peak_web_key : NULL); + emscripten_set_keyup_callback(sel, w, EM_TRUE, on ? peak_web_key : NULL); + emscripten_set_mousedown_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); + emscripten_set_mouseup_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); + emscripten_set_mousemove_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); } -/* --- End of p_emscripten.c --- */ -#endif -/* --- Start of peak.c --- */ -static inline void peak__put_pixel(int x, int y, uint32_t color); -static inline uint32_t peak__lerp_color(uint32_t c0, uint32_t c1, float t); -static inline float peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy); - -static uint32_t *peak_window_buffer; -static size_t peak_window_width; -static size_t peak_window_height; -static int peak_clip_x; -static int peak_clip_y; -static size_t peak_clip_width; -static size_t peak_clip_height; +static int +peak_platform_init(void) +{ + return 1; +} -void -peak__put_pixel(int x, int y, uint32_t color) +static void +peak_platform_quit(void) { - if (x < peak_clip_x || x >= (int)(peak_clip_x + peak_clip_width) || - y < peak_clip_y || y >= (int)(peak_clip_y + peak_clip_height)) { - return; - } - if (x < 0 || x >= (int)peak_window_width || y < 0 || y >= (int)peak_window_height) { - return; - } - peak_window_buffer[y * peak_window_width + x] = color; } -uint32_t -peak__lerp_color(uint32_t c0, uint32_t c1, float t) +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) { - if (t <= 0.0f) return c0; - if (t >= 1.0f) return c1; + PeakWindowInternal intern = {0}; + struct peak_web_win *w; - uint32_t a0 = (c0 >> 24) & 0xFF, a1 = (c1 >> 24) & 0xFF; - uint32_t r0 = (c0 >> 16) & 0xFF, r1 = (c1 >> 16) & 0xFF; - uint32_t g0 = (c0 >> 8) & 0xFF, g1 = (c1 >> 8) & 0xFF; - uint32_t b0 = c0 & 0xFF, b1 = c1 & 0xFF; + (void)flags; - uint32_t a = (uint32_t)(a0 + t * ((float)a1 - (float)a0)); - uint32_t r = (uint32_t)(r0 + t * ((float)r1 - (float)r0)); - uint32_t g = (uint32_t)(g0 + t * ((float)g1 - (float)g0)); - uint32_t b = (uint32_t)(b0 + t * ((float)b1 - (float)b0)); + w = calloc(1, sizeof *w + (size_t)width * height * sizeof *w->buffer); + if (!w) + return intern; + strncpy(w->name, name, sizeof w->name - 1); + w->width = width; + w->height = height; - return (a << 24) | (r << 16) | (g << 8) | b; -} + peak_web_dom_open(w->name, (int)width, (int)height); -float -peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy) -{ - return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax); + peak_web_listen(w, 1); + intern.w = w; + return intern; } -void -peak_init(void) +static void +peak_platform_window_close(PeakWindowInternal *intern) { - peak_platform_window_open(); - peak_window_buffer = peak_platform_window_buffer(&peak_window_width, &peak_window_height); - peak_clip_x = 0; - peak_clip_y = 0; - peak_clip_width = peak_window_width; - peak_clip_height = peak_window_height; + struct peak_web_win *w; + if (!intern || !intern->w) + return; + w = intern->w; + peak_web_listen(w, 0); + free(w); + intern->w = NULL; } -void -peak_quit(void) +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) { - peak_platform_window_close(); + struct peak_web_win *w = intern ? intern->w : NULL; + if (!w) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; } -void -peak_extensions(void) +static void +peak_platform_window_present(PeakWindowInternal *intern) { - // TODO + struct peak_web_win *w = intern ? intern->w : NULL; + if (!w) + return; + peak_web_dom_present(w->name, (int)w->width, (int)w->height, (uintptr_t)w->buffer); } -bool -peak_poll_events(PeakEvent *ev) +static bool +peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) { - return peak_platform_epoll(ev); + struct peak_web_win *w = intern ? intern->w : NULL; + return w ? peak_q_pop(&w->q, ev) : 0; } +/* --- End of p_emscripten.c --- */ +#endif -void -peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height) -{ - for (size_t y = 0; y < height; ++y) { - int target_y = offset_y + (int)y; - if (target_y < peak_clip_y || target_y >= (int)(peak_clip_y + peak_clip_height) || - target_y < 0 || target_y >= (int)peak_window_height) continue; - - int start_x = offset_x < peak_clip_x ? peak_clip_x : offset_x; - int end_x = (offset_x + (int)width) > (int)(peak_clip_x + peak_clip_width) - ? (int)(peak_clip_x + peak_clip_width) - : (offset_x + (int)width); +/* We define PeakWindow only when we know the type of peak_window_internal_t */ +struct PeakWindow { + PeakWindowInternal internal; + int (*tick)(struct PeakWindow *win, void *userdata); + void *userdata; + uint32_t *buffer; + uint32_t width; + uint32_t height; + uint32_t bufsize; + int running; +}; - if (start_x >= end_x) continue; - size_t src_offset_x = (size_t)(start_x - offset_x); - size_t copy_pixels = (size_t)(end_x - start_x); +/* --- Start of peak.c --- */ +static uint32_t * +peak_window_sync(PeakWindow *win, size_t *width, size_t *height) +{ + size_t w = 0, h = 0; + win->buffer = peak_platform_window_buffer(&win->internal, &w, &h); + win->width = (uint32_t)w; + win->height = (uint32_t)h; + win->bufsize = win->width * win->height; + if (width) *width = w; + if (height) *height = h; + return win->buffer; +} - memcpy(&peak_window_buffer[target_y * peak_window_width + start_x], &rgba[y * width + src_offset_x], copy_pixels * sizeof(uint32_t)); - } +int +peak_init(void) +{ + return peak_platform_init(); } void -peak_clip(int x, int y, size_t w, size_t h) +peak_quit(void) { - peak_clip_x = x; - peak_clip_y = y; - peak_clip_width = w; - peak_clip_height = h; + peak_platform_quit(); } -void -peak_clip_reset(void) +PeakWindow +peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) { - peak_clip_x = 0; - peak_clip_y = 0; - peak_clip_width = peak_window_width; - peak_clip_height = peak_window_height; + PeakWindow win = {0}; + if (!name || !name[0]) name = "Peak"; + if (!width) width = 800; + if (!height) height = 600; + win.internal = peak_platform_window_open(name, width, height, flags); + if (!peak_window_sync(&win, NULL, NULL)) return win; + win.running = 1; + return win; } void -peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color) -{ - int start_x = (x < peak_clip_x) ? peak_clip_x : x; - int start_y = (y < peak_clip_y) ? peak_clip_y : y; - int end_x = (x + (int)w > (int)(peak_clip_x + peak_clip_width)) ? (int)(peak_clip_x + peak_clip_width) : x + (int)w; - int end_y = (y + (int)h > (int)(peak_clip_y + peak_clip_height)) ? (int)(peak_clip_y + peak_clip_height) : y + (int)h; - - for (int py = start_y; py < end_y; ++py) { - if (py < 0 || py >= (int)peak_window_height) continue; - for (int px = start_x; px < end_x; ++px) { - if (px < 0 || px >= (int)peak_window_width) continue; - peak_window_buffer[py * peak_window_width + px] = color; - } - } +peak_window_close(PeakWindow *win) +{ + win->running = 0; + peak_platform_window_close(&win->internal); + win->buffer = NULL; + win->width = 0; + win->height = 0; + win->bufsize = 0; } -void -peak_draw_rectangle_gradient(int x, int y, size_t w, size_t h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) -{ - for (size_t ry = 0; ry < h; ++ry) { - float ty = (h > 1) ? (float)ry / (float)(h - 1) : 0.0f; - uint32_t left_color = peak__lerp_color(c0, c3, ty); - uint32_t right_color = peak__lerp_color(c1, c2, ty); - - for (size_t rx = 0; rx < w; ++rx) { - float tx = (w > 1) ? (float)rx / (float)(w - 1) : 0.0f; - uint32_t final_color = peak__lerp_color(left_color, right_color, tx); - peak__put_pixel(x + (int)rx, y + (int)ry, final_color); - } - } +int +peak_window_epoll(PeakWindow *win, PeakEvent *ev) +{ + int got = peak_platform_epoll(&win->internal, ev); + if (got && ev->type == PEAK_EVENT_WINDOW_RESIZE) + peak_window_sync(win, NULL, NULL); + return got; } -void -peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) -{ - int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); - int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); - int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); - int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); - - float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); - if (area == 0.0f) return; - - for (int y = min_y; y <= max_y; ++y) { - for (int x = min_x; x <= max_x; ++x) { - float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y); - float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y); - float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y); - - if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) { - peak__put_pixel(x, y, color); - } - } - } +uint32_t * +peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height) +{ + return peak_window_sync(win, width, height); } void -peak_draw_triangle_gradient(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2) -{ - int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); - int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); - int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); - int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); - - float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); - if (area == 0.0f) return; - - for (int y = min_y; y <= max_y; ++y) { - for (int x = min_x; x <= max_x; ++x) { - float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y) / area; - float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y) / area; - float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y) / area; - - if (w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) { - uint32_t col_a = peak__lerp_color(c0, c1, w1 / (w0 + w1 + 1e-6f)); - uint32_t final_color = peak__lerp_color(col_a, c2, w2); - peak__put_pixel(x, y, final_color); - } - } - } +peak_window_clear(PeakWindow *win, float r, float g, float b, float a) +{ + uint32_t c, i; + if (!win || !win->buffer) return; +#if defined(PEAK_WEB) + /* ImageData RGBA bytes = LE 0xAABBGGRR */ + c = (uint32_t)(r * 255.f) + | ((uint32_t)(g * 255.f) << 8) + | ((uint32_t)(b * 255.f) << 16) + | ((uint32_t)(a * 255.f) << 24); +#else + c = ((uint32_t)(a * 255.f) << 24) + | ((uint32_t)(r * 255.f) << 16) + | ((uint32_t)(g * 255.f) << 8) + | (uint32_t)(b * 255.f); +#endif + for (i = 0; i < win->bufsize; i++) + win->buffer[i] = c; } void -peak_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color) -{ - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - - int half_w = (int)width / 2; - - while (1) { - /* Draw square footprint for line thickness */ - for (int ox = -half_w; ox <= half_w; ++ox) { - for (int oy = -half_w; oy <= half_w; ++oy) { - peak__put_pixel(x0 + ox, y0 + oy, color); - } - } - if (x0 == x1 && y0 == y1) break; - e2 = 2 * err; - if (e2 >= dy) { err += dy; x0 += sx; } - if (e2 <= dx) { err += dx; y0 += sy; } - } +peak_window_present(PeakWindow *win) +{ + if (win) peak_platform_window_present(&win->internal); } -void -peak_draw_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1) -{ - float total_dist = sqrtf((float)((x1 - x0)*(x1 - x0) + (y1 - y0)*(y1 - y0))); - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - - int orig_x0 = x0, orig_y0 = y0; - int half_w = (int)width / 2; - - while (1) { - float current_dist = sqrtf((float)((x0 - orig_x0)*(x0 - orig_x0) + (y0 - orig_y0)*(y0 - orig_y0))); - float t = (total_dist > 0.0f) ? (current_dist / total_dist) : 0.0f; - uint32_t color = peak__lerp_color(c0, c1, t); - - for (int ox = -half_w; ox <= half_w; ++ox) { - for (int oy = -half_w; oy <= half_w; ++oy) { - peak__put_pixel(x0 + ox, y0 + oy, color); - } - } - - if (x0 == x1 && y0 == y1) break; - e2 = 2 * err; - if (e2 >= dy) { err += dy; x0 += sx; } - if (e2 <= dx) { err += dx; y0 += sy; } +#ifdef PEAK_WEB +#include +static void +peak_internal_web_step(void *arg) +{ + PeakWindow *win = arg; + if (!win->tick(win, win->userdata) || !win->running) { + win->running = 0; + emscripten_cancel_main_loop(); } } +#endif +void +peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata) +{ + assert(win && "peak_window_run needs a window"); + assert(peak_tick && "peak_window_run needs tick callback"); + win->tick = peak_tick; + win->userdata = userdata; + win->running = 1; +#ifdef PEAK_WEB + emscripten_set_main_loop_arg(peak_internal_web_step, win, 0, 1); +#else + while (win->running && win->tick(win, win->userdata)); +#endif +} /* --- End of peak.c --- */ #endif // PEAK_IMPLEMENTATION diff --git a/Peak/src/p_emscripten.c b/Peak/src/p_emscripten.c index dc558f5..f79f575 100644 --- a/Peak/src/p_emscripten.c +++ b/Peak/src/p_emscripten.c @@ -1,38 +1,188 @@ +#include +#include #include #include -#include +#include -#define PEAK_CANVAS "#PeakCanvas" +EM_JS(void, peak_web_dom_open, (const char *id, int w, int h), { + var name = UTF8ToString(id); + var c = document.getElementById(name); + if (!c) { + c = document.createElement('canvas'); + c.id = name; + document.body.appendChild(c); + } + c.width = w; + c.height = h; + c.tabIndex = 0; + c.focus(); +}); -static PeakKeyCode map_key(const char *code); -static PeakKeyMod map_mod(const EmscriptenKeyboardEvent *keyEvent); -static EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); -static EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); -static EM_BOOL resize_callback(int eventType, const EmscriptenUiEvent *uiEvent, void *userData); +EM_JS(void, peak_web_dom_present, (const char *id, int w, int h, uintptr_t pixels), { + var c = document.getElementById(UTF8ToString(id)); + if (!c) return; + var ctx = c.getContext('2d'); + if (c.width !== w || c.height !== h) { + c.width = w; + c.height = h; + } + var img = ctx.createImageData(w, h); + img.data.set(HEAPU8.subarray(pixels, pixels + w * h * 4)); + ctx.putImageData(img, 0, 0); +}); -void -peak_platform_window_open() +struct peak_web_win { + char name[64]; + uint32_t width, height; + PeakQ q; + uint32_t buffer[]; +}; + +struct peak_window_internal_t { + struct peak_web_win *w; +}; + +static void +peak_web_sel(const char *name, char *out, size_t n) +{ + out[0] = '#'; + strncpy(out + 1, name, n - 2); + out[n - 1] = 0; +} + +static PeakKeyCode +peak_web_key_map(const char *code) +{ + if (code[0] == 'K' && code[1] == 'e' && code[2] == 'y' && code[3] >= 'A' && code[3] <= 'Z' && code[4] == 0) + return (PeakKeyCode)(PEAK_KEY_A + (code[3] - 'A')); + if (!strcmp(code, "ArrowUp")) return PEAK_KEY_UP; + if (!strcmp(code, "ArrowDown")) return PEAK_KEY_DOWN; + if (!strcmp(code, "ArrowLeft")) return PEAK_KEY_LEFT; + if (!strcmp(code, "ArrowRight")) return PEAK_KEY_RIGHT; + if (!strcmp(code, "Space")) return PEAK_KEY_SPACE; + if (!strcmp(code, "Escape")) return PEAK_KEY_ESCAPE; + if (!strcmp(code, "Enter")) return PEAK_KEY_ENTER; + return PEAK_KEY_UNKNOWN; +} + +static EM_BOOL +peak_web_key(int type, const EmscriptenKeyboardEvent *e, void *ud) +{ + PeakEvent ev = {0}; + ev.type = (type == EMSCRIPTEN_EVENT_KEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev.key.key = peak_web_key_map(e->code); + ev.key.mod = e->ctrlKey ? PEAK_KEYMOD_CTRL : e->altKey ? PEAK_KEYMOD_ALT : e->shiftKey ? PEAK_KEYMOD_SHIFT : 0; + peak_q_push(&((struct peak_web_win *)ud)->q, ev); + return EM_TRUE; +} + +static EM_BOOL +peak_web_mouse(int type, const EmscriptenMouseEvent *e, void *ud) +{ + PeakEvent ev = {0}; + ev.type = PEAK_EVENT_POINTER; + ev.pointer.x = (float)e->targetX; + ev.pointer.y = (float)e->targetY; + if (type == EMSCRIPTEN_EVENT_MOUSEDOWN) + ev.pointer.state = PEAK_POINTER_PRESSED; + else if (type == EMSCRIPTEN_EVENT_MOUSEUP) + ev.pointer.state = PEAK_POINTER_RELEASED; + else + ev.pointer.state = PEAK_POINTER_MOVED; + if (type == EMSCRIPTEN_EVENT_MOUSEMOVE) { + ev.pointer.type = (e->buttons & 4) ? PEAK_POINTER_MIDDLE : + (e->buttons & 2) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + } else { + ev.pointer.type = (e->button == 1) ? PEAK_POINTER_MIDDLE : + (e->button == 2) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + } + peak_q_push(&((struct peak_web_win *)ud)->q, ev); + return EM_TRUE; +} + +static void +peak_web_listen(struct peak_web_win *w, int on) +{ + char sel[66]; + peak_web_sel(w->name, sel, sizeof sel); + emscripten_set_keydown_callback(sel, w, EM_TRUE, on ? peak_web_key : NULL); + emscripten_set_keyup_callback(sel, w, EM_TRUE, on ? peak_web_key : NULL); + emscripten_set_mousedown_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); + emscripten_set_mouseup_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); + emscripten_set_mousemove_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); +} + +static int +peak_platform_init(void) +{ + return 1; +} + +static void +peak_platform_quit(void) +{ +} + +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal intern = {0}; + struct peak_web_win *w; + + (void)flags; + + w = calloc(1, sizeof *w + (size_t)width * height * sizeof *w->buffer); + if (!w) + return intern; + strncpy(w->name, name, sizeof w->name - 1); + w->width = width; + w->height = height; + + peak_web_dom_open(w->name, (int)width, (int)height); + + peak_web_listen(w, 1); + intern.w = w; + return intern; +} + +static void +peak_platform_window_close(PeakWindowInternal *intern) { - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, key_callback); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, key_callback); - emscripten_set_mousedown_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_mouseup_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_mousemove_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, resize_callback); + struct peak_web_win *w; + if (!intern || !intern->w) + return; + w = intern->w; + peak_web_listen(w, 0); + free(w); + intern->w = NULL; } -void -peak_platform_window_close() +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) { + struct peak_web_win *w = intern ? intern->w : NULL; + if (!w) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; } -uint32_t* -peak_platform_window_buffer(size_t *width, size_t *height) +static void +peak_platform_window_present(PeakWindowInternal *intern) { + struct peak_web_win *w = intern ? intern->w : NULL; + if (!w) + return; + peak_web_dom_present(w->name, (int)w->width, (int)w->height, (uintptr_t)w->buffer); } -bool -peak_platform_epoll(PeakEvent *ev) +static bool +peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) { - return 0; + struct peak_web_win *w = intern ? intern->w : NULL; + return w ? peak_q_pop(&w->q, ev) : 0; } diff --git a/Peak/src/p_linux.c b/Peak/src/p_linux.c index c3d70bf..0dc5e2f 100644 --- a/Peak/src/p_linux.c +++ b/Peak/src/p_linux.c @@ -1,295 +1,296 @@ -#include +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif + #include -#include #include +#include #include -#include +#include +#include #include -#include - -#define PEAK_X11_LINUX "/usr/lib/libX11.so.6" -#define PEAK_LINUX_DEFAULT_WIDTH 800 -#define PEAK_LINUX_DEFAULT_HEIGHT 600 - -typedef Display* (*XOpenDisplay_t)(const char*); -typedef int (*XCloseDisplay_t)(Display*); -typedef Window (*XCreateSimpleWindow_t)(Display*, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned long, unsigned long); -typedef int (*XStoreName_t)(Display*, Window, const char*); -typedef Atom (*XInternAtom_t)(Display*, const char*, Bool); -typedef Status (*XSetWMProtocols_t)(Display*, Window, Atom*, int); -typedef int (*XSelectInput_t)(Display*, Window, long); -typedef GC (*XCreateGC_t)(Display*, Window, unsigned long, XGCValues*); -typedef XImage* (*XCreateImage_t)(Display*, Visual*, unsigned int, int, int, char*, unsigned int, unsigned int, int, int); -typedef int (*XMapRaised_t)(Display*, Window); -typedef int (*XFlush_t)(Display*); -typedef int (*XFreeGC_t)(Display*, GC); -typedef int (*XDestroyWindow_t)(Display*, Window); -typedef int (*XPending_t)(Display*); -typedef int (*XNextEvent_t)(Display*, XEvent*); -typedef KeySym (*XLookupKeysym_t)(XKeyEvent*, int); -typedef int (*XPutImage_t)(Display*, Drawable, GC, XImage*, int, int, int, int, unsigned int, unsigned int); -typedef int (*XDefaultScreen_t)(Display*); -typedef Window (*XRootWindow_t)(Display*, int); -typedef unsigned long (*XBlackPixel_t)(Display*, int); -typedef Visual* (*XDefaultVisual_t)(Display*, int); -typedef int (*XDefaultDepth_t)(Display*, int); +#define PEAK_X11_LINUX "libX11.so.6" + +#define PEAK_X11_API(X) \ + X(XOpenDisplay, Display *, (const char *)) \ + X(XCloseDisplay, int, (Display *)) \ + X(XCreateSimpleWindow, Window, (Display *, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned long, unsigned long)) \ + X(XStoreName, int, (Display *, Window, const char *)) \ + X(XInternAtom, Atom, (Display *, const char *, Bool)) \ + X(XSetWMProtocols, Status, (Display *, Window, Atom *, int)) \ + X(XSelectInput, int, (Display *, Window, long)) \ + X(XCreateGC, GC, (Display *, Window, unsigned long, XGCValues *)) \ + X(XCreateImage, XImage *, (Display *, Visual *, unsigned int, int, int, char *, unsigned int, unsigned int, int, int)) \ + X(XMapRaised, int, (Display *, Window)) \ + X(XFlush, int, (Display *)) \ + X(XFreeGC, int, (Display *, GC)) \ + X(XDestroyWindow, int, (Display *, Window)) \ + X(XCheckIfEvent, Bool, (Display *, XEvent *, Bool (*)(Display *, XEvent *, XPointer), XPointer)) \ + X(XLookupKeysym, KeySym, (XKeyEvent *, int)) \ + X(XPutImage, int, (Display *, Drawable, GC, XImage *, int, int, int, int, unsigned int, unsigned int)) typedef struct { - XOpenDisplay_t XOpenDisplay; - XCloseDisplay_t XCloseDisplay; - XCreateSimpleWindow_t XCreateSimpleWindow; - XStoreName_t XStoreName; - XInternAtom_t XInternAtom; - XSetWMProtocols_t XSetWMProtocols; - XSelectInput_t XSelectInput; - XCreateGC_t XCreateGC; - XCreateImage_t XCreateImage; - XMapRaised_t XMapRaised; - XFlush_t XFlush; - XFreeGC_t XFreeGC; - XDestroyWindow_t XDestroyWindow; - XPending_t XPending; - XNextEvent_t XNextEvent; - XLookupKeysym_t XLookupKeysym; - XPutImage_t XPutImage; - XDefaultScreen_t XDefaultScreen; - XRootWindow_t XRootWindow; - XBlackPixel_t XBlackPixel; - XDefaultVisual_t XDefaultVisual; - XDefaultDepth_t XDefaultDepth; +#define X(name, ret, args) ret (*name) args; + PEAK_X11_API(X) +#undef X } PeakX11Api; -static PeakX11Api peak_x11; - -static int peak_load_x11(void *handle) { - peak_x11.XOpenDisplay = (XOpenDisplay_t)dlsym(handle, "XOpenDisplay"); - peak_x11.XCloseDisplay = (XCloseDisplay_t)dlsym(handle, "XCloseDisplay"); - peak_x11.XCreateSimpleWindow = (XCreateSimpleWindow_t)dlsym(handle, "XCreateSimpleWindow"); - peak_x11.XStoreName = (XStoreName_t)dlsym(handle, "XStoreName"); - peak_x11.XInternAtom = (XInternAtom_t)dlsym(handle, "XInternAtom"); - peak_x11.XSetWMProtocols = (XSetWMProtocols_t)dlsym(handle, "XSetWMProtocols"); - peak_x11.XSelectInput = (XSelectInput_t)dlsym(handle, "XSelectInput"); - peak_x11.XCreateGC = (XCreateGC_t)dlsym(handle, "XCreateGC"); - peak_x11.XCreateImage = (XCreateImage_t)dlsym(handle, "XCreateImage"); - peak_x11.XMapRaised = (XMapRaised_t)dlsym(handle, "XMapRaised"); - peak_x11.XFlush = (XFlush_t)dlsym(handle, "XFlush"); - peak_x11.XFreeGC = (XFreeGC_t)dlsym(handle, "XFreeGC"); - peak_x11.XDestroyWindow = (XDestroyWindow_t)dlsym(handle, "XDestroyWindow"); - peak_x11.XPending = (XPending_t)dlsym(handle, "XPending"); - peak_x11.XNextEvent = (XNextEvent_t)dlsym(handle, "XNextEvent"); - peak_x11.XLookupKeysym = (XLookupKeysym_t)dlsym(handle, "XLookupKeysym"); - peak_x11.XPutImage = (XPutImage_t)dlsym(handle, "XPutImage"); - peak_x11.XDefaultScreen = (XDefaultScreen_t)dlsym(handle, "XDefaultScreen"); - peak_x11.XRootWindow = (XRootWindow_t)dlsym(handle, "XRootWindow"); - peak_x11.XBlackPixel = (XBlackPixel_t)dlsym(handle, "XBlackPixel"); - peak_x11.XDefaultVisual = (XDefaultVisual_t)dlsym(handle, "XDefaultVisual"); - peak_x11.XDefaultDepth = (XDefaultDepth_t)dlsym(handle, "XDefaultDepth"); - - if (!peak_x11.XOpenDisplay || !peak_x11.XCloseDisplay || !peak_x11.XCreateSimpleWindow || - !peak_x11.XStoreName || !peak_x11.XInternAtom || !peak_x11.XSetWMProtocols || - !peak_x11.XSelectInput || !peak_x11.XCreateGC || !peak_x11.XCreateImage || - !peak_x11.XMapRaised || !peak_x11.XFlush || - !peak_x11.XFreeGC || !peak_x11.XDestroyWindow || !peak_x11.XPending || - !peak_x11.XNextEvent || !peak_x11.XLookupKeysym || !peak_x11.XPutImage || - !peak_x11.XDefaultScreen || !peak_x11.XRootWindow || !peak_x11.XBlackPixel || - !peak_x11.XDefaultVisual || !peak_x11.XDefaultDepth) { - return 0; - } - return 1; -} - - typedef struct { - Display *display; - Atom wm_delete_window; - bool initialized; + Display *display; + Atom wm_delete_window; } PeakLinux; -typedef struct { - Window window; - GC gfx_ctx; /* GRAPHICS CONTEXT, not garbage collector lol */ -} PeakLinuxWindow; +struct peak_window_internal_t { + Window window; + GC gfx_ctx; + XImage *ximage; + uint32_t *buffer; + uint32_t width; + uint32_t height; +}; -static PeakLinux peak_linux = {0}; -static PeakLinuxWindow peak_linux_window = {0}; -static XImage *peak_linux_ximage = NULL; -static uint32_t *peak_linux_buffer = NULL; -static size_t peak_linux_width = PEAK_LINUX_DEFAULT_WIDTH; -static size_t peak_linux_height = PEAK_LINUX_DEFAULT_HEIGHT; +static PeakLinux peak_linux; +static PeakX11Api peak_x11; + +static int +peak_internal_x11_load(void *handle) +{ +#define X(name, ret, args) peak_x11.name = (ret (*) args)dlsym(handle, #name); + PEAK_X11_API(X) +#undef X +#define X(name, ret, args) || !peak_x11.name + if (0 PEAK_X11_API(X)) + return 0; +#undef X + return 1; +} static PeakKeyCode -peak__x11_map_key(KeySym sym) +peak_internal_x11_key_map(KeySym sym) { - if (sym >= XK_a && sym <= XK_z) return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_a)); - if (sym >= XK_A && sym <= XK_Z) return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_A)); - switch (sym) { - case XK_Up: return PEAK_KEY_UP; - case XK_Down: return PEAK_KEY_DOWN; - case XK_Left: return PEAK_KEY_LEFT; - case XK_Right: return PEAK_KEY_RIGHT; - case XK_space: return PEAK_KEY_SPACE; - case XK_Escape: return PEAK_KEY_ESCAPE; - case XK_Return: return PEAK_KEY_ENTER; - default: return PEAK_KEY_UNKNOWN; - } + if (sym >= XK_a && sym <= XK_z) + return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_a)); + if (sym >= XK_A && sym <= XK_Z) + return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_A)); + switch (sym) { + case XK_Up: return PEAK_KEY_UP; + case XK_Down: return PEAK_KEY_DOWN; + case XK_Left: return PEAK_KEY_LEFT; + case XK_Right: return PEAK_KEY_RIGHT; + case XK_space: return PEAK_KEY_SPACE; + case XK_Escape: return PEAK_KEY_ESCAPE; + case XK_Return: return PEAK_KEY_ENTER; + default: return PEAK_KEY_UNKNOWN; + } } static PeakKeyMod -peak__x11_map_mod(unsigned int state) +peak_internal_x11_mod_map(unsigned int state) { - if (state & ControlMask) return PEAK_KEYMOD_CTRL; - if (state & Mod1Mask) return PEAK_KEYMOD_ALT; - if (state & ShiftMask) return PEAK_KEYMOD_SHIFT; - if (state & LockMask) return PEAK_KEYMOD_CAPS; - return (PeakKeyMod)0; + if (state & ControlMask) return PEAK_KEYMOD_CTRL; + if (state & Mod1Mask) return PEAK_KEYMOD_ALT; + if (state & ShiftMask) return PEAK_KEYMOD_SHIFT; + if (state & LockMask) return PEAK_KEYMOD_CAPS; + return (PeakKeyMod)0; } -void -peak_platform_window_open() +static Bool +peak_internal_x11_window_match(Display *dpy, XEvent *ev, XPointer arg) { - void *x11_handle = dlopen(PEAK_X11_LINUX, RTLD_LOCAL | RTLD_NOW); - assert(x11_handle && "Failed to load X11 library. What system are you fucking using and abusing?"); - - int success = peak_load_x11(x11_handle); - assert(success && "Failed to open X11 display"); - - peak_linux.display = peak_x11.XOpenDisplay(NULL); - assert(peak_linux.display && "Failed to open X11 display"); - - /* Who design this API??? */ - int screen = DefaultScreen(peak_linux.display); - peak_linux_window.window = peak_x11.XCreateSimpleWindow(peak_linux.display, RootWindow(peak_linux.display, screen), 0, 0, peak_linux_width, peak_linux_height, 0, BlackPixel(peak_linux.display, screen), BlackPixel(peak_linux.display, screen)); - peak_x11.XStoreName(peak_linux.display, peak_linux_window.window, "Peak"); - peak_linux.wm_delete_window = peak_x11.XInternAtom(peak_linux.display, "WM_DELETE_WINDOW", False); - peak_x11.XSetWMProtocols(peak_linux.display, peak_linux_window.window, &peak_linux.wm_delete_window, 1); - peak_x11.XSelectInput(peak_linux.display, peak_linux_window.window, ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask); - peak_linux_window.gfx_ctx = peak_x11.XCreateGC(peak_linux.display, peak_linux_window.window, 0, NULL); - peak_linux_buffer = (uint32_t *)calloc(peak_linux_width * peak_linux_height, sizeof(uint32_t)); - assert(peak_linux_buffer && "Failed to allocate framebuffer"); - - /* Wrap the buffer in an XImage for XPutImage blitting */ - Visual *visual = DefaultVisual(peak_linux.display, screen); - int depth = DefaultDepth(peak_linux.display, screen); - - peak_linux_ximage = peak_x11.XCreateImage(peak_linux.display, visual, depth, ZPixmap, 0, (char *)peak_linux_buffer, peak_linux_width, peak_linux_height, 32, 0); - assert(peak_linux_ximage && "Failed to create XImage"); - - peak_x11.XMapRaised(peak_linux.display, peak_linux_window.window); - peak_x11.XFlush(peak_linux.display); - - peak_linux.initialized = true; + (void)dpy; + return ev->xany.window == *(Window *)arg; } -void -peak_platform_window_close() +static int +peak_linux_buffer(PeakWindowInternal *w, uint32_t width, uint32_t height) { - if (!peak_linux.initialized) return; - - if (peak_linux_ximage) { - peak_linux_ximage->data = NULL; - XDestroyImage(peak_linux_ximage); - peak_linux_ximage = NULL; - } - - free(peak_linux_buffer); - peak_linux_buffer = NULL; - - peak_x11.XFreeGC(peak_linux.display, peak_linux_window.gfx_ctx); - peak_x11.XDestroyWindow(peak_linux.display, peak_linux_window.window); - peak_x11.XCloseDisplay(peak_linux.display); - - peak_linux.initialized = false; + int screen; + + if (w->ximage) { + w->ximage->data = NULL; + XDestroyImage(w->ximage); + w->ximage = NULL; + } + free(w->buffer); + w->buffer = calloc((size_t)width * height, sizeof *w->buffer); + if (!w->buffer) + return 0; + + w->width = width; + w->height = height; + screen = DefaultScreen(peak_linux.display); + w->ximage = peak_x11.XCreateImage(peak_linux.display, + DefaultVisual(peak_linux.display, screen), + DefaultDepth(peak_linux.display, screen), + ZPixmap, 0, (char *)w->buffer, width, height, 32, 0); + if (!w->ximage) { + free(w->buffer); + w->buffer = NULL; + return 0; + } + return 1; } -uint32_t * -peak_platform_window_buffer(size_t *width, size_t *height) +static int +peak_platform_init(void) { - *width = peak_linux_width; - *height = peak_linux_height; - return peak_linux_buffer; + void *handle; + + if (!peak_x11.XOpenDisplay) { + if (!(handle = dlopen(PEAK_X11_LINUX, RTLD_LOCAL | RTLD_NOW))) { + fputs("Failed to load X11 library. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_x11_load(handle)) { + fputs("Failed to load X11 symbols", stderr); + return 0; + } + } + if (!peak_linux.display) { + if (!(peak_linux.display = peak_x11.XOpenDisplay(NULL))) { + fputs("Failed to open X11 display", stderr); + return 0; + } + peak_linux.wm_delete_window = peak_x11.XInternAtom(peak_linux.display, "WM_DELETE_WINDOW", False); + } + return 1; } -bool -peak_platform_epoll(PeakEvent *ev) +static void +peak_platform_quit(void) { - if (!peak_linux.initialized) return false; - - Display *dpy = peak_linux.display; - - while (peak_x11.XPending(dpy) > 0) { - XEvent xev; - peak_x11.XNextEvent(dpy, &xev); - - switch (xev.type) { - - case ClientMessage: - if ((Atom)xev.xclient.data.l[0] == peak_linux.wm_delete_window) { - ev->type = PEAK_EVENT_WINDOW_CLOSE; - printf("CLLOOOOOOOSEEEE"); - return true; - } - continue; - - case KeyPress: - case KeyRelease: { - KeySym sym = peak_x11.XLookupKeysym(&xev.xkey, 0); - ev->type = (xev.type == KeyPress) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; - ev->key.key = peak__x11_map_key(sym); - ev->key.mod = peak__x11_map_mod(xev.xkey.state); - return true; - } - - case ButtonPress: - case ButtonRelease: { - ev->type = PEAK_EVENT_POINTER; - ev->pointer.state = (xev.type == ButtonPress) ? PEAK_POINTER_PRESSED : PEAK_POINTER_RELEASED; - ev->pointer.x = (float)xev.xbutton.x; - ev->pointer.y = (float)xev.xbutton.y; - switch (xev.xbutton.button) { - case Button1: ev->pointer.type = PEAK_POINTER_LEFT; break; - case Button2: ev->pointer.type = PEAK_POINTER_MIDDLE; break; - case Button3: ev->pointer.type = PEAK_POINTER_RIGHT; break; - default: ev->pointer.type = PEAK_POINTER_LEFT; break; - } - return true; - } + if (!peak_linux.display) + return; + peak_x11.XCloseDisplay(peak_linux.display); + peak_linux.display = 0; +} - case MotionNotify: - ev->type = PEAK_EVENT_POINTER; - ev->pointer.state = PEAK_POINTER_MOVED; - ev->pointer.x = (float)xev.xmotion.x; - ev->pointer.y = (float)xev.xmotion.y; - if (xev.xmotion.state & Button3Mask) - ev->pointer.type = PEAK_POINTER_RIGHT; - else if (xev.xmotion.state & Button2Mask) - ev->pointer.type = PEAK_POINTER_MIDDLE; - else - ev->pointer.type = PEAK_POINTER_LEFT; - return true; +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal w = {0}; + int screen; + + (void)flags; + if (!peak_linux.display && !peak_platform_init()) + return w; + + screen = DefaultScreen(peak_linux.display); + w.window = peak_x11.XCreateSimpleWindow(peak_linux.display, + RootWindow(peak_linux.display, screen), 0, 0, width, height, 0, + BlackPixel(peak_linux.display, screen), BlackPixel(peak_linux.display, screen)); + peak_x11.XStoreName(peak_linux.display, w.window, name); + peak_x11.XSetWMProtocols(peak_linux.display, w.window, &peak_linux.wm_delete_window, 1); + peak_x11.XSelectInput(peak_linux.display, w.window, + KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | StructureNotifyMask); + w.gfx_ctx = peak_x11.XCreateGC(peak_linux.display, w.window, 0, NULL); + + if (!peak_linux_buffer(&w, width, height)) { + peak_x11.XFreeGC(peak_linux.display, w.gfx_ctx); + peak_x11.XDestroyWindow(peak_linux.display, w.window); + return (PeakWindowInternal){0}; + } + + peak_x11.XMapRaised(peak_linux.display, w.window); + peak_x11.XFlush(peak_linux.display); + return w; +} - case ConfigureNotify: - if ((size_t)xev.xconfigure.width != peak_linux_width || - (size_t)xev.xconfigure.height != peak_linux_height) { - ev->type = PEAK_EVENT_WINDOW_RESIZE; - ev->resize.width = (uint32_t)xev.xconfigure.width; - ev->resize.height = (uint32_t)xev.xconfigure.height; - return true; - } - continue; +static void +peak_platform_window_close(PeakWindowInternal *w) +{ + if (!w || !w->window || !peak_linux.display) + return; + if (w->ximage) { + w->ximage->data = NULL; + XDestroyImage(w->ximage); + } + free(w->buffer); + if (w->gfx_ctx) + peak_x11.XFreeGC(peak_linux.display, w->gfx_ctx); + peak_x11.XDestroyWindow(peak_linux.display, w->window); + *w = (PeakWindowInternal){0}; +} - case Expose: - if (xev.xexpose.count == 0) { - peak_x11.XPutImage(dpy, peak_linux_window.window, peak_linux_window.gfx_ctx, peak_linux_ximage, 0, 0, 0, 0, peak_linux_width, peak_linux_height); - } - continue; +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *w, size_t *width, size_t *height) +{ + if (!w || !w->window) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; +} - default: - continue; - } - } +static void +peak_platform_window_present(PeakWindowInternal *w) +{ + if (!w || !w->ximage || !peak_linux.display) + return; + peak_x11.XPutImage(peak_linux.display, w->window, w->gfx_ctx, w->ximage, + 0, 0, 0, 0, w->width, w->height); + peak_x11.XFlush(peak_linux.display); +} - peak_x11.XPutImage(dpy, peak_linux_window.window, peak_linux_window.gfx_ctx, peak_linux_ximage, 0, 0, 0, 0, peak_linux_width, peak_linux_height); - peak_x11.XFlush(dpy); - return false; +static bool +peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev) +{ + XEvent xev; + + if (!w || !w->window || !peak_linux.display) + return 0; + + while (peak_x11.XCheckIfEvent(peak_linux.display, &xev, peak_internal_x11_window_match, (XPointer)&w->window)) { + switch (xev.type) { + case ClientMessage: + if ((Atom)xev.xclient.data.l[0] == peak_linux.wm_delete_window) { + ev->type = PEAK_EVENT_WINDOW_CLOSE; + return 1; + } + continue; + case KeyPress: + case KeyRelease: + ev->type = (xev.type == KeyPress) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev->key.key = peak_internal_x11_key_map(peak_x11.XLookupKeysym(&xev.xkey, 0)); + ev->key.mod = peak_internal_x11_mod_map(xev.xkey.state); + return 1; + case ButtonPress: + case ButtonRelease: + ev->type = PEAK_EVENT_POINTER; + ev->pointer.state = (xev.type == ButtonPress) ? PEAK_POINTER_PRESSED : PEAK_POINTER_RELEASED; + ev->pointer.x = (float)xev.xbutton.x; + ev->pointer.y = (float)xev.xbutton.y; + ev->pointer.type = (xev.xbutton.button == Button2) ? PEAK_POINTER_MIDDLE : + (xev.xbutton.button == Button3) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + return 1; + case MotionNotify: + ev->type = PEAK_EVENT_POINTER; + ev->pointer.state = PEAK_POINTER_MOVED; + ev->pointer.x = (float)xev.xmotion.x; + ev->pointer.y = (float)xev.xmotion.y; + ev->pointer.type = (xev.xmotion.state & Button3Mask) ? PEAK_POINTER_RIGHT : + (xev.xmotion.state & Button2Mask) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + return 1; + case ConfigureNotify: { + uint32_t width = (uint32_t)xev.xconfigure.width; + uint32_t height = (uint32_t)xev.xconfigure.height; + if (width == w->width && height == w->height) + continue; + if (!peak_linux_buffer(w, width, height)) + continue; + ev->type = PEAK_EVENT_WINDOW_RESIZE; + ev->resize.width = w->width; + ev->resize.height = w->height; + return 1; + } + default: + continue; + } + } + return 0; } diff --git a/Peak/src/p_win32.c b/Peak/src/p_win32.c index 16fab7f..c1a289b 100644 --- a/Peak/src/p_win32.c +++ b/Peak/src/p_win32.c @@ -1,21 +1,404 @@ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + #include +#include +#include +#include + +#define PEAK_WIN32_USER32 "user32.dll" +#define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_CLASS "PeakWindow" +#define PEAK_WIN32_PROP "Peak" + +#define PEAK_USER32_API(X) \ + X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ + X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ + X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ + X(DestroyWindow, BOOL, WINAPI, (HWND)) \ + X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ + X(GetDC, HDC, WINAPI, (HWND)) \ + X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ + X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ + X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ + X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ + X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ + X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ + X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ + X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ + X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ + X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ + X(GetKeyState, SHORT, WINAPI, (int)) + +#define PEAK_GDI32_API(X) \ + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_USER32_API(X) +#undef X +} PeakUser32Api; + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_GDI32_API(X) +#undef X +} PeakGdi32Api; + +typedef struct { + HMODULE user32; + HMODULE gdi32; + int class_reg; +} PeakWin32; + +struct peak_win32_win { + HWND hwnd; + HDC hdc; + uint32_t *buffer; + uint32_t width; + uint32_t height; + PeakQ q; +}; + +struct peak_window_internal_t { + struct peak_win32_win *w; +}; + +static PeakWin32 peak_win32; +static PeakUser32Api peak_user32; +static PeakGdi32Api peak_gdi32; + +static int +peak_internal_user32_load(HMODULE handle) +{ +#define X(name, ret, conv, args) peak_user32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_USER32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_user32.name + if (0 PEAK_USER32_API(X)) + return 0; +#undef X + return 1; +} -void -peak_platform_window_open() +static int +peak_internal_gdi32_load(HMODULE handle) { +#define X(name, ret, conv, args) peak_gdi32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_GDI32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_gdi32.name + if (0 PEAK_GDI32_API(X)) + return 0; +#undef X + return 1; } -void -peak_platform_window_close() +static PeakKeyCode +peak_internal_win32_key_map(WPARAM vk) { + if (vk >= 'A' && vk <= 'Z') + return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A')); + switch (vk) { + case VK_UP: return PEAK_KEY_UP; + case VK_DOWN: return PEAK_KEY_DOWN; + case VK_LEFT: return PEAK_KEY_LEFT; + case VK_RIGHT: return PEAK_KEY_RIGHT; + case VK_SPACE: return PEAK_KEY_SPACE; + case VK_ESCAPE: return PEAK_KEY_ESCAPE; + case VK_RETURN: return PEAK_KEY_ENTER; + default: return PEAK_KEY_UNKNOWN; + } } -uint32_t* -peak_platform_window_buffer(size_t *width, size_t *height) +static PeakKeyMod +peak_internal_win32_mod_map(void) { + if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL; + if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT; + if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT; + if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS; + return (PeakKeyMod)0; } -bool -peak_platform_epoll(PeakEvent *ev) +static int +peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) { + uint32_t *buffer; + + buffer = calloc((size_t)width * height, sizeof *buffer); + if (!buffer) + return 0; + free(w->buffer); + w->buffer = buffer; + w->width = width; + w->height = height; + return 1; +} + +static LRESULT CALLBACK +peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + struct peak_win32_win *w; + PeakEvent ev; + + if (msg == WM_NCCREATE) { + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); + } + + w = (struct peak_win32_win *)peak_user32.GetPropA(hwnd, PEAK_WIN32_PROP); + if (!w) + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + + switch (msg) { + case WM_CLOSE: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_CLOSE; + peak_q_push(&w->q, ev); + return 0; + case WM_SIZE: { + uint32_t width, height; + if (wparam == SIZE_MINIMIZED) + return 0; + width = (uint32_t)LOWORD(lparam); + height = (uint32_t)HIWORD(lparam); + if (!width || !height || (width == w->width && height == w->height)) + return 0; + if (!peak_win32_buffer(w, width, height)) + return 0; + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_RESIZE; + ev.resize.width = w->width; + ev.resize.height = w->height; + peak_q_push(&w->q, ev); + return 0; + } + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + memset(&ev, 0, sizeof ev); + ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev.key.key = peak_internal_win32_key_map(wparam); + ev.key.mod = peak_internal_win32_mod_map(); + peak_q_push(&w->q, ev); + return 0; + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_POINTER; + ev.pointer.x = (float)(short)LOWORD(lparam); + ev.pointer.y = (float)(short)HIWORD(lparam); + if (msg == WM_MOUSEMOVE) { + ev.pointer.state = PEAK_POINTER_MOVED; + ev.pointer.type = (wparam & MK_RBUTTON) ? PEAK_POINTER_RIGHT : + (wparam & MK_MBUTTON) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) { + ev.pointer.state = PEAK_POINTER_PRESSED; + ev.pointer.type = (msg == WM_RBUTTONDOWN) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONDOWN) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else { + ev.pointer.state = PEAK_POINTER_RELEASED; + ev.pointer.type = (msg == WM_RBUTTONUP) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONUP) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } + peak_q_push(&w->q, ev); + return 0; + case WM_PAINT: { + PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); + peak_user32.EndPaint(hwnd, &ps); + return 0; + } + case WM_ERASEBKGND: + return 1; + case WM_DESTROY: + peak_user32.RemovePropA(hwnd, PEAK_WIN32_PROP); + return 0; + default: + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + } +} + +static int +peak_platform_init(void) +{ + WNDCLASSEXA wc; + + if (!peak_user32.CreateWindowExA) { + if (!(peak_win32.user32 = LoadLibraryA(PEAK_WIN32_USER32))) { + fputs("Failed to load user32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_user32_load(peak_win32.user32)) { + fputs("Failed to load user32 symbols", stderr); + return 0; + } + } + if (!peak_gdi32.StretchDIBits) { + if (!(peak_win32.gdi32 = LoadLibraryA(PEAK_WIN32_GDI32))) { + fputs("Failed to load gdi32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_gdi32_load(peak_win32.gdi32)) { + fputs("Failed to load gdi32 symbols", stderr); + return 0; + } + } + if (!peak_win32.class_reg) { + memset(&wc, 0, sizeof wc); + wc.cbSize = sizeof wc; + wc.style = CS_OWNDC; + wc.lpfnWndProc = peak_internal_win32_wndproc; + wc.hInstance = GetModuleHandleA(NULL); + wc.hCursor = peak_user32.LoadCursorA(NULL, IDC_ARROW); + wc.lpszClassName = PEAK_WIN32_CLASS; + if (!peak_user32.RegisterClassExA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { + fputs("Failed to register window class", stderr); + return 0; + } + peak_win32.class_reg = 1; + } + return 1; +} + +static void +peak_platform_quit(void) +{ + if (!peak_win32.class_reg) + return; + peak_user32.UnregisterClassA(PEAK_WIN32_CLASS, GetModuleHandleA(NULL)); + peak_win32.class_reg = 0; +} + +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal intern = {0}; + struct peak_win32_win *w; + DWORD style, ex; + RECT r; + HINSTANCE inst; + + (void)flags; + if (!peak_user32.CreateWindowExA && !peak_platform_init()) + return intern; + + w = calloc(1, sizeof *w); + if (!w) + return intern; + if (!peak_win32_buffer(w, width, height)) { + free(w); + return intern; + } + + ex = 0; + style = WS_OVERLAPPEDWINDOW; + r.left = 0; + r.top = 0; + r.right = (LONG)width; + r.bottom = (LONG)height; + peak_user32.AdjustWindowRectEx(&r, style, FALSE, ex); + inst = GetModuleHandleA(NULL); + w->hwnd = peak_user32.CreateWindowExA(ex, PEAK_WIN32_CLASS, name, style, + CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, + NULL, NULL, inst, w); + if (!w->hwnd) { + free(w->buffer); + free(w); + return intern; + } + + w->hdc = peak_user32.GetDC(w->hwnd); + if (!w->hdc) { + peak_user32.DestroyWindow(w->hwnd); + free(w->buffer); + free(w); + return intern; + } + + peak_user32.ShowWindow(w->hwnd, SW_SHOWNORMAL); + intern.w = w; + return intern; +} + +static void +peak_platform_window_close(PeakWindowInternal *intern) +{ + struct peak_win32_win *w; + if (!intern || !intern->w) + return; + w = intern->w; + if (w->hdc && w->hwnd) + peak_user32.ReleaseDC(w->hwnd, w->hdc); + if (w->hwnd) { + peak_user32.RemovePropA(w->hwnd, PEAK_WIN32_PROP); + peak_user32.DestroyWindow(w->hwnd); + } + free(w->buffer); + free(w); + intern->w = NULL; +} + +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + if (!w) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; +} + +static void +peak_platform_window_present(PeakWindowInternal *intern) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + BITMAPINFO bmi; + + if (!w || !w->hwnd || !w->hdc || !w->buffer) + return; + + memset(&bmi, 0, sizeof bmi); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = (LONG)w->width; + bmi.bmiHeader.biHeight = -(LONG)w->height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + peak_gdi32.StretchDIBits(w->hdc, + 0, 0, (int)w->width, (int)w->height, + 0, 0, (int)w->width, (int)w->height, + w->buffer, &bmi, DIB_RGB_COLORS, SRCCOPY); +} + +static bool +peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) +{ + MSG msg; + struct peak_win32_win *w = intern ? intern->w : NULL; + + if (!w || !w->hwnd) + return 0; + + while (peak_user32.PeekMessageA(&msg, w->hwnd, 0, 0, PM_REMOVE)) { + peak_user32.TranslateMessage(&msg); + peak_user32.DispatchMessageA(&msg); + if (peak_q_pop(&w->q, ev)) + return 1; + } + return peak_q_pop(&w->q, ev); } diff --git a/Peak/src/peak.c b/Peak/src/peak.c index 5e09070..6c4c038 100644 --- a/Peak/src/peak.c +++ b/Peak/src/peak.c @@ -1,254 +1,118 @@ -static inline void peak__put_pixel(int x, int y, uint32_t color); -static inline uint32_t peak__lerp_color(uint32_t c0, uint32_t c1, float t); -static inline float peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy); - -static uint32_t *peak_window_buffer; -static size_t peak_window_width; -static size_t peak_window_height; -static int peak_clip_x; -static int peak_clip_y; -static size_t peak_clip_width; -static size_t peak_clip_height; - -void -peak__put_pixel(int x, int y, uint32_t color) +static uint32_t * +peak_window_sync(PeakWindow *win, size_t *width, size_t *height) { - if (x < peak_clip_x || x >= (int)(peak_clip_x + peak_clip_width) || - y < peak_clip_y || y >= (int)(peak_clip_y + peak_clip_height)) { - return; - } - if (x < 0 || x >= (int)peak_window_width || y < 0 || y >= (int)peak_window_height) { - return; - } - peak_window_buffer[y * peak_window_width + x] = color; + size_t w = 0, h = 0; + win->buffer = peak_platform_window_buffer(&win->internal, &w, &h); + win->width = (uint32_t)w; + win->height = (uint32_t)h; + win->bufsize = win->width * win->height; + if (width) *width = w; + if (height) *height = h; + return win->buffer; } -uint32_t -peak__lerp_color(uint32_t c0, uint32_t c1, float t) +int +peak_init(void) { - if (t <= 0.0f) return c0; - if (t >= 1.0f) return c1; - - uint32_t a0 = (c0 >> 24) & 0xFF, a1 = (c1 >> 24) & 0xFF; - uint32_t r0 = (c0 >> 16) & 0xFF, r1 = (c1 >> 16) & 0xFF; - uint32_t g0 = (c0 >> 8) & 0xFF, g1 = (c1 >> 8) & 0xFF; - uint32_t b0 = c0 & 0xFF, b1 = c1 & 0xFF; - - uint32_t a = (uint32_t)(a0 + t * ((float)a1 - (float)a0)); - uint32_t r = (uint32_t)(r0 + t * ((float)r1 - (float)r0)); - uint32_t g = (uint32_t)(g0 + t * ((float)g1 - (float)g0)); - uint32_t b = (uint32_t)(b0 + t * ((float)b1 - (float)b0)); - - return (a << 24) | (r << 16) | (g << 8) | b; -} - -float -peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy) -{ - return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax); + return peak_platform_init(); } void -peak_init(void) +peak_quit(void) { - peak_platform_window_open(); - peak_window_buffer = peak_platform_window_buffer(&peak_window_width, &peak_window_height); - peak_clip_x = 0; - peak_clip_y = 0; - peak_clip_width = peak_window_width; - peak_clip_height = peak_window_height; + peak_platform_quit(); } -void -peak_quit(void) +PeakWindow +peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) { - peak_platform_window_close(); + PeakWindow win = {0}; + if (!name || !name[0]) name = "Peak"; + if (!width) width = 800; + if (!height) height = 600; + win.internal = peak_platform_window_open(name, width, height, flags); + if (!peak_window_sync(&win, NULL, NULL)) return win; + win.running = 1; + return win; } void -peak_extensions(void) +peak_window_close(PeakWindow *win) { - // TODO + win->running = 0; + peak_platform_window_close(&win->internal); + win->buffer = NULL; + win->width = 0; + win->height = 0; + win->bufsize = 0; } -bool -peak_poll_events(PeakEvent *ev) +int +peak_window_epoll(PeakWindow *win, PeakEvent *ev) { - return peak_platform_epoll(ev); + int got = peak_platform_epoll(&win->internal, ev); + if (got && ev->type == PEAK_EVENT_WINDOW_RESIZE) + peak_window_sync(win, NULL, NULL); + return got; } -void -peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height) +uint32_t * +peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height) { - for (size_t y = 0; y < height; ++y) { - int target_y = offset_y + (int)y; - if (target_y < peak_clip_y || target_y >= (int)(peak_clip_y + peak_clip_height) || - target_y < 0 || target_y >= (int)peak_window_height) continue; - - int start_x = offset_x < peak_clip_x ? peak_clip_x : offset_x; - int end_x = (offset_x + (int)width) > (int)(peak_clip_x + peak_clip_width) - ? (int)(peak_clip_x + peak_clip_width) - : (offset_x + (int)width); - - if (start_x >= end_x) continue; - - size_t src_offset_x = (size_t)(start_x - offset_x); - size_t copy_pixels = (size_t)(end_x - start_x); - - memcpy(&peak_window_buffer[target_y * peak_window_width + start_x], &rgba[y * width + src_offset_x], copy_pixels * sizeof(uint32_t)); - } + return peak_window_sync(win, width, height); } void -peak_clip(int x, int y, size_t w, size_t h) -{ - peak_clip_x = x; - peak_clip_y = y; - peak_clip_width = w; - peak_clip_height = h; +peak_window_clear(PeakWindow *win, float r, float g, float b, float a) +{ + uint32_t c, i; + if (!win || !win->buffer) return; +#if defined(PEAK_WEB) + /* ImageData RGBA bytes = LE 0xAABBGGRR */ + c = (uint32_t)(r * 255.f) + | ((uint32_t)(g * 255.f) << 8) + | ((uint32_t)(b * 255.f) << 16) + | ((uint32_t)(a * 255.f) << 24); +#else + c = ((uint32_t)(a * 255.f) << 24) + | ((uint32_t)(r * 255.f) << 16) + | ((uint32_t)(g * 255.f) << 8) + | (uint32_t)(b * 255.f); +#endif + for (i = 0; i < win->bufsize; i++) + win->buffer[i] = c; } void -peak_clip_reset(void) +peak_window_present(PeakWindow *win) { - peak_clip_x = 0; - peak_clip_y = 0; - peak_clip_width = peak_window_width; - peak_clip_height = peak_window_height; + if (win) peak_platform_window_present(&win->internal); } -void -peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color) +#ifdef PEAK_WEB +#include +static void +peak_internal_web_step(void *arg) { - int start_x = (x < peak_clip_x) ? peak_clip_x : x; - int start_y = (y < peak_clip_y) ? peak_clip_y : y; - int end_x = (x + (int)w > (int)(peak_clip_x + peak_clip_width)) ? (int)(peak_clip_x + peak_clip_width) : x + (int)w; - int end_y = (y + (int)h > (int)(peak_clip_y + peak_clip_height)) ? (int)(peak_clip_y + peak_clip_height) : y + (int)h; - - for (int py = start_y; py < end_y; ++py) { - if (py < 0 || py >= (int)peak_window_height) continue; - for (int px = start_x; px < end_x; ++px) { - if (px < 0 || px >= (int)peak_window_width) continue; - peak_window_buffer[py * peak_window_width + px] = color; - } + PeakWindow *win = arg; + if (!win->tick(win, win->userdata) || !win->running) { + win->running = 0; + emscripten_cancel_main_loop(); } } +#endif void -peak_draw_rectangle_gradient(int x, int y, size_t w, size_t h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) -{ - for (size_t ry = 0; ry < h; ++ry) { - float ty = (h > 1) ? (float)ry / (float)(h - 1) : 0.0f; - uint32_t left_color = peak__lerp_color(c0, c3, ty); - uint32_t right_color = peak__lerp_color(c1, c2, ty); - - for (size_t rx = 0; rx < w; ++rx) { - float tx = (w > 1) ? (float)rx / (float)(w - 1) : 0.0f; - uint32_t final_color = peak__lerp_color(left_color, right_color, tx); - peak__put_pixel(x + (int)rx, y + (int)ry, final_color); - } - } +peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata) +{ + assert(win && "peak_window_run needs a window"); + assert(peak_tick && "peak_window_run needs tick callback"); + win->tick = peak_tick; + win->userdata = userdata; + win->running = 1; +#ifdef PEAK_WEB + emscripten_set_main_loop_arg(peak_internal_web_step, win, 0, 1); +#else + while (win->running && win->tick(win, win->userdata)); +#endif } - -void -peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) -{ - int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); - int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); - int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); - int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); - - float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); - if (area == 0.0f) return; - - for (int y = min_y; y <= max_y; ++y) { - for (int x = min_x; x <= max_x; ++x) { - float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y); - float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y); - float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y); - - if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) { - peak__put_pixel(x, y, color); - } - } - } -} - -void -peak_draw_triangle_gradient(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2) -{ - int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); - int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); - int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); - int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); - - float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); - if (area == 0.0f) return; - - for (int y = min_y; y <= max_y; ++y) { - for (int x = min_x; x <= max_x; ++x) { - float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y) / area; - float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y) / area; - float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y) / area; - - if (w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) { - uint32_t col_a = peak__lerp_color(c0, c1, w1 / (w0 + w1 + 1e-6f)); - uint32_t final_color = peak__lerp_color(col_a, c2, w2); - peak__put_pixel(x, y, final_color); - } - } - } -} - -void -peak_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color) -{ - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - - int half_w = (int)width / 2; - - while (1) { - /* Draw square footprint for line thickness */ - for (int ox = -half_w; ox <= half_w; ++ox) { - for (int oy = -half_w; oy <= half_w; ++oy) { - peak__put_pixel(x0 + ox, y0 + oy, color); - } - } - if (x0 == x1 && y0 == y1) break; - e2 = 2 * err; - if (e2 >= dy) { err += dy; x0 += sx; } - if (e2 <= dx) { err += dx; y0 += sy; } - } -} - -void -peak_draw_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1) -{ - float total_dist = sqrtf((float)((x1 - x0)*(x1 - x0) + (y1 - y0)*(y1 - y0))); - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - - int orig_x0 = x0, orig_y0 = y0; - int half_w = (int)width / 2; - - while (1) { - float current_dist = sqrtf((float)((x0 - orig_x0)*(x0 - orig_x0) + (y0 - orig_y0)*(y0 - orig_y0))); - float t = (total_dist > 0.0f) ? (current_dist / total_dist) : 0.0f; - uint32_t color = peak__lerp_color(c0, c1, t); - - for (int ox = -half_w; ox <= half_w; ++ox) { - for (int oy = -half_w; oy <= half_w; ++oy) { - peak__put_pixel(x0 + ox, y0 + oy, color); - } - } - - if (x0 == x1 && y0 == y1) break; - e2 = 2 * err; - if (e2 >= dy) { err += dy; x0 += sx; } - if (e2 <= dx) { err += dx; y0 += sy; } - } -} - diff --git a/Peak/src/peak_header.h b/Peak/src/peak_header.h index 9521abb..b0a3023 100644 --- a/Peak/src/peak_header.h +++ b/Peak/src/peak_header.h @@ -17,12 +17,13 @@ * 0.0.0 - @vasco - prototyping * 0.1.0 - @vasco - linux x11 that automagically loads X11 DLL * 0.1.1 - @vasco - fixed event handling on linux - * - * TODO: demo that stress tests the whole API + * 0.1.2 - @vasco - better handling of windows, if multiple windows becomes necessary in the future + * 0.2.0 - @vasco - multiple windows, major-ish API changes. + * 0.3.0 - @vasco - web via emscripten + * 0.4.0 - @vasco - win32 */ #include -#include #include #include #include @@ -52,15 +53,13 @@ #else #define PEAK_MACOS #endif -#elif defined(__linux__) - #define PEAK_LINUX -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) - #define PEAK_BSD #elif defined(__ANDROID__) #define PEAK_ANDROID #define PEAK_LINUX #elif defined(__linux__) #define PEAK_LINUX +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) + #define PEAK_BSD #endif #if defined(PEAK_LINUX) || defined(PEAK_BSD) || defined(PEAK_APPLE) @@ -77,7 +76,8 @@ #endif typedef enum { - PEAK_KEYMOD_ALT = 0, + PEAK_KEYMOD_NONE = 0, + PEAK_KEYMOD_ALT, PEAK_KEYMOD_SHIFT, PEAK_KEYMOD_CTRL, PEAK_KEYMOD_CAPS, @@ -102,7 +102,7 @@ typedef enum { PEAK_EVENT_POINTER_CONNECTED, PEAK_EVENT_POINTER_DISCONNECTED, PEAK_EVENT_LAST -} PeakEvenType; +} PeakEventType; typedef enum { PEAK_POINTER_MOVED = 0, @@ -118,7 +118,7 @@ typedef enum { } PeakPointerType; typedef struct { - PeakEvenType type; + PeakEventType type; union { struct { PeakKeyCode key; PeakKeyMod mod; } key; struct { uint32_t width, height; } resize; @@ -126,106 +126,53 @@ typedef struct { }; } PeakEvent; +/* Implemented by the platform */ +typedef struct peak_window_internal_t PeakWindowInternal; +typedef struct PeakWindow PeakWindow; + /* Public API */ -PEAK void peak_init(void); // open a window and initialize graphics. called automatically if using peak_setup(). -PEAK void peak_quit(void); // close the window and release resources. called automatically if using peak_setup(). -PEAK bool peak_poll_events(PeakEvent *ev); // Poll the next event from the window queue. Returns true if an event was retrieved. -PEAK void peak_extensions(void); // retrieve gpu window surface extensions for native api access (e.g., vulkan/opengl). -PEAK void peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height); // blit rgba pixels to the screen. -PEAK void peak_clip(int x, int y, size_t w, size_t h); // restrict all subsequent rendering operations to this bounding rectangle. -PEAK void peak_clip_reset(void); // reset clipping plane to window size. -PEAK void peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color); // draw a solid-color filled rectangle. -PEAK void peak_draw_rectangle_gradient(int x, int y, size_t w, size_t h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3); // fill rectangle interpolating four corner colors in clock-wise order. -PEAK void peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color); // draw a solid-color filled triangle. -PEAK void peak_draw_triangle_gradient(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2); // fill triangle with color interpolation clock-wise across its three vertices. -PEAK void peak_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color); // draw a line between two points. -PEAK void peak_draw_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1); // draw a line with a color gradient. +PEAK int peak_init(void); // Initialize platform context and load necessary DLLs. +PEAK void peak_quit(void); // Close platform context. +PEAK PeakWindow peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); // Open a window. +PEAK void peak_window_close(PeakWindow *window); // Close a window. +PEAK void peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata); // Hijacking the main loop makes life easier on platforms like web +PEAK int peak_window_epoll(PeakWindow *win, PeakEvent *ev); // Poll a window for events. +PEAK uint32_t* peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height); // Get the windows backbuffer. +PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); +PEAK void peak_window_present(PeakWindow *win); -/* Implemented by the platform */ -PEAK void peak_platform_window_open(); -PEAK void peak_platform_window_close(); -PEAK uint32_t *peak_platform_window_buffer(size_t *width, size_t *height); -PEAK bool peak_platform_epoll(PeakEvent *ev); +#endif // PEAK_H -/* - * He was wipping up amazing foods, like making happiness. - * Actual happiness? - * Actual happiness and joy. - * Oh the smell, it was so divine. - * He was making happiness!? - * Happiness in the kettle! - */ -#ifdef PEAK_REPLACE_MAIN -enum PeakReturn { PEAK_CONTINUE = 0, PEAK_STOP = 1 }; -extern int peak__main(int argc, char**argv); -extern void peak_events(PeakEvent ev); -extern void peak_tick(); -static bool running = true; -static inline void peak_stop() { running = false; } -#ifndef PEAK_WEB -#define main(...)\ -main(int argc, char **argv) {\ - assert(peak_events != 0 && "Declare peak_events() to handle window events.");\ - assert(peak_tick != 0 && "Declare peak_tick() to handle each frame ticks.");\ - peak_init();\ - int ret = peak__main(argc, argv);\ - if (ret != PEAK_CONTINUE) return ret;\ - PeakEvent ev;\ - while (running) {\ - while (running && peak_poll_events(&ev)) {\ - peak_events(ev);\ - }\ - peak_tick();\ - }\ - peak_quit();\ -}\ -int peak__main(__VA_ARGS__) -#else -#include -/* - * He was wipping up agony in the kettle. - * Actual pain? - * Boiling hatred in the kettle. - * The smell, it's pain. - * Boiling anger!? - * Yes, boiling anger. - */ +#ifdef PEAK_IMPLEMENTATION +#undef PEAK_IMPLEMENTATION + +#if defined(PEAK_WIN32) || defined(PEAK_WEB) +#define PEAK_Q 64 -static inline void -peak__emscripten_loop_step(void *arg) +typedef struct { + unsigned h, n; + PeakEvent e[PEAK_Q]; +} PeakQ; + +static void +peak_q_push(PeakQ *q, PeakEvent ev) { - (void)arg; - PeakEvent ev; - while (peak_poll_events(&ev)) { - peak_events(ev); - } - - if (running) { - peak_tick(); - } else { - emscripten_cancel_main_loop(); - peak_quit(); - } + if (!q || q->n == PEAK_Q) + return; + q->e[(q->h + q->n++) % PEAK_Q] = ev; } -#define main(...)\ - main(int argc, char **argv) {\ - _Static_assert(peak_events != 0, "When using peak_setup, declare peak_events() to handle window events.");\ - _Static_assert(peak_tick != 0, "When using peak_setup, declare peak_tick() to handle frame ticks.");\ - peak_init();\ - int ret = peak__main(argc, argv);\ - if (ret != PEAK_CONTINUE) return ret;\ - emscripten_set_main_loop_arg(peak__emscripten_loop_step, NULL, 0, 1);\ - return 0;\ - }\ - int peak__main(__VA_ARGS__) +static int +peak_q_pop(PeakQ *q, PeakEvent *ev) +{ + if (!q || !q->n) + return 0; + *ev = q->e[q->h]; + q->h = (q->h + 1) % PEAK_Q; + q->n--; + return 1; +} #endif -#endif // PEAK_DONT_REPLACE_MAIN - -#endif // PEAK_H - -#ifdef PEAK_IMPLEMENTATION -#undef PEAK_IMPLEMENTATION #if defined(PEAK_WIN32) #include "p_win32.c" // @@ -235,6 +182,19 @@ peak__emscripten_loop_step(void *arg) #include "p_emscripten.c" // #endif +/* We define PeakWindow only when we know the type of peak_window_internal_t */ +struct PeakWindow { + PeakWindowInternal internal; + int (*tick)(struct PeakWindow *win, void *userdata); + void *userdata; + uint32_t *buffer; + uint32_t width; + uint32_t height; + uint32_t bufsize; + int running; +}; + + #include "peak.c" // #endif // PEAK_IMPLEMENTATION diff --git a/Peak/src/peak_header.h.out b/Peak/src/peak_header.h.out index 581aa74..97bb0a3 100644 --- a/Peak/src/peak_header.h.out +++ b/Peak/src/peak_header.h.out @@ -17,12 +17,11 @@ * 0.0.0 - @vasco - prototyping * 0.1.0 - @vasco - linux x11 that automagically loads X11 DLL * 0.1.1 - @vasco - fixed event handling on linux - * - * TODO: demo that stress tests the whole API + * 0.1.2 - @vasco - better handling of windows, if multiple windows becomes necessary in the future + * 0.2.0 - @vasco - win32 */ #include -#include #include #include #include @@ -52,15 +51,13 @@ #else #define PEAK_MACOS #endif -#elif defined(__linux__) - #define PEAK_LINUX -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) - #define PEAK_BSD #elif defined(__ANDROID__) #define PEAK_ANDROID #define PEAK_LINUX #elif defined(__linux__) #define PEAK_LINUX +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) + #define PEAK_BSD #endif #if defined(PEAK_LINUX) || defined(PEAK_BSD) || defined(PEAK_APPLE) @@ -77,7 +74,8 @@ #endif typedef enum { - PEAK_KEYMOD_ALT = 0, + PEAK_KEYMOD_NONE = 0, + PEAK_KEYMOD_ALT, PEAK_KEYMOD_SHIFT, PEAK_KEYMOD_CTRL, PEAK_KEYMOD_CAPS, @@ -102,7 +100,7 @@ typedef enum { PEAK_EVENT_POINTER_CONNECTED, PEAK_EVENT_POINTER_DISCONNECTED, PEAK_EVENT_LAST -} PeakEvenType; +} PeakEventType; typedef enum { PEAK_POINTER_MOVED = 0, @@ -118,7 +116,7 @@ typedef enum { } PeakPointerType; typedef struct { - PeakEvenType type; + PeakEventType type; union { struct { PeakKeyCode key; PeakKeyMod mod; } key; struct { uint32_t width, height; } resize; @@ -126,727 +124,1085 @@ typedef struct { }; } PeakEvent; +/* Implemented by the platform */ +typedef struct peak_window_internal_t PeakWindowInternal; +typedef struct PeakWindow PeakWindow; + /* Public API */ -PEAK void peak_init(void); // open a window and initialize graphics. called automatically if using peak_setup(). -PEAK void peak_quit(void); // close the window and release resources. called automatically if using peak_setup(). -PEAK bool peak_poll_events(PeakEvent *ev); // Poll the next event from the window queue. Returns true if an event was retrieved. -PEAK void peak_extensions(void); // retrieve gpu window surface extensions for native api access (e.g., vulkan/opengl). -PEAK void peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height); // blit rgba pixels to the screen. -PEAK void peak_clip(int x, int y, size_t w, size_t h); // restrict all subsequent rendering operations to this bounding rectangle. -PEAK void peak_clip_reset(void); // reset clipping plane to window size. -PEAK void peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color); // draw a solid-color filled rectangle. -PEAK void peak_draw_rectangle_gradient(int x, int y, size_t w, size_t h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3); // fill rectangle interpolating four corner colors in clock-wise order. -PEAK void peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color); // draw a solid-color filled triangle. -PEAK void peak_draw_triangle_gradient(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2); // fill triangle with color interpolation clock-wise across its three vertices. -PEAK void peak_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color); // draw a line between two points. -PEAK void peak_draw_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1); // draw a line with a color gradient. +PEAK int peak_init(void); // Initialize platform context and load necessary DLLs. +PEAK void peak_quit(void); // Close platform context. +PEAK PeakWindow peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags); // Open a window. +PEAK void peak_window_close(PeakWindow *window); // Close a window. +PEAK void peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata); // Hijacking the main loop makes life easier on platforms like web +PEAK int peak_window_epoll(PeakWindow *win, PeakEvent *ev); // Poll a window for events. +PEAK uint32_t* peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height); // Get the windows backbuffer. +PEAK void peak_window_clear(PeakWindow *win, float r, float g, float b, float a); +PEAK void peak_window_present(PeakWindow *win); -/* Implemented by the platform */ -PEAK void peak_platform_window_open(); -PEAK void peak_platform_window_close(); -PEAK uint32_t *peak_platform_window_buffer(size_t *width, size_t *height); -PEAK bool peak_platform_epoll(PeakEvent *ev); +#endif // PEAK_H -/* - * He was wipping up amazing foods, like making happiness. - * Actual happiness? - * Actual happiness and joy. - * Oh the smell, it was so divine. - * He was making happiness!? - * Happiness in the kettle! - */ -#ifdef PEAK_REPLACE_MAIN -enum PeakReturn { PEAK_CONTINUE = 0, PEAK_STOP = 1 }; -extern int peak__main(int argc, char**argv); -extern void peak_events(PeakEvent ev); -extern void peak_tick(); -static bool running = true; -static inline void peak_stop() { running = false; } -#ifndef PEAK_WEB -#define main(...)\ -main(int argc, char **argv) {\ - assert(peak_events != 0 && "Declare peak_events() to handle window events.");\ - assert(peak_tick != 0 && "Declare peak_tick() to handle each frame ticks.");\ - peak_init();\ - int ret = peak__main(argc, argv);\ - if (ret != PEAK_CONTINUE) return ret;\ - PeakEvent ev;\ - while (running) {\ - while (running && peak_poll_events(&ev)) {\ - peak_events(ev);\ - }\ - peak_tick();\ - }\ - peak_quit();\ -}\ -int peak__main(__VA_ARGS__) -#else -#include -/* - * He was wipping up agony in the kettle. - * Actual pain? - * Boiling hatred in the kettle. - * The smell, it's pain. - * Boiling anger!? - * Yes, boiling anger. - */ +#ifdef PEAK_IMPLEMENTATION +#undef PEAK_IMPLEMENTATION -static inline void -peak__emscripten_loop_step(void *arg) +#if defined(PEAK_WIN32) || defined(PEAK_WEB) +#define PEAK_Q 64 + +typedef struct { + unsigned h, n; + PeakEvent e[PEAK_Q]; +} PeakQ; + +static void +peak_q_push(PeakQ *q, PeakEvent ev) { - (void)arg; - PeakEvent ev; - while (peak_poll_events(&ev)) { - peak_events(ev); - } - - if (running) { - peak_tick(); - } else { - emscripten_cancel_main_loop(); - peak_quit(); - } + if (!q || q->n == PEAK_Q) + return; + q->e[(q->h + q->n++) % PEAK_Q] = ev; } -#define main(...)\ - main(int argc, char **argv) {\ - _Static_assert(peak_events != 0, "When using peak_setup, declare peak_events() to handle window events.");\ - _Static_assert(peak_tick != 0, "When using peak_setup, declare peak_tick() to handle frame ticks.");\ - peak_init();\ - int ret = peak__main(argc, argv);\ - if (ret != PEAK_CONTINUE) return ret;\ - emscripten_set_main_loop_arg(peak__emscripten_loop_step, NULL, 0, 1);\ - return 0;\ - }\ - int peak__main(__VA_ARGS__) +static int +peak_q_pop(PeakQ *q, PeakEvent *ev) +{ + if (!q || !q->n) + return 0; + *ev = q->e[q->h]; + q->h = (q->h + 1) % PEAK_Q; + q->n--; + return 1; +} #endif -#endif // PEAK_DONT_REPLACE_MAIN - -#endif // PEAK_H - -#ifdef PEAK_IMPLEMENTATION -#undef PEAK_IMPLEMENTATION #if defined(PEAK_WIN32) /* --- Start of p_win32.c --- */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + #include +#include +#include +#include -void -peak_platform_window_open() +#define PEAK_WIN32_USER32 "user32.dll" +#define PEAK_WIN32_GDI32 "gdi32.dll" +#define PEAK_WIN32_CLASS "PeakWindow" +#define PEAK_WIN32_PROP "Peak" + +#define PEAK_USER32_API(X) \ + X(RegisterClassExA, ATOM, WINAPI, (const WNDCLASSEXA *)) \ + X(UnregisterClassA, BOOL, WINAPI, (LPCSTR, HINSTANCE)) \ + X(CreateWindowExA, HWND, WINAPI, (DWORD, LPCSTR, LPCSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID)) \ + X(DestroyWindow, BOOL, WINAPI, (HWND)) \ + X(ShowWindow, BOOL, WINAPI, (HWND, int)) \ + X(GetDC, HDC, WINAPI, (HWND)) \ + X(ReleaseDC, int, WINAPI, (HWND, HDC)) \ + X(PeekMessageA, BOOL, WINAPI, (LPMSG, HWND, UINT, UINT, UINT)) \ + X(TranslateMessage, BOOL, WINAPI, (const MSG *)) \ + X(DispatchMessageA, LRESULT, WINAPI, (const MSG *)) \ + X(DefWindowProcA, LRESULT, WINAPI, (HWND, UINT, WPARAM, LPARAM)) \ + X(SetPropA, BOOL, WINAPI, (HWND, LPCSTR, HANDLE)) \ + X(GetPropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(RemovePropA, HANDLE, WINAPI, (HWND, LPCSTR)) \ + X(BeginPaint, HDC, WINAPI, (HWND, LPPAINTSTRUCT)) \ + X(EndPaint, BOOL, WINAPI, (HWND, const PAINTSTRUCT *)) \ + X(AdjustWindowRectEx, BOOL, WINAPI, (LPRECT, DWORD, BOOL, DWORD)) \ + X(LoadCursorA, HCURSOR, WINAPI, (HINSTANCE, LPCSTR)) \ + X(GetKeyState, SHORT, WINAPI, (int)) + +#define PEAK_GDI32_API(X) \ + X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD)) + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_USER32_API(X) +#undef X +} PeakUser32Api; + +typedef struct { +#define X(name, ret, conv, args) ret (conv *name) args; + PEAK_GDI32_API(X) +#undef X +} PeakGdi32Api; + +typedef struct { + HMODULE user32; + HMODULE gdi32; + int class_reg; +} PeakWin32; + +struct peak_win32_win { + HWND hwnd; + HDC hdc; + uint32_t *buffer; + uint32_t width; + uint32_t height; + PeakQ q; +}; + +struct peak_window_internal_t { + struct peak_win32_win *w; +}; + +static PeakWin32 peak_win32; +static PeakUser32Api peak_user32; +static PeakGdi32Api peak_gdi32; + +static int +peak_internal_user32_load(HMODULE handle) { +#define X(name, ret, conv, args) peak_user32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_USER32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_user32.name + if (0 PEAK_USER32_API(X)) + return 0; +#undef X + return 1; } -void -peak_platform_window_close() +static int +peak_internal_gdi32_load(HMODULE handle) { +#define X(name, ret, conv, args) peak_gdi32.name = (ret (conv *) args)(void *)GetProcAddress(handle, #name); + PEAK_GDI32_API(X) +#undef X +#define X(name, ret, conv, args) || !peak_gdi32.name + if (0 PEAK_GDI32_API(X)) + return 0; +#undef X + return 1; } -uint32_t* -peak_platform_window_buffer(size_t *width, size_t *height) +static PeakKeyCode +peak_internal_win32_key_map(WPARAM vk) { + if (vk >= 'A' && vk <= 'Z') + return (PeakKeyCode)(PEAK_KEY_A + (int)(vk - 'A')); + switch (vk) { + case VK_UP: return PEAK_KEY_UP; + case VK_DOWN: return PEAK_KEY_DOWN; + case VK_LEFT: return PEAK_KEY_LEFT; + case VK_RIGHT: return PEAK_KEY_RIGHT; + case VK_SPACE: return PEAK_KEY_SPACE; + case VK_ESCAPE: return PEAK_KEY_ESCAPE; + case VK_RETURN: return PEAK_KEY_ENTER; + default: return PEAK_KEY_UNKNOWN; + } } -bool -peak_platform_epoll(PeakEvent *ev) +static PeakKeyMod +peak_internal_win32_mod_map(void) +{ + if (peak_user32.GetKeyState(VK_CONTROL) & 0x8000) return PEAK_KEYMOD_CTRL; + if (peak_user32.GetKeyState(VK_MENU) & 0x8000) return PEAK_KEYMOD_ALT; + if (peak_user32.GetKeyState(VK_SHIFT) & 0x8000) return PEAK_KEYMOD_SHIFT; + if (peak_user32.GetKeyState(VK_CAPITAL) & 1) return PEAK_KEYMOD_CAPS; + return (PeakKeyMod)0; +} + +static int +peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height) +{ + uint32_t *buffer; + + buffer = calloc((size_t)width * height, sizeof *buffer); + if (!buffer) + return 0; + free(w->buffer); + w->buffer = buffer; + w->width = width; + w->height = height; + return 1; +} + +static LRESULT CALLBACK +peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + struct peak_win32_win *w; + PeakEvent ev; + + if (msg == WM_NCCREATE) { + CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam; + peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams); + } + + w = (struct peak_win32_win *)peak_user32.GetPropA(hwnd, PEAK_WIN32_PROP); + if (!w) + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + + switch (msg) { + case WM_CLOSE: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_CLOSE; + peak_q_push(&w->q, ev); + return 0; + case WM_SIZE: { + uint32_t width, height; + if (wparam == SIZE_MINIMIZED) + return 0; + width = (uint32_t)LOWORD(lparam); + height = (uint32_t)HIWORD(lparam); + if (!width || !height || (width == w->width && height == w->height)) + return 0; + if (!peak_win32_buffer(w, width, height)) + return 0; + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_WINDOW_RESIZE; + ev.resize.width = w->width; + ev.resize.height = w->height; + peak_q_push(&w->q, ev); + return 0; + } + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + memset(&ev, 0, sizeof ev); + ev.type = (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev.key.key = peak_internal_win32_key_map(wparam); + ev.key.mod = peak_internal_win32_mod_map(); + peak_q_push(&w->q, ev); + return 0; + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + memset(&ev, 0, sizeof ev); + ev.type = PEAK_EVENT_POINTER; + ev.pointer.x = (float)(short)LOWORD(lparam); + ev.pointer.y = (float)(short)HIWORD(lparam); + if (msg == WM_MOUSEMOVE) { + ev.pointer.state = PEAK_POINTER_MOVED; + ev.pointer.type = (wparam & MK_RBUTTON) ? PEAK_POINTER_RIGHT : + (wparam & MK_MBUTTON) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) { + ev.pointer.state = PEAK_POINTER_PRESSED; + ev.pointer.type = (msg == WM_RBUTTONDOWN) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONDOWN) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } else { + ev.pointer.state = PEAK_POINTER_RELEASED; + ev.pointer.type = (msg == WM_RBUTTONUP) ? PEAK_POINTER_RIGHT : + (msg == WM_MBUTTONUP) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + } + peak_q_push(&w->q, ev); + return 0; + case WM_PAINT: { + PAINTSTRUCT ps; + peak_user32.BeginPaint(hwnd, &ps); + peak_user32.EndPaint(hwnd, &ps); + return 0; + } + case WM_ERASEBKGND: + return 1; + case WM_DESTROY: + peak_user32.RemovePropA(hwnd, PEAK_WIN32_PROP); + return 0; + default: + return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam); + } +} + +static int +peak_platform_init(void) { + WNDCLASSEXA wc; + + if (!peak_user32.CreateWindowExA) { + if (!(peak_win32.user32 = LoadLibraryA(PEAK_WIN32_USER32))) { + fputs("Failed to load user32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_user32_load(peak_win32.user32)) { + fputs("Failed to load user32 symbols", stderr); + return 0; + } + } + if (!peak_gdi32.StretchDIBits) { + if (!(peak_win32.gdi32 = LoadLibraryA(PEAK_WIN32_GDI32))) { + fputs("Failed to load gdi32.dll. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_gdi32_load(peak_win32.gdi32)) { + fputs("Failed to load gdi32 symbols", stderr); + return 0; + } + } + if (!peak_win32.class_reg) { + memset(&wc, 0, sizeof wc); + wc.cbSize = sizeof wc; + wc.style = CS_OWNDC; + wc.lpfnWndProc = peak_internal_win32_wndproc; + wc.hInstance = GetModuleHandleA(NULL); + wc.hCursor = peak_user32.LoadCursorA(NULL, IDC_ARROW); + wc.lpszClassName = PEAK_WIN32_CLASS; + if (!peak_user32.RegisterClassExA(&wc) && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) { + fputs("Failed to register window class", stderr); + return 0; + } + peak_win32.class_reg = 1; + } + return 1; +} + +static void +peak_platform_quit(void) +{ + if (!peak_win32.class_reg) + return; + peak_user32.UnregisterClassA(PEAK_WIN32_CLASS, GetModuleHandleA(NULL)); + peak_win32.class_reg = 0; +} + +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal intern = {0}; + struct peak_win32_win *w; + DWORD style, ex; + RECT r; + HINSTANCE inst; + + (void)flags; + if (!peak_user32.CreateWindowExA && !peak_platform_init()) + return intern; + + w = calloc(1, sizeof *w); + if (!w) + return intern; + if (!peak_win32_buffer(w, width, height)) { + free(w); + return intern; + } + + ex = 0; + style = WS_OVERLAPPEDWINDOW; + r.left = 0; + r.top = 0; + r.right = (LONG)width; + r.bottom = (LONG)height; + peak_user32.AdjustWindowRectEx(&r, style, FALSE, ex); + inst = GetModuleHandleA(NULL); + w->hwnd = peak_user32.CreateWindowExA(ex, PEAK_WIN32_CLASS, name, style, + CW_USEDEFAULT, CW_USEDEFAULT, r.right - r.left, r.bottom - r.top, + NULL, NULL, inst, w); + if (!w->hwnd) { + free(w->buffer); + free(w); + return intern; + } + + w->hdc = peak_user32.GetDC(w->hwnd); + if (!w->hdc) { + peak_user32.DestroyWindow(w->hwnd); + free(w->buffer); + free(w); + return intern; + } + + peak_user32.ShowWindow(w->hwnd, SW_SHOWNORMAL); + intern.w = w; + return intern; +} + +static void +peak_platform_window_close(PeakWindowInternal *intern) +{ + struct peak_win32_win *w; + if (!intern || !intern->w) + return; + w = intern->w; + if (w->hdc && w->hwnd) + peak_user32.ReleaseDC(w->hwnd, w->hdc); + if (w->hwnd) { + peak_user32.RemovePropA(w->hwnd, PEAK_WIN32_PROP); + peak_user32.DestroyWindow(w->hwnd); + } + free(w->buffer); + free(w); + intern->w = NULL; +} + +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + if (!w) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; +} + +static void +peak_platform_window_present(PeakWindowInternal *intern) +{ + struct peak_win32_win *w = intern ? intern->w : NULL; + BITMAPINFO bmi; + + if (!w || !w->hwnd || !w->hdc || !w->buffer) + return; + + memset(&bmi, 0, sizeof bmi); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = (LONG)w->width; + bmi.bmiHeader.biHeight = -(LONG)w->height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + peak_gdi32.StretchDIBits(w->hdc, + 0, 0, (int)w->width, (int)w->height, + 0, 0, (int)w->width, (int)w->height, + w->buffer, &bmi, DIB_RGB_COLORS, SRCCOPY); +} + +static bool +peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) +{ + MSG msg; + struct peak_win32_win *w = intern ? intern->w : NULL; + + if (!w || !w->hwnd) + return 0; + + while (peak_user32.PeekMessageA(&msg, w->hwnd, 0, 0, PM_REMOVE)) { + peak_user32.TranslateMessage(&msg); + peak_user32.DispatchMessageA(&msg); + if (peak_q_pop(&w->q, ev)) + return 1; + } + return peak_q_pop(&w->q, ev); } /* --- End of p_win32.c --- */ #elif defined(PEAK_LINUX) /* --- Start of p_linux.c --- */ -#include +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#endif + #include -#include #include +#include #include -#include +#include +#include #include -#include -#define PEAK_X11_LINUX "/usr/lib/libX11.so.6" - -#define PEAK_LINUX_DEFAULT_WIDTH 800 -#define PEAK_LINUX_DEFAULT_HEIGHT 600 - -typedef Display* (*XOpenDisplay_t)(const char*); -typedef int (*XCloseDisplay_t)(Display*); -typedef Window (*XCreateSimpleWindow_t)(Display*, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned long, unsigned long); -typedef int (*XStoreName_t)(Display*, Window, const char*); -typedef Atom (*XInternAtom_t)(Display*, const char*, Bool); -typedef Status (*XSetWMProtocols_t)(Display*, Window, Atom*, int); -typedef int (*XSelectInput_t)(Display*, Window, long); -typedef GC (*XCreateGC_t)(Display*, Window, unsigned long, XGCValues*); -typedef XImage* (*XCreateImage_t)(Display*, Visual*, unsigned int, int, int, char*, unsigned int, unsigned int, int, int); -typedef int (*XMapRaised_t)(Display*, Window); -typedef int (*XFlush_t)(Display*); -typedef int (*XFreeGC_t)(Display*, GC); -typedef int (*XDestroyWindow_t)(Display*, Window); -typedef int (*XPending_t)(Display*); -typedef int (*XNextEvent_t)(Display*, XEvent*); -typedef KeySym (*XLookupKeysym_t)(XKeyEvent*, int); -typedef int (*XPutImage_t)(Display*, Drawable, GC, XImage*, int, int, int, int, unsigned int, unsigned int); -typedef int (*XDefaultScreen_t)(Display*); -typedef Window (*XRootWindow_t)(Display*, int); -typedef unsigned long (*XBlackPixel_t)(Display*, int); -typedef Visual* (*XDefaultVisual_t)(Display*, int); -typedef int (*XDefaultDepth_t)(Display*, int); +#define PEAK_X11_LINUX "libX11.so.6" + +#define PEAK_X11_API(X) \ + X(XOpenDisplay, Display *, (const char *)) \ + X(XCloseDisplay, int, (Display *)) \ + X(XCreateSimpleWindow, Window, (Display *, Window, int, int, unsigned int, unsigned int, unsigned int, unsigned long, unsigned long)) \ + X(XStoreName, int, (Display *, Window, const char *)) \ + X(XInternAtom, Atom, (Display *, const char *, Bool)) \ + X(XSetWMProtocols, Status, (Display *, Window, Atom *, int)) \ + X(XSelectInput, int, (Display *, Window, long)) \ + X(XCreateGC, GC, (Display *, Window, unsigned long, XGCValues *)) \ + X(XCreateImage, XImage *, (Display *, Visual *, unsigned int, int, int, char *, unsigned int, unsigned int, int, int)) \ + X(XMapRaised, int, (Display *, Window)) \ + X(XFlush, int, (Display *)) \ + X(XFreeGC, int, (Display *, GC)) \ + X(XDestroyWindow, int, (Display *, Window)) \ + X(XCheckIfEvent, Bool, (Display *, XEvent *, Bool (*)(Display *, XEvent *, XPointer), XPointer)) \ + X(XLookupKeysym, KeySym, (XKeyEvent *, int)) \ + X(XPutImage, int, (Display *, Drawable, GC, XImage *, int, int, int, int, unsigned int, unsigned int)) typedef struct { - XOpenDisplay_t XOpenDisplay; - XCloseDisplay_t XCloseDisplay; - XCreateSimpleWindow_t XCreateSimpleWindow; - XStoreName_t XStoreName; - XInternAtom_t XInternAtom; - XSetWMProtocols_t XSetWMProtocols; - XSelectInput_t XSelectInput; - XCreateGC_t XCreateGC; - XCreateImage_t XCreateImage; - XMapRaised_t XMapRaised; - XFlush_t XFlush; - XFreeGC_t XFreeGC; - XDestroyWindow_t XDestroyWindow; - XPending_t XPending; - XNextEvent_t XNextEvent; - XLookupKeysym_t XLookupKeysym; - XPutImage_t XPutImage; - XDefaultScreen_t XDefaultScreen; - XRootWindow_t XRootWindow; - XBlackPixel_t XBlackPixel; - XDefaultVisual_t XDefaultVisual; - XDefaultDepth_t XDefaultDepth; +#define X(name, ret, args) ret (*name) args; + PEAK_X11_API(X) +#undef X } PeakX11Api; -static PeakX11Api peak_x11; - -static int peak_load_x11(void *handle) { - peak_x11.XOpenDisplay = (XOpenDisplay_t)dlsym(handle, "XOpenDisplay"); - peak_x11.XCloseDisplay = (XCloseDisplay_t)dlsym(handle, "XCloseDisplay"); - peak_x11.XCreateSimpleWindow = (XCreateSimpleWindow_t)dlsym(handle, "XCreateSimpleWindow"); - peak_x11.XStoreName = (XStoreName_t)dlsym(handle, "XStoreName"); - peak_x11.XInternAtom = (XInternAtom_t)dlsym(handle, "XInternAtom"); - peak_x11.XSetWMProtocols = (XSetWMProtocols_t)dlsym(handle, "XSetWMProtocols"); - peak_x11.XSelectInput = (XSelectInput_t)dlsym(handle, "XSelectInput"); - peak_x11.XCreateGC = (XCreateGC_t)dlsym(handle, "XCreateGC"); - peak_x11.XCreateImage = (XCreateImage_t)dlsym(handle, "XCreateImage"); - peak_x11.XMapRaised = (XMapRaised_t)dlsym(handle, "XMapRaised"); - peak_x11.XFlush = (XFlush_t)dlsym(handle, "XFlush"); - peak_x11.XFreeGC = (XFreeGC_t)dlsym(handle, "XFreeGC"); - peak_x11.XDestroyWindow = (XDestroyWindow_t)dlsym(handle, "XDestroyWindow"); - peak_x11.XPending = (XPending_t)dlsym(handle, "XPending"); - peak_x11.XNextEvent = (XNextEvent_t)dlsym(handle, "XNextEvent"); - peak_x11.XLookupKeysym = (XLookupKeysym_t)dlsym(handle, "XLookupKeysym"); - peak_x11.XPutImage = (XPutImage_t)dlsym(handle, "XPutImage"); - peak_x11.XDefaultScreen = (XDefaultScreen_t)dlsym(handle, "XDefaultScreen"); - peak_x11.XRootWindow = (XRootWindow_t)dlsym(handle, "XRootWindow"); - peak_x11.XBlackPixel = (XBlackPixel_t)dlsym(handle, "XBlackPixel"); - peak_x11.XDefaultVisual = (XDefaultVisual_t)dlsym(handle, "XDefaultVisual"); - peak_x11.XDefaultDepth = (XDefaultDepth_t)dlsym(handle, "XDefaultDepth"); - - if (!peak_x11.XOpenDisplay || !peak_x11.XCloseDisplay || !peak_x11.XCreateSimpleWindow || - !peak_x11.XStoreName || !peak_x11.XInternAtom || !peak_x11.XSetWMProtocols || - !peak_x11.XSelectInput || !peak_x11.XCreateGC || !peak_x11.XCreateImage || - !peak_x11.XMapRaised || !peak_x11.XFlush || - !peak_x11.XFreeGC || !peak_x11.XDestroyWindow || !peak_x11.XPending || - !peak_x11.XNextEvent || !peak_x11.XLookupKeysym || !peak_x11.XPutImage || - !peak_x11.XDefaultScreen || !peak_x11.XRootWindow || !peak_x11.XBlackPixel || - !peak_x11.XDefaultVisual || !peak_x11.XDefaultDepth) { - return 0; - } - return 1; -} - - typedef struct { - Display *display; - Atom wm_delete_window; - bool initialized; + Display *display; + Atom wm_delete_window; } PeakLinux; -typedef struct { - Window window; - GC gfx_ctx; /* GRAPHICS CONTEXT, not garbage collector lol */ -} PeakLinuxWindow; +struct peak_window_internal_t { + Window window; + GC gfx_ctx; + XImage *ximage; + uint32_t *buffer; + uint32_t width; + uint32_t height; +}; -static PeakLinux peak_linux = {0}; -static PeakLinuxWindow peak_linux_window = {0}; -static XImage *peak_linux_ximage = NULL; -static uint32_t *peak_linux_buffer = NULL; -static size_t peak_linux_width = PEAK_LINUX_DEFAULT_WIDTH; -static size_t peak_linux_height = PEAK_LINUX_DEFAULT_HEIGHT; +static PeakLinux peak_linux; +static PeakX11Api peak_x11; + +static int +peak_internal_x11_load(void *handle) +{ +#define X(name, ret, args) peak_x11.name = (ret (*) args)dlsym(handle, #name); + PEAK_X11_API(X) +#undef X +#define X(name, ret, args) || !peak_x11.name + if (0 PEAK_X11_API(X)) + return 0; +#undef X + return 1; +} static PeakKeyCode -peak__x11_map_key(KeySym sym) -{ - if (sym >= XK_a && sym <= XK_z) return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_a)); - if (sym >= XK_A && sym <= XK_Z) return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_A)); - switch (sym) { - case XK_Up: return PEAK_KEY_UP; - case XK_Down: return PEAK_KEY_DOWN; - case XK_Left: return PEAK_KEY_LEFT; - case XK_Right: return PEAK_KEY_RIGHT; - case XK_space: return PEAK_KEY_SPACE; - case XK_Escape: return PEAK_KEY_ESCAPE; - case XK_Return: return PEAK_KEY_ENTER; - default: return PEAK_KEY_UNKNOWN; - } +peak_internal_x11_key_map(KeySym sym) +{ + if (sym >= XK_a && sym <= XK_z) + return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_a)); + if (sym >= XK_A && sym <= XK_Z) + return (PeakKeyCode)(PEAK_KEY_A + (int)(sym - XK_A)); + switch (sym) { + case XK_Up: return PEAK_KEY_UP; + case XK_Down: return PEAK_KEY_DOWN; + case XK_Left: return PEAK_KEY_LEFT; + case XK_Right: return PEAK_KEY_RIGHT; + case XK_space: return PEAK_KEY_SPACE; + case XK_Escape: return PEAK_KEY_ESCAPE; + case XK_Return: return PEAK_KEY_ENTER; + default: return PEAK_KEY_UNKNOWN; + } } static PeakKeyMod -peak__x11_map_mod(unsigned int state) +peak_internal_x11_mod_map(unsigned int state) { - if (state & ControlMask) return PEAK_KEYMOD_CTRL; - if (state & Mod1Mask) return PEAK_KEYMOD_ALT; - if (state & ShiftMask) return PEAK_KEYMOD_SHIFT; - if (state & LockMask) return PEAK_KEYMOD_CAPS; - return (PeakKeyMod)0; + if (state & ControlMask) return PEAK_KEYMOD_CTRL; + if (state & Mod1Mask) return PEAK_KEYMOD_ALT; + if (state & ShiftMask) return PEAK_KEYMOD_SHIFT; + if (state & LockMask) return PEAK_KEYMOD_CAPS; + return (PeakKeyMod)0; } -void -peak_platform_window_open() +static Bool +peak_internal_x11_window_match(Display *dpy, XEvent *ev, XPointer arg) { - void *x11_handle = dlopen(PEAK_X11_LINUX, RTLD_LOCAL | RTLD_NOW); - assert(x11_handle && "Failed to load X11 library. What system are you fucking using and abusing?"); - - int success = peak_load_x11(x11_handle); - assert(success && "Failed to open X11 display"); - - peak_linux.display = peak_x11.XOpenDisplay(NULL); - assert(peak_linux.display && "Failed to open X11 display"); - - /* Who design this API??? */ - int screen = DefaultScreen(peak_linux.display); - peak_linux_window.window = peak_x11.XCreateSimpleWindow(peak_linux.display, RootWindow(peak_linux.display, screen), 0, 0, peak_linux_width, peak_linux_height, 0, BlackPixel(peak_linux.display, screen), BlackPixel(peak_linux.display, screen)); - peak_x11.XStoreName(peak_linux.display, peak_linux_window.window, "Peak"); - peak_linux.wm_delete_window = peak_x11.XInternAtom(peak_linux.display, "WM_DELETE_WINDOW", False); - peak_x11.XSetWMProtocols(peak_linux.display, peak_linux_window.window, &peak_linux.wm_delete_window, 1); - peak_x11.XSelectInput(peak_linux.display, peak_linux_window.window, ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask); - peak_linux_window.gfx_ctx = peak_x11.XCreateGC(peak_linux.display, peak_linux_window.window, 0, NULL); - peak_linux_buffer = (uint32_t *)calloc(peak_linux_width * peak_linux_height, sizeof(uint32_t)); - assert(peak_linux_buffer && "Failed to allocate framebuffer"); - - /* Wrap the buffer in an XImage for XPutImage blitting */ - Visual *visual = DefaultVisual(peak_linux.display, screen); - int depth = DefaultDepth(peak_linux.display, screen); - - peak_linux_ximage = peak_x11.XCreateImage(peak_linux.display, visual, depth, ZPixmap, 0, (char *)peak_linux_buffer, peak_linux_width, peak_linux_height, 32, 0); - assert(peak_linux_ximage && "Failed to create XImage"); - - peak_x11.XMapRaised(peak_linux.display, peak_linux_window.window); - peak_x11.XFlush(peak_linux.display); + (void)dpy; + return ev->xany.window == *(Window *)arg; +} - peak_linux.initialized = true; +static int +peak_linux_buffer(PeakWindowInternal *w, uint32_t width, uint32_t height) +{ + int screen; + + if (w->ximage) { + w->ximage->data = NULL; + XDestroyImage(w->ximage); + w->ximage = NULL; + } + free(w->buffer); + w->buffer = calloc((size_t)width * height, sizeof *w->buffer); + if (!w->buffer) + return 0; + + w->width = width; + w->height = height; + screen = DefaultScreen(peak_linux.display); + w->ximage = peak_x11.XCreateImage(peak_linux.display, + DefaultVisual(peak_linux.display, screen), + DefaultDepth(peak_linux.display, screen), + ZPixmap, 0, (char *)w->buffer, width, height, 32, 0); + if (!w->ximage) { + free(w->buffer); + w->buffer = NULL; + return 0; + } + return 1; } -void -peak_platform_window_close() +static int +peak_platform_init(void) { - if (!peak_linux.initialized) return; + void *handle; + + if (!peak_x11.XOpenDisplay) { + if (!(handle = dlopen(PEAK_X11_LINUX, RTLD_LOCAL | RTLD_NOW))) { + fputs("Failed to load X11 library. What system are you fucking using and abusing?", stderr); + return 0; + } + if (!peak_internal_x11_load(handle)) { + fputs("Failed to load X11 symbols", stderr); + return 0; + } + } + if (!peak_linux.display) { + if (!(peak_linux.display = peak_x11.XOpenDisplay(NULL))) { + fputs("Failed to open X11 display", stderr); + return 0; + } + peak_linux.wm_delete_window = peak_x11.XInternAtom(peak_linux.display, "WM_DELETE_WINDOW", False); + } + return 1; +} - if (peak_linux_ximage) { - peak_linux_ximage->data = NULL; - XDestroyImage(peak_linux_ximage); - peak_linux_ximage = NULL; - } +static void +peak_platform_quit(void) +{ + if (!peak_linux.display) + return; + peak_x11.XCloseDisplay(peak_linux.display); + peak_linux.display = 0; +} - free(peak_linux_buffer); - peak_linux_buffer = NULL; +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) +{ + PeakWindowInternal w = {0}; + int screen; + + (void)flags; + if (!peak_linux.display && !peak_platform_init()) + return w; + + screen = DefaultScreen(peak_linux.display); + w.window = peak_x11.XCreateSimpleWindow(peak_linux.display, + RootWindow(peak_linux.display, screen), 0, 0, width, height, 0, + BlackPixel(peak_linux.display, screen), BlackPixel(peak_linux.display, screen)); + peak_x11.XStoreName(peak_linux.display, w.window, name); + peak_x11.XSetWMProtocols(peak_linux.display, w.window, &peak_linux.wm_delete_window, 1); + peak_x11.XSelectInput(peak_linux.display, w.window, + KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | StructureNotifyMask); + w.gfx_ctx = peak_x11.XCreateGC(peak_linux.display, w.window, 0, NULL); + + if (!peak_linux_buffer(&w, width, height)) { + peak_x11.XFreeGC(peak_linux.display, w.gfx_ctx); + peak_x11.XDestroyWindow(peak_linux.display, w.window); + return (PeakWindowInternal){0}; + } + + peak_x11.XMapRaised(peak_linux.display, w.window); + peak_x11.XFlush(peak_linux.display); + return w; +} - peak_x11.XFreeGC(peak_linux.display, peak_linux_window.gfx_ctx); - peak_x11.XDestroyWindow(peak_linux.display, peak_linux_window.window); - peak_x11.XCloseDisplay(peak_linux.display); +static void +peak_platform_window_close(PeakWindowInternal *w) +{ + if (!w || !w->window || !peak_linux.display) + return; + if (w->ximage) { + w->ximage->data = NULL; + XDestroyImage(w->ximage); + } + free(w->buffer); + if (w->gfx_ctx) + peak_x11.XFreeGC(peak_linux.display, w->gfx_ctx); + peak_x11.XDestroyWindow(peak_linux.display, w->window); + *w = (PeakWindowInternal){0}; +} - peak_linux.initialized = false; +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *w, size_t *width, size_t *height) +{ + if (!w || !w->window) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; } -uint32_t * -peak_platform_window_buffer(size_t *width, size_t *height) -{ - *width = peak_linux_width; - *height = peak_linux_height; - return peak_linux_buffer; -} - -bool -peak_platform_epoll(PeakEvent *ev) -{ - if (!peak_linux.initialized) return false; - - Display *dpy = peak_linux.display; - - while (peak_x11.XPending(dpy) > 0) { - XEvent xev; - peak_x11.XNextEvent(dpy, &xev); - - switch (xev.type) { - - case ClientMessage: - if ((Atom)xev.xclient.data.l[0] == peak_linux.wm_delete_window) { - ev->type = PEAK_EVENT_WINDOW_CLOSE; - printf("CLLOOOOOOOSEEEE"); - return true; - } - continue; - - case KeyPress: - case KeyRelease: { - KeySym sym = peak_x11.XLookupKeysym(&xev.xkey, 0); - ev->type = (xev.type == KeyPress) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; - ev->key.key = peak__x11_map_key(sym); - ev->key.mod = peak__x11_map_mod(xev.xkey.state); - return true; - } - - case ButtonPress: - case ButtonRelease: { - ev->type = PEAK_EVENT_POINTER; - ev->pointer.state = (xev.type == ButtonPress) ? PEAK_POINTER_PRESSED : PEAK_POINTER_RELEASED; - ev->pointer.x = (float)xev.xbutton.x; - ev->pointer.y = (float)xev.xbutton.y; - switch (xev.xbutton.button) { - case Button1: ev->pointer.type = PEAK_POINTER_LEFT; break; - case Button2: ev->pointer.type = PEAK_POINTER_MIDDLE; break; - case Button3: ev->pointer.type = PEAK_POINTER_RIGHT; break; - default: ev->pointer.type = PEAK_POINTER_LEFT; break; - } - return true; - } - - case MotionNotify: - ev->type = PEAK_EVENT_POINTER; - ev->pointer.state = PEAK_POINTER_MOVED; - ev->pointer.x = (float)xev.xmotion.x; - ev->pointer.y = (float)xev.xmotion.y; - if (xev.xmotion.state & Button3Mask) - ev->pointer.type = PEAK_POINTER_RIGHT; - else if (xev.xmotion.state & Button2Mask) - ev->pointer.type = PEAK_POINTER_MIDDLE; - else - ev->pointer.type = PEAK_POINTER_LEFT; - return true; - - case ConfigureNotify: - if ((size_t)xev.xconfigure.width != peak_linux_width || - (size_t)xev.xconfigure.height != peak_linux_height) { - ev->type = PEAK_EVENT_WINDOW_RESIZE; - ev->resize.width = (uint32_t)xev.xconfigure.width; - ev->resize.height = (uint32_t)xev.xconfigure.height; - return true; - } - continue; - - case Expose: - if (xev.xexpose.count == 0) { - peak_x11.XPutImage(dpy, peak_linux_window.window, peak_linux_window.gfx_ctx, peak_linux_ximage, 0, 0, 0, 0, peak_linux_width, peak_linux_height); - } - continue; - - default: - continue; - } - } +static void +peak_platform_window_present(PeakWindowInternal *w) +{ + if (!w || !w->ximage || !peak_linux.display) + return; + peak_x11.XPutImage(peak_linux.display, w->window, w->gfx_ctx, w->ximage, + 0, 0, 0, 0, w->width, w->height); + peak_x11.XFlush(peak_linux.display); +} - peak_x11.XPutImage(dpy, peak_linux_window.window, peak_linux_window.gfx_ctx, peak_linux_ximage, 0, 0, 0, 0, peak_linux_width, peak_linux_height); - peak_x11.XFlush(dpy); - return false; +static bool +peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev) +{ + XEvent xev; + + if (!w || !w->window || !peak_linux.display) + return 0; + + while (peak_x11.XCheckIfEvent(peak_linux.display, &xev, peak_internal_x11_window_match, (XPointer)&w->window)) { + switch (xev.type) { + case ClientMessage: + if ((Atom)xev.xclient.data.l[0] == peak_linux.wm_delete_window) { + ev->type = PEAK_EVENT_WINDOW_CLOSE; + return 1; + } + continue; + case KeyPress: + case KeyRelease: + ev->type = (xev.type == KeyPress) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev->key.key = peak_internal_x11_key_map(peak_x11.XLookupKeysym(&xev.xkey, 0)); + ev->key.mod = peak_internal_x11_mod_map(xev.xkey.state); + return 1; + case ButtonPress: + case ButtonRelease: + ev->type = PEAK_EVENT_POINTER; + ev->pointer.state = (xev.type == ButtonPress) ? PEAK_POINTER_PRESSED : PEAK_POINTER_RELEASED; + ev->pointer.x = (float)xev.xbutton.x; + ev->pointer.y = (float)xev.xbutton.y; + ev->pointer.type = (xev.xbutton.button == Button2) ? PEAK_POINTER_MIDDLE : + (xev.xbutton.button == Button3) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + return 1; + case MotionNotify: + ev->type = PEAK_EVENT_POINTER; + ev->pointer.state = PEAK_POINTER_MOVED; + ev->pointer.x = (float)xev.xmotion.x; + ev->pointer.y = (float)xev.xmotion.y; + ev->pointer.type = (xev.xmotion.state & Button3Mask) ? PEAK_POINTER_RIGHT : + (xev.xmotion.state & Button2Mask) ? PEAK_POINTER_MIDDLE : PEAK_POINTER_LEFT; + return 1; + case ConfigureNotify: { + uint32_t width = (uint32_t)xev.xconfigure.width; + uint32_t height = (uint32_t)xev.xconfigure.height; + if (width == w->width && height == w->height) + continue; + if (!peak_linux_buffer(w, width, height)) + continue; + ev->type = PEAK_EVENT_WINDOW_RESIZE; + ev->resize.width = w->width; + ev->resize.height = w->height; + return 1; + } + default: + continue; + } + } + return 0; } /* --- End of p_linux.c --- */ #elif defined(PEAK_WEB) /* --- Start of p_emscripten.c --- */ +#include +#include #include #include -#include - -#define PEAK_CANVAS "#PeakCanvas" +#include -static PeakKeyCode map_key(const char *code); -static PeakKeyMod map_mod(const EmscriptenKeyboardEvent *keyEvent); -static EM_BOOL key_callback(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData); -static EM_BOOL mouse_callback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); -static EM_BOOL resize_callback(int eventType, const EmscriptenUiEvent *uiEvent, void *userData); +EM_JS(void, peak_web_dom_open, (const char *id, int w, int h), { + var name = UTF8ToString(id); + var c = document.getElementById(name); + if (!c) { + c = document.createElement('canvas'); + c.id = name; + document.body.appendChild(c); + } + c.width = w; + c.height = h; + c.tabIndex = 0; + c.focus(); +}); + +EM_JS(void, peak_web_dom_present, (const char *id, int w, int h, uintptr_t pixels), { + var c = document.getElementById(UTF8ToString(id)); + if (!c) return; + var ctx = c.getContext('2d'); + if (c.width !== w || c.height !== h) { + c.width = w; + c.height = h; + } + var img = ctx.createImageData(w, h); + img.data.set(HEAPU8.subarray(pixels, pixels + w * h * 4)); + ctx.putImageData(img, 0, 0); +}); + +struct peak_web_win { + char name[64]; + uint32_t width, height; + PeakQ q; + uint32_t buffer[]; +}; + +struct peak_window_internal_t { + struct peak_web_win *w; +}; + +static void +peak_web_sel(const char *name, char *out, size_t n) +{ + out[0] = '#'; + strncpy(out + 1, name, n - 2); + out[n - 1] = 0; +} -void -peak_platform_window_open() +static PeakKeyCode +peak_web_key_map(const char *code) { - emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, key_callback); - emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, key_callback); - emscripten_set_mousedown_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_mouseup_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_mousemove_callback(PEAK_CANVAS, NULL, EM_TRUE, mouse_callback); - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, EM_TRUE, resize_callback); + if (code[0] == 'K' && code[1] == 'e' && code[2] == 'y' && code[3] >= 'A' && code[3] <= 'Z' && code[4] == 0) + return (PeakKeyCode)(PEAK_KEY_A + (code[3] - 'A')); + if (!strcmp(code, "ArrowUp")) return PEAK_KEY_UP; + if (!strcmp(code, "ArrowDown")) return PEAK_KEY_DOWN; + if (!strcmp(code, "ArrowLeft")) return PEAK_KEY_LEFT; + if (!strcmp(code, "ArrowRight")) return PEAK_KEY_RIGHT; + if (!strcmp(code, "Space")) return PEAK_KEY_SPACE; + if (!strcmp(code, "Escape")) return PEAK_KEY_ESCAPE; + if (!strcmp(code, "Enter")) return PEAK_KEY_ENTER; + return PEAK_KEY_UNKNOWN; } -void -peak_platform_window_close() +static EM_BOOL +peak_web_key(int type, const EmscriptenKeyboardEvent *e, void *ud) { + PeakEvent ev = {0}; + ev.type = (type == EMSCRIPTEN_EVENT_KEYDOWN) ? PEAK_EVENT_KEY_DOWN : PEAK_EVENT_KEY_UP; + ev.key.key = peak_web_key_map(e->code); + ev.key.mod = e->ctrlKey ? PEAK_KEYMOD_CTRL : e->altKey ? PEAK_KEYMOD_ALT : e->shiftKey ? PEAK_KEYMOD_SHIFT : 0; + peak_q_push(&((struct peak_web_win *)ud)->q, ev); + return EM_TRUE; } -uint32_t* -peak_platform_window_buffer(size_t *width, size_t *height) +static EM_BOOL +peak_web_mouse(int type, const EmscriptenMouseEvent *e, void *ud) { + PeakEvent ev = {0}; + ev.type = PEAK_EVENT_POINTER; + ev.pointer.x = (float)e->targetX; + ev.pointer.y = (float)e->targetY; + if (type == EMSCRIPTEN_EVENT_MOUSEDOWN) + ev.pointer.state = PEAK_POINTER_PRESSED; + else if (type == EMSCRIPTEN_EVENT_MOUSEUP) + ev.pointer.state = PEAK_POINTER_RELEASED; + else + ev.pointer.state = PEAK_POINTER_MOVED; + if (type == EMSCRIPTEN_EVENT_MOUSEMOVE) { + ev.pointer.type = (e->buttons & 4) ? PEAK_POINTER_MIDDLE : + (e->buttons & 2) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + } else { + ev.pointer.type = (e->button == 1) ? PEAK_POINTER_MIDDLE : + (e->button == 2) ? PEAK_POINTER_RIGHT : PEAK_POINTER_LEFT; + } + peak_q_push(&((struct peak_web_win *)ud)->q, ev); + return EM_TRUE; } -bool -peak_platform_epoll(PeakEvent *ev) +static void +peak_web_listen(struct peak_web_win *w, int on) { - return 0; + char sel[66]; + peak_web_sel(w->name, sel, sizeof sel); + emscripten_set_keydown_callback(sel, w, EM_TRUE, on ? peak_web_key : NULL); + emscripten_set_keyup_callback(sel, w, EM_TRUE, on ? peak_web_key : NULL); + emscripten_set_mousedown_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); + emscripten_set_mouseup_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); + emscripten_set_mousemove_callback(sel, w, EM_TRUE, on ? peak_web_mouse : NULL); } -/* --- End of p_emscripten.c --- */ -#endif -/* --- Start of peak.c --- */ -static inline void peak__put_pixel(int x, int y, uint32_t color); -static inline uint32_t peak__lerp_color(uint32_t c0, uint32_t c1, float t); -static inline float peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy); - -static uint32_t *peak_window_buffer; -static size_t peak_window_width; -static size_t peak_window_height; -static int peak_clip_x; -static int peak_clip_y; -static size_t peak_clip_width; -static size_t peak_clip_height; +static int +peak_platform_init(void) +{ + return 1; +} -void -peak__put_pixel(int x, int y, uint32_t color) +static void +peak_platform_quit(void) { - if (x < peak_clip_x || x >= (int)(peak_clip_x + peak_clip_width) || - y < peak_clip_y || y >= (int)(peak_clip_y + peak_clip_height)) { - return; - } - if (x < 0 || x >= (int)peak_window_width || y < 0 || y >= (int)peak_window_height) { - return; - } - peak_window_buffer[y * peak_window_width + x] = color; } -uint32_t -peak__lerp_color(uint32_t c0, uint32_t c1, float t) +static PeakWindowInternal +peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) { - if (t <= 0.0f) return c0; - if (t >= 1.0f) return c1; + PeakWindowInternal intern = {0}; + struct peak_web_win *w; - uint32_t a0 = (c0 >> 24) & 0xFF, a1 = (c1 >> 24) & 0xFF; - uint32_t r0 = (c0 >> 16) & 0xFF, r1 = (c1 >> 16) & 0xFF; - uint32_t g0 = (c0 >> 8) & 0xFF, g1 = (c1 >> 8) & 0xFF; - uint32_t b0 = c0 & 0xFF, b1 = c1 & 0xFF; + (void)flags; - uint32_t a = (uint32_t)(a0 + t * ((float)a1 - (float)a0)); - uint32_t r = (uint32_t)(r0 + t * ((float)r1 - (float)r0)); - uint32_t g = (uint32_t)(g0 + t * ((float)g1 - (float)g0)); - uint32_t b = (uint32_t)(b0 + t * ((float)b1 - (float)b0)); + w = calloc(1, sizeof *w + (size_t)width * height * sizeof *w->buffer); + if (!w) + return intern; + strncpy(w->name, name, sizeof w->name - 1); + w->width = width; + w->height = height; - return (a << 24) | (r << 16) | (g << 8) | b; -} + peak_web_dom_open(w->name, (int)width, (int)height); -float -peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy) -{ - return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax); + peak_web_listen(w, 1); + intern.w = w; + return intern; } -void -peak_init(void) +static void +peak_platform_window_close(PeakWindowInternal *intern) { - peak_platform_window_open(); - peak_window_buffer = peak_platform_window_buffer(&peak_window_width, &peak_window_height); - peak_clip_x = 0; - peak_clip_y = 0; - peak_clip_width = peak_window_width; - peak_clip_height = peak_window_height; + struct peak_web_win *w; + if (!intern || !intern->w) + return; + w = intern->w; + peak_web_listen(w, 0); + free(w); + intern->w = NULL; } -void -peak_quit(void) +static uint32_t * +peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height) { - peak_platform_window_close(); + struct peak_web_win *w = intern ? intern->w : NULL; + if (!w) { + *width = 0; + *height = 0; + return NULL; + } + *width = w->width; + *height = w->height; + return w->buffer; } -void -peak_extensions(void) +static void +peak_platform_window_present(PeakWindowInternal *intern) { - // TODO + struct peak_web_win *w = intern ? intern->w : NULL; + if (!w) + return; + peak_web_dom_present(w->name, (int)w->width, (int)w->height, (uintptr_t)w->buffer); } -bool -peak_poll_events(PeakEvent *ev) +static bool +peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev) { - return peak_platform_epoll(ev); + struct peak_web_win *w = intern ? intern->w : NULL; + return w ? peak_q_pop(&w->q, ev) : 0; } +/* --- End of p_emscripten.c --- */ +#endif -void -peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height) -{ - for (size_t y = 0; y < height; ++y) { - int target_y = offset_y + (int)y; - if (target_y < peak_clip_y || target_y >= (int)(peak_clip_y + peak_clip_height) || - target_y < 0 || target_y >= (int)peak_window_height) continue; - - int start_x = offset_x < peak_clip_x ? peak_clip_x : offset_x; - int end_x = (offset_x + (int)width) > (int)(peak_clip_x + peak_clip_width) - ? (int)(peak_clip_x + peak_clip_width) - : (offset_x + (int)width); +/* We define PeakWindow only when we know the type of peak_window_internal_t */ +struct PeakWindow { + PeakWindowInternal internal; + int (*tick)(struct PeakWindow *win, void *userdata); + void *userdata; + uint32_t *buffer; + uint32_t width; + uint32_t height; + uint32_t bufsize; + int running; +}; - if (start_x >= end_x) continue; - size_t src_offset_x = (size_t)(start_x - offset_x); - size_t copy_pixels = (size_t)(end_x - start_x); +/* --- Start of peak.c --- */ +static uint32_t * +peak_window_sync(PeakWindow *win, size_t *width, size_t *height) +{ + size_t w = 0, h = 0; + win->buffer = peak_platform_window_buffer(&win->internal, &w, &h); + win->width = (uint32_t)w; + win->height = (uint32_t)h; + win->bufsize = win->width * win->height; + if (width) *width = w; + if (height) *height = h; + return win->buffer; +} - memcpy(&peak_window_buffer[target_y * peak_window_width + start_x], &rgba[y * width + src_offset_x], copy_pixels * sizeof(uint32_t)); - } +int +peak_init(void) +{ + return peak_platform_init(); } void -peak_clip(int x, int y, size_t w, size_t h) +peak_quit(void) { - peak_clip_x = x; - peak_clip_y = y; - peak_clip_width = w; - peak_clip_height = h; + peak_platform_quit(); } -void -peak_clip_reset(void) +PeakWindow +peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags) { - peak_clip_x = 0; - peak_clip_y = 0; - peak_clip_width = peak_window_width; - peak_clip_height = peak_window_height; + PeakWindow win = {0}; + if (!name || !name[0]) name = "Peak"; + if (!width) width = 800; + if (!height) height = 600; + win.internal = peak_platform_window_open(name, width, height, flags); + if (!peak_window_sync(&win, NULL, NULL)) return win; + win.running = 1; + return win; } void -peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color) -{ - int start_x = (x < peak_clip_x) ? peak_clip_x : x; - int start_y = (y < peak_clip_y) ? peak_clip_y : y; - int end_x = (x + (int)w > (int)(peak_clip_x + peak_clip_width)) ? (int)(peak_clip_x + peak_clip_width) : x + (int)w; - int end_y = (y + (int)h > (int)(peak_clip_y + peak_clip_height)) ? (int)(peak_clip_y + peak_clip_height) : y + (int)h; - - for (int py = start_y; py < end_y; ++py) { - if (py < 0 || py >= (int)peak_window_height) continue; - for (int px = start_x; px < end_x; ++px) { - if (px < 0 || px >= (int)peak_window_width) continue; - peak_window_buffer[py * peak_window_width + px] = color; - } - } +peak_window_close(PeakWindow *win) +{ + win->running = 0; + peak_platform_window_close(&win->internal); + win->buffer = NULL; + win->width = 0; + win->height = 0; + win->bufsize = 0; } -void -peak_draw_rectangle_gradient(int x, int y, size_t w, size_t h, uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3) -{ - for (size_t ry = 0; ry < h; ++ry) { - float ty = (h > 1) ? (float)ry / (float)(h - 1) : 0.0f; - uint32_t left_color = peak__lerp_color(c0, c3, ty); - uint32_t right_color = peak__lerp_color(c1, c2, ty); - - for (size_t rx = 0; rx < w; ++rx) { - float tx = (w > 1) ? (float)rx / (float)(w - 1) : 0.0f; - uint32_t final_color = peak__lerp_color(left_color, right_color, tx); - peak__put_pixel(x + (int)rx, y + (int)ry, final_color); - } - } +int +peak_window_epoll(PeakWindow *win, PeakEvent *ev) +{ + int got = peak_platform_epoll(&win->internal, ev); + if (got && ev->type == PEAK_EVENT_WINDOW_RESIZE) + peak_window_sync(win, NULL, NULL); + return got; } -void -peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color) -{ - int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); - int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); - int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); - int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); - - float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); - if (area == 0.0f) return; - - for (int y = min_y; y <= max_y; ++y) { - for (int x = min_x; x <= max_x; ++x) { - float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y); - float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y); - float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y); - - if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) { - peak__put_pixel(x, y, color); - } - } - } +uint32_t * +peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height) +{ + return peak_window_sync(win, width, height); } void -peak_draw_triangle_gradient(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t c0, uint32_t c1, uint32_t c2) -{ - int min_x = (x0 < x1) ? ((x0 < x2) ? x0 : x2) : ((x1 < x2) ? x1 : x2); - int max_x = (x0 > x1) ? ((x0 > x2) ? x0 : x2) : ((x1 > x2) ? x1 : x2); - int min_y = (y0 < y1) ? ((y0 < y2) ? y0 : y2) : ((y1 < y2) ? y1 : y2); - int max_y = (y0 > y1) ? ((y0 > y2) ? y0 : y2) : ((y1 > y2) ? y1 : y2); - - float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2); - if (area == 0.0f) return; - - for (int y = min_y; y <= max_y; ++y) { - for (int x = min_x; x <= max_x; ++x) { - float w0 = peak__edge_cross((float)x1, (float)y1, (float)x2, (float)y2, (float)x, (float)y) / area; - float w1 = peak__edge_cross((float)x2, (float)y2, (float)x0, (float)y0, (float)x, (float)y) / area; - float w2 = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x, (float)y) / area; - - if (w0 >= 0.0f && w1 >= 0.0f && w2 >= 0.0f) { - uint32_t col_a = peak__lerp_color(c0, c1, w1 / (w0 + w1 + 1e-6f)); - uint32_t final_color = peak__lerp_color(col_a, c2, w2); - peak__put_pixel(x, y, final_color); - } - } - } +peak_window_clear(PeakWindow *win, float r, float g, float b, float a) +{ + uint32_t c, i; + if (!win || !win->buffer) return; +#if defined(PEAK_WEB) + /* ImageData RGBA bytes = LE 0xAABBGGRR */ + c = (uint32_t)(r * 255.f) + | ((uint32_t)(g * 255.f) << 8) + | ((uint32_t)(b * 255.f) << 16) + | ((uint32_t)(a * 255.f) << 24); +#else + c = ((uint32_t)(a * 255.f) << 24) + | ((uint32_t)(r * 255.f) << 16) + | ((uint32_t)(g * 255.f) << 8) + | (uint32_t)(b * 255.f); +#endif + for (i = 0; i < win->bufsize; i++) + win->buffer[i] = c; } void -peak_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color) -{ - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - - int half_w = (int)width / 2; - - while (1) { - /* Draw square footprint for line thickness */ - for (int ox = -half_w; ox <= half_w; ++ox) { - for (int oy = -half_w; oy <= half_w; ++oy) { - peak__put_pixel(x0 + ox, y0 + oy, color); - } - } - if (x0 == x1 && y0 == y1) break; - e2 = 2 * err; - if (e2 >= dy) { err += dy; x0 += sx; } - if (e2 <= dx) { err += dx; y0 += sy; } - } +peak_window_present(PeakWindow *win) +{ + if (win) peak_platform_window_present(&win->internal); } -void -peak_draw_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1) -{ - float total_dist = sqrtf((float)((x1 - x0)*(x1 - x0) + (y1 - y0)*(y1 - y0))); - int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; - int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; - int err = dx + dy, e2; - - int orig_x0 = x0, orig_y0 = y0; - int half_w = (int)width / 2; - - while (1) { - float current_dist = sqrtf((float)((x0 - orig_x0)*(x0 - orig_x0) + (y0 - orig_y0)*(y0 - orig_y0))); - float t = (total_dist > 0.0f) ? (current_dist / total_dist) : 0.0f; - uint32_t color = peak__lerp_color(c0, c1, t); - - for (int ox = -half_w; ox <= half_w; ++ox) { - for (int oy = -half_w; oy <= half_w; ++oy) { - peak__put_pixel(x0 + ox, y0 + oy, color); - } - } - - if (x0 == x1 && y0 == y1) break; - e2 = 2 * err; - if (e2 >= dy) { err += dy; x0 += sx; } - if (e2 <= dx) { err += dx; y0 += sy; } +#ifdef PEAK_WEB +#include +static void +peak_internal_web_step(void *arg) +{ + PeakWindow *win = arg; + if (!win->tick(win, win->userdata) || !win->running) { + win->running = 0; + emscripten_cancel_main_loop(); } } +#endif +void +peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata) +{ + assert(win && "peak_window_run needs a window"); + assert(peak_tick && "peak_window_run needs tick callback"); + win->tick = peak_tick; + win->userdata = userdata; + win->running = 1; +#ifdef PEAK_WEB + emscripten_set_main_loop_arg(peak_internal_web_step, win, 0, 1); +#else + while (win->running && win->tick(win, win->userdata)); +#endif +} /* --- End of peak.c --- */ #endif // PEAK_IMPLEMENTATION diff --git a/Podium/README.md b/Podium/README.md deleted file mode 100644 index fccb70d..0000000 --- a/Podium/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Podium - -Single header platform library with batteries included. - -NOTE TO SELF: Stop trying to redesign your own library! -This shit works! Podium stands on business!!! - -The library does it's best to auto-detect your operating system -and the include the correct OS code for that platform. diff --git a/Podium/p_allocators.c b/Podium/p_allocators.c deleted file mode 100644 index 7e59807..0000000 --- a/Podium/p_allocators.c +++ /dev/null @@ -1,301 +0,0 @@ -PODEF bool -is_power_of_two(uintptr x) { - return (x & (x-1)) == 0; -} - -PODEF uintptr -align_forward(uintptr ptr, usize align) -{ - uintptr p, a, remainder; - assert(is_power_of_two(align)); - - p = ptr; - a = (uintptr)align; - remainder = p & (a-1); - - return (remainder != 0) ? p + a - remainder : p; -} - -PODEF void -p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length) -{ - a->buf = (unsigned char *)backing_buffer; - a->buf_len = backing_buffer_length; - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void* -p_arena_alloc_align(Arena *a, usize s, usize align) -{ - - // Align 'curr_offset' forward to the specified alignment - uintptr curr_ptr = (uintptr)a->buf + (uintptr)a->curr_offset; - uintptr offset = align_forward(curr_ptr, align); - offset -= (uintptr)a->buf; // Change to relative offset - - // Check to see if the backing memory has space left - if (offset+s <= a->buf_len) { - void *ptr = &a->buf[offset]; - a->prev_offset = offset; - a->curr_offset = offset+s; - - memset(ptr, 0, s); - return ptr; - } - - return NULL; -} - -PODEF void* -p_arena_alloc(Arena *a, usize s) { - return p_arena_alloc_align(a, s, DEFAULT_ALIGNMENT); -} - -PODEF void* -p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align) -{ - assert(is_power_of_two(align)); - unsigned char *old_mem = (unsigned char *)old_memory; - - if (old_mem == NULL || old_size == 0) { - return p_arena_alloc_align(a, new_size, align); - } else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) { - if (a->buf+a->prev_offset == old_mem) { - a->curr_offset = a->prev_offset + new_size; - if (new_size > old_size) { - // Zero the new memory by default - memset(&a->buf[a->curr_offset], 0, new_size-old_size); - } - return old_memory; - } else { - void *new_memory = p_arena_alloc_align(a, new_size, align); - size_t copy_size = old_size < new_size ? old_size : new_size; - memmove(new_memory, old_memory, copy_size); - return new_memory; - } - - } else { - assert(0 && "Memory is out of bounds of the buffer in this arena"); - return NULL; - } -} - -PODEF void* -p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size) -{ - return p_arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT); -} - -PODEF void -p_arena_free_all(Arena *a) -{ - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void -p_arena_destroy(Arena* a) -{ - a->buf_len = 0; - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void -p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length) -{ - s->buf = (unsigned char*) backing_buffer; - s->buf_len = backing_buffer_length; - s->offset = 0; -} - -PODEF usize -calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size) -{ - uintptr p, a, modulo, padding, needed_space; - - assert(is_power_of_two(alignment)); - - p = ptr; - a = alignment; - modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two - - padding = 0; - needed_space = 0; - - if (modulo != 0) { // Same logic as 'align_forward' - padding = a - modulo; - } - - needed_space = (uintptr)header_size; - - if (padding < needed_space) { - needed_space -= padding; - - if ((needed_space & (a-1)) != 0) { - padding += a * (1+(needed_space/a)); - } else { - padding += a * (needed_space/a); - } - } - - return (usize)padding; -} - -PODEF void* -p_stack_alloc(Stack *s, usize len, usize alignment) -{ - uintptr curr_addr, next_addr; - usize padding; - Stack_Allocation_Header *header; - - assert(is_power_of_two(alignment)); - - if (alignment > 128) { - // As the padding is 8 bits (1 byte), the largest alignment that can - // be used is 128 bytes - alignment = 128; - } - - curr_addr = (uintptr)s->buf + (uintptr)s->offset; - padding = calc_padding_with_header(curr_addr, (uintptr)alignment, sizeof(Stack_Allocation_Header)); - if (s->offset + padding + len > s->buf_len) { - // Stack allocator is out of memory - return NULL; - } - s->offset += padding; - - next_addr = curr_addr + (uintptr)padding; - header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header)); - header->padding = (u8)padding; - - s->offset += len; - - return memset((void *)next_addr, 0, len); -} - -PODEF void -p_stack_free(Stack *s, void *ptr) -{ - if (ptr != NULL) { - uintptr start, end, curr_addr; - Stack_Allocation_Header *header; - usize prev_offset; - - start = (uintptr)s->buf; - end = start + (uintptr)s->buf_len; - curr_addr = (uintptr)ptr; - - if (!(start <= curr_addr && curr_addr < end)) { - assert(0 && "Out of bounds memory address passed to stack allocator (free)"); - return; - } - - if (curr_addr >= start+(uintptr)s->offset) { - // Allow double frees - return; - } - - header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header)); - prev_offset = (usize)(curr_addr - (uintptr)header->padding - start); - - s->offset = prev_offset; - } -} - -PODEF void -p_stack_free_all(Stack* s) -{ - s->offset = 0; -} - -PODEF void -p_stack_destroy(Stack* s) -{ - s->buf = 0; - s->buf_len = 0; - s->offset = 0; -} - -PODEF void -p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align) -{ - /* align start */ - uintptr start = (uintptr) buf; - uintptr aligned = align_forward(start, chunk_align); - buf_len -= (aligned - start); - - /* align chunk usize */ - chunk_size = (usize) align_forward(chunk_size, chunk_align); - - assert(chunk_size >= sizeof(Pool_Free_Node) && "Chunk usize is too small"); - assert(buf_len >= chunk_size && "Backing buffer length is smaller than the chunk size"); - - p->buf = (unsigned char *) buf; - p->pool_size = buf_len; - p->chunk_size = chunk_size; - p->head = NULL; - - p_pool_free_all(p); -} - -PODEF void* -p_pool_alloc(Pool *p) -{ - Pool_Free_Node *node = p->head; - - assert(node != NULL && "Pool allocator has no free memory"); - - p->head = p->head->next; - - return memset(node, 0 , p->chunk_size); -} - -PODEF void -p_pool_free(Pool *p, void *ptr) -{ - Pool_Free_Node *node; - - void *start = p->buf; - void *end = &p->buf[p->pool_size]; - - if (ptr == NULL) { - return; - } - - if (!(start <= ptr && ptr < end)) { - assert(0 && "Memory is out of bounds of the buffer in this pool"); - return; - } - - node = (Pool_Free_Node *)ptr; - node->next = p->head; - p->head = node; -} - -PODEF void -p_pool_free_all(Pool *p) -{ - usize chunk_count = p->pool_size / p->chunk_size; - usize i; - - for (i = 0; i < chunk_count; i++) { - void *ptr = &p->buf[i * p->chunk_size]; - Pool_Free_Node *node = (Pool_Free_Node *)ptr; - - // Push free node onto thte free list - node->next = p->head; - p->head = node; - } -} - -PODEF void -p_pool_destroy(Pool *p) -{ - p->buf = NULL; - p->pool_size = 0; - p->chunk_size = 0; - p->head = NULL; -} - diff --git a/Podium/p_allocators.h b/Podium/p_allocators.h deleted file mode 100644 index 72bae56..0000000 --- a/Podium/p_allocators.h +++ /dev/null @@ -1,44 +0,0 @@ -typedef struct Arena { - unsigned char *buf; - usize buf_len; - usize curr_offset; - usize prev_offset; -} Arena; - -PODEF void p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length); -PODEF void* p_arena_alloc_align(Arena *a, usize s, usize align); -PODEF void* p_arena_alloc(Arena *a, usize s); -PODEF void* p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align); -PODEF void* p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size); -PODEF void p_arena_free_all(Arena *a); -PODEF void p_arena_destroy(Arena* a); - -typedef struct { u8 padding; } Stack_Allocation_Header; -typedef struct Stack { - unsigned char *buf; - usize buf_len; - usize offset; -} Stack; - -PODEF void p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length); -PODEF usize calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size); -PODEF void* p_stack_alloc(Stack *s, usize len, usize alignment); -PODEF void p_stack_free(Stack *s, void *ptr); -PODEF void p_stack_free_all(Stack* s); -PODEF void p_stack_destroy(Stack* s); - -typedef struct Pool_Free_Node Pool_Free_Node; - -typedef struct Pool { - unsigned char *buf; - usize chunk_size; - usize pool_size; - Pool_Free_Node *head; -} Pool; - -struct Pool_Free_Node { struct Pool_Free_Node *next; }; -PODEF void p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align); -PODEF void* p_pool_alloc(Pool *p); -PODEF void p_pool_free(Pool *p, void *ptr); -PODEF void p_pool_free_all(Pool *p); -PODEF void p_pool_destroy(Pool *p); diff --git a/Podium/p_ds.h b/Podium/p_ds.h deleted file mode 100644 index 8731975..0000000 --- a/Podium/p_ds.h +++ /dev/null @@ -1,52 +0,0 @@ -#define DEFAULT_ALIGNMENT (2 * sizeof(void *)) -#define ARRAY_GROW_FACTOR 2 - -typedef struct { - u32 size; - u32 capacity; -} DynamicArrayHeader; - -#define DARRAY_INIT_CAPACITY 64 -#define p_darray_push(array, var) \ - do { \ - if (array == NULL) { \ - DynamicArrayHeader *header = \ - malloc(DARRAY_INIT_CAPACITY * sizeof *array + sizeof *header); \ - header->size = 0; \ - header->capacity = DARRAY_INIT_CAPACITY; \ - array = (void *)(header + 1); \ - } \ - DynamicArrayHeader *header = (DynamicArrayHeader *)(array) - 1; \ - if (header->size >= header->capacity - 1) { \ - unsigned long new_capacity = header->capacity * 2; \ - DynamicArrayHeader *new_header = \ - realloc(header, new_capacity * sizeof *array + sizeof *header); \ - if (!new_header) \ - break; \ - header = new_header; \ - array = (void *)(header + 1); \ - header->capacity = new_capacity; \ - } \ - (array)[header->size++] = var; \ - } while (0) - -#define p_darray_len(array) ((array) ? ((DynamicArrayHeader *)(array) - 1)->size : 0) -#define p_darray_destroy(array) \ -{ \ - free((DynamicArrayHeader *)(array) - 1); \ -} - -#define p_darray_shrink(array) \ - do { \ - DynamicArrayHeader *header = (array) ? ((DynamicArrayHeader *)(array) - 1) : 0;\ - if (header) {\ - if (header->capacity > 4) {\ - unsigned long new_capacity = (header->capacity >> 1) + (header->capacity & 1); \ - void *new_header = realloc(header, new_capacity * sizeof *array + sizeof *header); \ - if (!new_header) break; \ - header = new_header; \ - array = (void *)(header + 1); \ - header->capacity = new_capacity; \ - }\ - }\ - } while (0) diff --git a/Podium/p_log.h b/Podium/p_log.h deleted file mode 100644 index 0f6ce6c..0000000 --- a/Podium/p_log.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Logging! - */ - -#include - -#ifdef DEBUG -#define LOG_DEBUG(...) PRINTF(__VA_ARGS__) -#else -#define LOG_DEBUG(...) ((void)0) -#endif - -/* fatal and error always enabled */ -#define PFATAL(message, ...) p_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) -#define PERROR(message, ...) p_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) - -/* warn and info enabled by default but can be disabled */ -#define P_LOG_WARN_ENABLED 1 -#define P_LOG_INFO_ENABLED 1 - -#if P_LOG_WARN_ENABLED == 1 -#define PWARN(message, ...) p_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) -#else -#define PWARN(message, ...) -#endif - -#if P_LOG_INFO_ENABLED == 1 -#define PINFO(message, ...) p_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) -#else -#define PINFO(message, ...) -#endif - -#if P_LOG_DEBUG_ENABLED == 1 -#define PDEBUG(message, ...) p_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) -#else -#define PDEBUG(message, ...) -#endif - -#if P_LOG_TRACE_ENABLED == 1 -#define PTRACE(message, ...) p_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) -#else -#define PTRACE(message, ...) -#endif - -typedef enum P_Log_Level { - P_LOG_LEVEL_FATAL = 0, - P_LOG_LEVEL_ERROR, - P_LOG_LEVEL_WARN, - P_LOG_LEVEL_INFO, - P_LOG_LEVEL_DEBUG, - P_LOG_LEVEL_TRACE, - P_COUNT_LOG_LEVEL -} P_Log_Level; - -#define P_PREFIX_LEN 7 -static const char *p_prefix[P_COUNT_LOG_LEVEL] = { - [P_LOG_LEVEL_FATAL] = "[FATAL]", - [P_LOG_LEVEL_ERROR] = "[ERROR]", - [P_LOG_LEVEL_WARN] = "[WARNI]", - [P_LOG_LEVEL_INFO] = "[INFOR]", - [P_LOG_LEVEL_DEBUG] = "[DEBUG]", - [P_LOG_LEVEL_TRACE] = "[TRACE]", -}; - -#define P_MAX_ALLOCS 512 - -typedef struct P_DebugMemoryInfo { - void *ptr; - size_t size; - const char *file; - const char *func; - int line; -} P_DebugMemoryInfo; - -static uint64_t p_alloc_count = 0; -static P_DebugMemoryInfo p_ptr_array[P_MAX_ALLOCS]; - -static inline void * -p_debug_malloc_impl(size_t size, const char *file, int line, const char *func) -{ - void *ptr = malloc(size); - printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); - - if (ptr) { - if (p_alloc_count < P_MAX_ALLOCS) { - p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ - .ptr = ptr, - .size = size, - .file = file, - .func = func, - .line = line - }; - } else { - fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", P_MAX_ALLOCS); - } - } - return ptr; -} - -static inline void -p_debug_free_impl(void *ptr, const char *file, int line, const char *func) -{ - printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); - - if (!ptr) return; // Standard C allows free(NULL) - - bool found = false; - uint64_t index = 0; - - /* Find allocation metadata */ - for (index = 0; index < p_alloc_count; ++index) { - if (p_ptr_array[index].ptr == ptr) { - found = true; - break; - } - } - - if (found) { - /* Shift array left to remove tracked struct */ - for (uint64_t j = index; j < p_alloc_count - 1; ++j) { - p_ptr_array[j] = p_ptr_array[j + 1]; - } - p_alloc_count--; - } else { - fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", - ptr, file, line, func); - } - - free(ptr); -} - -static inline void * -p_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) -{ - if (!ptr) { - return p_debug_malloc_impl(size, file, line, func); - } - if (size == 0) { - p_debug_free_impl(ptr, file, line, func); - return NULL; - } - - void *new_ptr = realloc(ptr, size); - printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", ptr, new_ptr, size, file, line, func); - - if (new_ptr) { - bool found = false; - for (uint64_t i = 0; i < p_alloc_count; ++i) { - if (p_ptr_array[i].ptr == ptr) { - p_ptr_array[i].ptr = new_ptr; - p_ptr_array[i].size = size; - p_ptr_array[i].file = file; - p_ptr_array[i].line = line; - p_ptr_array[i].func = func; - found = true; - break; - } - } - if (!found) { - if (p_alloc_count < P_MAX_ALLOCS) { - p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ - .ptr = new_ptr, - .size = size, - .file = file, - .func = func, - .line = line - }; - } - } - } - return new_ptr; -} - -static void -p_debug_memory_report(void) -{ - printf("\n==================== MEMORY REPORT ====================\n"); - printf("Remaining unfreed allocations: %lu\n", (unsigned long)p_alloc_count); - - for (uint64_t i = 0; i < p_alloc_count; ++i) { - P_DebugMemoryInfo *info = &p_ptr_array[i]; - printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", - info->ptr, info->size, info->file, info->line, info->func); - } - printf("=======================================================\n"); -} diff --git a/Podium/p_math.c b/Podium/p_math.c deleted file mode 100644 index 928738b..0000000 --- a/Podium/p_math.c +++ /dev/null @@ -1,255 +0,0 @@ -PODEF -int p_ceil(float x) -{ - int base = (int) x; - return (x > base) ? base + 1 : base; -} - -PODEF float -p_cosf(float x) -{ - x += 1.57079632679f; - - while (x > P_PI) x -= P_PI2; - while (x < -P_PI) x += P_PI2; - - const float B = 4.0f / P_PI; - const float C = -4.0f / P_PI_POW2; - - float y = B * x + C * x * (x < 0 ? -x : x); - - const float P = 0.225f; - y = P * (y * (y < 0 ? -y : y) - y) + y; - - return y; -} - -PODEF float -p_sinf(float x) -{ - while (x > P_PI) x -= P_PI2; - while (x < -P_PI) x += P_PI2; - - const float B = 4.0f / P_PI; - const float C = -4.0f / (P_PI_POW2); - - float y = B * x + C * x * (x < 0 ? -x : x); - - const float P = 0.225f; - y = P * (y * (y < 0 ? -y : y) - y) + y; - - return y; -} - -PODEF Vec2f -p_vec2f(float x, float y) -{ - return (Vec2f) { .x = x, .y = y }; -} - -PODEF Vec2f -p_vec2f_add(Vec2f a, Vec2f b) -{ - return (Vec2f) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -PODEF float -p_vec2f_cross(Vec2f a, Vec2f b) -{ - // axby − aybx - return a.x * b.y - a.y * b.x; -} - -PODEF float -p_vec2f_dot(Vec2f a, Vec2f b) -{ - // a · b = ax × bx + ay × by - return a.x * b.x + a.y * b.y; -} - -PODEF Vec2f -p_vec2f_mult(Vec2f a, float b) -{ - return (Vec2f) { - .x = a.x * b, - .y = a.y * b, - }; -} - -PODEF Vec2f -p_vec2f_sub(Vec2f a, Vec2f b) -{ - return (Vec2f) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -PODEF Vec2i -p_vec2i(float x, float y) -{ - return (Vec2i) { .x = x, .y = y }; -} - -PODEF Vec2i -p_vec2i_add(Vec2i a, Vec2i b) -{ - return (Vec2i) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -PODEF float -p_vec2i_cross(Vec2i a, Vec2i b) -{ - // axby − aybx - return a.x * b.y - a.y * b.x; -} - -PODEF float -p_vec2i_dot(Vec2i a, Vec2i b) -{ - // a · b = ax × bx + ay × by - return a.x * b.x + a.y * b.y; -} - -PODEF Vec2i -p_vec2i_mult(Vec2i a, float b) -{ - return (Vec2i) { - .x = a.x * b, - .y = a.y * b, - }; -} - -PODEF Vec2i -p_vec2i_sub(Vec2i a, Vec2i b) -{ - return (Vec2i) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -PODEF Vec3f -p_vec3f(float x, float y, float z) -{ - return (Vec3f) { .x = x, .y = y, .z = z }; -} - -PODEF P_Mat4 -p_mat4_identity(void) -{ - P_Mat4 res = {0}; - res.m[0] = 1.0f; - res.m[5] = 1.0f; - res.m[10] = 1.0f; - res.m[15] = 1.0f; - return res; -} - -PODEF P_Mat4 -p_mat4_mul(P_Mat4 a, P_Mat4 b) -{ - P_Mat4 res = {0}; - for (int col = 0; col < 4; ++col) { - for (int row = 0; row < 4; ++row) { - float sum = 0.0f; - for (int k = 0; k < 4; ++k) { - sum += a.m[k * 4 + row] * b.m[col * 4 + k]; - } - res.m[col * 4 + row] = sum; - } - } - return res; -} - -PODEF P_Mat4 -p_mat4_translate(Vec3f v) -{ - P_Mat4 res = p_mat4_identity(); - res.m[12] = v.x; - res.m[13] = v.y; - res.m[14] = v.z; - return res; -} - -PODEF P_Mat4 -p_mat4_translate_by(P_Mat4 m, Vec3f v) -{ - P_Mat4 res = m; - res.m[12] = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12]; - res.m[13] = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13]; - res.m[14] = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14]; - res.m[15] = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15]; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_x(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[5] = c; - res.m[6] = s; - res.m[9] = -s; - res.m[10] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_x_by(P_Mat4 m, float rad) -{ - return p_mat4_mul(m, p_mat4_rotate_x(rad)); -} - -PODEF P_Mat4 -p_mat4_rotate_y(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[0] = c; - res.m[2] = -s; - res.m[8] = s; - res.m[10] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_y_by(P_Mat4 m, float rad) -{ - return p_mat4_mul(m, p_mat4_rotate_y(rad)); -} - -PODEF P_Mat4 -p_mat4_rotate_z(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[0] = c; - res.m[1] = s; - res.m[4] = -s; - res.m[5] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z) -{ - P_Mat4 res = {0}; - float tan_half_fov = p_sinf(fov_rad * 0.5f) / p_cosf(fov_rad * 0.5f); - - res.m[0] = 1.0f / (aspect * tan_half_fov); - res.m[5] = 1.0f / tan_half_fov; - res.m[10] = far_z / (near_z - far_z); - res.m[11] = -1.0f; - res.m[14] = (near_z * far_z) / (near_z - far_z); - return res; -} diff --git a/Podium/p_math.h b/Podium/p_math.h deleted file mode 100644 index 1dad6ec..0000000 --- a/Podium/p_math.h +++ /dev/null @@ -1,33 +0,0 @@ -typedef struct {int x, y; } Vec2i; -typedef struct {float x, y; } Vec2f; -typedef struct {float x, y, z; } Vec3f; -typedef struct { float m[16];} P_Mat4; // column-major: m[col * 4 + row] -PODEF int p_ceil(float x); -PODEF float p_cosf(float x); -PODEF float p_sinf(float x); - -PODEF Vec2f p_vec2f(float, float); -PODEF Vec2f p_vec2f_add(Vec2f, Vec2f); -PODEF float p_vec2f_cross(Vec2f, Vec2f); -PODEF float p_vec2f_dot(Vec2f, Vec2f); -PODEF Vec2f p_vec2f_mult(Vec2f a, float b); -PODEF Vec2f p_vec2f_sub(Vec2f, Vec2f); -PODEF Vec2i p_vec2i(float, float); -PODEF Vec2i p_vec2i_add(Vec2i, Vec2i); -PODEF float p_vec2i_cross(Vec2i, Vec2i); -PODEF float p_vec2i_dot(Vec2i, Vec2i); -PODEF Vec2i p_vec2i_mult(Vec2i a, float b); -PODEF Vec2i p_vec2i_sub(Vec2i, Vec2i); - -PODEF Vec3f p_vec3f(float, float, float); - -PODEF P_Mat4 p_mat4_identity(void); -PODEF P_Mat4 p_mat4_mul(P_Mat4 a, P_Mat4 b); -PODEF P_Mat4 p_mat4_translate(Vec3f v); -PODEF P_Mat4 p_mat4_translate_by(P_Mat4 m, Vec3f v); -PODEF P_Mat4 p_mat4_rotate_x(float rad); -PODEF P_Mat4 p_mat4_rotate_x_by(P_Mat4 m, float rad); -PODEF P_Mat4 p_mat4_rotate_y(float rad); -PODEF P_Mat4 p_mat4_rotate_y_by(P_Mat4 m, float rad); -PODEF P_Mat4 p_mat4_rotate_z(float rad); -PODEF P_Mat4 p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z); diff --git a/Podium/p_platform_linux.c b/Podium/p_platform_linux.c deleted file mode 100644 index 366e3d7..0000000 --- a/Podium/p_platform_linux.c +++ /dev/null @@ -1,404 +0,0 @@ -/* linux */ -#include -#include -#include -#include -#include -#include -/* x11 */ -#include -#include -/* pulse audio */ -#include -#include -#include -/* joystick */ -#include - -typedef struct { - pa_simple *audio_stream; -} P_LinuxCtx; - -struct P_Window_Impl { - Display *display; - XImage *image; - Window window; - GC gc; - int screen; - int width; - int height; -}; - -P_LinuxCtx p_ctx = {0}; - -static inline P_KeyCode -_x11_translate_keycode(XEvent xev) -{ - static KeySym ks; - ks = XLookupKeysym(&xev.xkey, 0); - switch (ks) { - /* Arrow keys */ - case XK_Left: return P_KEY_LEFT; - case XK_Right: return P_KEY_RIGHT; - case XK_Up: return P_KEY_UP; - case XK_Down: return P_KEY_DOWN; - /* Common keys */ - case XK_space: return P_KEY_SPACE; - case XK_Escape: return P_KEY_ESCAPE; - case XK_Return: return P_KEY_ENTER; - case XK_A: case XK_a: return P_KEY_A; - case XK_B: case XK_b: return P_KEY_B; - case XK_C: case XK_c: return P_KEY_C; - case XK_D: case XK_d: return P_KEY_D; - case XK_E: case XK_e: return P_KEY_E; - case XK_F: case XK_f: return P_KEY_F; - case XK_G: case XK_g: return P_KEY_G; - case XK_H: case XK_h: return P_KEY_H; - case XK_I: case XK_i: return P_KEY_I; - case XK_J: case XK_j: return P_KEY_J; - case XK_K: case XK_k: return P_KEY_K; - case XK_L: case XK_l: return P_KEY_L; - case XK_M: case XK_m: return P_KEY_M; - case XK_N: case XK_n: return P_KEY_N; - case XK_O: case XK_o: return P_KEY_O; - case XK_P: case XK_p: return P_KEY_P; - case XK_Q: case XK_q: return P_KEY_Q; - case XK_R: case XK_r: return P_KEY_R; - case XK_S: case XK_s: return P_KEY_S; - case XK_T: case XK_t: return P_KEY_T; - case XK_U: case XK_u: return P_KEY_U; - case XK_V: case XK_v: return P_KEY_V; - case XK_W: case XK_w: return P_KEY_W; - case XK_X: case XK_x: return P_KEY_X; - case XK_Y: case XK_y: return P_KEY_Y; - case XK_Z: case XK_z: return P_KEY_Z; - default: return P_KEY_UNKNOWN; - } -} - -POAPI u64 -p_get_time(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec; -} - -POAPI void -p_sleep_ns(i64 ns) -{ - struct timespec ts; - ts.tv_sec = ns / 1000000000L; - ts.tv_nsec = ns % 1000000000L; - clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); -} - -POAPI void -p_stdout(void *msg, usize bytes) -{ - write(1, msg, bytes); -} - -POAPI bool -p_file_exists(const char *path) -{ - return (access(path, F_OK) == 0); -} - -POAPI usize -p_file_size(const char *path) -{ - struct stat st; - if (stat(path, &st) == 0) { - return st.st_size; - } - return 0; -} - -PODEF void -p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) -{ - int fd = open(path, O_RDONLY); - if (fd >= 0) { - read(fd, buf_ptr, (size_t)buf_size); - close(fd); - } -} - -PODEF void -p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) -{ - int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); - if (fd >= 0) { - write(fd, buf_ptr, (size_t)buf_size); - close(fd); - } -} - -POAPI bool -p_window_open(P_Window *win, int width, int height, const char *title) -{ - win->width = width; - win->height = height; - - XInitThreads(); - - win->display = XOpenDisplay(NULL); - if (win->display == NULL) { - PERROR("Cannot open display!"); - return false; - } - - int screen = DefaultScreen(win->display); - win->window = XCreateSimpleWindow( - win->display, - RootWindow(win->display, screen), - 0, 0, width, height, - 1, - BlackPixel(win->display, screen), - BlackPixel(win->display, screen) - ); - - win->screen = screen; - - XStoreName(win->display, win->window, title); - XSelectInput(win->display, win->window, - KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | StructureNotifyMask); - XMapWindow(win->display, win->window); - - win->gc = DefaultGC(win->display, screen); - - Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False); - XSetWMProtocols(win->display, win->window, &wm_delete, 1); - - return true; -} - -POAPI void -p_window_close(P_Window *win) -{ - if (!win) return; - - if (win->image) { - // XDestroyImage(win->image); - win->image = NULL; - } - - if (win->display) { - Display *dpy = win->display; - Window w = win->window; - win->display = NULL; - win->window = 0; - - if (w) { - XDestroyWindow(dpy, w); - } - XSync(dpy, False); - } -} - -POAPI bool -p_window_is_open(P_Window *win) -{ - return win && win->display != NULL; -} - -POAPI void -p_window_size(P_Window *win, int *window_width, int *window_height) -{ - if (!win) return; - - XWindowAttributes attr; - XGetWindowAttributes(win->display, win->window, &attr); - *window_width = attr.width; - *window_height = attr.height; - - win->width = attr.width; - win->height = attr.height; -} - -POAPI void -p_window_draw(P_Window *win, u32 *pixels, int width, int height) -{ - int screen = win->screen; - - win->image = XCreateImage( - win->display, - DefaultVisual(win->display, screen), - DefaultDepth(win->display, screen), - ZPixmap, - 0, - (char*)pixels, - width, - height, - 32, - width * 4 - ); - - int window_width, window_height; - p_window_size(win, &window_width, &window_height); - - XPutImage( - win->display, - win->window, - win->gc, - win->image, - 0, 0, - 0, 0, - win->width, - win->height); - - XFlush(win->display); -} - -POAPI bool -p_window_poll_event(P_Window *win, P_Event *ev) -{ - if (XPending(win->display)) { - static XEvent xev; - XNextEvent(win->display, &xev); - - switch (xev.type) { - - case KeyPress: - ev->type = P_EVENT_KEY_DOWN; - ev->key.key = _x11_translate_keycode(xev); - return true; - - case KeyRelease: - ev->type = P_EVENT_KEY_UP; - ev->key.key = _x11_translate_keycode(xev); - return true; - - case ConfigureNotify: - ev->type = P_EVENT_WINDOW_RESIZE; - ev->resize.width = xev.xconfigure.width; - ev->resize.height = xev.xconfigure.height; - return true; - - case ClientMessage: - if ((Atom)xev.xclient.data.l[0] == - XInternAtom(win->display, "WM_DELETE_WINDOW", False)) { - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - } - break; - - case MotionNotify: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_MOVED; - ev->pointer.button = 0; - ev->pointer.x = xev.xmotion.x; - ev->pointer.y = xev.xmotion.y; - return true; - - case ButtonPress: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */ - ev->pointer.x = xev.xbutton.x; - ev->pointer.y = xev.xbutton.y; - return true; - - case ButtonRelease: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = xev.xbutton.button; - ev->pointer.x = xev.xbutton.x; - ev->pointer.y = xev.xbutton.y; - return true; - } - } - - ev->type = P_EVENT_NONE; - return false; -} - -POAPI void -p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) -{ - pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = sample_rate, - .channels = channels, - }; - - int error; - p_ctx.audio_stream = pa_simple_new( - NULL, - name, - PA_STREAM_PLAYBACK, - NULL, - desc, - &ss, - NULL, - NULL, - &error - ); - - if (!p_ctx.audio_stream) { - PERROR("PulseAudio init failed: %s\n", pa_strerror(error)); - exit(1); - } -} - -POAPI void -p_audio_quit() -{ - if (p_ctx.audio_stream) { - pa_simple_free(p_ctx.audio_stream); - p_ctx.audio_stream = NULL; - } -} - -POAPI void -p_audio_write(const i16 *samples, usize count) -{ - if (!p_ctx.audio_stream) return; - - int error; - if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) { - PERROR("PulseAudio write failed: %s\n", pa_strerror(error)); - } - - pa_simple_drain(p_ctx.audio_stream, &error); -} - -#ifdef P_MODULE_VULKAN - -#ifndef VK_USE_PLATFORM_XLIB_KHR -#define VK_USE_PLATFORM_XLIB_KHR -#endif -#include - -POAPI char** -p_vulkan_get_extensions() -{ - char **darr = NULL; - p_darray_push(darr, "VK_KHR_surface"); - p_darray_push(darr, "VK_KHR_xcb_surface"); - p_darray_push(darr, "VK_KHR_xlib_surface"); - return darr; -} - -POAPI bool -p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) -{ - if (!instance || !window || !window->display || !window->window || !out_surface) { - return false; - } - - VkXlibSurfaceCreateInfoKHR create_info = {0}; - create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - create_info.pNext = NULL; - create_info.flags = 0; - create_info.dpy = window->display; - create_info.window = window->window; - - VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface); - - return (result == VK_SUCCESS); -} -#endif diff --git a/Podium/p_platform_win32.c b/Podium/p_platform_win32.c deleted file mode 100644 index 556b43b..0000000 --- a/Podium/p_platform_win32.c +++ /dev/null @@ -1,402 +0,0 @@ -#define WIN32_LEAN_AND_MEAN -#include -#include -#include /* gamepad support */ - -typedef struct { - HWAVEOUT wave_out; - WAVEHDR wave_header; -} P_Win32Ctx; - -struct P_Window_Impl { - HWND hwnd; - HDC hdc; - BITMAPINFO bitmap_info; - int width; - int height; - bool closed; -}; - -P_Win32Ctx p_ctx = {0}; - -static inline P_KeyCode -_win32_translate_vkey(WPARAM vk) -{ - switch (vk) { - case VK_LEFT: return P_KEY_LEFT; - case VK_RIGHT: return P_KEY_RIGHT; - case VK_UP: return P_KEY_UP; - case VK_DOWN: return P_KEY_DOWN; - case VK_SPACE: return P_KEY_SPACE; - case VK_ESCAPE: return P_KEY_ESCAPE; - case VK_RETURN: return P_KEY_ENTER; - case 'A': return P_KEY_A; - case 'B': return P_KEY_B; - case 'C': return P_KEY_C; - case 'D': return P_KEY_D; - case 'E': return P_KEY_E; - case 'F': return P_KEY_F; - case 'G': return P_KEY_G; - case 'H': return P_KEY_H; - case 'I': return P_KEY_I; - case 'J': return P_KEY_J; - case 'K': return P_KEY_K; - case 'L': return P_KEY_L; - case 'M': return P_KEY_M; - case 'N': return P_KEY_N; - case 'O': return P_KEY_O; - case 'P': return P_KEY_P; - case 'Q': return P_KEY_Q; - case 'R': return P_KEY_R; - case 'S': return P_KEY_S; - case 'T': return P_KEY_T; - case 'U': return P_KEY_U; - case 'V': return P_KEY_V; - case 'W': return P_KEY_W; - case 'X': return P_KEY_X; - case 'Y': return P_KEY_Y; - case 'Z': return P_KEY_Z; - default: return P_KEY_UNKNOWN; - } -} - -POAPI u64 -p_get_time(void) -{ - LARGE_INTEGER count, freq; - QueryPerformanceCounter(&count); - QueryPerformanceFrequency(&freq); - return (u64)((count.QuadPart * 1000000000ULL) / freq.QuadPart); -} - -POAPI void -p_sleep_ns(i64 ns) -{ - /* windows sleep is millisecond-based........ */ - Sleep((DWORD)(ns / 1000000)); -} - -POAPI void -p_stdout(void *msg, usize bytes) -{ - HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD bytes_written; - WriteFile(stdout_handle, msg, bytes, &bytes_written, NULL); -} - -POAPI bool -p_file_exists(const char *path) -{ - DWORD attr = GetFileAttributesA(path); - return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); -} - -POAPI usize -p_file_size(const char *path) -{ - WIN32_FILE_ATTRIBUTE_DATA data; - if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) { - return data.nFileSizeLow; - } - return 0; -} - -PODEF void -p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) -{ - HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (file != INVALID_HANDLE_VALUE) { - DWORD bytes_read; - ReadFile(file, buf_ptr, buf_size, &bytes_read, NULL); - CloseHandle(file); - } -} - -PODEF void -p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) -{ - HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (file != INVALID_HANDLE_VALUE) { - DWORD bytes_written; - WriteFile(file, buf_ptr, buf_size, &bytes_written, NULL); - CloseHandle(file); - } -} - -static LRESULT CALLBACK -_win32_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - P_Window *win = (P_Window*)GetWindowLongPtrA(hwnd, GWLP_USERDATA); - switch (msg) { - case WM_CLOSE: - /* Handled in poll_event; mark as closed */ - if (win) win->closed = true; - return 0; - } - return DefWindowProcA(hwnd, msg, wparam, lparam); -} - -POAPI bool -p_window_open(P_Window *win, int width, int height, const char *title) -{ - HINSTANCE instance = GetModuleHandleA(NULL); - - WNDCLASSA wc = {0}; - wc.lpfnWndProc = _win32_wnd_proc; - wc.hInstance = instance; - wc.lpszClassName = "P_Window_Class"; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - - RegisterClassA(&wc); - - win->hwnd = CreateWindowExA(0, wc.lpszClassName, title, - WS_OVERLAPPEDWINDOW | WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, width, height, - NULL, NULL, instance, NULL); - - if (!win->hwnd) return false; - - SetWindowLongPtrA(win->hwnd, GWLP_USERDATA, (LONG_PTR)win); - win->hdc = GetDC(win->hwnd); - win->width = width; - win->height = height; - win->closed = false; - - return true; -} - -POAPI void -p_window_close(P_Window *win) -{ - if (!win) return; - if (win->hdc) ReleaseDC(win->hwnd, win->hdc); - if (win->hwnd) DestroyWindow(win->hwnd); - win->hdc = NULL; - win->hwnd = NULL; -} - -POAPI bool -p_window_is_open(P_Window *win) -{ - return win && win->hwnd != NULL; -} - -POAPI void -p_window_size(P_Window *win, int *window_width, int *window_height) -{ - if (!win) return; - RECT rect; - GetClientRect(win->hwnd, &rect); - *window_width = rect.right - rect.left; - *window_height = rect.bottom - rect.top; - win->width = *window_width; - win->height = *window_height; -} - -POAPI void -p_window_draw(P_Window *win, u32 *pixels, int width, int height) -{ - BITMAPINFO bmi = {0}; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = width; - bmi.bmiHeader.biHeight = -height; /* top-down */ - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - StretchDIBits(win->hdc, - 0, 0, win->width, win->height, - 0, 0, width, height, - pixels, &bmi, DIB_RGB_COLORS, SRCCOPY); -} - -POAPI bool -p_window_poll_event(P_Window *win, P_Event *ev) -{ - if (win->closed) { - win->closed = false; - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - } - - MSG msg; - while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { - switch (msg.message) { - - case WM_QUIT: - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - - /* Key press */ - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - ev->type = P_EVENT_KEY_DOWN; - ev->key.key = _win32_translate_vkey(msg.wParam); - return true; - - /* Key release */ - case WM_KEYUP: - case WM_SYSKEYUP: - ev->type = P_EVENT_KEY_UP; - ev->key.key = _win32_translate_vkey(msg.wParam); - return true; - - /* Mouse movement */ - case WM_MOUSEMOVE: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_MOVED; - ev->pointer.button = 0; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - /* Mouse buttons */ - case WM_LBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 1; /* 1 = LMB, matching X11 convention */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_LBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 1; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_RBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 3; /* 3 = RMB */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_RBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 3; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_MBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 2; /* 2 = MMB */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_MBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 2; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - /* Window resize */ - case WM_SIZE: - ev->type = P_EVENT_WINDOW_RESIZE; - ev->resize.width = (u32)(msg.lParam & 0xFFFF); - ev->resize.height = (u32)((msg.lParam >> 16) & 0xFFFF); - win->width = ev->resize.width; - win->height = ev->resize.height; - return true; - - default: - TranslateMessage(&msg); - DispatchMessageA(&msg); - break; - } - } - - ev->type = P_EVENT_NONE; - return false; -} - - -POAPI void -p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) -{ - (void)name; (void)desc; /* WinMM doesn't use a stream name */ - - WAVEFORMATEX wfx = {0}; - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = channels; - wfx.nSamplesPerSec = sample_rate; - wfx.wBitsPerSample = 16; - wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - - if (waveOutOpen(&p_ctx.wave_out, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) { - exit(1); - } -} - -POAPI void -p_audio_quit() -{ - if (p_ctx.wave_out) { - waveOutReset(p_ctx.wave_out); - waveOutClose(p_ctx.wave_out); - p_ctx.wave_out = NULL; - } -} - -POAPI void -p_audio_write(const i16 *samples, usize count) -{ - if (!p_ctx.wave_out) return; - - WAVEHDR header = {0}; - header.lpData = (LPSTR)samples; - header.dwBufferLength = (DWORD)(count * sizeof(i16)); - - waveOutPrepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); - waveOutWrite(p_ctx.wave_out, &header, sizeof(WAVEHDR)); - - /* wait for playback to finish (synchronous, mirrors pa_simple_drain) */ - while (!(header.dwFlags & WHDR_DONE)) Sleep(1); - - waveOutUnprepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); -} - -#ifdef P_MODULE_VULKAN - -#ifndef VK_USE_PLATFORM_WIN32_KHR -#define VK_USE_PLATFORM_WIN32_KHR -#endif -#include - -POAPI char** -p_vulkan_get_extensions() -{ - char **darr = NULL; - p_darray_push(darr, "VK_KHR_surface"); - p_darray_push(darr, "VK_KHR_win32_surface"); - return darr; -} - -POAPI bool -p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) -{ - if (!instance || !window || !window->hwnd || !out_surface) { - return false; - } - - VkWin32SurfaceCreateInfoKHR create_info = {0}; - create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - create_info.pNext = NULL; - create_info.flags = 0; - create_info.hwnd = window->hwnd; - create_info.hinstance = GetModuleHandleA(NULL); - - VkResult result = vkCreateWin32SurfaceKHR(instance, &create_info, allocator, out_surface); - return (result == VK_SUCCESS); -} -#endif diff --git a/Podium/p_string.c b/Podium/p_string.c deleted file mode 100644 index 73cb0ea..0000000 --- a/Podium/p_string.c +++ /dev/null @@ -1,201 +0,0 @@ -PODEF char* -p_find_char(char *buf, char needle) -{ - while (buf && *buf != needle) buf++; - return buf; -} - -PODEF bool -p_strcmp(const char *a, const char *b) -{ - if (p_strlen(a) != p_strlen(b)) - return false; - - while (a && b && *a != '\0') { - if (*(a++) != *(b++)) return false; - } - - return true; -} - -PODEF int -p_strlen(const char *a) -{ - int i = 0; - while (*a++ != '\0') i++; - return i; -} - -PODEF int -p_strtoi(const char *str) -{ - int res = 0; - - while (*str) { - if (*str >= '0' && *str <= '9') { - res = res * 10 + (*str - '0'); - } else { - return res; - } - str++; - } - - return res; -} - -PODEF char* -p_strtok(const char *str, char c) -{ - while (*str != c && *str++ != '\0'); - return (*str == c) ? (char*) ++str : 0; -} - -PODEF P_StringView -p_strview(const char *cstr) -{ - P_StringView sv = {0}; - sv.cstr = cstr; - sv.len = p_strlen(cstr); - return sv; -} - -PODEF void -p_strview_chop_left(P_StringView *sv, u32 n) -{ - if (n > sv->len) n = sv->len; - sv->cstr += n; - sv->len -= n; -} - -PODEF void -p_strview_chop_right(P_StringView *sv, u32 n) -{ - if (n > sv->len) n = sv->len; - sv->len -= n; -} - -PODEF P_StringView -p_strview_chop_delim(P_StringView *sv, char delim) -{ - u32 i = 0; - while (i < sv->len && sv->cstr[i] != delim) { - i++; - } - - P_StringView tok; - if (i < sv->len) { - tok.cstr = sv->cstr; - tok.len = i; - p_strview_chop_left(sv, i + 1); - return tok; - } - - tok = *sv; - p_strview_chop_left(sv, sv->len); - return tok; -} - -PODEF P_StringView -p_strview_chop_type(P_StringView *sv, int(*istype)(int)) -{ - u32 i = 0; - while (i < sv->len && istype(sv->cstr[i])) { - i++; - } - - P_StringView tok; - if (i < sv->len) { - tok.cstr = sv->cstr; - tok.len = i; - p_strview_chop_left(sv, i + 1); - return tok; - } - - tok = *sv; - p_strview_chop_left(sv, sv->len); - return tok; -} - - -PODEF void -p_strview_trim_left(P_StringView *sv) -{ - while (sv->len > 0 && p_is_space(sv->cstr[0])) { - p_strview_chop_left(sv, 1); - } -} - -PODEF void -p_strview_trim_right(P_StringView *sv) -{ - while (sv->len > 0 && p_is_space(sv->cstr[sv->len-1])) { - p_strview_chop_right(sv, 1); - } -} - -PODEF void -p_strview_trim(P_StringView *sv) -{ - p_strview_trim_left(sv); - p_strview_trim_right(sv); -} - -PODEF int -p_is_space(int c) -{ - /* Checks for: space, form feed (\f), line feed (\n), - * carriage return (\r), horizontal tab (\t), vertical tab (\v) */ - return (c == ' ' || (c >= '\t' && c <= '\r')); -} - -PODEF int -p_is_digit(int c) -{ - return (c >= '0' && c <= '9'); -} - -PODEF int -p_is_alpha(int c) -{ - return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); -} - -PODEF int -p_is_alnum(int c) -{ - return (p_is_alpha(c) || p_is_digit(c)); -} - -PODEF int -p_is_hex(int c) -{ - return (p_is_digit(c) || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F')); -} - -PODEF int -p_is_upper(int c) -{ - return (c >= 'A' && c <= 'Z'); -} - -PODEF int -p_is_lower(int c) -{ - return (c >= 'a' && c <= 'z'); -} - -PODEF int -p_to_lower(int c) -{ - if (p_is_upper(c)) return (c + ('a' - 'A')); - return c; -} - -PODEF int -p_to_upper(int c) -{ - if (p_is_lower(c)) return (c - ('a' - 'A')); - return c; -} diff --git a/Podium/p_string.h b/Podium/p_string.h deleted file mode 100644 index 5c4077b..0000000 --- a/Podium/p_string.h +++ /dev/null @@ -1,38 +0,0 @@ -typedef struct { - const char *cstr; - u32 len; -} P_StringView; - -typedef struct { - P_StringView sv; -} P_StringBuilder; - -/* Classic C string functions */ -PODEF char* p_find_char(char *buf, char needle); -PODEF bool p_strcmp(const char *a, const char *b); -PODEF int p_strlen(const char *a); -PODEF int p_strtoi(const char *str); -PODEF char* p_strtok(const char *str, char c); - -/* String views */ -PODEF P_StringView p_strview(const char *cstr); -PODEF void p_strview_chop_left(P_StringView *sv, u32 n); -PODEF void p_strview_chop_right(P_StringView *sv, u32 n); -PODEF P_StringView p_strview_chop_delim(P_StringView *sv, char delim); -PODEF P_StringView p_strview_chop_type(P_StringView *sv, int(*istype)(int)); -PODEF void p_strview_trim_left(P_StringView *sv); -PODEF void p_strview_trim_right(P_StringView *sv); -PODEF void p_strview_trim(P_StringView *sv); - -/* Classification */ -PODEF int p_is_space(int c); -PODEF int p_is_digit(int c); -PODEF int p_is_alpha(int c); -PODEF int p_is_alnum(int c); -PODEF int p_is_upper(int c); -PODEF int p_is_lower(int c); -PODEF int p_is_hex(int c); - -/* Transformation */ -PODEF int p_to_lower(int c); -PODEF int p_to_upper(int c); diff --git a/Podium/p_types.h b/Podium/p_types.h deleted file mode 100644 index 1da81be..0000000 --- a/Podium/p_types.h +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef _PODIUM_TYPES_ -#define _PODIUM_TYPES_ - -#define NANOS_PER_SEC (1000*1000*1000) -#define P_PI_HALF 1.57079632679f -#define P_PI 3.14159265f -#define P_PI2 6.28318531f -#define P_PI_POW2 9.86960440f - -#ifdef NDEBUG - #define assert(x) ((void)0) -#else - #ifndef assert - #define assert(x) \ - do { \ - if (!(x)) { \ - __builtin_trap(); \ - } \ - } while (0) - #endif -#endif - -#ifndef BUFSIZ - #define BUFSIZ 8192 -#endif - -typedef unsigned char u8; -typedef signed char i8; -typedef unsigned short u16; -typedef short i16; -typedef unsigned int u32; -typedef int i32; -typedef unsigned long long u64; -typedef long long i64; -typedef float f32; -typedef double f64; - -#ifndef __cplusplus -#define bool _Bool -#define false 0 -#define true 1 -#endif - -/* C++ uses static_assert, C11 uses _Static_assert */ -#ifdef __cplusplus - #define STATIC_ASSERT static_assert -#else - #define STATIC_ASSERT _Static_assert -#endif - -STATIC_ASSERT(sizeof(u8) == 1, "u8 must be 1 byte"); -STATIC_ASSERT(sizeof(u16) == 2, "u16 must be 2 bytes"); -STATIC_ASSERT(sizeof(u32) == 4, "u32 must be 4 bytes"); -STATIC_ASSERT(sizeof(u64) == 8, "u64 must be 8 bytes"); - -STATIC_ASSERT(sizeof(i8) == 1, "i8 must be 1 byte"); -STATIC_ASSERT(sizeof(i16) == 2, "i16 must be 2 bytes"); -STATIC_ASSERT(sizeof(i32) == 4, "i32 must be 4 bytes"); -STATIC_ASSERT(sizeof(i64) == 8, "i64 must be 8 bytes"); - -STATIC_ASSERT(sizeof(f32) == 4, "f32 must be 4 bytes"); -STATIC_ASSERT(sizeof(f64) == 8, "f64 must be 8 bytes"); -#define U8_MAX 255 -#define U16_MAX 65535 -#define U32_MAX 4294967295 - -/* Architecture specific sizing */ -#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64) - typedef u64 usize; - typedef u64 uintptr; - typedef u32 idx; - STATIC_ASSERT(sizeof(usize) == 8, "usize must be 8 bytes on 64-bit"); -#else - typedef u32 usize; - typedef u32 uintptr; - typedef u16 idx; - STATIC_ASSERT(sizeof(usize) == 4, "usize must be 4 bytes on 32-bit"); -#endif - -#ifndef NULL - #ifdef __cplusplus - #define NULL 0 - #else - #define NULL ((void*)0) - #endif -#endif - -#endif // _PODIUM_TYPES_ diff --git a/Podium/podium.c b/Podium/podium.c deleted file mode 100644 index 306e337..0000000 --- a/Podium/podium.c +++ /dev/null @@ -1 +0,0 @@ -#define PODIUM_IMPLEMENTATION diff --git a/Podium/podium.h b/Podium/podium.h deleted file mode 100644 index d94b4e5..0000000 --- a/Podium/podium.h +++ /dev/null @@ -1,2269 +0,0 @@ -/* =========================================================================== - * PODIUM - Kitchen Sink - Copyright (c) 2026 Vasco Alves - * - * --------------------------------------------------------------------------- - * To stand on the shoulders of giants... - * Giants must stand still!" - * - Eskil Steenberg - "You should finish your software" – BSC 2025 - * --------------------------------------------------------------------------- - * - * PREFIX: P_ (types) or p_ (functions & variables) - * - * COMPILE TIME FLAGS - * | Name | Description | - * |-----------------|---------------------------------------------------------| - * | PODIUM_WIN32 | Used to explicitly compile for Windows. | - * | PODIUM_LINUX | Used to explicitly compile for Linux and FreeBSD. | - * - * MODULES - * | Name | Description | - * |-----------------|---------------------------------------------------------| - * | P_MODULE_VULKAN | Gives access to vulkan related utilities. | - * | P_MODULE_STRING | Include string manipulation utilities. | - * | P_MODULE_MATH | Trigonometry and linear algebra functions. | - * | P_MODULE_ALLOC | Memory allocators: Arena, Stack & Pool. | - * - * =========================================================================== */ - -#ifndef _PODIUM_H_ -#define _PODIUM_H_ - -#define PODIUM_MAJOR "0" // needs more revisions and use to reach 1.0 status -#define PODIUM_MINOR "2" // vulkan module -#define PODIUM_PATCH "2" // gamepad - -#define POAPI static inline -#define PODEF static inline - -/* - * Auto-detect platforms. - */ - -/* Windows */ -#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) - #define PODIUM_WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif -/* Apple */ -#elif defined(__APPLE__) || defined(__MACH__) - #include - #define PODIUM_APPLE - #if TARGET_OS_IPHONE - #define PODIUM_IOS - #else - #define PODIUM_MACOS - #endif -/* Linux / BSD */ -#elif defined(__linux__) || defined(__FreeBSD__) - #define PODIUM_LINUX - #ifndef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 200112L - #endif -#endif - -#include -#include -#include -#include /* memcpy */ -#include /* vsnprintf */ - -/* - * NOTE: The comments are used by the build - * script to know what includes to inline into the - * final single header file! - */ - -/* --- Start of p_types.h --- */ -#ifndef _PODIUM_TYPES_ -#define _PODIUM_TYPES_ - -#define NANOS_PER_SEC (1000*1000*1000) -#define P_PI_HALF 1.57079632679f -#define P_PI 3.14159265f -#define P_PI2 6.28318531f -#define P_PI_POW2 9.86960440f - -#ifdef NDEBUG - #define assert(x) ((void)0) -#else - #ifndef assert - #define assert(x) \ - do { \ - if (!(x)) { \ - __builtin_trap(); \ - } \ - } while (0) - #endif -#endif - -#ifndef BUFSIZ - #define BUFSIZ 8192 -#endif - -typedef unsigned char u8; -typedef signed char i8; -typedef unsigned short u16; -typedef short i16; -typedef unsigned int u32; -typedef int i32; -typedef unsigned long long u64; -typedef long long i64; -typedef float f32; -typedef double f64; - -#ifndef __cplusplus -#define bool _Bool -#define false 0 -#define true 1 -#endif - -/* C++ uses static_assert, C11 uses _Static_assert */ -#ifdef __cplusplus - #define STATIC_ASSERT static_assert -#else - #define STATIC_ASSERT _Static_assert -#endif - -STATIC_ASSERT(sizeof(u8) == 1, "u8 must be 1 byte"); -STATIC_ASSERT(sizeof(u16) == 2, "u16 must be 2 bytes"); -STATIC_ASSERT(sizeof(u32) == 4, "u32 must be 4 bytes"); -STATIC_ASSERT(sizeof(u64) == 8, "u64 must be 8 bytes"); - -STATIC_ASSERT(sizeof(i8) == 1, "i8 must be 1 byte"); -STATIC_ASSERT(sizeof(i16) == 2, "i16 must be 2 bytes"); -STATIC_ASSERT(sizeof(i32) == 4, "i32 must be 4 bytes"); -STATIC_ASSERT(sizeof(i64) == 8, "i64 must be 8 bytes"); - -STATIC_ASSERT(sizeof(f32) == 4, "f32 must be 4 bytes"); -STATIC_ASSERT(sizeof(f64) == 8, "f64 must be 8 bytes"); -#define U8_MAX 255 -#define U16_MAX 65535 -#define U32_MAX 4294967295 - -/* Architecture specific sizing */ -#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64) - typedef u64 usize; - typedef u64 uintptr; - typedef u32 idx; - STATIC_ASSERT(sizeof(usize) == 8, "usize must be 8 bytes on 64-bit"); -#else - typedef u32 usize; - typedef u32 uintptr; - typedef u16 idx; - STATIC_ASSERT(sizeof(usize) == 4, "usize must be 4 bytes on 32-bit"); -#endif - -#ifndef NULL - #ifdef __cplusplus - #define NULL 0 - #else - #define NULL ((void*)0) - #endif -#endif - -#endif // _PODIUM_TYPES_ -/* --- End of p_types.h --- */ -/* --- Start of p_log.h --- */ -/* - * Logging! - */ - -#include - -#ifdef DEBUG -#define LOG_DEBUG(...) PRINTF(__VA_ARGS__) -#else -#define LOG_DEBUG(...) ((void)0) -#endif - -/* fatal and error always enabled */ -#define PFATAL(message, ...) p_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) -#define PERROR(message, ...) p_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) - -/* warn and info enabled by default but can be disabled */ -#define P_LOG_WARN_ENABLED 1 -#define P_LOG_INFO_ENABLED 1 - -#if P_LOG_WARN_ENABLED == 1 -#define PWARN(message, ...) p_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) -#else -#define PWARN(message, ...) -#endif - -#if P_LOG_INFO_ENABLED == 1 -#define PINFO(message, ...) p_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) -#else -#define PINFO(message, ...) -#endif - -#if P_LOG_DEBUG_ENABLED == 1 -#define PDEBUG(message, ...) p_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) -#else -#define PDEBUG(message, ...) -#endif - -#if P_LOG_TRACE_ENABLED == 1 -#define PTRACE(message, ...) p_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) -#else -#define PTRACE(message, ...) -#endif - -typedef enum P_Log_Level { - P_LOG_LEVEL_FATAL = 0, - P_LOG_LEVEL_ERROR, - P_LOG_LEVEL_WARN, - P_LOG_LEVEL_INFO, - P_LOG_LEVEL_DEBUG, - P_LOG_LEVEL_TRACE, - P_COUNT_LOG_LEVEL -} P_Log_Level; - -#define P_PREFIX_LEN 7 -static const char *p_prefix[P_COUNT_LOG_LEVEL] = { - [P_LOG_LEVEL_FATAL] = "[FATAL]", - [P_LOG_LEVEL_ERROR] = "[ERROR]", - [P_LOG_LEVEL_WARN] = "[WARNI]", - [P_LOG_LEVEL_INFO] = "[INFOR]", - [P_LOG_LEVEL_DEBUG] = "[DEBUG]", - [P_LOG_LEVEL_TRACE] = "[TRACE]", -}; - -#define P_MAX_ALLOCS 512 - -typedef struct P_DebugMemoryInfo { - void *ptr; - size_t size; - const char *file; - const char *func; - int line; -} P_DebugMemoryInfo; - -static uint64_t p_alloc_count = 0; -static P_DebugMemoryInfo p_ptr_array[P_MAX_ALLOCS]; - -static inline void * -p_debug_malloc_impl(size_t size, const char *file, int line, const char *func) -{ - void *ptr = malloc(size); - printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); - - if (ptr) { - if (p_alloc_count < P_MAX_ALLOCS) { - p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ - .ptr = ptr, - .size = size, - .file = file, - .func = func, - .line = line - }; - } else { - fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", P_MAX_ALLOCS); - } - } - return ptr; -} - -static inline void -p_debug_free_impl(void *ptr, const char *file, int line, const char *func) -{ - printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); - - if (!ptr) return; // Standard C allows free(NULL) - - bool found = false; - uint64_t index = 0; - - /* Find allocation metadata */ - for (index = 0; index < p_alloc_count; ++index) { - if (p_ptr_array[index].ptr == ptr) { - found = true; - break; - } - } - - if (found) { - /* Shift array left to remove tracked struct */ - for (uint64_t j = index; j < p_alloc_count - 1; ++j) { - p_ptr_array[j] = p_ptr_array[j + 1]; - } - p_alloc_count--; - } else { - fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", - ptr, file, line, func); - } - - free(ptr); -} - -static inline void * -p_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) -{ - if (!ptr) { - return p_debug_malloc_impl(size, file, line, func); - } - if (size == 0) { - p_debug_free_impl(ptr, file, line, func); - return NULL; - } - - void *new_ptr = realloc(ptr, size); - printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", ptr, new_ptr, size, file, line, func); - - if (new_ptr) { - bool found = false; - for (uint64_t i = 0; i < p_alloc_count; ++i) { - if (p_ptr_array[i].ptr == ptr) { - p_ptr_array[i].ptr = new_ptr; - p_ptr_array[i].size = size; - p_ptr_array[i].file = file; - p_ptr_array[i].line = line; - p_ptr_array[i].func = func; - found = true; - break; - } - } - if (!found) { - if (p_alloc_count < P_MAX_ALLOCS) { - p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ - .ptr = new_ptr, - .size = size, - .file = file, - .func = func, - .line = line - }; - } - } - } - return new_ptr; -} - -static void -p_debug_memory_report(void) -{ - printf("\n==================== MEMORY REPORT ====================\n"); - printf("Remaining unfreed allocations: %lu\n", (unsigned long)p_alloc_count); - - for (uint64_t i = 0; i < p_alloc_count; ++i) { - P_DebugMemoryInfo *info = &p_ptr_array[i]; - printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", - info->ptr, info->size, info->file, info->line, info->func); - } - printf("=======================================================\n"); -} -/* --- End of p_log.h --- */ -/* --- Start of p_ds.h --- */ -#define DEFAULT_ALIGNMENT (2 * sizeof(void *)) -#define ARRAY_GROW_FACTOR 2 - -typedef struct { - u32 size; - u32 capacity; -} DynamicArrayHeader; - -#define DARRAY_INIT_CAPACITY 64 -#define p_darray_push(array, var) \ - do { \ - if (array == NULL) { \ - DynamicArrayHeader *header = \ - malloc(DARRAY_INIT_CAPACITY * sizeof *array + sizeof *header); \ - header->size = 0; \ - header->capacity = DARRAY_INIT_CAPACITY; \ - array = (void *)(header + 1); \ - } \ - DynamicArrayHeader *header = (DynamicArrayHeader *)(array) - 1; \ - if (header->size >= header->capacity - 1) { \ - unsigned long new_capacity = header->capacity * 2; \ - DynamicArrayHeader *new_header = \ - realloc(header, new_capacity * sizeof *array + sizeof *header); \ - if (!new_header) \ - break; \ - header = new_header; \ - array = (void *)(header + 1); \ - header->capacity = new_capacity; \ - } \ - (array)[header->size++] = var; \ - } while (0) - -#define p_darray_len(array) ((array) ? ((DynamicArrayHeader *)(array) - 1)->size : 0) -#define p_darray_destroy(array) \ -{ \ - free((DynamicArrayHeader *)(array) - 1); \ -} - -#define p_darray_shrink(array) \ - do { \ - DynamicArrayHeader *header = (array) ? ((DynamicArrayHeader *)(array) - 1) : 0;\ - if (header) {\ - if (header->capacity > 4) {\ - unsigned long new_capacity = (header->capacity >> 1) + (header->capacity & 1); \ - void *new_header = realloc(header, new_capacity * sizeof *array + sizeof *header); \ - if (!new_header) break; \ - header = new_header; \ - array = (void *)(header + 1); \ - header->capacity = new_capacity; \ - }\ - }\ - } while (0) -/* --- End of p_ds.h --- */ - -/* - * Main Platform Types (Keyboard, Events, Context, etc.) - */ - -typedef enum { - P_KEYMOD_ALT = 0, - P_KEYMOD_SHIFT, - P_KEYMOD_CTRL, - P_KEYMOD_CAPS, -} P_KeyMod; - -typedef enum { - P_KEY_UNKNOWN = 0, - P_KEY_UP, P_KEY_DOWN, P_KEY_LEFT, P_KEY_RIGHT, - P_KEY_SPACE, P_KEY_ESCAPE, P_KEY_ENTER, - P_KEY_A, P_KEY_B, P_KEY_C, P_KEY_D, P_KEY_E, P_KEY_F, P_KEY_G, P_KEY_H, P_KEY_I, - P_KEY_J, P_KEY_K, P_KEY_L, P_KEY_M, P_KEY_N, P_KEY_O, P_KEY_P, P_KEY_Q, P_KEY_R, - P_KEY_S, P_KEY_T, P_KEY_U, P_KEY_V, P_KEY_W, P_KEY_X, P_KEY_Y, P_KEY_Z, -} P_KeyCode; - -/* Platform event type */ -typedef enum { - P_EVENT_NONE = 0, - P_EVENT_KEY_DOWN, - P_EVENT_KEY_UP, - P_EVENT_WINDOW_CLOSE, - P_EVENT_WINDOW_RESIZE, - P_EVENT_POINTER, - P_EVENT_CONTROLLER_CONNECTED, - P_EVENT_CONTROLLER_DISCONNECTED, -} P_EvenType; - -typedef enum { - P_POINTER_MOVED = 0, - P_POINTER_PRESSED, - P_POINTER_RELEASED -} P_PointerState; - -typedef struct { - P_EvenType type; - union { - struct {P_KeyCode key; P_KeyMod mod; } key; - struct { u32 width, height; } resize; - struct { P_PointerState state; u32 button; u32 x, y; } pointer; - }; -} P_Event; - -/* - * Platform dependent functions to be implemented by each layer. - */ - -POAPI u64 p_get_time(void); // Get time in nanoseconds. -POAPI void p_sleep_ns(i64 ns); // Sleep for nanoseconds. - -POAPI void p_stdout(void *msg, usize bytes); // Print buffer to stdout. -PODEF void p_log_printf(P_Log_Level level, const char* src, ...); // Print buffer to stdout. - - -POAPI void* p_file_alloc(const char *path, unsigned long *buf_size); -POAPI bool p_file_exists(const char *path); // Check if file exists. -POAPI usize p_file_size(const char *path); // Check file size. -POAPI void p_file_load(const char *path, void *buf_ptr, unsigned long buf_size); // Load file to buffer. -PODEF void p_file_write(const char *path, void *buf_ptr, unsigned long buf_size); // Write buffer to file. - -typedef struct P_Window_Impl P_Window; -POAPI bool p_window_open(P_Window *win, int width, int height, const char *title); // Opens a window. Returns false on failure. -POAPI void p_window_close(P_Window *win); // Close window. -POAPI bool p_window_is_open(P_Window *win); // Check if window is open. -POAPI void p_window_size(P_Window *win, int *window_width, int *window_height); // Get window size. -POAPI void p_window_draw(P_Window *win, u32 *pixels, int width, int height); // Draw directly to the window's buffer. Normally much slower than using the GPU. -POAPI bool p_window_poll_event(P_Window *win, P_Event *ev); // Returns true while there are events to poll. Write event data to a pointer. - -POAPI void p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc); -POAPI void p_audio_quit(); -POAPI void p_audio_write(const i16 *samples, usize count); - -/* - * The Vulkan Module is platform specific - */ - -#ifdef P_MODULE_VULKAN -#include -POAPI char** p_vulkan_get_extensions(); -POAPI bool p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface); -#endif - -/* - * The Remaining Modules are PLATFORM INDEPENDENT! - */ - -#ifdef P_MODULE_STRING -/* --- Start of p_string.h --- */ -typedef struct { - const char *cstr; - u32 len; -} P_StringView; - -typedef struct { - P_StringView sv; -} P_StringBuilder; - -/* Classic C string functions */ -PODEF char* p_find_char(char *buf, char needle); -PODEF bool p_strcmp(const char *a, const char *b); -PODEF int p_strlen(const char *a); -PODEF int p_strtoi(const char *str); -PODEF char* p_strtok(const char *str, char c); - -/* String views */ -PODEF P_StringView p_strview(const char *cstr); -PODEF void p_strview_chop_left(P_StringView *sv, u32 n); -PODEF void p_strview_chop_right(P_StringView *sv, u32 n); -PODEF P_StringView p_strview_chop_delim(P_StringView *sv, char delim); -PODEF P_StringView p_strview_chop_type(P_StringView *sv, int(*istype)(int)); -PODEF void p_strview_trim_left(P_StringView *sv); -PODEF void p_strview_trim_right(P_StringView *sv); -PODEF void p_strview_trim(P_StringView *sv); - -/* Classification */ -PODEF int p_is_space(int c); -PODEF int p_is_digit(int c); -PODEF int p_is_alpha(int c); -PODEF int p_is_alnum(int c); -PODEF int p_is_upper(int c); -PODEF int p_is_lower(int c); -PODEF int p_is_hex(int c); - -/* Transformation */ -PODEF int p_to_lower(int c); -PODEF int p_to_upper(int c); -/* --- End of p_string.h --- */ -#endif - -#ifdef P_MODULE_MATH -/* --- Start of p_math.h --- */ -typedef struct {int x, y; } Vec2i; -typedef struct {float x, y; } Vec2f; -typedef struct {float x, y, z; } Vec3f; -typedef struct { float m[16];} P_Mat4; // column-major: m[col * 4 + row] -PODEF int p_ceil(float x); -PODEF float p_cosf(float x); -PODEF float p_sinf(float x); - -PODEF Vec2f p_vec2f(float, float); -PODEF Vec2f p_vec2f_add(Vec2f, Vec2f); -PODEF float p_vec2f_cross(Vec2f, Vec2f); -PODEF float p_vec2f_dot(Vec2f, Vec2f); -PODEF Vec2f p_vec2f_mult(Vec2f a, float b); -PODEF Vec2f p_vec2f_sub(Vec2f, Vec2f); -PODEF Vec2i p_vec2i(float, float); -PODEF Vec2i p_vec2i_add(Vec2i, Vec2i); -PODEF float p_vec2i_cross(Vec2i, Vec2i); -PODEF float p_vec2i_dot(Vec2i, Vec2i); -PODEF Vec2i p_vec2i_mult(Vec2i a, float b); -PODEF Vec2i p_vec2i_sub(Vec2i, Vec2i); - -PODEF Vec3f p_vec3f(float, float, float); - -PODEF P_Mat4 p_mat4_identity(void); -PODEF P_Mat4 p_mat4_mul(P_Mat4 a, P_Mat4 b); -PODEF P_Mat4 p_mat4_translate(Vec3f v); -PODEF P_Mat4 p_mat4_translate_by(P_Mat4 m, Vec3f v); -PODEF P_Mat4 p_mat4_rotate_x(float rad); -PODEF P_Mat4 p_mat4_rotate_x_by(P_Mat4 m, float rad); -PODEF P_Mat4 p_mat4_rotate_y(float rad); -PODEF P_Mat4 p_mat4_rotate_y_by(P_Mat4 m, float rad); -PODEF P_Mat4 p_mat4_rotate_z(float rad); -PODEF P_Mat4 p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z); -/* --- End of p_math.h --- */ -#endif - -#ifdef P_MODULE_ALLOC -/* --- Start of p_allocators.h --- */ -typedef struct Arena { - unsigned char *buf; - usize buf_len; - usize curr_offset; - usize prev_offset; -} Arena; - -PODEF void p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length); -PODEF void* p_arena_alloc_align(Arena *a, usize s, usize align); -PODEF void* p_arena_alloc(Arena *a, usize s); -PODEF void* p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align); -PODEF void* p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size); -PODEF void p_arena_free_all(Arena *a); -PODEF void p_arena_destroy(Arena* a); - -typedef struct { u8 padding; } Stack_Allocation_Header; -typedef struct Stack { - unsigned char *buf; - usize buf_len; - usize offset; -} Stack; - -PODEF void p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length); -PODEF usize calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size); -PODEF void* p_stack_alloc(Stack *s, usize len, usize alignment); -PODEF void p_stack_free(Stack *s, void *ptr); -PODEF void p_stack_free_all(Stack* s); -PODEF void p_stack_destroy(Stack* s); - -typedef struct Pool_Free_Node Pool_Free_Node; - -typedef struct Pool { - unsigned char *buf; - usize chunk_size; - usize pool_size; - Pool_Free_Node *head; -} Pool; - -struct Pool_Free_Node { struct Pool_Free_Node *next; }; -PODEF void p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align); -PODEF void* p_pool_alloc(Pool *p); -PODEF void p_pool_free(Pool *p, void *ptr); -PODEF void p_pool_free_all(Pool *p); -PODEF void p_pool_destroy(Pool *p); -/* --- End of p_allocators.h --- */ -#endif - -#endif /* _PODIUM_H_ */ - -#ifdef PODIUM_IMPLEMENTATION -#undef PODIUM_IMPLEMENTATION - -#if defined(PODIUM_SDL) -/* #include "p_platform_sdl.c" */ -#elif defined(PODIUM_WIN32) -/* --- Start of p_platform_win32.c --- */ -#define WIN32_LEAN_AND_MEAN -#include -#include -#include /* gamepad support */ - -typedef struct { - HWAVEOUT wave_out; - WAVEHDR wave_header; -} P_Win32Ctx; - -struct P_Window_Impl { - HWND hwnd; - HDC hdc; - BITMAPINFO bitmap_info; - int width; - int height; - bool closed; -}; - -P_Win32Ctx p_ctx = {0}; - -static inline P_KeyCode -_win32_translate_vkey(WPARAM vk) -{ - switch (vk) { - case VK_LEFT: return P_KEY_LEFT; - case VK_RIGHT: return P_KEY_RIGHT; - case VK_UP: return P_KEY_UP; - case VK_DOWN: return P_KEY_DOWN; - case VK_SPACE: return P_KEY_SPACE; - case VK_ESCAPE: return P_KEY_ESCAPE; - case VK_RETURN: return P_KEY_ENTER; - case 'A': return P_KEY_A; - case 'B': return P_KEY_B; - case 'C': return P_KEY_C; - case 'D': return P_KEY_D; - case 'E': return P_KEY_E; - case 'F': return P_KEY_F; - case 'G': return P_KEY_G; - case 'H': return P_KEY_H; - case 'I': return P_KEY_I; - case 'J': return P_KEY_J; - case 'K': return P_KEY_K; - case 'L': return P_KEY_L; - case 'M': return P_KEY_M; - case 'N': return P_KEY_N; - case 'O': return P_KEY_O; - case 'P': return P_KEY_P; - case 'Q': return P_KEY_Q; - case 'R': return P_KEY_R; - case 'S': return P_KEY_S; - case 'T': return P_KEY_T; - case 'U': return P_KEY_U; - case 'V': return P_KEY_V; - case 'W': return P_KEY_W; - case 'X': return P_KEY_X; - case 'Y': return P_KEY_Y; - case 'Z': return P_KEY_Z; - default: return P_KEY_UNKNOWN; - } -} - -POAPI u64 -p_get_time(void) -{ - LARGE_INTEGER count, freq; - QueryPerformanceCounter(&count); - QueryPerformanceFrequency(&freq); - return (u64)((count.QuadPart * 1000000000ULL) / freq.QuadPart); -} - -POAPI void -p_sleep_ns(i64 ns) -{ - /* windows sleep is millisecond-based........ */ - Sleep((DWORD)(ns / 1000000)); -} - -POAPI void -p_stdout(void *msg, usize bytes) -{ - HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD bytes_written; - WriteFile(stdout_handle, msg, bytes, &bytes_written, NULL); -} - -POAPI bool -p_file_exists(const char *path) -{ - DWORD attr = GetFileAttributesA(path); - return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); -} - -POAPI usize -p_file_size(const char *path) -{ - WIN32_FILE_ATTRIBUTE_DATA data; - if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) { - return data.nFileSizeLow; - } - return 0; -} - -PODEF void -p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) -{ - HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if (file != INVALID_HANDLE_VALUE) { - DWORD bytes_read; - ReadFile(file, buf_ptr, buf_size, &bytes_read, NULL); - CloseHandle(file); - } -} - -PODEF void -p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) -{ - HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (file != INVALID_HANDLE_VALUE) { - DWORD bytes_written; - WriteFile(file, buf_ptr, buf_size, &bytes_written, NULL); - CloseHandle(file); - } -} - -static LRESULT CALLBACK -_win32_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { - P_Window *win = (P_Window*)GetWindowLongPtrA(hwnd, GWLP_USERDATA); - switch (msg) { - case WM_CLOSE: - /* Handled in poll_event; mark as closed */ - if (win) win->closed = true; - return 0; - } - return DefWindowProcA(hwnd, msg, wparam, lparam); -} - -POAPI bool -p_window_open(P_Window *win, int width, int height, const char *title) -{ - HINSTANCE instance = GetModuleHandleA(NULL); - - WNDCLASSA wc = {0}; - wc.lpfnWndProc = _win32_wnd_proc; - wc.hInstance = instance; - wc.lpszClassName = "P_Window_Class"; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - - RegisterClassA(&wc); - - win->hwnd = CreateWindowExA(0, wc.lpszClassName, title, - WS_OVERLAPPEDWINDOW | WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, width, height, - NULL, NULL, instance, NULL); - - if (!win->hwnd) return false; - - SetWindowLongPtrA(win->hwnd, GWLP_USERDATA, (LONG_PTR)win); - win->hdc = GetDC(win->hwnd); - win->width = width; - win->height = height; - win->closed = false; - - return true; -} - -POAPI void -p_window_close(P_Window *win) -{ - if (!win) return; - if (win->hdc) ReleaseDC(win->hwnd, win->hdc); - if (win->hwnd) DestroyWindow(win->hwnd); - win->hdc = NULL; - win->hwnd = NULL; -} - -POAPI bool -p_window_is_open(P_Window *win) -{ - return win && win->hwnd != NULL; -} - -POAPI void -p_window_size(P_Window *win, int *window_width, int *window_height) -{ - if (!win) return; - RECT rect; - GetClientRect(win->hwnd, &rect); - *window_width = rect.right - rect.left; - *window_height = rect.bottom - rect.top; - win->width = *window_width; - win->height = *window_height; -} - -POAPI void -p_window_draw(P_Window *win, u32 *pixels, int width, int height) -{ - BITMAPINFO bmi = {0}; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = width; - bmi.bmiHeader.biHeight = -height; /* top-down */ - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - StretchDIBits(win->hdc, - 0, 0, win->width, win->height, - 0, 0, width, height, - pixels, &bmi, DIB_RGB_COLORS, SRCCOPY); -} - -POAPI bool -p_window_poll_event(P_Window *win, P_Event *ev) -{ - if (win->closed) { - win->closed = false; - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - } - - MSG msg; - while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { - switch (msg.message) { - - case WM_QUIT: - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - - /* Key press */ - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - ev->type = P_EVENT_KEY_DOWN; - ev->key.key = _win32_translate_vkey(msg.wParam); - return true; - - /* Key release */ - case WM_KEYUP: - case WM_SYSKEYUP: - ev->type = P_EVENT_KEY_UP; - ev->key.key = _win32_translate_vkey(msg.wParam); - return true; - - /* Mouse movement */ - case WM_MOUSEMOVE: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_MOVED; - ev->pointer.button = 0; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - /* Mouse buttons */ - case WM_LBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 1; /* 1 = LMB, matching X11 convention */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_LBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 1; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_RBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 3; /* 3 = RMB */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_RBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 3; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_MBUTTONDOWN: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = 2; /* 2 = MMB */ - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - case WM_MBUTTONUP: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = 2; - ev->pointer.x = (u32)(msg.lParam & 0xFFFF); - ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); - return true; - - /* Window resize */ - case WM_SIZE: - ev->type = P_EVENT_WINDOW_RESIZE; - ev->resize.width = (u32)(msg.lParam & 0xFFFF); - ev->resize.height = (u32)((msg.lParam >> 16) & 0xFFFF); - win->width = ev->resize.width; - win->height = ev->resize.height; - return true; - - default: - TranslateMessage(&msg); - DispatchMessageA(&msg); - break; - } - } - - ev->type = P_EVENT_NONE; - return false; -} - - -POAPI void -p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) -{ - (void)name; (void)desc; /* WinMM doesn't use a stream name */ - - WAVEFORMATEX wfx = {0}; - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nChannels = channels; - wfx.nSamplesPerSec = sample_rate; - wfx.wBitsPerSample = 16; - wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8; - wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; - - if (waveOutOpen(&p_ctx.wave_out, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) { - exit(1); - } -} - -POAPI void -p_audio_quit() -{ - if (p_ctx.wave_out) { - waveOutReset(p_ctx.wave_out); - waveOutClose(p_ctx.wave_out); - p_ctx.wave_out = NULL; - } -} - -POAPI void -p_audio_write(const i16 *samples, usize count) -{ - if (!p_ctx.wave_out) return; - - WAVEHDR header = {0}; - header.lpData = (LPSTR)samples; - header.dwBufferLength = (DWORD)(count * sizeof(i16)); - - waveOutPrepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); - waveOutWrite(p_ctx.wave_out, &header, sizeof(WAVEHDR)); - - /* wait for playback to finish (synchronous, mirrors pa_simple_drain) */ - while (!(header.dwFlags & WHDR_DONE)) Sleep(1); - - waveOutUnprepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); -} - -#ifdef P_MODULE_VULKAN - -#ifndef VK_USE_PLATFORM_WIN32_KHR -#define VK_USE_PLATFORM_WIN32_KHR -#endif -#include - -POAPI char** -p_vulkan_get_extensions() -{ - char **darr = NULL; - p_darray_push(darr, "VK_KHR_surface"); - p_darray_push(darr, "VK_KHR_win32_surface"); - return darr; -} - -POAPI bool -p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) -{ - if (!instance || !window || !window->hwnd || !out_surface) { - return false; - } - - VkWin32SurfaceCreateInfoKHR create_info = {0}; - create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - create_info.pNext = NULL; - create_info.flags = 0; - create_info.hwnd = window->hwnd; - create_info.hinstance = GetModuleHandleA(NULL); - - VkResult result = vkCreateWin32SurfaceKHR(instance, &create_info, allocator, out_surface); - return (result == VK_SUCCESS); -} -#endif -/* --- End of p_platform_win32.c --- */ -#elif defined(PODIUM_LINUX) -/* --- Start of p_platform_linux.c --- */ -/* linux */ -#include -#include -#include -#include -#include -#include -/* x11 */ -#include -#include -/* pulse audio */ -#include -#include -#include -/* joystick */ -#include - -typedef struct { - pa_simple *audio_stream; -} P_LinuxCtx; - -struct P_Window_Impl { - Display *display; - XImage *image; - Window window; - GC gc; - int screen; - int width; - int height; -}; - -P_LinuxCtx p_ctx = {0}; - -static inline P_KeyCode -_x11_translate_keycode(XEvent xev) -{ - static KeySym ks; - ks = XLookupKeysym(&xev.xkey, 0); - switch (ks) { - /* Arrow keys */ - case XK_Left: return P_KEY_LEFT; - case XK_Right: return P_KEY_RIGHT; - case XK_Up: return P_KEY_UP; - case XK_Down: return P_KEY_DOWN; - /* Common keys */ - case XK_space: return P_KEY_SPACE; - case XK_Escape: return P_KEY_ESCAPE; - case XK_Return: return P_KEY_ENTER; - case XK_A: case XK_a: return P_KEY_A; - case XK_B: case XK_b: return P_KEY_B; - case XK_C: case XK_c: return P_KEY_C; - case XK_D: case XK_d: return P_KEY_D; - case XK_E: case XK_e: return P_KEY_E; - case XK_F: case XK_f: return P_KEY_F; - case XK_G: case XK_g: return P_KEY_G; - case XK_H: case XK_h: return P_KEY_H; - case XK_I: case XK_i: return P_KEY_I; - case XK_J: case XK_j: return P_KEY_J; - case XK_K: case XK_k: return P_KEY_K; - case XK_L: case XK_l: return P_KEY_L; - case XK_M: case XK_m: return P_KEY_M; - case XK_N: case XK_n: return P_KEY_N; - case XK_O: case XK_o: return P_KEY_O; - case XK_P: case XK_p: return P_KEY_P; - case XK_Q: case XK_q: return P_KEY_Q; - case XK_R: case XK_r: return P_KEY_R; - case XK_S: case XK_s: return P_KEY_S; - case XK_T: case XK_t: return P_KEY_T; - case XK_U: case XK_u: return P_KEY_U; - case XK_V: case XK_v: return P_KEY_V; - case XK_W: case XK_w: return P_KEY_W; - case XK_X: case XK_x: return P_KEY_X; - case XK_Y: case XK_y: return P_KEY_Y; - case XK_Z: case XK_z: return P_KEY_Z; - default: return P_KEY_UNKNOWN; - } -} - -POAPI u64 -p_get_time(void) -{ - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec; -} - -POAPI void -p_sleep_ns(i64 ns) -{ - struct timespec ts; - ts.tv_sec = ns / 1000000000L; - ts.tv_nsec = ns % 1000000000L; - clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); -} - -POAPI void -p_stdout(void *msg, usize bytes) -{ - write(1, msg, bytes); -} - -POAPI bool -p_file_exists(const char *path) -{ - return (access(path, F_OK) == 0); -} - -POAPI usize -p_file_size(const char *path) -{ - struct stat st; - if (stat(path, &st) == 0) { - return st.st_size; - } - return 0; -} - -PODEF void -p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) -{ - int fd = open(path, O_RDONLY); - if (fd >= 0) { - read(fd, buf_ptr, (size_t)buf_size); - close(fd); - } -} - -PODEF void -p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) -{ - int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); - if (fd >= 0) { - write(fd, buf_ptr, (size_t)buf_size); - close(fd); - } -} - -POAPI bool -p_window_open(P_Window *win, int width, int height, const char *title) -{ - win->width = width; - win->height = height; - - XInitThreads(); - - win->display = XOpenDisplay(NULL); - if (win->display == NULL) { - PERROR("Cannot open display!"); - return false; - } - - int screen = DefaultScreen(win->display); - win->window = XCreateSimpleWindow( - win->display, - RootWindow(win->display, screen), - 0, 0, width, height, - 1, - BlackPixel(win->display, screen), - BlackPixel(win->display, screen) - ); - - win->screen = screen; - - XStoreName(win->display, win->window, title); - XSelectInput(win->display, win->window, - KeyPressMask | KeyReleaseMask | - ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | StructureNotifyMask); - XMapWindow(win->display, win->window); - - win->gc = DefaultGC(win->display, screen); - - Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False); - XSetWMProtocols(win->display, win->window, &wm_delete, 1); - - return true; -} - -POAPI void -p_window_close(P_Window *win) -{ - if (!win) return; - - if (win->image) { - // XDestroyImage(win->image); - win->image = NULL; - } - - if (win->display) { - Display *dpy = win->display; - Window w = win->window; - win->display = NULL; - win->window = 0; - - if (w) { - XDestroyWindow(dpy, w); - } - XSync(dpy, False); - } -} - -POAPI bool -p_window_is_open(P_Window *win) -{ - return win && win->display != NULL; -} - -POAPI void -p_window_size(P_Window *win, int *window_width, int *window_height) -{ - if (!win) return; - - XWindowAttributes attr; - XGetWindowAttributes(win->display, win->window, &attr); - *window_width = attr.width; - *window_height = attr.height; - - win->width = attr.width; - win->height = attr.height; -} - -POAPI void -p_window_draw(P_Window *win, u32 *pixels, int width, int height) -{ - int screen = win->screen; - - win->image = XCreateImage( - win->display, - DefaultVisual(win->display, screen), - DefaultDepth(win->display, screen), - ZPixmap, - 0, - (char*)pixels, - width, - height, - 32, - width * 4 - ); - - int window_width, window_height; - p_window_size(win, &window_width, &window_height); - - XPutImage( - win->display, - win->window, - win->gc, - win->image, - 0, 0, - 0, 0, - win->width, - win->height); - - XFlush(win->display); -} - -POAPI bool -p_window_poll_event(P_Window *win, P_Event *ev) -{ - if (XPending(win->display)) { - static XEvent xev; - XNextEvent(win->display, &xev); - - switch (xev.type) { - - case KeyPress: - ev->type = P_EVENT_KEY_DOWN; - ev->key.key = _x11_translate_keycode(xev); - return true; - - case KeyRelease: - ev->type = P_EVENT_KEY_UP; - ev->key.key = _x11_translate_keycode(xev); - return true; - - case ConfigureNotify: - ev->type = P_EVENT_WINDOW_RESIZE; - ev->resize.width = xev.xconfigure.width; - ev->resize.height = xev.xconfigure.height; - return true; - - case ClientMessage: - if ((Atom)xev.xclient.data.l[0] == - XInternAtom(win->display, "WM_DELETE_WINDOW", False)) { - ev->type = P_EVENT_WINDOW_CLOSE; - return true; - } - break; - - case MotionNotify: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_MOVED; - ev->pointer.button = 0; - ev->pointer.x = xev.xmotion.x; - ev->pointer.y = xev.xmotion.y; - return true; - - case ButtonPress: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_PRESSED; - ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */ - ev->pointer.x = xev.xbutton.x; - ev->pointer.y = xev.xbutton.y; - return true; - - case ButtonRelease: - ev->type = P_EVENT_POINTER; - ev->pointer.state = P_POINTER_RELEASED; - ev->pointer.button = xev.xbutton.button; - ev->pointer.x = xev.xbutton.x; - ev->pointer.y = xev.xbutton.y; - return true; - } - } - - ev->type = P_EVENT_NONE; - return false; -} - -POAPI void -p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) -{ - pa_sample_spec ss = { - .format = PA_SAMPLE_S16LE, - .rate = sample_rate, - .channels = channels, - }; - - int error; - p_ctx.audio_stream = pa_simple_new( - NULL, - name, - PA_STREAM_PLAYBACK, - NULL, - desc, - &ss, - NULL, - NULL, - &error - ); - - if (!p_ctx.audio_stream) { - PERROR("PulseAudio init failed: %s\n", pa_strerror(error)); - exit(1); - } -} - -POAPI void -p_audio_quit() -{ - if (p_ctx.audio_stream) { - pa_simple_free(p_ctx.audio_stream); - p_ctx.audio_stream = NULL; - } -} - -POAPI void -p_audio_write(const i16 *samples, usize count) -{ - if (!p_ctx.audio_stream) return; - - int error; - if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) { - PERROR("PulseAudio write failed: %s\n", pa_strerror(error)); - } - - pa_simple_drain(p_ctx.audio_stream, &error); -} - -#ifdef P_MODULE_VULKAN - -#ifndef VK_USE_PLATFORM_XLIB_KHR -#define VK_USE_PLATFORM_XLIB_KHR -#endif -#include - -POAPI char** -p_vulkan_get_extensions() -{ - char **darr = NULL; - p_darray_push(darr, "VK_KHR_surface"); - p_darray_push(darr, "VK_KHR_xcb_surface"); - p_darray_push(darr, "VK_KHR_xlib_surface"); - return darr; -} - -POAPI bool -p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) -{ - if (!instance || !window || !window->display || !window->window || !out_surface) { - return false; - } - - VkXlibSurfaceCreateInfoKHR create_info = {0}; - create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; - create_info.pNext = NULL; - create_info.flags = 0; - create_info.dpy = window->display; - create_info.window = window->window; - - VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface); - - return (result == VK_SUCCESS); -} -#endif -/* --- End of p_platform_linux.c --- */ -#endif /* linux */ - - -POAPI void* p_file_alloc(const char *path, unsigned long *buf_size) { - if (p_file_exists(path) == false) { - return NULL; - } - - usize file_size = p_file_size(path); - *buf_size = file_size; - - void *ptr = malloc(file_size); - if (ptr) { - p_file_load(path, ptr, file_size); // Load file to buffer. - } - return ptr; -} - - -/* - * Import Module Code - */ - -#ifdef P_MODULE_STRING -/* --- Start of p_string.c --- */ -PODEF char* -p_find_char(char *buf, char needle) -{ - while (buf && *buf != needle) buf++; - return buf; -} - -PODEF bool -p_strcmp(const char *a, const char *b) -{ - if (p_strlen(a) != p_strlen(b)) - return false; - - while (a && b && *a != '\0') { - if (*(a++) != *(b++)) return false; - } - - return true; -} - -PODEF int -p_strlen(const char *a) -{ - int i = 0; - while (*a++ != '\0') i++; - return i; -} - -PODEF int -p_strtoi(const char *str) -{ - int res = 0; - - while (*str) { - if (*str >= '0' && *str <= '9') { - res = res * 10 + (*str - '0'); - } else { - return res; - } - str++; - } - - return res; -} - -PODEF char* -p_strtok(const char *str, char c) -{ - while (*str != c && *str++ != '\0'); - return (*str == c) ? (char*) ++str : 0; -} - -PODEF P_StringView -p_strview(const char *cstr) -{ - P_StringView sv = {0}; - sv.cstr = cstr; - sv.len = p_strlen(cstr); - return sv; -} - -PODEF void -p_strview_chop_left(P_StringView *sv, u32 n) -{ - if (n > sv->len) n = sv->len; - sv->cstr += n; - sv->len -= n; -} - -PODEF void -p_strview_chop_right(P_StringView *sv, u32 n) -{ - if (n > sv->len) n = sv->len; - sv->len -= n; -} - -PODEF P_StringView -p_strview_chop_delim(P_StringView *sv, char delim) -{ - u32 i = 0; - while (i < sv->len && sv->cstr[i] != delim) { - i++; - } - - P_StringView tok; - if (i < sv->len) { - tok.cstr = sv->cstr; - tok.len = i; - p_strview_chop_left(sv, i + 1); - return tok; - } - - tok = *sv; - p_strview_chop_left(sv, sv->len); - return tok; -} - -PODEF P_StringView -p_strview_chop_type(P_StringView *sv, int(*istype)(int)) -{ - u32 i = 0; - while (i < sv->len && istype(sv->cstr[i])) { - i++; - } - - P_StringView tok; - if (i < sv->len) { - tok.cstr = sv->cstr; - tok.len = i; - p_strview_chop_left(sv, i + 1); - return tok; - } - - tok = *sv; - p_strview_chop_left(sv, sv->len); - return tok; -} - - -PODEF void -p_strview_trim_left(P_StringView *sv) -{ - while (sv->len > 0 && p_is_space(sv->cstr[0])) { - p_strview_chop_left(sv, 1); - } -} - -PODEF void -p_strview_trim_right(P_StringView *sv) -{ - while (sv->len > 0 && p_is_space(sv->cstr[sv->len-1])) { - p_strview_chop_right(sv, 1); - } -} - -PODEF void -p_strview_trim(P_StringView *sv) -{ - p_strview_trim_left(sv); - p_strview_trim_right(sv); -} - -PODEF int -p_is_space(int c) -{ - /* Checks for: space, form feed (\f), line feed (\n), - * carriage return (\r), horizontal tab (\t), vertical tab (\v) */ - return (c == ' ' || (c >= '\t' && c <= '\r')); -} - -PODEF int -p_is_digit(int c) -{ - return (c >= '0' && c <= '9'); -} - -PODEF int -p_is_alpha(int c) -{ - return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); -} - -PODEF int -p_is_alnum(int c) -{ - return (p_is_alpha(c) || p_is_digit(c)); -} - -PODEF int -p_is_hex(int c) -{ - return (p_is_digit(c) || - (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F')); -} - -PODEF int -p_is_upper(int c) -{ - return (c >= 'A' && c <= 'Z'); -} - -PODEF int -p_is_lower(int c) -{ - return (c >= 'a' && c <= 'z'); -} - -PODEF int -p_to_lower(int c) -{ - if (p_is_upper(c)) return (c + ('a' - 'A')); - return c; -} - -PODEF int -p_to_upper(int c) -{ - if (p_is_lower(c)) return (c - ('a' - 'A')); - return c; -} -/* --- End of p_string.c --- */ -#endif - -#ifdef P_MODULE_MATH -/* --- Start of p_math.c --- */ -PODEF -int p_ceil(float x) -{ - int base = (int) x; - return (x > base) ? base + 1 : base; -} - -PODEF float -p_cosf(float x) -{ - x += 1.57079632679f; - - while (x > P_PI) x -= P_PI2; - while (x < -P_PI) x += P_PI2; - - const float B = 4.0f / P_PI; - const float C = -4.0f / P_PI_POW2; - - float y = B * x + C * x * (x < 0 ? -x : x); - - const float P = 0.225f; - y = P * (y * (y < 0 ? -y : y) - y) + y; - - return y; -} - -PODEF float -p_sinf(float x) -{ - while (x > P_PI) x -= P_PI2; - while (x < -P_PI) x += P_PI2; - - const float B = 4.0f / P_PI; - const float C = -4.0f / (P_PI_POW2); - - float y = B * x + C * x * (x < 0 ? -x : x); - - const float P = 0.225f; - y = P * (y * (y < 0 ? -y : y) - y) + y; - - return y; -} - -PODEF Vec2f -p_vec2f(float x, float y) -{ - return (Vec2f) { .x = x, .y = y }; -} - -PODEF Vec2f -p_vec2f_add(Vec2f a, Vec2f b) -{ - return (Vec2f) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -PODEF float -p_vec2f_cross(Vec2f a, Vec2f b) -{ - // axby − aybx - return a.x * b.y - a.y * b.x; -} - -PODEF float -p_vec2f_dot(Vec2f a, Vec2f b) -{ - // a · b = ax × bx + ay × by - return a.x * b.x + a.y * b.y; -} - -PODEF Vec2f -p_vec2f_mult(Vec2f a, float b) -{ - return (Vec2f) { - .x = a.x * b, - .y = a.y * b, - }; -} - -PODEF Vec2f -p_vec2f_sub(Vec2f a, Vec2f b) -{ - return (Vec2f) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -PODEF Vec2i -p_vec2i(float x, float y) -{ - return (Vec2i) { .x = x, .y = y }; -} - -PODEF Vec2i -p_vec2i_add(Vec2i a, Vec2i b) -{ - return (Vec2i) { - .x = a.x + b.x, - .y = a.y + b.y, - }; -} - -PODEF float -p_vec2i_cross(Vec2i a, Vec2i b) -{ - // axby − aybx - return a.x * b.y - a.y * b.x; -} - -PODEF float -p_vec2i_dot(Vec2i a, Vec2i b) -{ - // a · b = ax × bx + ay × by - return a.x * b.x + a.y * b.y; -} - -PODEF Vec2i -p_vec2i_mult(Vec2i a, float b) -{ - return (Vec2i) { - .x = a.x * b, - .y = a.y * b, - }; -} - -PODEF Vec2i -p_vec2i_sub(Vec2i a, Vec2i b) -{ - return (Vec2i) { - .x = a.x - b.x, - .y = a.y - b.y, - }; -} - -PODEF Vec3f -p_vec3f(float x, float y, float z) -{ - return (Vec3f) { .x = x, .y = y, .z = z }; -} - -PODEF P_Mat4 -p_mat4_identity(void) -{ - P_Mat4 res = {0}; - res.m[0] = 1.0f; - res.m[5] = 1.0f; - res.m[10] = 1.0f; - res.m[15] = 1.0f; - return res; -} - -PODEF P_Mat4 -p_mat4_mul(P_Mat4 a, P_Mat4 b) -{ - P_Mat4 res = {0}; - for (int col = 0; col < 4; ++col) { - for (int row = 0; row < 4; ++row) { - float sum = 0.0f; - for (int k = 0; k < 4; ++k) { - sum += a.m[k * 4 + row] * b.m[col * 4 + k]; - } - res.m[col * 4 + row] = sum; - } - } - return res; -} - -PODEF P_Mat4 -p_mat4_translate(Vec3f v) -{ - P_Mat4 res = p_mat4_identity(); - res.m[12] = v.x; - res.m[13] = v.y; - res.m[14] = v.z; - return res; -} - -PODEF P_Mat4 -p_mat4_translate_by(P_Mat4 m, Vec3f v) -{ - P_Mat4 res = m; - res.m[12] = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12]; - res.m[13] = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13]; - res.m[14] = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14]; - res.m[15] = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15]; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_x(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[5] = c; - res.m[6] = s; - res.m[9] = -s; - res.m[10] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_x_by(P_Mat4 m, float rad) -{ - return p_mat4_mul(m, p_mat4_rotate_x(rad)); -} - -PODEF P_Mat4 -p_mat4_rotate_y(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[0] = c; - res.m[2] = -s; - res.m[8] = s; - res.m[10] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_rotate_y_by(P_Mat4 m, float rad) -{ - return p_mat4_mul(m, p_mat4_rotate_y(rad)); -} - -PODEF P_Mat4 -p_mat4_rotate_z(float rad) -{ - P_Mat4 res = p_mat4_identity(); - float c = p_cosf(rad); - float s = p_sinf(rad); - res.m[0] = c; - res.m[1] = s; - res.m[4] = -s; - res.m[5] = c; - return res; -} - -PODEF P_Mat4 -p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z) -{ - P_Mat4 res = {0}; - float tan_half_fov = p_sinf(fov_rad * 0.5f) / p_cosf(fov_rad * 0.5f); - - res.m[0] = 1.0f / (aspect * tan_half_fov); - res.m[5] = 1.0f / tan_half_fov; - res.m[10] = far_z / (near_z - far_z); - res.m[11] = -1.0f; - res.m[14] = (near_z * far_z) / (near_z - far_z); - return res; -} -/* --- End of p_math.c --- */ -#endif - -#ifdef P_MODULE_ALLOC -/* --- Start of p_allocators.c --- */ -PODEF bool -is_power_of_two(uintptr x) { - return (x & (x-1)) == 0; -} - -PODEF uintptr -align_forward(uintptr ptr, usize align) -{ - uintptr p, a, remainder; - assert(is_power_of_two(align)); - - p = ptr; - a = (uintptr)align; - remainder = p & (a-1); - - return (remainder != 0) ? p + a - remainder : p; -} - -PODEF void -p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length) -{ - a->buf = (unsigned char *)backing_buffer; - a->buf_len = backing_buffer_length; - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void* -p_arena_alloc_align(Arena *a, usize s, usize align) -{ - - // Align 'curr_offset' forward to the specified alignment - uintptr curr_ptr = (uintptr)a->buf + (uintptr)a->curr_offset; - uintptr offset = align_forward(curr_ptr, align); - offset -= (uintptr)a->buf; // Change to relative offset - - // Check to see if the backing memory has space left - if (offset+s <= a->buf_len) { - void *ptr = &a->buf[offset]; - a->prev_offset = offset; - a->curr_offset = offset+s; - - memset(ptr, 0, s); - return ptr; - } - - return NULL; -} - -PODEF void* -p_arena_alloc(Arena *a, usize s) { - return p_arena_alloc_align(a, s, DEFAULT_ALIGNMENT); -} - -PODEF void* -p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align) -{ - assert(is_power_of_two(align)); - unsigned char *old_mem = (unsigned char *)old_memory; - - if (old_mem == NULL || old_size == 0) { - return p_arena_alloc_align(a, new_size, align); - } else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) { - if (a->buf+a->prev_offset == old_mem) { - a->curr_offset = a->prev_offset + new_size; - if (new_size > old_size) { - // Zero the new memory by default - memset(&a->buf[a->curr_offset], 0, new_size-old_size); - } - return old_memory; - } else { - void *new_memory = p_arena_alloc_align(a, new_size, align); - size_t copy_size = old_size < new_size ? old_size : new_size; - memmove(new_memory, old_memory, copy_size); - return new_memory; - } - - } else { - assert(0 && "Memory is out of bounds of the buffer in this arena"); - return NULL; - } -} - -PODEF void* -p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size) -{ - return p_arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT); -} - -PODEF void -p_arena_free_all(Arena *a) -{ - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void -p_arena_destroy(Arena* a) -{ - a->buf_len = 0; - a->curr_offset = 0; - a->prev_offset = 0; -} - -PODEF void -p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length) -{ - s->buf = (unsigned char*) backing_buffer; - s->buf_len = backing_buffer_length; - s->offset = 0; -} - -PODEF usize -calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size) -{ - uintptr p, a, modulo, padding, needed_space; - - assert(is_power_of_two(alignment)); - - p = ptr; - a = alignment; - modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two - - padding = 0; - needed_space = 0; - - if (modulo != 0) { // Same logic as 'align_forward' - padding = a - modulo; - } - - needed_space = (uintptr)header_size; - - if (padding < needed_space) { - needed_space -= padding; - - if ((needed_space & (a-1)) != 0) { - padding += a * (1+(needed_space/a)); - } else { - padding += a * (needed_space/a); - } - } - - return (usize)padding; -} - -PODEF void* -p_stack_alloc(Stack *s, usize len, usize alignment) -{ - uintptr curr_addr, next_addr; - usize padding; - Stack_Allocation_Header *header; - - assert(is_power_of_two(alignment)); - - if (alignment > 128) { - // As the padding is 8 bits (1 byte), the largest alignment that can - // be used is 128 bytes - alignment = 128; - } - - curr_addr = (uintptr)s->buf + (uintptr)s->offset; - padding = calc_padding_with_header(curr_addr, (uintptr)alignment, sizeof(Stack_Allocation_Header)); - if (s->offset + padding + len > s->buf_len) { - // Stack allocator is out of memory - return NULL; - } - s->offset += padding; - - next_addr = curr_addr + (uintptr)padding; - header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header)); - header->padding = (u8)padding; - - s->offset += len; - - return memset((void *)next_addr, 0, len); -} - -PODEF void -p_stack_free(Stack *s, void *ptr) -{ - if (ptr != NULL) { - uintptr start, end, curr_addr; - Stack_Allocation_Header *header; - usize prev_offset; - - start = (uintptr)s->buf; - end = start + (uintptr)s->buf_len; - curr_addr = (uintptr)ptr; - - if (!(start <= curr_addr && curr_addr < end)) { - assert(0 && "Out of bounds memory address passed to stack allocator (free)"); - return; - } - - if (curr_addr >= start+(uintptr)s->offset) { - // Allow double frees - return; - } - - header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header)); - prev_offset = (usize)(curr_addr - (uintptr)header->padding - start); - - s->offset = prev_offset; - } -} - -PODEF void -p_stack_free_all(Stack* s) -{ - s->offset = 0; -} - -PODEF void -p_stack_destroy(Stack* s) -{ - s->buf = 0; - s->buf_len = 0; - s->offset = 0; -} - -PODEF void -p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align) -{ - /* align start */ - uintptr start = (uintptr) buf; - uintptr aligned = align_forward(start, chunk_align); - buf_len -= (aligned - start); - - /* align chunk usize */ - chunk_size = (usize) align_forward(chunk_size, chunk_align); - - assert(chunk_size >= sizeof(Pool_Free_Node) && "Chunk usize is too small"); - assert(buf_len >= chunk_size && "Backing buffer length is smaller than the chunk size"); - - p->buf = (unsigned char *) buf; - p->pool_size = buf_len; - p->chunk_size = chunk_size; - p->head = NULL; - - p_pool_free_all(p); -} - -PODEF void* -p_pool_alloc(Pool *p) -{ - Pool_Free_Node *node = p->head; - - assert(node != NULL && "Pool allocator has no free memory"); - - p->head = p->head->next; - - return memset(node, 0 , p->chunk_size); -} - -PODEF void -p_pool_free(Pool *p, void *ptr) -{ - Pool_Free_Node *node; - - void *start = p->buf; - void *end = &p->buf[p->pool_size]; - - if (ptr == NULL) { - return; - } - - if (!(start <= ptr && ptr < end)) { - assert(0 && "Memory is out of bounds of the buffer in this pool"); - return; - } - - node = (Pool_Free_Node *)ptr; - node->next = p->head; - p->head = node; -} - -PODEF void -p_pool_free_all(Pool *p) -{ - usize chunk_count = p->pool_size / p->chunk_size; - usize i; - - for (i = 0; i < chunk_count; i++) { - void *ptr = &p->buf[i * p->chunk_size]; - Pool_Free_Node *node = (Pool_Free_Node *)ptr; - - // Push free node onto thte free list - node->next = p->head; - p->head = node; - } -} - -PODEF void -p_pool_destroy(Pool *p) -{ - p->buf = NULL; - p->pool_size = 0; - p->chunk_size = 0; - p->head = NULL; -} - -/* --- End of p_allocators.c --- */ -#endif - -#define P_MAX_PRINTF_SIZE 1024 -PODEF void -p_log_printf(P_Log_Level level, const char* src, ...) -{ - static char out_message[P_MAX_PRINTF_SIZE]; - static u32 offset = P_PREFIX_LEN + 1; - - memcpy(out_message, p_prefix[level], P_PREFIX_LEN); /* prefixes have a fixed len */ - out_message[P_PREFIX_LEN] = ' '; /* write space */ - - __builtin_va_list arg_ptr; - va_start(arg_ptr, src); - int len = vsnprintf(out_message+offset, P_MAX_PRINTF_SIZE-offset, src, arg_ptr); // append message - va_end(arg_ptr); - - if (len < 0) len = 0; - usize total_len = offset + len; - if (total_len > P_MAX_PRINTF_SIZE) { - total_len = P_MAX_PRINTF_SIZE; - } - - p_stdout(out_message, total_len); - p_stdout("\n", 1); -} - -#endif // PODIUM_IMPLEMENTATION diff --git a/Podium/podium_header.h b/Podium/podium_header.h deleted file mode 100644 index 6407f62..0000000 --- a/Podium/podium_header.h +++ /dev/null @@ -1,258 +0,0 @@ -/* =========================================================================== - * PODIUM - Kitchen Sink - Copyright (c) 2026 Vasco Alves - * --------------------------------------------------------------------------- - * To stand on the shoulders of giants... Giants must stand still!" - * --------------------------------------------------------------------------- - * PREFIX: P_ (types) or p_ (functions & variables) - * --------------------------------------------------------------------------- - * COMPILE TIME FLAGS - * ._________________________________________________________________________, - * | Name | Description | - * |-----------------|-------------------------------------------------------| - * | PODIUM_WIN32 | Used to explicitly compile for Windows | - * | PODIUM_LINUX | Used to explicitly compile for Linux and FreeBSD. | - * '-------------------------------------------------------------------------' - * MODULES - * ._________________________________________________________________________, - * | Name | Description | - * |-----------------|-------------------------------------------------------| - * | P_MODULE_VULKAN | Gives access to vulkan related utilities. | - * | P_MODULE_STRING | Include string manipulation utilities. | - * | P_MODULE_MATH | Trigonometry and linear algebra functions. | - * | P_MODULE_ALLOC | Memory allocators: Arena, Stack & Pool. | - * '-------------------------------------------------------------------------' - * =========================================================================== */ - -#ifndef _PODIUM_H_ -#define _PODIUM_H_ - -#define PODIUM_MAJOR "0" // needs more revisions and use to reach 1.0 status -#define PODIUM_MINOR "2" // vulkan module -#define PODIUM_PATCH "2" // gamepad - -#define POAPI static inline -#define PODEF static inline - -/* - * Auto-detect platforms. - */ - -/* Windows */ -#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) - #ifndef PODIUM_WIN32 - #define PODIUM_WIN32 - #endif - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif -/* Apple */ -#elif defined(__APPLE__) || defined(__MACH__) - #include - #ifndef PODIUM_APPLE - #define PODIUM_APPLE - #endif - #if TARGET_OS_IPHONE - #define PODIUM_IOS - #else - #define PODIUM_MACOS - #endif -/* Linux / BSD */ -#elif defined(__linux__) || defined(__FreeBSD__) - #ifndef PODIUM_LINUX - #define PODIUM_LINUX - #endif - #ifndef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 200112L - #endif -#endif - -#include -#include -#include -#include /* memcpy */ -#include /* vsnprintf */ - -/* - * NOTE: The comments are used by the build - * script to know what includes to inline into the - * final single header file! - */ - -#include "p_types.h" // -#include "p_log.h" // -#include "p_ds.h" // - -/* - * Main Platform Types (Keyboard, Events, Context, etc.) - */ - -typedef enum { - P_KEYMOD_ALT = 0, - P_KEYMOD_SHIFT, - P_KEYMOD_CTRL, - P_KEYMOD_CAPS, -} P_KeyMod; - -typedef enum { - P_KEY_UNKNOWN = 0, - P_KEY_UP, P_KEY_DOWN, P_KEY_LEFT, P_KEY_RIGHT, - P_KEY_SPACE, P_KEY_ESCAPE, P_KEY_ENTER, - P_KEY_A, P_KEY_B, P_KEY_C, P_KEY_D, P_KEY_E, P_KEY_F, P_KEY_G, P_KEY_H, P_KEY_I, - P_KEY_J, P_KEY_K, P_KEY_L, P_KEY_M, P_KEY_N, P_KEY_O, P_KEY_P, P_KEY_Q, P_KEY_R, - P_KEY_S, P_KEY_T, P_KEY_U, P_KEY_V, P_KEY_W, P_KEY_X, P_KEY_Y, P_KEY_Z, -} P_KeyCode; - -/* Platform event type */ -typedef enum { - P_EVENT_NONE = 0, - P_EVENT_KEY_DOWN, - P_EVENT_KEY_UP, - P_EVENT_WINDOW_CLOSE, - P_EVENT_WINDOW_RESIZE, - P_EVENT_POINTER, - P_EVENT_CONTROLLER_CONNECTED, - P_EVENT_CONTROLLER_DISCONNECTED, -} P_EvenType; - -typedef enum { - P_POINTER_MOVED = 0, - P_POINTER_PRESSED, - P_POINTER_RELEASED -} P_PointerState; - -typedef struct { - P_EvenType type; - union { - struct {P_KeyCode key; P_KeyMod mod; } key; - struct { u32 width, height; } resize; - struct { P_PointerState state; u32 button; u32 x, y; } pointer; - }; -} P_Event; - -/* - * Platform dependent functions to be implemented by each layer. - */ - -POAPI u64 p_get_time(void); // Get time in nanoseconds. -POAPI void p_sleep_ns(i64 ns); // Sleep for nanoseconds. - -POAPI void p_stdout(void *msg, usize bytes); // Print buffer to stdout. -PODEF void p_log_printf(P_Log_Level level, const char* src, ...); // Print buffer to stdout. - - -POAPI void* p_file_alloc(const char *path, unsigned long *buf_size); -POAPI bool p_file_exists(const char *path); // Check if file exists. -POAPI usize p_file_size(const char *path); // Check file size. -POAPI void p_file_load(const char *path, void *buf_ptr, unsigned long buf_size); // Load file to buffer. -PODEF void p_file_write(const char *path, void *buf_ptr, unsigned long buf_size); // Write buffer to file. - -typedef struct P_Window_Impl P_Window; -POAPI bool p_window_open(P_Window *win, int width, int height, const char *title); // Opens a window. Returns false on failure. -POAPI void p_window_close(P_Window *win); // Close window. -POAPI bool p_window_is_open(P_Window *win); // Check if window is open. -POAPI void p_window_size(P_Window *win, int *window_width, int *window_height); // Get window size. -POAPI void p_window_draw(P_Window *win, u32 *pixels, int width, int height); // Draw directly to the window's buffer. Normally much slower than using the GPU. -POAPI bool p_window_poll_event(P_Window *win, P_Event *ev); // Returns true while there are events to poll. Write event data to a pointer. - -POAPI void p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc); -POAPI void p_audio_quit(); -POAPI void p_audio_write(const i16 *samples, usize count); - -/* - * The Vulkan Module is platform specific - */ - -#ifdef P_MODULE_VULKAN -#include -POAPI char** p_vulkan_get_extensions(); -POAPI bool p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface); -#endif - -/* - * The Remaining Modules are PLATFORM INDEPENDENT! - */ - -#ifdef P_MODULE_STRING -#include // -#endif - -#ifdef P_MODULE_MATH -#include // -#endif - -#ifdef P_MODULE_ALLOC -#include // -#endif - -#endif /* _PODIUM_H_ */ - -#ifdef PODIUM_IMPLEMENTATION -#undef PODIUM_IMPLEMENTATION - -#if defined(PODIUM_SDL) -/* #include "p_platform_sdl.c" */ -#elif defined(PODIUM_WIN32) -#include "p_platform_win32.c" // -#elif defined(PODIUM_LINUX) -#include "p_platform_linux.c" // -#endif /* linux */ - - -POAPI void* p_file_alloc(const char *path, unsigned long *buf_size) { - if (p_file_exists(path) == false) { - return NULL; - } - - usize file_size = p_file_size(path); - *buf_size = file_size; - - void *ptr = malloc(file_size); - if (ptr) { - p_file_load(path, ptr, file_size); // Load file to buffer. - } - return ptr; -} - - -/* - * Import Module Code - */ - -#ifdef P_MODULE_STRING -#include // -#endif - -#ifdef P_MODULE_MATH -#include // -#endif - -#ifdef P_MODULE_ALLOC -#include // -#endif - -#define P_MAX_PRINTF_SIZE 1024 -PODEF void -p_log_printf(P_Log_Level level, const char* src, ...) -{ - static char out_message[P_MAX_PRINTF_SIZE]; - static u32 offset = P_PREFIX_LEN + 1; - - memcpy(out_message, p_prefix[level], P_PREFIX_LEN); /* prefixes have a fixed len */ - out_message[P_PREFIX_LEN] = ' '; /* write space */ - - __builtin_va_list arg_ptr; - va_start(arg_ptr, src); - int len = vsnprintf(out_message+offset, P_MAX_PRINTF_SIZE-offset, src, arg_ptr); // append message - va_end(arg_ptr); - - if (len < 0) len = 0; - usize total_len = offset + len; - if (total_len > P_MAX_PRINTF_SIZE) { - total_len = P_MAX_PRINTF_SIZE; - } - - p_stdout(out_message, total_len); - p_stdout("\n", 1); -} - -#endif // PODIUM_IMPLEMENTATION diff --git a/README.md b/README.md index dfde148..846362e 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,15 @@ Use at your own risk. | Name | Description | | ------ | ------------------------------------------ | -| Podium | Platform layer & kitchen sink. | -| Peak | Automagic cross-platform dark magic. | +| Peak | Single-header platform library that automatically links the write system libraries. | +| Draw | Simple RGBA software raster | | Poof | Build software without going insane. | | Rend | Modern graphics API layer. | | Cool | Render HTML by calling functions. | ## Maturity -Rend and Podium are the most mature. Some libraries are still in development. Check the version +Rend and Peak are the most mature. Some libraries are still in development. Check the version included in each header file. Be encouraged and not discouraged if a library "isn't updated," it's probably because it works. @@ -30,3 +30,12 @@ because it works. Documentation can be found on [godgun.net](https://godgun.net/docs) (results may vary). We recommend just reading the header files. It's all there. + +## Archive + +Archived projects are incomplete with no intentions of being completed +or just have been succeeded. + +| Name | Description | +| ------ | ------------------------------------------ | +| Podium | Platform layer & kitchen sink. Underrated. | diff --git a/archive/Podium/README.md b/archive/Podium/README.md new file mode 100644 index 0000000..fccb70d --- /dev/null +++ b/archive/Podium/README.md @@ -0,0 +1,9 @@ +# Podium + +Single header platform library with batteries included. + +NOTE TO SELF: Stop trying to redesign your own library! +This shit works! Podium stands on business!!! + +The library does it's best to auto-detect your operating system +and the include the correct OS code for that platform. diff --git a/archive/Podium/p_allocators.c b/archive/Podium/p_allocators.c new file mode 100644 index 0000000..7e59807 --- /dev/null +++ b/archive/Podium/p_allocators.c @@ -0,0 +1,301 @@ +PODEF bool +is_power_of_two(uintptr x) { + return (x & (x-1)) == 0; +} + +PODEF uintptr +align_forward(uintptr ptr, usize align) +{ + uintptr p, a, remainder; + assert(is_power_of_two(align)); + + p = ptr; + a = (uintptr)align; + remainder = p & (a-1); + + return (remainder != 0) ? p + a - remainder : p; +} + +PODEF void +p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length) +{ + a->buf = (unsigned char *)backing_buffer; + a->buf_len = backing_buffer_length; + a->curr_offset = 0; + a->prev_offset = 0; +} + +PODEF void* +p_arena_alloc_align(Arena *a, usize s, usize align) +{ + + // Align 'curr_offset' forward to the specified alignment + uintptr curr_ptr = (uintptr)a->buf + (uintptr)a->curr_offset; + uintptr offset = align_forward(curr_ptr, align); + offset -= (uintptr)a->buf; // Change to relative offset + + // Check to see if the backing memory has space left + if (offset+s <= a->buf_len) { + void *ptr = &a->buf[offset]; + a->prev_offset = offset; + a->curr_offset = offset+s; + + memset(ptr, 0, s); + return ptr; + } + + return NULL; +} + +PODEF void* +p_arena_alloc(Arena *a, usize s) { + return p_arena_alloc_align(a, s, DEFAULT_ALIGNMENT); +} + +PODEF void* +p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align) +{ + assert(is_power_of_two(align)); + unsigned char *old_mem = (unsigned char *)old_memory; + + if (old_mem == NULL || old_size == 0) { + return p_arena_alloc_align(a, new_size, align); + } else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) { + if (a->buf+a->prev_offset == old_mem) { + a->curr_offset = a->prev_offset + new_size; + if (new_size > old_size) { + // Zero the new memory by default + memset(&a->buf[a->curr_offset], 0, new_size-old_size); + } + return old_memory; + } else { + void *new_memory = p_arena_alloc_align(a, new_size, align); + size_t copy_size = old_size < new_size ? old_size : new_size; + memmove(new_memory, old_memory, copy_size); + return new_memory; + } + + } else { + assert(0 && "Memory is out of bounds of the buffer in this arena"); + return NULL; + } +} + +PODEF void* +p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size) +{ + return p_arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT); +} + +PODEF void +p_arena_free_all(Arena *a) +{ + a->curr_offset = 0; + a->prev_offset = 0; +} + +PODEF void +p_arena_destroy(Arena* a) +{ + a->buf_len = 0; + a->curr_offset = 0; + a->prev_offset = 0; +} + +PODEF void +p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length) +{ + s->buf = (unsigned char*) backing_buffer; + s->buf_len = backing_buffer_length; + s->offset = 0; +} + +PODEF usize +calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size) +{ + uintptr p, a, modulo, padding, needed_space; + + assert(is_power_of_two(alignment)); + + p = ptr; + a = alignment; + modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two + + padding = 0; + needed_space = 0; + + if (modulo != 0) { // Same logic as 'align_forward' + padding = a - modulo; + } + + needed_space = (uintptr)header_size; + + if (padding < needed_space) { + needed_space -= padding; + + if ((needed_space & (a-1)) != 0) { + padding += a * (1+(needed_space/a)); + } else { + padding += a * (needed_space/a); + } + } + + return (usize)padding; +} + +PODEF void* +p_stack_alloc(Stack *s, usize len, usize alignment) +{ + uintptr curr_addr, next_addr; + usize padding; + Stack_Allocation_Header *header; + + assert(is_power_of_two(alignment)); + + if (alignment > 128) { + // As the padding is 8 bits (1 byte), the largest alignment that can + // be used is 128 bytes + alignment = 128; + } + + curr_addr = (uintptr)s->buf + (uintptr)s->offset; + padding = calc_padding_with_header(curr_addr, (uintptr)alignment, sizeof(Stack_Allocation_Header)); + if (s->offset + padding + len > s->buf_len) { + // Stack allocator is out of memory + return NULL; + } + s->offset += padding; + + next_addr = curr_addr + (uintptr)padding; + header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header)); + header->padding = (u8)padding; + + s->offset += len; + + return memset((void *)next_addr, 0, len); +} + +PODEF void +p_stack_free(Stack *s, void *ptr) +{ + if (ptr != NULL) { + uintptr start, end, curr_addr; + Stack_Allocation_Header *header; + usize prev_offset; + + start = (uintptr)s->buf; + end = start + (uintptr)s->buf_len; + curr_addr = (uintptr)ptr; + + if (!(start <= curr_addr && curr_addr < end)) { + assert(0 && "Out of bounds memory address passed to stack allocator (free)"); + return; + } + + if (curr_addr >= start+(uintptr)s->offset) { + // Allow double frees + return; + } + + header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header)); + prev_offset = (usize)(curr_addr - (uintptr)header->padding - start); + + s->offset = prev_offset; + } +} + +PODEF void +p_stack_free_all(Stack* s) +{ + s->offset = 0; +} + +PODEF void +p_stack_destroy(Stack* s) +{ + s->buf = 0; + s->buf_len = 0; + s->offset = 0; +} + +PODEF void +p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align) +{ + /* align start */ + uintptr start = (uintptr) buf; + uintptr aligned = align_forward(start, chunk_align); + buf_len -= (aligned - start); + + /* align chunk usize */ + chunk_size = (usize) align_forward(chunk_size, chunk_align); + + assert(chunk_size >= sizeof(Pool_Free_Node) && "Chunk usize is too small"); + assert(buf_len >= chunk_size && "Backing buffer length is smaller than the chunk size"); + + p->buf = (unsigned char *) buf; + p->pool_size = buf_len; + p->chunk_size = chunk_size; + p->head = NULL; + + p_pool_free_all(p); +} + +PODEF void* +p_pool_alloc(Pool *p) +{ + Pool_Free_Node *node = p->head; + + assert(node != NULL && "Pool allocator has no free memory"); + + p->head = p->head->next; + + return memset(node, 0 , p->chunk_size); +} + +PODEF void +p_pool_free(Pool *p, void *ptr) +{ + Pool_Free_Node *node; + + void *start = p->buf; + void *end = &p->buf[p->pool_size]; + + if (ptr == NULL) { + return; + } + + if (!(start <= ptr && ptr < end)) { + assert(0 && "Memory is out of bounds of the buffer in this pool"); + return; + } + + node = (Pool_Free_Node *)ptr; + node->next = p->head; + p->head = node; +} + +PODEF void +p_pool_free_all(Pool *p) +{ + usize chunk_count = p->pool_size / p->chunk_size; + usize i; + + for (i = 0; i < chunk_count; i++) { + void *ptr = &p->buf[i * p->chunk_size]; + Pool_Free_Node *node = (Pool_Free_Node *)ptr; + + // Push free node onto thte free list + node->next = p->head; + p->head = node; + } +} + +PODEF void +p_pool_destroy(Pool *p) +{ + p->buf = NULL; + p->pool_size = 0; + p->chunk_size = 0; + p->head = NULL; +} + diff --git a/archive/Podium/p_allocators.h b/archive/Podium/p_allocators.h new file mode 100644 index 0000000..72bae56 --- /dev/null +++ b/archive/Podium/p_allocators.h @@ -0,0 +1,44 @@ +typedef struct Arena { + unsigned char *buf; + usize buf_len; + usize curr_offset; + usize prev_offset; +} Arena; + +PODEF void p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length); +PODEF void* p_arena_alloc_align(Arena *a, usize s, usize align); +PODEF void* p_arena_alloc(Arena *a, usize s); +PODEF void* p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align); +PODEF void* p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size); +PODEF void p_arena_free_all(Arena *a); +PODEF void p_arena_destroy(Arena* a); + +typedef struct { u8 padding; } Stack_Allocation_Header; +typedef struct Stack { + unsigned char *buf; + usize buf_len; + usize offset; +} Stack; + +PODEF void p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length); +PODEF usize calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size); +PODEF void* p_stack_alloc(Stack *s, usize len, usize alignment); +PODEF void p_stack_free(Stack *s, void *ptr); +PODEF void p_stack_free_all(Stack* s); +PODEF void p_stack_destroy(Stack* s); + +typedef struct Pool_Free_Node Pool_Free_Node; + +typedef struct Pool { + unsigned char *buf; + usize chunk_size; + usize pool_size; + Pool_Free_Node *head; +} Pool; + +struct Pool_Free_Node { struct Pool_Free_Node *next; }; +PODEF void p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align); +PODEF void* p_pool_alloc(Pool *p); +PODEF void p_pool_free(Pool *p, void *ptr); +PODEF void p_pool_free_all(Pool *p); +PODEF void p_pool_destroy(Pool *p); diff --git a/archive/Podium/p_ds.h b/archive/Podium/p_ds.h new file mode 100644 index 0000000..8731975 --- /dev/null +++ b/archive/Podium/p_ds.h @@ -0,0 +1,52 @@ +#define DEFAULT_ALIGNMENT (2 * sizeof(void *)) +#define ARRAY_GROW_FACTOR 2 + +typedef struct { + u32 size; + u32 capacity; +} DynamicArrayHeader; + +#define DARRAY_INIT_CAPACITY 64 +#define p_darray_push(array, var) \ + do { \ + if (array == NULL) { \ + DynamicArrayHeader *header = \ + malloc(DARRAY_INIT_CAPACITY * sizeof *array + sizeof *header); \ + header->size = 0; \ + header->capacity = DARRAY_INIT_CAPACITY; \ + array = (void *)(header + 1); \ + } \ + DynamicArrayHeader *header = (DynamicArrayHeader *)(array) - 1; \ + if (header->size >= header->capacity - 1) { \ + unsigned long new_capacity = header->capacity * 2; \ + DynamicArrayHeader *new_header = \ + realloc(header, new_capacity * sizeof *array + sizeof *header); \ + if (!new_header) \ + break; \ + header = new_header; \ + array = (void *)(header + 1); \ + header->capacity = new_capacity; \ + } \ + (array)[header->size++] = var; \ + } while (0) + +#define p_darray_len(array) ((array) ? ((DynamicArrayHeader *)(array) - 1)->size : 0) +#define p_darray_destroy(array) \ +{ \ + free((DynamicArrayHeader *)(array) - 1); \ +} + +#define p_darray_shrink(array) \ + do { \ + DynamicArrayHeader *header = (array) ? ((DynamicArrayHeader *)(array) - 1) : 0;\ + if (header) {\ + if (header->capacity > 4) {\ + unsigned long new_capacity = (header->capacity >> 1) + (header->capacity & 1); \ + void *new_header = realloc(header, new_capacity * sizeof *array + sizeof *header); \ + if (!new_header) break; \ + header = new_header; \ + array = (void *)(header + 1); \ + header->capacity = new_capacity; \ + }\ + }\ + } while (0) diff --git a/archive/Podium/p_log.h b/archive/Podium/p_log.h new file mode 100644 index 0000000..0f6ce6c --- /dev/null +++ b/archive/Podium/p_log.h @@ -0,0 +1,186 @@ +/* + * Logging! + */ + +#include + +#ifdef DEBUG +#define LOG_DEBUG(...) PRINTF(__VA_ARGS__) +#else +#define LOG_DEBUG(...) ((void)0) +#endif + +/* fatal and error always enabled */ +#define PFATAL(message, ...) p_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) +#define PERROR(message, ...) p_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) + +/* warn and info enabled by default but can be disabled */ +#define P_LOG_WARN_ENABLED 1 +#define P_LOG_INFO_ENABLED 1 + +#if P_LOG_WARN_ENABLED == 1 +#define PWARN(message, ...) p_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) +#else +#define PWARN(message, ...) +#endif + +#if P_LOG_INFO_ENABLED == 1 +#define PINFO(message, ...) p_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) +#else +#define PINFO(message, ...) +#endif + +#if P_LOG_DEBUG_ENABLED == 1 +#define PDEBUG(message, ...) p_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) +#else +#define PDEBUG(message, ...) +#endif + +#if P_LOG_TRACE_ENABLED == 1 +#define PTRACE(message, ...) p_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) +#else +#define PTRACE(message, ...) +#endif + +typedef enum P_Log_Level { + P_LOG_LEVEL_FATAL = 0, + P_LOG_LEVEL_ERROR, + P_LOG_LEVEL_WARN, + P_LOG_LEVEL_INFO, + P_LOG_LEVEL_DEBUG, + P_LOG_LEVEL_TRACE, + P_COUNT_LOG_LEVEL +} P_Log_Level; + +#define P_PREFIX_LEN 7 +static const char *p_prefix[P_COUNT_LOG_LEVEL] = { + [P_LOG_LEVEL_FATAL] = "[FATAL]", + [P_LOG_LEVEL_ERROR] = "[ERROR]", + [P_LOG_LEVEL_WARN] = "[WARNI]", + [P_LOG_LEVEL_INFO] = "[INFOR]", + [P_LOG_LEVEL_DEBUG] = "[DEBUG]", + [P_LOG_LEVEL_TRACE] = "[TRACE]", +}; + +#define P_MAX_ALLOCS 512 + +typedef struct P_DebugMemoryInfo { + void *ptr; + size_t size; + const char *file; + const char *func; + int line; +} P_DebugMemoryInfo; + +static uint64_t p_alloc_count = 0; +static P_DebugMemoryInfo p_ptr_array[P_MAX_ALLOCS]; + +static inline void * +p_debug_malloc_impl(size_t size, const char *file, int line, const char *func) +{ + void *ptr = malloc(size); + printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); + + if (ptr) { + if (p_alloc_count < P_MAX_ALLOCS) { + p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ + .ptr = ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } else { + fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", P_MAX_ALLOCS); + } + } + return ptr; +} + +static inline void +p_debug_free_impl(void *ptr, const char *file, int line, const char *func) +{ + printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); + + if (!ptr) return; // Standard C allows free(NULL) + + bool found = false; + uint64_t index = 0; + + /* Find allocation metadata */ + for (index = 0; index < p_alloc_count; ++index) { + if (p_ptr_array[index].ptr == ptr) { + found = true; + break; + } + } + + if (found) { + /* Shift array left to remove tracked struct */ + for (uint64_t j = index; j < p_alloc_count - 1; ++j) { + p_ptr_array[j] = p_ptr_array[j + 1]; + } + p_alloc_count--; + } else { + fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", + ptr, file, line, func); + } + + free(ptr); +} + +static inline void * +p_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) +{ + if (!ptr) { + return p_debug_malloc_impl(size, file, line, func); + } + if (size == 0) { + p_debug_free_impl(ptr, file, line, func); + return NULL; + } + + void *new_ptr = realloc(ptr, size); + printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", ptr, new_ptr, size, file, line, func); + + if (new_ptr) { + bool found = false; + for (uint64_t i = 0; i < p_alloc_count; ++i) { + if (p_ptr_array[i].ptr == ptr) { + p_ptr_array[i].ptr = new_ptr; + p_ptr_array[i].size = size; + p_ptr_array[i].file = file; + p_ptr_array[i].line = line; + p_ptr_array[i].func = func; + found = true; + break; + } + } + if (!found) { + if (p_alloc_count < P_MAX_ALLOCS) { + p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ + .ptr = new_ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } + } + } + return new_ptr; +} + +static void +p_debug_memory_report(void) +{ + printf("\n==================== MEMORY REPORT ====================\n"); + printf("Remaining unfreed allocations: %lu\n", (unsigned long)p_alloc_count); + + for (uint64_t i = 0; i < p_alloc_count; ++i) { + P_DebugMemoryInfo *info = &p_ptr_array[i]; + printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", + info->ptr, info->size, info->file, info->line, info->func); + } + printf("=======================================================\n"); +} diff --git a/archive/Podium/p_math.c b/archive/Podium/p_math.c new file mode 100644 index 0000000..928738b --- /dev/null +++ b/archive/Podium/p_math.c @@ -0,0 +1,255 @@ +PODEF +int p_ceil(float x) +{ + int base = (int) x; + return (x > base) ? base + 1 : base; +} + +PODEF float +p_cosf(float x) +{ + x += 1.57079632679f; + + while (x > P_PI) x -= P_PI2; + while (x < -P_PI) x += P_PI2; + + const float B = 4.0f / P_PI; + const float C = -4.0f / P_PI_POW2; + + float y = B * x + C * x * (x < 0 ? -x : x); + + const float P = 0.225f; + y = P * (y * (y < 0 ? -y : y) - y) + y; + + return y; +} + +PODEF float +p_sinf(float x) +{ + while (x > P_PI) x -= P_PI2; + while (x < -P_PI) x += P_PI2; + + const float B = 4.0f / P_PI; + const float C = -4.0f / (P_PI_POW2); + + float y = B * x + C * x * (x < 0 ? -x : x); + + const float P = 0.225f; + y = P * (y * (y < 0 ? -y : y) - y) + y; + + return y; +} + +PODEF Vec2f +p_vec2f(float x, float y) +{ + return (Vec2f) { .x = x, .y = y }; +} + +PODEF Vec2f +p_vec2f_add(Vec2f a, Vec2f b) +{ + return (Vec2f) { + .x = a.x + b.x, + .y = a.y + b.y, + }; +} + +PODEF float +p_vec2f_cross(Vec2f a, Vec2f b) +{ + // axby − aybx + return a.x * b.y - a.y * b.x; +} + +PODEF float +p_vec2f_dot(Vec2f a, Vec2f b) +{ + // a · b = ax × bx + ay × by + return a.x * b.x + a.y * b.y; +} + +PODEF Vec2f +p_vec2f_mult(Vec2f a, float b) +{ + return (Vec2f) { + .x = a.x * b, + .y = a.y * b, + }; +} + +PODEF Vec2f +p_vec2f_sub(Vec2f a, Vec2f b) +{ + return (Vec2f) { + .x = a.x - b.x, + .y = a.y - b.y, + }; +} + +PODEF Vec2i +p_vec2i(float x, float y) +{ + return (Vec2i) { .x = x, .y = y }; +} + +PODEF Vec2i +p_vec2i_add(Vec2i a, Vec2i b) +{ + return (Vec2i) { + .x = a.x + b.x, + .y = a.y + b.y, + }; +} + +PODEF float +p_vec2i_cross(Vec2i a, Vec2i b) +{ + // axby − aybx + return a.x * b.y - a.y * b.x; +} + +PODEF float +p_vec2i_dot(Vec2i a, Vec2i b) +{ + // a · b = ax × bx + ay × by + return a.x * b.x + a.y * b.y; +} + +PODEF Vec2i +p_vec2i_mult(Vec2i a, float b) +{ + return (Vec2i) { + .x = a.x * b, + .y = a.y * b, + }; +} + +PODEF Vec2i +p_vec2i_sub(Vec2i a, Vec2i b) +{ + return (Vec2i) { + .x = a.x - b.x, + .y = a.y - b.y, + }; +} + +PODEF Vec3f +p_vec3f(float x, float y, float z) +{ + return (Vec3f) { .x = x, .y = y, .z = z }; +} + +PODEF P_Mat4 +p_mat4_identity(void) +{ + P_Mat4 res = {0}; + res.m[0] = 1.0f; + res.m[5] = 1.0f; + res.m[10] = 1.0f; + res.m[15] = 1.0f; + return res; +} + +PODEF P_Mat4 +p_mat4_mul(P_Mat4 a, P_Mat4 b) +{ + P_Mat4 res = {0}; + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + float sum = 0.0f; + for (int k = 0; k < 4; ++k) { + sum += a.m[k * 4 + row] * b.m[col * 4 + k]; + } + res.m[col * 4 + row] = sum; + } + } + return res; +} + +PODEF P_Mat4 +p_mat4_translate(Vec3f v) +{ + P_Mat4 res = p_mat4_identity(); + res.m[12] = v.x; + res.m[13] = v.y; + res.m[14] = v.z; + return res; +} + +PODEF P_Mat4 +p_mat4_translate_by(P_Mat4 m, Vec3f v) +{ + P_Mat4 res = m; + res.m[12] = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12]; + res.m[13] = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13]; + res.m[14] = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14]; + res.m[15] = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15]; + return res; +} + +PODEF P_Mat4 +p_mat4_rotate_x(float rad) +{ + P_Mat4 res = p_mat4_identity(); + float c = p_cosf(rad); + float s = p_sinf(rad); + res.m[5] = c; + res.m[6] = s; + res.m[9] = -s; + res.m[10] = c; + return res; +} + +PODEF P_Mat4 +p_mat4_rotate_x_by(P_Mat4 m, float rad) +{ + return p_mat4_mul(m, p_mat4_rotate_x(rad)); +} + +PODEF P_Mat4 +p_mat4_rotate_y(float rad) +{ + P_Mat4 res = p_mat4_identity(); + float c = p_cosf(rad); + float s = p_sinf(rad); + res.m[0] = c; + res.m[2] = -s; + res.m[8] = s; + res.m[10] = c; + return res; +} + +PODEF P_Mat4 +p_mat4_rotate_y_by(P_Mat4 m, float rad) +{ + return p_mat4_mul(m, p_mat4_rotate_y(rad)); +} + +PODEF P_Mat4 +p_mat4_rotate_z(float rad) +{ + P_Mat4 res = p_mat4_identity(); + float c = p_cosf(rad); + float s = p_sinf(rad); + res.m[0] = c; + res.m[1] = s; + res.m[4] = -s; + res.m[5] = c; + return res; +} + +PODEF P_Mat4 +p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z) +{ + P_Mat4 res = {0}; + float tan_half_fov = p_sinf(fov_rad * 0.5f) / p_cosf(fov_rad * 0.5f); + + res.m[0] = 1.0f / (aspect * tan_half_fov); + res.m[5] = 1.0f / tan_half_fov; + res.m[10] = far_z / (near_z - far_z); + res.m[11] = -1.0f; + res.m[14] = (near_z * far_z) / (near_z - far_z); + return res; +} diff --git a/archive/Podium/p_math.h b/archive/Podium/p_math.h new file mode 100644 index 0000000..1dad6ec --- /dev/null +++ b/archive/Podium/p_math.h @@ -0,0 +1,33 @@ +typedef struct {int x, y; } Vec2i; +typedef struct {float x, y; } Vec2f; +typedef struct {float x, y, z; } Vec3f; +typedef struct { float m[16];} P_Mat4; // column-major: m[col * 4 + row] +PODEF int p_ceil(float x); +PODEF float p_cosf(float x); +PODEF float p_sinf(float x); + +PODEF Vec2f p_vec2f(float, float); +PODEF Vec2f p_vec2f_add(Vec2f, Vec2f); +PODEF float p_vec2f_cross(Vec2f, Vec2f); +PODEF float p_vec2f_dot(Vec2f, Vec2f); +PODEF Vec2f p_vec2f_mult(Vec2f a, float b); +PODEF Vec2f p_vec2f_sub(Vec2f, Vec2f); +PODEF Vec2i p_vec2i(float, float); +PODEF Vec2i p_vec2i_add(Vec2i, Vec2i); +PODEF float p_vec2i_cross(Vec2i, Vec2i); +PODEF float p_vec2i_dot(Vec2i, Vec2i); +PODEF Vec2i p_vec2i_mult(Vec2i a, float b); +PODEF Vec2i p_vec2i_sub(Vec2i, Vec2i); + +PODEF Vec3f p_vec3f(float, float, float); + +PODEF P_Mat4 p_mat4_identity(void); +PODEF P_Mat4 p_mat4_mul(P_Mat4 a, P_Mat4 b); +PODEF P_Mat4 p_mat4_translate(Vec3f v); +PODEF P_Mat4 p_mat4_translate_by(P_Mat4 m, Vec3f v); +PODEF P_Mat4 p_mat4_rotate_x(float rad); +PODEF P_Mat4 p_mat4_rotate_x_by(P_Mat4 m, float rad); +PODEF P_Mat4 p_mat4_rotate_y(float rad); +PODEF P_Mat4 p_mat4_rotate_y_by(P_Mat4 m, float rad); +PODEF P_Mat4 p_mat4_rotate_z(float rad); +PODEF P_Mat4 p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z); diff --git a/archive/Podium/p_platform_linux.c b/archive/Podium/p_platform_linux.c new file mode 100644 index 0000000..366e3d7 --- /dev/null +++ b/archive/Podium/p_platform_linux.c @@ -0,0 +1,404 @@ +/* linux */ +#include +#include +#include +#include +#include +#include +/* x11 */ +#include +#include +/* pulse audio */ +#include +#include +#include +/* joystick */ +#include + +typedef struct { + pa_simple *audio_stream; +} P_LinuxCtx; + +struct P_Window_Impl { + Display *display; + XImage *image; + Window window; + GC gc; + int screen; + int width; + int height; +}; + +P_LinuxCtx p_ctx = {0}; + +static inline P_KeyCode +_x11_translate_keycode(XEvent xev) +{ + static KeySym ks; + ks = XLookupKeysym(&xev.xkey, 0); + switch (ks) { + /* Arrow keys */ + case XK_Left: return P_KEY_LEFT; + case XK_Right: return P_KEY_RIGHT; + case XK_Up: return P_KEY_UP; + case XK_Down: return P_KEY_DOWN; + /* Common keys */ + case XK_space: return P_KEY_SPACE; + case XK_Escape: return P_KEY_ESCAPE; + case XK_Return: return P_KEY_ENTER; + case XK_A: case XK_a: return P_KEY_A; + case XK_B: case XK_b: return P_KEY_B; + case XK_C: case XK_c: return P_KEY_C; + case XK_D: case XK_d: return P_KEY_D; + case XK_E: case XK_e: return P_KEY_E; + case XK_F: case XK_f: return P_KEY_F; + case XK_G: case XK_g: return P_KEY_G; + case XK_H: case XK_h: return P_KEY_H; + case XK_I: case XK_i: return P_KEY_I; + case XK_J: case XK_j: return P_KEY_J; + case XK_K: case XK_k: return P_KEY_K; + case XK_L: case XK_l: return P_KEY_L; + case XK_M: case XK_m: return P_KEY_M; + case XK_N: case XK_n: return P_KEY_N; + case XK_O: case XK_o: return P_KEY_O; + case XK_P: case XK_p: return P_KEY_P; + case XK_Q: case XK_q: return P_KEY_Q; + case XK_R: case XK_r: return P_KEY_R; + case XK_S: case XK_s: return P_KEY_S; + case XK_T: case XK_t: return P_KEY_T; + case XK_U: case XK_u: return P_KEY_U; + case XK_V: case XK_v: return P_KEY_V; + case XK_W: case XK_w: return P_KEY_W; + case XK_X: case XK_x: return P_KEY_X; + case XK_Y: case XK_y: return P_KEY_Y; + case XK_Z: case XK_z: return P_KEY_Z; + default: return P_KEY_UNKNOWN; + } +} + +POAPI u64 +p_get_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec; +} + +POAPI void +p_sleep_ns(i64 ns) +{ + struct timespec ts; + ts.tv_sec = ns / 1000000000L; + ts.tv_nsec = ns % 1000000000L; + clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); +} + +POAPI void +p_stdout(void *msg, usize bytes) +{ + write(1, msg, bytes); +} + +POAPI bool +p_file_exists(const char *path) +{ + return (access(path, F_OK) == 0); +} + +POAPI usize +p_file_size(const char *path) +{ + struct stat st; + if (stat(path, &st) == 0) { + return st.st_size; + } + return 0; +} + +PODEF void +p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) +{ + int fd = open(path, O_RDONLY); + if (fd >= 0) { + read(fd, buf_ptr, (size_t)buf_size); + close(fd); + } +} + +PODEF void +p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) +{ + int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); + if (fd >= 0) { + write(fd, buf_ptr, (size_t)buf_size); + close(fd); + } +} + +POAPI bool +p_window_open(P_Window *win, int width, int height, const char *title) +{ + win->width = width; + win->height = height; + + XInitThreads(); + + win->display = XOpenDisplay(NULL); + if (win->display == NULL) { + PERROR("Cannot open display!"); + return false; + } + + int screen = DefaultScreen(win->display); + win->window = XCreateSimpleWindow( + win->display, + RootWindow(win->display, screen), + 0, 0, width, height, + 1, + BlackPixel(win->display, screen), + BlackPixel(win->display, screen) + ); + + win->screen = screen; + + XStoreName(win->display, win->window, title); + XSelectInput(win->display, win->window, + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | StructureNotifyMask); + XMapWindow(win->display, win->window); + + win->gc = DefaultGC(win->display, screen); + + Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(win->display, win->window, &wm_delete, 1); + + return true; +} + +POAPI void +p_window_close(P_Window *win) +{ + if (!win) return; + + if (win->image) { + // XDestroyImage(win->image); + win->image = NULL; + } + + if (win->display) { + Display *dpy = win->display; + Window w = win->window; + win->display = NULL; + win->window = 0; + + if (w) { + XDestroyWindow(dpy, w); + } + XSync(dpy, False); + } +} + +POAPI bool +p_window_is_open(P_Window *win) +{ + return win && win->display != NULL; +} + +POAPI void +p_window_size(P_Window *win, int *window_width, int *window_height) +{ + if (!win) return; + + XWindowAttributes attr; + XGetWindowAttributes(win->display, win->window, &attr); + *window_width = attr.width; + *window_height = attr.height; + + win->width = attr.width; + win->height = attr.height; +} + +POAPI void +p_window_draw(P_Window *win, u32 *pixels, int width, int height) +{ + int screen = win->screen; + + win->image = XCreateImage( + win->display, + DefaultVisual(win->display, screen), + DefaultDepth(win->display, screen), + ZPixmap, + 0, + (char*)pixels, + width, + height, + 32, + width * 4 + ); + + int window_width, window_height; + p_window_size(win, &window_width, &window_height); + + XPutImage( + win->display, + win->window, + win->gc, + win->image, + 0, 0, + 0, 0, + win->width, + win->height); + + XFlush(win->display); +} + +POAPI bool +p_window_poll_event(P_Window *win, P_Event *ev) +{ + if (XPending(win->display)) { + static XEvent xev; + XNextEvent(win->display, &xev); + + switch (xev.type) { + + case KeyPress: + ev->type = P_EVENT_KEY_DOWN; + ev->key.key = _x11_translate_keycode(xev); + return true; + + case KeyRelease: + ev->type = P_EVENT_KEY_UP; + ev->key.key = _x11_translate_keycode(xev); + return true; + + case ConfigureNotify: + ev->type = P_EVENT_WINDOW_RESIZE; + ev->resize.width = xev.xconfigure.width; + ev->resize.height = xev.xconfigure.height; + return true; + + case ClientMessage: + if ((Atom)xev.xclient.data.l[0] == + XInternAtom(win->display, "WM_DELETE_WINDOW", False)) { + ev->type = P_EVENT_WINDOW_CLOSE; + return true; + } + break; + + case MotionNotify: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_MOVED; + ev->pointer.button = 0; + ev->pointer.x = xev.xmotion.x; + ev->pointer.y = xev.xmotion.y; + return true; + + case ButtonPress: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */ + ev->pointer.x = xev.xbutton.x; + ev->pointer.y = xev.xbutton.y; + return true; + + case ButtonRelease: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = xev.xbutton.button; + ev->pointer.x = xev.xbutton.x; + ev->pointer.y = xev.xbutton.y; + return true; + } + } + + ev->type = P_EVENT_NONE; + return false; +} + +POAPI void +p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) +{ + pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = sample_rate, + .channels = channels, + }; + + int error; + p_ctx.audio_stream = pa_simple_new( + NULL, + name, + PA_STREAM_PLAYBACK, + NULL, + desc, + &ss, + NULL, + NULL, + &error + ); + + if (!p_ctx.audio_stream) { + PERROR("PulseAudio init failed: %s\n", pa_strerror(error)); + exit(1); + } +} + +POAPI void +p_audio_quit() +{ + if (p_ctx.audio_stream) { + pa_simple_free(p_ctx.audio_stream); + p_ctx.audio_stream = NULL; + } +} + +POAPI void +p_audio_write(const i16 *samples, usize count) +{ + if (!p_ctx.audio_stream) return; + + int error; + if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) { + PERROR("PulseAudio write failed: %s\n", pa_strerror(error)); + } + + pa_simple_drain(p_ctx.audio_stream, &error); +} + +#ifdef P_MODULE_VULKAN + +#ifndef VK_USE_PLATFORM_XLIB_KHR +#define VK_USE_PLATFORM_XLIB_KHR +#endif +#include + +POAPI char** +p_vulkan_get_extensions() +{ + char **darr = NULL; + p_darray_push(darr, "VK_KHR_surface"); + p_darray_push(darr, "VK_KHR_xcb_surface"); + p_darray_push(darr, "VK_KHR_xlib_surface"); + return darr; +} + +POAPI bool +p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) +{ + if (!instance || !window || !window->display || !window->window || !out_surface) { + return false; + } + + VkXlibSurfaceCreateInfoKHR create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.dpy = window->display; + create_info.window = window->window; + + VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface); + + return (result == VK_SUCCESS); +} +#endif diff --git a/archive/Podium/p_platform_win32.c b/archive/Podium/p_platform_win32.c new file mode 100644 index 0000000..556b43b --- /dev/null +++ b/archive/Podium/p_platform_win32.c @@ -0,0 +1,402 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include /* gamepad support */ + +typedef struct { + HWAVEOUT wave_out; + WAVEHDR wave_header; +} P_Win32Ctx; + +struct P_Window_Impl { + HWND hwnd; + HDC hdc; + BITMAPINFO bitmap_info; + int width; + int height; + bool closed; +}; + +P_Win32Ctx p_ctx = {0}; + +static inline P_KeyCode +_win32_translate_vkey(WPARAM vk) +{ + switch (vk) { + case VK_LEFT: return P_KEY_LEFT; + case VK_RIGHT: return P_KEY_RIGHT; + case VK_UP: return P_KEY_UP; + case VK_DOWN: return P_KEY_DOWN; + case VK_SPACE: return P_KEY_SPACE; + case VK_ESCAPE: return P_KEY_ESCAPE; + case VK_RETURN: return P_KEY_ENTER; + case 'A': return P_KEY_A; + case 'B': return P_KEY_B; + case 'C': return P_KEY_C; + case 'D': return P_KEY_D; + case 'E': return P_KEY_E; + case 'F': return P_KEY_F; + case 'G': return P_KEY_G; + case 'H': return P_KEY_H; + case 'I': return P_KEY_I; + case 'J': return P_KEY_J; + case 'K': return P_KEY_K; + case 'L': return P_KEY_L; + case 'M': return P_KEY_M; + case 'N': return P_KEY_N; + case 'O': return P_KEY_O; + case 'P': return P_KEY_P; + case 'Q': return P_KEY_Q; + case 'R': return P_KEY_R; + case 'S': return P_KEY_S; + case 'T': return P_KEY_T; + case 'U': return P_KEY_U; + case 'V': return P_KEY_V; + case 'W': return P_KEY_W; + case 'X': return P_KEY_X; + case 'Y': return P_KEY_Y; + case 'Z': return P_KEY_Z; + default: return P_KEY_UNKNOWN; + } +} + +POAPI u64 +p_get_time(void) +{ + LARGE_INTEGER count, freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&freq); + return (u64)((count.QuadPart * 1000000000ULL) / freq.QuadPart); +} + +POAPI void +p_sleep_ns(i64 ns) +{ + /* windows sleep is millisecond-based........ */ + Sleep((DWORD)(ns / 1000000)); +} + +POAPI void +p_stdout(void *msg, usize bytes) +{ + HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD bytes_written; + WriteFile(stdout_handle, msg, bytes, &bytes_written, NULL); +} + +POAPI bool +p_file_exists(const char *path) +{ + DWORD attr = GetFileAttributesA(path); + return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); +} + +POAPI usize +p_file_size(const char *path) +{ + WIN32_FILE_ATTRIBUTE_DATA data; + if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) { + return data.nFileSizeLow; + } + return 0; +} + +PODEF void +p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) +{ + HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (file != INVALID_HANDLE_VALUE) { + DWORD bytes_read; + ReadFile(file, buf_ptr, buf_size, &bytes_read, NULL); + CloseHandle(file); + } +} + +PODEF void +p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) +{ + HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) { + DWORD bytes_written; + WriteFile(file, buf_ptr, buf_size, &bytes_written, NULL); + CloseHandle(file); + } +} + +static LRESULT CALLBACK +_win32_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + P_Window *win = (P_Window*)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + switch (msg) { + case WM_CLOSE: + /* Handled in poll_event; mark as closed */ + if (win) win->closed = true; + return 0; + } + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +POAPI bool +p_window_open(P_Window *win, int width, int height, const char *title) +{ + HINSTANCE instance = GetModuleHandleA(NULL); + + WNDCLASSA wc = {0}; + wc.lpfnWndProc = _win32_wnd_proc; + wc.hInstance = instance; + wc.lpszClassName = "P_Window_Class"; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + + RegisterClassA(&wc); + + win->hwnd = CreateWindowExA(0, wc.lpszClassName, title, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, width, height, + NULL, NULL, instance, NULL); + + if (!win->hwnd) return false; + + SetWindowLongPtrA(win->hwnd, GWLP_USERDATA, (LONG_PTR)win); + win->hdc = GetDC(win->hwnd); + win->width = width; + win->height = height; + win->closed = false; + + return true; +} + +POAPI void +p_window_close(P_Window *win) +{ + if (!win) return; + if (win->hdc) ReleaseDC(win->hwnd, win->hdc); + if (win->hwnd) DestroyWindow(win->hwnd); + win->hdc = NULL; + win->hwnd = NULL; +} + +POAPI bool +p_window_is_open(P_Window *win) +{ + return win && win->hwnd != NULL; +} + +POAPI void +p_window_size(P_Window *win, int *window_width, int *window_height) +{ + if (!win) return; + RECT rect; + GetClientRect(win->hwnd, &rect); + *window_width = rect.right - rect.left; + *window_height = rect.bottom - rect.top; + win->width = *window_width; + win->height = *window_height; +} + +POAPI void +p_window_draw(P_Window *win, u32 *pixels, int width, int height) +{ + BITMAPINFO bmi = {0}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; /* top-down */ + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + StretchDIBits(win->hdc, + 0, 0, win->width, win->height, + 0, 0, width, height, + pixels, &bmi, DIB_RGB_COLORS, SRCCOPY); +} + +POAPI bool +p_window_poll_event(P_Window *win, P_Event *ev) +{ + if (win->closed) { + win->closed = false; + ev->type = P_EVENT_WINDOW_CLOSE; + return true; + } + + MSG msg; + while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { + switch (msg.message) { + + case WM_QUIT: + ev->type = P_EVENT_WINDOW_CLOSE; + return true; + + /* Key press */ + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + ev->type = P_EVENT_KEY_DOWN; + ev->key.key = _win32_translate_vkey(msg.wParam); + return true; + + /* Key release */ + case WM_KEYUP: + case WM_SYSKEYUP: + ev->type = P_EVENT_KEY_UP; + ev->key.key = _win32_translate_vkey(msg.wParam); + return true; + + /* Mouse movement */ + case WM_MOUSEMOVE: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_MOVED; + ev->pointer.button = 0; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + /* Mouse buttons */ + case WM_LBUTTONDOWN: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = 1; /* 1 = LMB, matching X11 convention */ + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_LBUTTONUP: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = 1; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_RBUTTONDOWN: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = 3; /* 3 = RMB */ + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_RBUTTONUP: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = 3; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_MBUTTONDOWN: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = 2; /* 2 = MMB */ + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_MBUTTONUP: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = 2; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + /* Window resize */ + case WM_SIZE: + ev->type = P_EVENT_WINDOW_RESIZE; + ev->resize.width = (u32)(msg.lParam & 0xFFFF); + ev->resize.height = (u32)((msg.lParam >> 16) & 0xFFFF); + win->width = ev->resize.width; + win->height = ev->resize.height; + return true; + + default: + TranslateMessage(&msg); + DispatchMessageA(&msg); + break; + } + } + + ev->type = P_EVENT_NONE; + return false; +} + + +POAPI void +p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) +{ + (void)name; (void)desc; /* WinMM doesn't use a stream name */ + + WAVEFORMATEX wfx = {0}; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = channels; + wfx.nSamplesPerSec = sample_rate; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + + if (waveOutOpen(&p_ctx.wave_out, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) { + exit(1); + } +} + +POAPI void +p_audio_quit() +{ + if (p_ctx.wave_out) { + waveOutReset(p_ctx.wave_out); + waveOutClose(p_ctx.wave_out); + p_ctx.wave_out = NULL; + } +} + +POAPI void +p_audio_write(const i16 *samples, usize count) +{ + if (!p_ctx.wave_out) return; + + WAVEHDR header = {0}; + header.lpData = (LPSTR)samples; + header.dwBufferLength = (DWORD)(count * sizeof(i16)); + + waveOutPrepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); + waveOutWrite(p_ctx.wave_out, &header, sizeof(WAVEHDR)); + + /* wait for playback to finish (synchronous, mirrors pa_simple_drain) */ + while (!(header.dwFlags & WHDR_DONE)) Sleep(1); + + waveOutUnprepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); +} + +#ifdef P_MODULE_VULKAN + +#ifndef VK_USE_PLATFORM_WIN32_KHR +#define VK_USE_PLATFORM_WIN32_KHR +#endif +#include + +POAPI char** +p_vulkan_get_extensions() +{ + char **darr = NULL; + p_darray_push(darr, "VK_KHR_surface"); + p_darray_push(darr, "VK_KHR_win32_surface"); + return darr; +} + +POAPI bool +p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) +{ + if (!instance || !window || !window->hwnd || !out_surface) { + return false; + } + + VkWin32SurfaceCreateInfoKHR create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.hwnd = window->hwnd; + create_info.hinstance = GetModuleHandleA(NULL); + + VkResult result = vkCreateWin32SurfaceKHR(instance, &create_info, allocator, out_surface); + return (result == VK_SUCCESS); +} +#endif diff --git a/archive/Podium/p_string.c b/archive/Podium/p_string.c new file mode 100644 index 0000000..73cb0ea --- /dev/null +++ b/archive/Podium/p_string.c @@ -0,0 +1,201 @@ +PODEF char* +p_find_char(char *buf, char needle) +{ + while (buf && *buf != needle) buf++; + return buf; +} + +PODEF bool +p_strcmp(const char *a, const char *b) +{ + if (p_strlen(a) != p_strlen(b)) + return false; + + while (a && b && *a != '\0') { + if (*(a++) != *(b++)) return false; + } + + return true; +} + +PODEF int +p_strlen(const char *a) +{ + int i = 0; + while (*a++ != '\0') i++; + return i; +} + +PODEF int +p_strtoi(const char *str) +{ + int res = 0; + + while (*str) { + if (*str >= '0' && *str <= '9') { + res = res * 10 + (*str - '0'); + } else { + return res; + } + str++; + } + + return res; +} + +PODEF char* +p_strtok(const char *str, char c) +{ + while (*str != c && *str++ != '\0'); + return (*str == c) ? (char*) ++str : 0; +} + +PODEF P_StringView +p_strview(const char *cstr) +{ + P_StringView sv = {0}; + sv.cstr = cstr; + sv.len = p_strlen(cstr); + return sv; +} + +PODEF void +p_strview_chop_left(P_StringView *sv, u32 n) +{ + if (n > sv->len) n = sv->len; + sv->cstr += n; + sv->len -= n; +} + +PODEF void +p_strview_chop_right(P_StringView *sv, u32 n) +{ + if (n > sv->len) n = sv->len; + sv->len -= n; +} + +PODEF P_StringView +p_strview_chop_delim(P_StringView *sv, char delim) +{ + u32 i = 0; + while (i < sv->len && sv->cstr[i] != delim) { + i++; + } + + P_StringView tok; + if (i < sv->len) { + tok.cstr = sv->cstr; + tok.len = i; + p_strview_chop_left(sv, i + 1); + return tok; + } + + tok = *sv; + p_strview_chop_left(sv, sv->len); + return tok; +} + +PODEF P_StringView +p_strview_chop_type(P_StringView *sv, int(*istype)(int)) +{ + u32 i = 0; + while (i < sv->len && istype(sv->cstr[i])) { + i++; + } + + P_StringView tok; + if (i < sv->len) { + tok.cstr = sv->cstr; + tok.len = i; + p_strview_chop_left(sv, i + 1); + return tok; + } + + tok = *sv; + p_strview_chop_left(sv, sv->len); + return tok; +} + + +PODEF void +p_strview_trim_left(P_StringView *sv) +{ + while (sv->len > 0 && p_is_space(sv->cstr[0])) { + p_strview_chop_left(sv, 1); + } +} + +PODEF void +p_strview_trim_right(P_StringView *sv) +{ + while (sv->len > 0 && p_is_space(sv->cstr[sv->len-1])) { + p_strview_chop_right(sv, 1); + } +} + +PODEF void +p_strview_trim(P_StringView *sv) +{ + p_strview_trim_left(sv); + p_strview_trim_right(sv); +} + +PODEF int +p_is_space(int c) +{ + /* Checks for: space, form feed (\f), line feed (\n), + * carriage return (\r), horizontal tab (\t), vertical tab (\v) */ + return (c == ' ' || (c >= '\t' && c <= '\r')); +} + +PODEF int +p_is_digit(int c) +{ + return (c >= '0' && c <= '9'); +} + +PODEF int +p_is_alpha(int c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); +} + +PODEF int +p_is_alnum(int c) +{ + return (p_is_alpha(c) || p_is_digit(c)); +} + +PODEF int +p_is_hex(int c) +{ + return (p_is_digit(c) || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')); +} + +PODEF int +p_is_upper(int c) +{ + return (c >= 'A' && c <= 'Z'); +} + +PODEF int +p_is_lower(int c) +{ + return (c >= 'a' && c <= 'z'); +} + +PODEF int +p_to_lower(int c) +{ + if (p_is_upper(c)) return (c + ('a' - 'A')); + return c; +} + +PODEF int +p_to_upper(int c) +{ + if (p_is_lower(c)) return (c - ('a' - 'A')); + return c; +} diff --git a/archive/Podium/p_string.h b/archive/Podium/p_string.h new file mode 100644 index 0000000..5c4077b --- /dev/null +++ b/archive/Podium/p_string.h @@ -0,0 +1,38 @@ +typedef struct { + const char *cstr; + u32 len; +} P_StringView; + +typedef struct { + P_StringView sv; +} P_StringBuilder; + +/* Classic C string functions */ +PODEF char* p_find_char(char *buf, char needle); +PODEF bool p_strcmp(const char *a, const char *b); +PODEF int p_strlen(const char *a); +PODEF int p_strtoi(const char *str); +PODEF char* p_strtok(const char *str, char c); + +/* String views */ +PODEF P_StringView p_strview(const char *cstr); +PODEF void p_strview_chop_left(P_StringView *sv, u32 n); +PODEF void p_strview_chop_right(P_StringView *sv, u32 n); +PODEF P_StringView p_strview_chop_delim(P_StringView *sv, char delim); +PODEF P_StringView p_strview_chop_type(P_StringView *sv, int(*istype)(int)); +PODEF void p_strview_trim_left(P_StringView *sv); +PODEF void p_strview_trim_right(P_StringView *sv); +PODEF void p_strview_trim(P_StringView *sv); + +/* Classification */ +PODEF int p_is_space(int c); +PODEF int p_is_digit(int c); +PODEF int p_is_alpha(int c); +PODEF int p_is_alnum(int c); +PODEF int p_is_upper(int c); +PODEF int p_is_lower(int c); +PODEF int p_is_hex(int c); + +/* Transformation */ +PODEF int p_to_lower(int c); +PODEF int p_to_upper(int c); diff --git a/archive/Podium/p_types.h b/archive/Podium/p_types.h new file mode 100644 index 0000000..1da81be --- /dev/null +++ b/archive/Podium/p_types.h @@ -0,0 +1,88 @@ +#ifndef _PODIUM_TYPES_ +#define _PODIUM_TYPES_ + +#define NANOS_PER_SEC (1000*1000*1000) +#define P_PI_HALF 1.57079632679f +#define P_PI 3.14159265f +#define P_PI2 6.28318531f +#define P_PI_POW2 9.86960440f + +#ifdef NDEBUG + #define assert(x) ((void)0) +#else + #ifndef assert + #define assert(x) \ + do { \ + if (!(x)) { \ + __builtin_trap(); \ + } \ + } while (0) + #endif +#endif + +#ifndef BUFSIZ + #define BUFSIZ 8192 +#endif + +typedef unsigned char u8; +typedef signed char i8; +typedef unsigned short u16; +typedef short i16; +typedef unsigned int u32; +typedef int i32; +typedef unsigned long long u64; +typedef long long i64; +typedef float f32; +typedef double f64; + +#ifndef __cplusplus +#define bool _Bool +#define false 0 +#define true 1 +#endif + +/* C++ uses static_assert, C11 uses _Static_assert */ +#ifdef __cplusplus + #define STATIC_ASSERT static_assert +#else + #define STATIC_ASSERT _Static_assert +#endif + +STATIC_ASSERT(sizeof(u8) == 1, "u8 must be 1 byte"); +STATIC_ASSERT(sizeof(u16) == 2, "u16 must be 2 bytes"); +STATIC_ASSERT(sizeof(u32) == 4, "u32 must be 4 bytes"); +STATIC_ASSERT(sizeof(u64) == 8, "u64 must be 8 bytes"); + +STATIC_ASSERT(sizeof(i8) == 1, "i8 must be 1 byte"); +STATIC_ASSERT(sizeof(i16) == 2, "i16 must be 2 bytes"); +STATIC_ASSERT(sizeof(i32) == 4, "i32 must be 4 bytes"); +STATIC_ASSERT(sizeof(i64) == 8, "i64 must be 8 bytes"); + +STATIC_ASSERT(sizeof(f32) == 4, "f32 must be 4 bytes"); +STATIC_ASSERT(sizeof(f64) == 8, "f64 must be 8 bytes"); +#define U8_MAX 255 +#define U16_MAX 65535 +#define U32_MAX 4294967295 + +/* Architecture specific sizing */ +#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64) + typedef u64 usize; + typedef u64 uintptr; + typedef u32 idx; + STATIC_ASSERT(sizeof(usize) == 8, "usize must be 8 bytes on 64-bit"); +#else + typedef u32 usize; + typedef u32 uintptr; + typedef u16 idx; + STATIC_ASSERT(sizeof(usize) == 4, "usize must be 4 bytes on 32-bit"); +#endif + +#ifndef NULL + #ifdef __cplusplus + #define NULL 0 + #else + #define NULL ((void*)0) + #endif +#endif + +#endif // _PODIUM_TYPES_ diff --git a/archive/Podium/podium.c b/archive/Podium/podium.c new file mode 100644 index 0000000..306e337 --- /dev/null +++ b/archive/Podium/podium.c @@ -0,0 +1 @@ +#define PODIUM_IMPLEMENTATION diff --git a/archive/Podium/podium.h b/archive/Podium/podium.h new file mode 100644 index 0000000..d94b4e5 --- /dev/null +++ b/archive/Podium/podium.h @@ -0,0 +1,2269 @@ +/* =========================================================================== + * PODIUM - Kitchen Sink - Copyright (c) 2026 Vasco Alves + * + * --------------------------------------------------------------------------- + * To stand on the shoulders of giants... + * Giants must stand still!" + * - Eskil Steenberg - "You should finish your software" – BSC 2025 + * --------------------------------------------------------------------------- + * + * PREFIX: P_ (types) or p_ (functions & variables) + * + * COMPILE TIME FLAGS + * | Name | Description | + * |-----------------|---------------------------------------------------------| + * | PODIUM_WIN32 | Used to explicitly compile for Windows. | + * | PODIUM_LINUX | Used to explicitly compile for Linux and FreeBSD. | + * + * MODULES + * | Name | Description | + * |-----------------|---------------------------------------------------------| + * | P_MODULE_VULKAN | Gives access to vulkan related utilities. | + * | P_MODULE_STRING | Include string manipulation utilities. | + * | P_MODULE_MATH | Trigonometry and linear algebra functions. | + * | P_MODULE_ALLOC | Memory allocators: Arena, Stack & Pool. | + * + * =========================================================================== */ + +#ifndef _PODIUM_H_ +#define _PODIUM_H_ + +#define PODIUM_MAJOR "0" // needs more revisions and use to reach 1.0 status +#define PODIUM_MINOR "2" // vulkan module +#define PODIUM_PATCH "2" // gamepad + +#define POAPI static inline +#define PODEF static inline + +/* + * Auto-detect platforms. + */ + +/* Windows */ +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) + #define PODIUM_WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif +/* Apple */ +#elif defined(__APPLE__) || defined(__MACH__) + #include + #define PODIUM_APPLE + #if TARGET_OS_IPHONE + #define PODIUM_IOS + #else + #define PODIUM_MACOS + #endif +/* Linux / BSD */ +#elif defined(__linux__) || defined(__FreeBSD__) + #define PODIUM_LINUX + #ifndef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 200112L + #endif +#endif + +#include +#include +#include +#include /* memcpy */ +#include /* vsnprintf */ + +/* + * NOTE: The comments are used by the build + * script to know what includes to inline into the + * final single header file! + */ + +/* --- Start of p_types.h --- */ +#ifndef _PODIUM_TYPES_ +#define _PODIUM_TYPES_ + +#define NANOS_PER_SEC (1000*1000*1000) +#define P_PI_HALF 1.57079632679f +#define P_PI 3.14159265f +#define P_PI2 6.28318531f +#define P_PI_POW2 9.86960440f + +#ifdef NDEBUG + #define assert(x) ((void)0) +#else + #ifndef assert + #define assert(x) \ + do { \ + if (!(x)) { \ + __builtin_trap(); \ + } \ + } while (0) + #endif +#endif + +#ifndef BUFSIZ + #define BUFSIZ 8192 +#endif + +typedef unsigned char u8; +typedef signed char i8; +typedef unsigned short u16; +typedef short i16; +typedef unsigned int u32; +typedef int i32; +typedef unsigned long long u64; +typedef long long i64; +typedef float f32; +typedef double f64; + +#ifndef __cplusplus +#define bool _Bool +#define false 0 +#define true 1 +#endif + +/* C++ uses static_assert, C11 uses _Static_assert */ +#ifdef __cplusplus + #define STATIC_ASSERT static_assert +#else + #define STATIC_ASSERT _Static_assert +#endif + +STATIC_ASSERT(sizeof(u8) == 1, "u8 must be 1 byte"); +STATIC_ASSERT(sizeof(u16) == 2, "u16 must be 2 bytes"); +STATIC_ASSERT(sizeof(u32) == 4, "u32 must be 4 bytes"); +STATIC_ASSERT(sizeof(u64) == 8, "u64 must be 8 bytes"); + +STATIC_ASSERT(sizeof(i8) == 1, "i8 must be 1 byte"); +STATIC_ASSERT(sizeof(i16) == 2, "i16 must be 2 bytes"); +STATIC_ASSERT(sizeof(i32) == 4, "i32 must be 4 bytes"); +STATIC_ASSERT(sizeof(i64) == 8, "i64 must be 8 bytes"); + +STATIC_ASSERT(sizeof(f32) == 4, "f32 must be 4 bytes"); +STATIC_ASSERT(sizeof(f64) == 8, "f64 must be 8 bytes"); +#define U8_MAX 255 +#define U16_MAX 65535 +#define U32_MAX 4294967295 + +/* Architecture specific sizing */ +#if defined(__x86_64__) || defined(__aarch64__) || defined(_M_X64) + typedef u64 usize; + typedef u64 uintptr; + typedef u32 idx; + STATIC_ASSERT(sizeof(usize) == 8, "usize must be 8 bytes on 64-bit"); +#else + typedef u32 usize; + typedef u32 uintptr; + typedef u16 idx; + STATIC_ASSERT(sizeof(usize) == 4, "usize must be 4 bytes on 32-bit"); +#endif + +#ifndef NULL + #ifdef __cplusplus + #define NULL 0 + #else + #define NULL ((void*)0) + #endif +#endif + +#endif // _PODIUM_TYPES_ +/* --- End of p_types.h --- */ +/* --- Start of p_log.h --- */ +/* + * Logging! + */ + +#include + +#ifdef DEBUG +#define LOG_DEBUG(...) PRINTF(__VA_ARGS__) +#else +#define LOG_DEBUG(...) ((void)0) +#endif + +/* fatal and error always enabled */ +#define PFATAL(message, ...) p_log_printf(P_LOG_LEVEL_FATAL, message, ##__VA_ARGS__) +#define PERROR(message, ...) p_log_printf(P_LOG_LEVEL_ERROR, message, ##__VA_ARGS__) + +/* warn and info enabled by default but can be disabled */ +#define P_LOG_WARN_ENABLED 1 +#define P_LOG_INFO_ENABLED 1 + +#if P_LOG_WARN_ENABLED == 1 +#define PWARN(message, ...) p_log_printf(P_LOG_LEVEL_WARN, message, ##__VA_ARGS__) +#else +#define PWARN(message, ...) +#endif + +#if P_LOG_INFO_ENABLED == 1 +#define PINFO(message, ...) p_log_printf(P_LOG_LEVEL_INFO, message, ##__VA_ARGS__) +#else +#define PINFO(message, ...) +#endif + +#if P_LOG_DEBUG_ENABLED == 1 +#define PDEBUG(message, ...) p_log_printf(P_LOG_LEVEL_DEBUG, message, ##__VA_ARGS__) +#else +#define PDEBUG(message, ...) +#endif + +#if P_LOG_TRACE_ENABLED == 1 +#define PTRACE(message, ...) p_log_printf(P_LOG_LEVEL_TRACE, message, ##__VA_ARGS__) +#else +#define PTRACE(message, ...) +#endif + +typedef enum P_Log_Level { + P_LOG_LEVEL_FATAL = 0, + P_LOG_LEVEL_ERROR, + P_LOG_LEVEL_WARN, + P_LOG_LEVEL_INFO, + P_LOG_LEVEL_DEBUG, + P_LOG_LEVEL_TRACE, + P_COUNT_LOG_LEVEL +} P_Log_Level; + +#define P_PREFIX_LEN 7 +static const char *p_prefix[P_COUNT_LOG_LEVEL] = { + [P_LOG_LEVEL_FATAL] = "[FATAL]", + [P_LOG_LEVEL_ERROR] = "[ERROR]", + [P_LOG_LEVEL_WARN] = "[WARNI]", + [P_LOG_LEVEL_INFO] = "[INFOR]", + [P_LOG_LEVEL_DEBUG] = "[DEBUG]", + [P_LOG_LEVEL_TRACE] = "[TRACE]", +}; + +#define P_MAX_ALLOCS 512 + +typedef struct P_DebugMemoryInfo { + void *ptr; + size_t size; + const char *file; + const char *func; + int line; +} P_DebugMemoryInfo; + +static uint64_t p_alloc_count = 0; +static P_DebugMemoryInfo p_ptr_array[P_MAX_ALLOCS]; + +static inline void * +p_debug_malloc_impl(size_t size, const char *file, int line, const char *func) +{ + void *ptr = malloc(size); + printf("[ALLOC] %p (%zu bytes) -> %s:%d %s()\n", ptr, size, file, line, func); + + if (ptr) { + if (p_alloc_count < P_MAX_ALLOCS) { + p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ + .ptr = ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } else { + fprintf(stderr, "[ERROR] Debug allocator tracking capacity (%d) exceeded!\n", P_MAX_ALLOCS); + } + } + return ptr; +} + +static inline void +p_debug_free_impl(void *ptr, const char *file, int line, const char *func) +{ + printf("[FREE] %p -> %s:%d %s()\n", ptr, file, line, func); + + if (!ptr) return; // Standard C allows free(NULL) + + bool found = false; + uint64_t index = 0; + + /* Find allocation metadata */ + for (index = 0; index < p_alloc_count; ++index) { + if (p_ptr_array[index].ptr == ptr) { + found = true; + break; + } + } + + if (found) { + /* Shift array left to remove tracked struct */ + for (uint64_t j = index; j < p_alloc_count - 1; ++j) { + p_ptr_array[j] = p_ptr_array[j + 1]; + } + p_alloc_count--; + } else { + fprintf(stderr, "[WARNING] Attempted to free untracked/double-freed pointer %p at %s:%d %s()\n", + ptr, file, line, func); + } + + free(ptr); +} + +static inline void * +p_debug_realloc_impl(void *ptr, size_t size, const char *file, int line, const char *func) +{ + if (!ptr) { + return p_debug_malloc_impl(size, file, line, func); + } + if (size == 0) { + p_debug_free_impl(ptr, file, line, func); + return NULL; + } + + void *new_ptr = realloc(ptr, size); + printf("[REALLOC] %p -> %p (%zu bytes) -> %s:%d %s()\n", ptr, new_ptr, size, file, line, func); + + if (new_ptr) { + bool found = false; + for (uint64_t i = 0; i < p_alloc_count; ++i) { + if (p_ptr_array[i].ptr == ptr) { + p_ptr_array[i].ptr = new_ptr; + p_ptr_array[i].size = size; + p_ptr_array[i].file = file; + p_ptr_array[i].line = line; + p_ptr_array[i].func = func; + found = true; + break; + } + } + if (!found) { + if (p_alloc_count < P_MAX_ALLOCS) { + p_ptr_array[p_alloc_count++] = (P_DebugMemoryInfo){ + .ptr = new_ptr, + .size = size, + .file = file, + .func = func, + .line = line + }; + } + } + } + return new_ptr; +} + +static void +p_debug_memory_report(void) +{ + printf("\n==================== MEMORY REPORT ====================\n"); + printf("Remaining unfreed allocations: %lu\n", (unsigned long)p_alloc_count); + + for (uint64_t i = 0; i < p_alloc_count; ++i) { + P_DebugMemoryInfo *info = &p_ptr_array[i]; + printf("[LEAK] %p (%zu bytes) allocated at %s:%d in %s()\n", + info->ptr, info->size, info->file, info->line, info->func); + } + printf("=======================================================\n"); +} +/* --- End of p_log.h --- */ +/* --- Start of p_ds.h --- */ +#define DEFAULT_ALIGNMENT (2 * sizeof(void *)) +#define ARRAY_GROW_FACTOR 2 + +typedef struct { + u32 size; + u32 capacity; +} DynamicArrayHeader; + +#define DARRAY_INIT_CAPACITY 64 +#define p_darray_push(array, var) \ + do { \ + if (array == NULL) { \ + DynamicArrayHeader *header = \ + malloc(DARRAY_INIT_CAPACITY * sizeof *array + sizeof *header); \ + header->size = 0; \ + header->capacity = DARRAY_INIT_CAPACITY; \ + array = (void *)(header + 1); \ + } \ + DynamicArrayHeader *header = (DynamicArrayHeader *)(array) - 1; \ + if (header->size >= header->capacity - 1) { \ + unsigned long new_capacity = header->capacity * 2; \ + DynamicArrayHeader *new_header = \ + realloc(header, new_capacity * sizeof *array + sizeof *header); \ + if (!new_header) \ + break; \ + header = new_header; \ + array = (void *)(header + 1); \ + header->capacity = new_capacity; \ + } \ + (array)[header->size++] = var; \ + } while (0) + +#define p_darray_len(array) ((array) ? ((DynamicArrayHeader *)(array) - 1)->size : 0) +#define p_darray_destroy(array) \ +{ \ + free((DynamicArrayHeader *)(array) - 1); \ +} + +#define p_darray_shrink(array) \ + do { \ + DynamicArrayHeader *header = (array) ? ((DynamicArrayHeader *)(array) - 1) : 0;\ + if (header) {\ + if (header->capacity > 4) {\ + unsigned long new_capacity = (header->capacity >> 1) + (header->capacity & 1); \ + void *new_header = realloc(header, new_capacity * sizeof *array + sizeof *header); \ + if (!new_header) break; \ + header = new_header; \ + array = (void *)(header + 1); \ + header->capacity = new_capacity; \ + }\ + }\ + } while (0) +/* --- End of p_ds.h --- */ + +/* + * Main Platform Types (Keyboard, Events, Context, etc.) + */ + +typedef enum { + P_KEYMOD_ALT = 0, + P_KEYMOD_SHIFT, + P_KEYMOD_CTRL, + P_KEYMOD_CAPS, +} P_KeyMod; + +typedef enum { + P_KEY_UNKNOWN = 0, + P_KEY_UP, P_KEY_DOWN, P_KEY_LEFT, P_KEY_RIGHT, + P_KEY_SPACE, P_KEY_ESCAPE, P_KEY_ENTER, + P_KEY_A, P_KEY_B, P_KEY_C, P_KEY_D, P_KEY_E, P_KEY_F, P_KEY_G, P_KEY_H, P_KEY_I, + P_KEY_J, P_KEY_K, P_KEY_L, P_KEY_M, P_KEY_N, P_KEY_O, P_KEY_P, P_KEY_Q, P_KEY_R, + P_KEY_S, P_KEY_T, P_KEY_U, P_KEY_V, P_KEY_W, P_KEY_X, P_KEY_Y, P_KEY_Z, +} P_KeyCode; + +/* Platform event type */ +typedef enum { + P_EVENT_NONE = 0, + P_EVENT_KEY_DOWN, + P_EVENT_KEY_UP, + P_EVENT_WINDOW_CLOSE, + P_EVENT_WINDOW_RESIZE, + P_EVENT_POINTER, + P_EVENT_CONTROLLER_CONNECTED, + P_EVENT_CONTROLLER_DISCONNECTED, +} P_EvenType; + +typedef enum { + P_POINTER_MOVED = 0, + P_POINTER_PRESSED, + P_POINTER_RELEASED +} P_PointerState; + +typedef struct { + P_EvenType type; + union { + struct {P_KeyCode key; P_KeyMod mod; } key; + struct { u32 width, height; } resize; + struct { P_PointerState state; u32 button; u32 x, y; } pointer; + }; +} P_Event; + +/* + * Platform dependent functions to be implemented by each layer. + */ + +POAPI u64 p_get_time(void); // Get time in nanoseconds. +POAPI void p_sleep_ns(i64 ns); // Sleep for nanoseconds. + +POAPI void p_stdout(void *msg, usize bytes); // Print buffer to stdout. +PODEF void p_log_printf(P_Log_Level level, const char* src, ...); // Print buffer to stdout. + + +POAPI void* p_file_alloc(const char *path, unsigned long *buf_size); +POAPI bool p_file_exists(const char *path); // Check if file exists. +POAPI usize p_file_size(const char *path); // Check file size. +POAPI void p_file_load(const char *path, void *buf_ptr, unsigned long buf_size); // Load file to buffer. +PODEF void p_file_write(const char *path, void *buf_ptr, unsigned long buf_size); // Write buffer to file. + +typedef struct P_Window_Impl P_Window; +POAPI bool p_window_open(P_Window *win, int width, int height, const char *title); // Opens a window. Returns false on failure. +POAPI void p_window_close(P_Window *win); // Close window. +POAPI bool p_window_is_open(P_Window *win); // Check if window is open. +POAPI void p_window_size(P_Window *win, int *window_width, int *window_height); // Get window size. +POAPI void p_window_draw(P_Window *win, u32 *pixels, int width, int height); // Draw directly to the window's buffer. Normally much slower than using the GPU. +POAPI bool p_window_poll_event(P_Window *win, P_Event *ev); // Returns true while there are events to poll. Write event data to a pointer. + +POAPI void p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc); +POAPI void p_audio_quit(); +POAPI void p_audio_write(const i16 *samples, usize count); + +/* + * The Vulkan Module is platform specific + */ + +#ifdef P_MODULE_VULKAN +#include +POAPI char** p_vulkan_get_extensions(); +POAPI bool p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface); +#endif + +/* + * The Remaining Modules are PLATFORM INDEPENDENT! + */ + +#ifdef P_MODULE_STRING +/* --- Start of p_string.h --- */ +typedef struct { + const char *cstr; + u32 len; +} P_StringView; + +typedef struct { + P_StringView sv; +} P_StringBuilder; + +/* Classic C string functions */ +PODEF char* p_find_char(char *buf, char needle); +PODEF bool p_strcmp(const char *a, const char *b); +PODEF int p_strlen(const char *a); +PODEF int p_strtoi(const char *str); +PODEF char* p_strtok(const char *str, char c); + +/* String views */ +PODEF P_StringView p_strview(const char *cstr); +PODEF void p_strview_chop_left(P_StringView *sv, u32 n); +PODEF void p_strview_chop_right(P_StringView *sv, u32 n); +PODEF P_StringView p_strview_chop_delim(P_StringView *sv, char delim); +PODEF P_StringView p_strview_chop_type(P_StringView *sv, int(*istype)(int)); +PODEF void p_strview_trim_left(P_StringView *sv); +PODEF void p_strview_trim_right(P_StringView *sv); +PODEF void p_strview_trim(P_StringView *sv); + +/* Classification */ +PODEF int p_is_space(int c); +PODEF int p_is_digit(int c); +PODEF int p_is_alpha(int c); +PODEF int p_is_alnum(int c); +PODEF int p_is_upper(int c); +PODEF int p_is_lower(int c); +PODEF int p_is_hex(int c); + +/* Transformation */ +PODEF int p_to_lower(int c); +PODEF int p_to_upper(int c); +/* --- End of p_string.h --- */ +#endif + +#ifdef P_MODULE_MATH +/* --- Start of p_math.h --- */ +typedef struct {int x, y; } Vec2i; +typedef struct {float x, y; } Vec2f; +typedef struct {float x, y, z; } Vec3f; +typedef struct { float m[16];} P_Mat4; // column-major: m[col * 4 + row] +PODEF int p_ceil(float x); +PODEF float p_cosf(float x); +PODEF float p_sinf(float x); + +PODEF Vec2f p_vec2f(float, float); +PODEF Vec2f p_vec2f_add(Vec2f, Vec2f); +PODEF float p_vec2f_cross(Vec2f, Vec2f); +PODEF float p_vec2f_dot(Vec2f, Vec2f); +PODEF Vec2f p_vec2f_mult(Vec2f a, float b); +PODEF Vec2f p_vec2f_sub(Vec2f, Vec2f); +PODEF Vec2i p_vec2i(float, float); +PODEF Vec2i p_vec2i_add(Vec2i, Vec2i); +PODEF float p_vec2i_cross(Vec2i, Vec2i); +PODEF float p_vec2i_dot(Vec2i, Vec2i); +PODEF Vec2i p_vec2i_mult(Vec2i a, float b); +PODEF Vec2i p_vec2i_sub(Vec2i, Vec2i); + +PODEF Vec3f p_vec3f(float, float, float); + +PODEF P_Mat4 p_mat4_identity(void); +PODEF P_Mat4 p_mat4_mul(P_Mat4 a, P_Mat4 b); +PODEF P_Mat4 p_mat4_translate(Vec3f v); +PODEF P_Mat4 p_mat4_translate_by(P_Mat4 m, Vec3f v); +PODEF P_Mat4 p_mat4_rotate_x(float rad); +PODEF P_Mat4 p_mat4_rotate_x_by(P_Mat4 m, float rad); +PODEF P_Mat4 p_mat4_rotate_y(float rad); +PODEF P_Mat4 p_mat4_rotate_y_by(P_Mat4 m, float rad); +PODEF P_Mat4 p_mat4_rotate_z(float rad); +PODEF P_Mat4 p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z); +/* --- End of p_math.h --- */ +#endif + +#ifdef P_MODULE_ALLOC +/* --- Start of p_allocators.h --- */ +typedef struct Arena { + unsigned char *buf; + usize buf_len; + usize curr_offset; + usize prev_offset; +} Arena; + +PODEF void p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length); +PODEF void* p_arena_alloc_align(Arena *a, usize s, usize align); +PODEF void* p_arena_alloc(Arena *a, usize s); +PODEF void* p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align); +PODEF void* p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size); +PODEF void p_arena_free_all(Arena *a); +PODEF void p_arena_destroy(Arena* a); + +typedef struct { u8 padding; } Stack_Allocation_Header; +typedef struct Stack { + unsigned char *buf; + usize buf_len; + usize offset; +} Stack; + +PODEF void p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length); +PODEF usize calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size); +PODEF void* p_stack_alloc(Stack *s, usize len, usize alignment); +PODEF void p_stack_free(Stack *s, void *ptr); +PODEF void p_stack_free_all(Stack* s); +PODEF void p_stack_destroy(Stack* s); + +typedef struct Pool_Free_Node Pool_Free_Node; + +typedef struct Pool { + unsigned char *buf; + usize chunk_size; + usize pool_size; + Pool_Free_Node *head; +} Pool; + +struct Pool_Free_Node { struct Pool_Free_Node *next; }; +PODEF void p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align); +PODEF void* p_pool_alloc(Pool *p); +PODEF void p_pool_free(Pool *p, void *ptr); +PODEF void p_pool_free_all(Pool *p); +PODEF void p_pool_destroy(Pool *p); +/* --- End of p_allocators.h --- */ +#endif + +#endif /* _PODIUM_H_ */ + +#ifdef PODIUM_IMPLEMENTATION +#undef PODIUM_IMPLEMENTATION + +#if defined(PODIUM_SDL) +/* #include "p_platform_sdl.c" */ +#elif defined(PODIUM_WIN32) +/* --- Start of p_platform_win32.c --- */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include /* gamepad support */ + +typedef struct { + HWAVEOUT wave_out; + WAVEHDR wave_header; +} P_Win32Ctx; + +struct P_Window_Impl { + HWND hwnd; + HDC hdc; + BITMAPINFO bitmap_info; + int width; + int height; + bool closed; +}; + +P_Win32Ctx p_ctx = {0}; + +static inline P_KeyCode +_win32_translate_vkey(WPARAM vk) +{ + switch (vk) { + case VK_LEFT: return P_KEY_LEFT; + case VK_RIGHT: return P_KEY_RIGHT; + case VK_UP: return P_KEY_UP; + case VK_DOWN: return P_KEY_DOWN; + case VK_SPACE: return P_KEY_SPACE; + case VK_ESCAPE: return P_KEY_ESCAPE; + case VK_RETURN: return P_KEY_ENTER; + case 'A': return P_KEY_A; + case 'B': return P_KEY_B; + case 'C': return P_KEY_C; + case 'D': return P_KEY_D; + case 'E': return P_KEY_E; + case 'F': return P_KEY_F; + case 'G': return P_KEY_G; + case 'H': return P_KEY_H; + case 'I': return P_KEY_I; + case 'J': return P_KEY_J; + case 'K': return P_KEY_K; + case 'L': return P_KEY_L; + case 'M': return P_KEY_M; + case 'N': return P_KEY_N; + case 'O': return P_KEY_O; + case 'P': return P_KEY_P; + case 'Q': return P_KEY_Q; + case 'R': return P_KEY_R; + case 'S': return P_KEY_S; + case 'T': return P_KEY_T; + case 'U': return P_KEY_U; + case 'V': return P_KEY_V; + case 'W': return P_KEY_W; + case 'X': return P_KEY_X; + case 'Y': return P_KEY_Y; + case 'Z': return P_KEY_Z; + default: return P_KEY_UNKNOWN; + } +} + +POAPI u64 +p_get_time(void) +{ + LARGE_INTEGER count, freq; + QueryPerformanceCounter(&count); + QueryPerformanceFrequency(&freq); + return (u64)((count.QuadPart * 1000000000ULL) / freq.QuadPart); +} + +POAPI void +p_sleep_ns(i64 ns) +{ + /* windows sleep is millisecond-based........ */ + Sleep((DWORD)(ns / 1000000)); +} + +POAPI void +p_stdout(void *msg, usize bytes) +{ + HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD bytes_written; + WriteFile(stdout_handle, msg, bytes, &bytes_written, NULL); +} + +POAPI bool +p_file_exists(const char *path) +{ + DWORD attr = GetFileAttributesA(path); + return (attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY)); +} + +POAPI usize +p_file_size(const char *path) +{ + WIN32_FILE_ATTRIBUTE_DATA data; + if (GetFileAttributesExA(path, GetFileExInfoStandard, &data)) { + return data.nFileSizeLow; + } + return 0; +} + +PODEF void +p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) +{ + HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (file != INVALID_HANDLE_VALUE) { + DWORD bytes_read; + ReadFile(file, buf_ptr, buf_size, &bytes_read, NULL); + CloseHandle(file); + } +} + +PODEF void +p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) +{ + HANDLE file = CreateFileA(path, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) { + DWORD bytes_written; + WriteFile(file, buf_ptr, buf_size, &bytes_written, NULL); + CloseHandle(file); + } +} + +static LRESULT CALLBACK +_win32_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + P_Window *win = (P_Window*)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + switch (msg) { + case WM_CLOSE: + /* Handled in poll_event; mark as closed */ + if (win) win->closed = true; + return 0; + } + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +POAPI bool +p_window_open(P_Window *win, int width, int height, const char *title) +{ + HINSTANCE instance = GetModuleHandleA(NULL); + + WNDCLASSA wc = {0}; + wc.lpfnWndProc = _win32_wnd_proc; + wc.hInstance = instance; + wc.lpszClassName = "P_Window_Class"; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + + RegisterClassA(&wc); + + win->hwnd = CreateWindowExA(0, wc.lpszClassName, title, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, width, height, + NULL, NULL, instance, NULL); + + if (!win->hwnd) return false; + + SetWindowLongPtrA(win->hwnd, GWLP_USERDATA, (LONG_PTR)win); + win->hdc = GetDC(win->hwnd); + win->width = width; + win->height = height; + win->closed = false; + + return true; +} + +POAPI void +p_window_close(P_Window *win) +{ + if (!win) return; + if (win->hdc) ReleaseDC(win->hwnd, win->hdc); + if (win->hwnd) DestroyWindow(win->hwnd); + win->hdc = NULL; + win->hwnd = NULL; +} + +POAPI bool +p_window_is_open(P_Window *win) +{ + return win && win->hwnd != NULL; +} + +POAPI void +p_window_size(P_Window *win, int *window_width, int *window_height) +{ + if (!win) return; + RECT rect; + GetClientRect(win->hwnd, &rect); + *window_width = rect.right - rect.left; + *window_height = rect.bottom - rect.top; + win->width = *window_width; + win->height = *window_height; +} + +POAPI void +p_window_draw(P_Window *win, u32 *pixels, int width, int height) +{ + BITMAPINFO bmi = {0}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -height; /* top-down */ + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + StretchDIBits(win->hdc, + 0, 0, win->width, win->height, + 0, 0, width, height, + pixels, &bmi, DIB_RGB_COLORS, SRCCOPY); +} + +POAPI bool +p_window_poll_event(P_Window *win, P_Event *ev) +{ + if (win->closed) { + win->closed = false; + ev->type = P_EVENT_WINDOW_CLOSE; + return true; + } + + MSG msg; + while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) { + switch (msg.message) { + + case WM_QUIT: + ev->type = P_EVENT_WINDOW_CLOSE; + return true; + + /* Key press */ + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + ev->type = P_EVENT_KEY_DOWN; + ev->key.key = _win32_translate_vkey(msg.wParam); + return true; + + /* Key release */ + case WM_KEYUP: + case WM_SYSKEYUP: + ev->type = P_EVENT_KEY_UP; + ev->key.key = _win32_translate_vkey(msg.wParam); + return true; + + /* Mouse movement */ + case WM_MOUSEMOVE: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_MOVED; + ev->pointer.button = 0; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + /* Mouse buttons */ + case WM_LBUTTONDOWN: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = 1; /* 1 = LMB, matching X11 convention */ + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_LBUTTONUP: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = 1; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_RBUTTONDOWN: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = 3; /* 3 = RMB */ + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_RBUTTONUP: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = 3; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_MBUTTONDOWN: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = 2; /* 2 = MMB */ + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + case WM_MBUTTONUP: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = 2; + ev->pointer.x = (u32)(msg.lParam & 0xFFFF); + ev->pointer.y = (u32)((msg.lParam >> 16) & 0xFFFF); + return true; + + /* Window resize */ + case WM_SIZE: + ev->type = P_EVENT_WINDOW_RESIZE; + ev->resize.width = (u32)(msg.lParam & 0xFFFF); + ev->resize.height = (u32)((msg.lParam >> 16) & 0xFFFF); + win->width = ev->resize.width; + win->height = ev->resize.height; + return true; + + default: + TranslateMessage(&msg); + DispatchMessageA(&msg); + break; + } + } + + ev->type = P_EVENT_NONE; + return false; +} + + +POAPI void +p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) +{ + (void)name; (void)desc; /* WinMM doesn't use a stream name */ + + WAVEFORMATEX wfx = {0}; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = channels; + wfx.nSamplesPerSec = sample_rate; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + + if (waveOutOpen(&p_ctx.wave_out, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) { + exit(1); + } +} + +POAPI void +p_audio_quit() +{ + if (p_ctx.wave_out) { + waveOutReset(p_ctx.wave_out); + waveOutClose(p_ctx.wave_out); + p_ctx.wave_out = NULL; + } +} + +POAPI void +p_audio_write(const i16 *samples, usize count) +{ + if (!p_ctx.wave_out) return; + + WAVEHDR header = {0}; + header.lpData = (LPSTR)samples; + header.dwBufferLength = (DWORD)(count * sizeof(i16)); + + waveOutPrepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); + waveOutWrite(p_ctx.wave_out, &header, sizeof(WAVEHDR)); + + /* wait for playback to finish (synchronous, mirrors pa_simple_drain) */ + while (!(header.dwFlags & WHDR_DONE)) Sleep(1); + + waveOutUnprepareHeader(p_ctx.wave_out, &header, sizeof(WAVEHDR)); +} + +#ifdef P_MODULE_VULKAN + +#ifndef VK_USE_PLATFORM_WIN32_KHR +#define VK_USE_PLATFORM_WIN32_KHR +#endif +#include + +POAPI char** +p_vulkan_get_extensions() +{ + char **darr = NULL; + p_darray_push(darr, "VK_KHR_surface"); + p_darray_push(darr, "VK_KHR_win32_surface"); + return darr; +} + +POAPI bool +p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) +{ + if (!instance || !window || !window->hwnd || !out_surface) { + return false; + } + + VkWin32SurfaceCreateInfoKHR create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.hwnd = window->hwnd; + create_info.hinstance = GetModuleHandleA(NULL); + + VkResult result = vkCreateWin32SurfaceKHR(instance, &create_info, allocator, out_surface); + return (result == VK_SUCCESS); +} +#endif +/* --- End of p_platform_win32.c --- */ +#elif defined(PODIUM_LINUX) +/* --- Start of p_platform_linux.c --- */ +/* linux */ +#include +#include +#include +#include +#include +#include +/* x11 */ +#include +#include +/* pulse audio */ +#include +#include +#include +/* joystick */ +#include + +typedef struct { + pa_simple *audio_stream; +} P_LinuxCtx; + +struct P_Window_Impl { + Display *display; + XImage *image; + Window window; + GC gc; + int screen; + int width; + int height; +}; + +P_LinuxCtx p_ctx = {0}; + +static inline P_KeyCode +_x11_translate_keycode(XEvent xev) +{ + static KeySym ks; + ks = XLookupKeysym(&xev.xkey, 0); + switch (ks) { + /* Arrow keys */ + case XK_Left: return P_KEY_LEFT; + case XK_Right: return P_KEY_RIGHT; + case XK_Up: return P_KEY_UP; + case XK_Down: return P_KEY_DOWN; + /* Common keys */ + case XK_space: return P_KEY_SPACE; + case XK_Escape: return P_KEY_ESCAPE; + case XK_Return: return P_KEY_ENTER; + case XK_A: case XK_a: return P_KEY_A; + case XK_B: case XK_b: return P_KEY_B; + case XK_C: case XK_c: return P_KEY_C; + case XK_D: case XK_d: return P_KEY_D; + case XK_E: case XK_e: return P_KEY_E; + case XK_F: case XK_f: return P_KEY_F; + case XK_G: case XK_g: return P_KEY_G; + case XK_H: case XK_h: return P_KEY_H; + case XK_I: case XK_i: return P_KEY_I; + case XK_J: case XK_j: return P_KEY_J; + case XK_K: case XK_k: return P_KEY_K; + case XK_L: case XK_l: return P_KEY_L; + case XK_M: case XK_m: return P_KEY_M; + case XK_N: case XK_n: return P_KEY_N; + case XK_O: case XK_o: return P_KEY_O; + case XK_P: case XK_p: return P_KEY_P; + case XK_Q: case XK_q: return P_KEY_Q; + case XK_R: case XK_r: return P_KEY_R; + case XK_S: case XK_s: return P_KEY_S; + case XK_T: case XK_t: return P_KEY_T; + case XK_U: case XK_u: return P_KEY_U; + case XK_V: case XK_v: return P_KEY_V; + case XK_W: case XK_w: return P_KEY_W; + case XK_X: case XK_x: return P_KEY_X; + case XK_Y: case XK_y: return P_KEY_Y; + case XK_Z: case XK_z: return P_KEY_Z; + default: return P_KEY_UNKNOWN; + } +} + +POAPI u64 +p_get_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return NANOS_PER_SEC * ts.tv_sec + ts.tv_nsec; +} + +POAPI void +p_sleep_ns(i64 ns) +{ + struct timespec ts; + ts.tv_sec = ns / 1000000000L; + ts.tv_nsec = ns % 1000000000L; + clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); +} + +POAPI void +p_stdout(void *msg, usize bytes) +{ + write(1, msg, bytes); +} + +POAPI bool +p_file_exists(const char *path) +{ + return (access(path, F_OK) == 0); +} + +POAPI usize +p_file_size(const char *path) +{ + struct stat st; + if (stat(path, &st) == 0) { + return st.st_size; + } + return 0; +} + +PODEF void +p_file_load(const char *path, void *buf_ptr, unsigned long buf_size) +{ + int fd = open(path, O_RDONLY); + if (fd >= 0) { + read(fd, buf_ptr, (size_t)buf_size); + close(fd); + } +} + +PODEF void +p_file_write(const char *path, void *buf_ptr, unsigned long buf_size) +{ + int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0644); + if (fd >= 0) { + write(fd, buf_ptr, (size_t)buf_size); + close(fd); + } +} + +POAPI bool +p_window_open(P_Window *win, int width, int height, const char *title) +{ + win->width = width; + win->height = height; + + XInitThreads(); + + win->display = XOpenDisplay(NULL); + if (win->display == NULL) { + PERROR("Cannot open display!"); + return false; + } + + int screen = DefaultScreen(win->display); + win->window = XCreateSimpleWindow( + win->display, + RootWindow(win->display, screen), + 0, 0, width, height, + 1, + BlackPixel(win->display, screen), + BlackPixel(win->display, screen) + ); + + win->screen = screen; + + XStoreName(win->display, win->window, title); + XSelectInput(win->display, win->window, + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | StructureNotifyMask); + XMapWindow(win->display, win->window); + + win->gc = DefaultGC(win->display, screen); + + Atom wm_delete = XInternAtom(win->display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(win->display, win->window, &wm_delete, 1); + + return true; +} + +POAPI void +p_window_close(P_Window *win) +{ + if (!win) return; + + if (win->image) { + // XDestroyImage(win->image); + win->image = NULL; + } + + if (win->display) { + Display *dpy = win->display; + Window w = win->window; + win->display = NULL; + win->window = 0; + + if (w) { + XDestroyWindow(dpy, w); + } + XSync(dpy, False); + } +} + +POAPI bool +p_window_is_open(P_Window *win) +{ + return win && win->display != NULL; +} + +POAPI void +p_window_size(P_Window *win, int *window_width, int *window_height) +{ + if (!win) return; + + XWindowAttributes attr; + XGetWindowAttributes(win->display, win->window, &attr); + *window_width = attr.width; + *window_height = attr.height; + + win->width = attr.width; + win->height = attr.height; +} + +POAPI void +p_window_draw(P_Window *win, u32 *pixels, int width, int height) +{ + int screen = win->screen; + + win->image = XCreateImage( + win->display, + DefaultVisual(win->display, screen), + DefaultDepth(win->display, screen), + ZPixmap, + 0, + (char*)pixels, + width, + height, + 32, + width * 4 + ); + + int window_width, window_height; + p_window_size(win, &window_width, &window_height); + + XPutImage( + win->display, + win->window, + win->gc, + win->image, + 0, 0, + 0, 0, + win->width, + win->height); + + XFlush(win->display); +} + +POAPI bool +p_window_poll_event(P_Window *win, P_Event *ev) +{ + if (XPending(win->display)) { + static XEvent xev; + XNextEvent(win->display, &xev); + + switch (xev.type) { + + case KeyPress: + ev->type = P_EVENT_KEY_DOWN; + ev->key.key = _x11_translate_keycode(xev); + return true; + + case KeyRelease: + ev->type = P_EVENT_KEY_UP; + ev->key.key = _x11_translate_keycode(xev); + return true; + + case ConfigureNotify: + ev->type = P_EVENT_WINDOW_RESIZE; + ev->resize.width = xev.xconfigure.width; + ev->resize.height = xev.xconfigure.height; + return true; + + case ClientMessage: + if ((Atom)xev.xclient.data.l[0] == + XInternAtom(win->display, "WM_DELETE_WINDOW", False)) { + ev->type = P_EVENT_WINDOW_CLOSE; + return true; + } + break; + + case MotionNotify: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_MOVED; + ev->pointer.button = 0; + ev->pointer.x = xev.xmotion.x; + ev->pointer.y = xev.xmotion.y; + return true; + + case ButtonPress: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_PRESSED; + ev->pointer.button = xev.xbutton.button; /* 1=LMB, 2=MMB, 3=RMB */ + ev->pointer.x = xev.xbutton.x; + ev->pointer.y = xev.xbutton.y; + return true; + + case ButtonRelease: + ev->type = P_EVENT_POINTER; + ev->pointer.state = P_POINTER_RELEASED; + ev->pointer.button = xev.xbutton.button; + ev->pointer.x = xev.xbutton.x; + ev->pointer.y = xev.xbutton.y; + return true; + } + } + + ev->type = P_EVENT_NONE; + return false; +} + +POAPI void +p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc) +{ + pa_sample_spec ss = { + .format = PA_SAMPLE_S16LE, + .rate = sample_rate, + .channels = channels, + }; + + int error; + p_ctx.audio_stream = pa_simple_new( + NULL, + name, + PA_STREAM_PLAYBACK, + NULL, + desc, + &ss, + NULL, + NULL, + &error + ); + + if (!p_ctx.audio_stream) { + PERROR("PulseAudio init failed: %s\n", pa_strerror(error)); + exit(1); + } +} + +POAPI void +p_audio_quit() +{ + if (p_ctx.audio_stream) { + pa_simple_free(p_ctx.audio_stream); + p_ctx.audio_stream = NULL; + } +} + +POAPI void +p_audio_write(const i16 *samples, usize count) +{ + if (!p_ctx.audio_stream) return; + + int error; + if (pa_simple_write(p_ctx.audio_stream, samples, count * sizeof(i16), &error) < 0) { + PERROR("PulseAudio write failed: %s\n", pa_strerror(error)); + } + + pa_simple_drain(p_ctx.audio_stream, &error); +} + +#ifdef P_MODULE_VULKAN + +#ifndef VK_USE_PLATFORM_XLIB_KHR +#define VK_USE_PLATFORM_XLIB_KHR +#endif +#include + +POAPI char** +p_vulkan_get_extensions() +{ + char **darr = NULL; + p_darray_push(darr, "VK_KHR_surface"); + p_darray_push(darr, "VK_KHR_xcb_surface"); + p_darray_push(darr, "VK_KHR_xlib_surface"); + return darr; +} + +POAPI bool +p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface) +{ + if (!instance || !window || !window->display || !window->window || !out_surface) { + return false; + } + + VkXlibSurfaceCreateInfoKHR create_info = {0}; + create_info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + create_info.pNext = NULL; + create_info.flags = 0; + create_info.dpy = window->display; + create_info.window = window->window; + + VkResult result = vkCreateXlibSurfaceKHR(instance, &create_info, allocator, out_surface); + + return (result == VK_SUCCESS); +} +#endif +/* --- End of p_platform_linux.c --- */ +#endif /* linux */ + + +POAPI void* p_file_alloc(const char *path, unsigned long *buf_size) { + if (p_file_exists(path) == false) { + return NULL; + } + + usize file_size = p_file_size(path); + *buf_size = file_size; + + void *ptr = malloc(file_size); + if (ptr) { + p_file_load(path, ptr, file_size); // Load file to buffer. + } + return ptr; +} + + +/* + * Import Module Code + */ + +#ifdef P_MODULE_STRING +/* --- Start of p_string.c --- */ +PODEF char* +p_find_char(char *buf, char needle) +{ + while (buf && *buf != needle) buf++; + return buf; +} + +PODEF bool +p_strcmp(const char *a, const char *b) +{ + if (p_strlen(a) != p_strlen(b)) + return false; + + while (a && b && *a != '\0') { + if (*(a++) != *(b++)) return false; + } + + return true; +} + +PODEF int +p_strlen(const char *a) +{ + int i = 0; + while (*a++ != '\0') i++; + return i; +} + +PODEF int +p_strtoi(const char *str) +{ + int res = 0; + + while (*str) { + if (*str >= '0' && *str <= '9') { + res = res * 10 + (*str - '0'); + } else { + return res; + } + str++; + } + + return res; +} + +PODEF char* +p_strtok(const char *str, char c) +{ + while (*str != c && *str++ != '\0'); + return (*str == c) ? (char*) ++str : 0; +} + +PODEF P_StringView +p_strview(const char *cstr) +{ + P_StringView sv = {0}; + sv.cstr = cstr; + sv.len = p_strlen(cstr); + return sv; +} + +PODEF void +p_strview_chop_left(P_StringView *sv, u32 n) +{ + if (n > sv->len) n = sv->len; + sv->cstr += n; + sv->len -= n; +} + +PODEF void +p_strview_chop_right(P_StringView *sv, u32 n) +{ + if (n > sv->len) n = sv->len; + sv->len -= n; +} + +PODEF P_StringView +p_strview_chop_delim(P_StringView *sv, char delim) +{ + u32 i = 0; + while (i < sv->len && sv->cstr[i] != delim) { + i++; + } + + P_StringView tok; + if (i < sv->len) { + tok.cstr = sv->cstr; + tok.len = i; + p_strview_chop_left(sv, i + 1); + return tok; + } + + tok = *sv; + p_strview_chop_left(sv, sv->len); + return tok; +} + +PODEF P_StringView +p_strview_chop_type(P_StringView *sv, int(*istype)(int)) +{ + u32 i = 0; + while (i < sv->len && istype(sv->cstr[i])) { + i++; + } + + P_StringView tok; + if (i < sv->len) { + tok.cstr = sv->cstr; + tok.len = i; + p_strview_chop_left(sv, i + 1); + return tok; + } + + tok = *sv; + p_strview_chop_left(sv, sv->len); + return tok; +} + + +PODEF void +p_strview_trim_left(P_StringView *sv) +{ + while (sv->len > 0 && p_is_space(sv->cstr[0])) { + p_strview_chop_left(sv, 1); + } +} + +PODEF void +p_strview_trim_right(P_StringView *sv) +{ + while (sv->len > 0 && p_is_space(sv->cstr[sv->len-1])) { + p_strview_chop_right(sv, 1); + } +} + +PODEF void +p_strview_trim(P_StringView *sv) +{ + p_strview_trim_left(sv); + p_strview_trim_right(sv); +} + +PODEF int +p_is_space(int c) +{ + /* Checks for: space, form feed (\f), line feed (\n), + * carriage return (\r), horizontal tab (\t), vertical tab (\v) */ + return (c == ' ' || (c >= '\t' && c <= '\r')); +} + +PODEF int +p_is_digit(int c) +{ + return (c >= '0' && c <= '9'); +} + +PODEF int +p_is_alpha(int c) +{ + return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); +} + +PODEF int +p_is_alnum(int c) +{ + return (p_is_alpha(c) || p_is_digit(c)); +} + +PODEF int +p_is_hex(int c) +{ + return (p_is_digit(c) || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')); +} + +PODEF int +p_is_upper(int c) +{ + return (c >= 'A' && c <= 'Z'); +} + +PODEF int +p_is_lower(int c) +{ + return (c >= 'a' && c <= 'z'); +} + +PODEF int +p_to_lower(int c) +{ + if (p_is_upper(c)) return (c + ('a' - 'A')); + return c; +} + +PODEF int +p_to_upper(int c) +{ + if (p_is_lower(c)) return (c - ('a' - 'A')); + return c; +} +/* --- End of p_string.c --- */ +#endif + +#ifdef P_MODULE_MATH +/* --- Start of p_math.c --- */ +PODEF +int p_ceil(float x) +{ + int base = (int) x; + return (x > base) ? base + 1 : base; +} + +PODEF float +p_cosf(float x) +{ + x += 1.57079632679f; + + while (x > P_PI) x -= P_PI2; + while (x < -P_PI) x += P_PI2; + + const float B = 4.0f / P_PI; + const float C = -4.0f / P_PI_POW2; + + float y = B * x + C * x * (x < 0 ? -x : x); + + const float P = 0.225f; + y = P * (y * (y < 0 ? -y : y) - y) + y; + + return y; +} + +PODEF float +p_sinf(float x) +{ + while (x > P_PI) x -= P_PI2; + while (x < -P_PI) x += P_PI2; + + const float B = 4.0f / P_PI; + const float C = -4.0f / (P_PI_POW2); + + float y = B * x + C * x * (x < 0 ? -x : x); + + const float P = 0.225f; + y = P * (y * (y < 0 ? -y : y) - y) + y; + + return y; +} + +PODEF Vec2f +p_vec2f(float x, float y) +{ + return (Vec2f) { .x = x, .y = y }; +} + +PODEF Vec2f +p_vec2f_add(Vec2f a, Vec2f b) +{ + return (Vec2f) { + .x = a.x + b.x, + .y = a.y + b.y, + }; +} + +PODEF float +p_vec2f_cross(Vec2f a, Vec2f b) +{ + // axby − aybx + return a.x * b.y - a.y * b.x; +} + +PODEF float +p_vec2f_dot(Vec2f a, Vec2f b) +{ + // a · b = ax × bx + ay × by + return a.x * b.x + a.y * b.y; +} + +PODEF Vec2f +p_vec2f_mult(Vec2f a, float b) +{ + return (Vec2f) { + .x = a.x * b, + .y = a.y * b, + }; +} + +PODEF Vec2f +p_vec2f_sub(Vec2f a, Vec2f b) +{ + return (Vec2f) { + .x = a.x - b.x, + .y = a.y - b.y, + }; +} + +PODEF Vec2i +p_vec2i(float x, float y) +{ + return (Vec2i) { .x = x, .y = y }; +} + +PODEF Vec2i +p_vec2i_add(Vec2i a, Vec2i b) +{ + return (Vec2i) { + .x = a.x + b.x, + .y = a.y + b.y, + }; +} + +PODEF float +p_vec2i_cross(Vec2i a, Vec2i b) +{ + // axby − aybx + return a.x * b.y - a.y * b.x; +} + +PODEF float +p_vec2i_dot(Vec2i a, Vec2i b) +{ + // a · b = ax × bx + ay × by + return a.x * b.x + a.y * b.y; +} + +PODEF Vec2i +p_vec2i_mult(Vec2i a, float b) +{ + return (Vec2i) { + .x = a.x * b, + .y = a.y * b, + }; +} + +PODEF Vec2i +p_vec2i_sub(Vec2i a, Vec2i b) +{ + return (Vec2i) { + .x = a.x - b.x, + .y = a.y - b.y, + }; +} + +PODEF Vec3f +p_vec3f(float x, float y, float z) +{ + return (Vec3f) { .x = x, .y = y, .z = z }; +} + +PODEF P_Mat4 +p_mat4_identity(void) +{ + P_Mat4 res = {0}; + res.m[0] = 1.0f; + res.m[5] = 1.0f; + res.m[10] = 1.0f; + res.m[15] = 1.0f; + return res; +} + +PODEF P_Mat4 +p_mat4_mul(P_Mat4 a, P_Mat4 b) +{ + P_Mat4 res = {0}; + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + float sum = 0.0f; + for (int k = 0; k < 4; ++k) { + sum += a.m[k * 4 + row] * b.m[col * 4 + k]; + } + res.m[col * 4 + row] = sum; + } + } + return res; +} + +PODEF P_Mat4 +p_mat4_translate(Vec3f v) +{ + P_Mat4 res = p_mat4_identity(); + res.m[12] = v.x; + res.m[13] = v.y; + res.m[14] = v.z; + return res; +} + +PODEF P_Mat4 +p_mat4_translate_by(P_Mat4 m, Vec3f v) +{ + P_Mat4 res = m; + res.m[12] = m.m[0] * v.x + m.m[4] * v.y + m.m[8] * v.z + m.m[12]; + res.m[13] = m.m[1] * v.x + m.m[5] * v.y + m.m[9] * v.z + m.m[13]; + res.m[14] = m.m[2] * v.x + m.m[6] * v.y + m.m[10] * v.z + m.m[14]; + res.m[15] = m.m[3] * v.x + m.m[7] * v.y + m.m[11] * v.z + m.m[15]; + return res; +} + +PODEF P_Mat4 +p_mat4_rotate_x(float rad) +{ + P_Mat4 res = p_mat4_identity(); + float c = p_cosf(rad); + float s = p_sinf(rad); + res.m[5] = c; + res.m[6] = s; + res.m[9] = -s; + res.m[10] = c; + return res; +} + +PODEF P_Mat4 +p_mat4_rotate_x_by(P_Mat4 m, float rad) +{ + return p_mat4_mul(m, p_mat4_rotate_x(rad)); +} + +PODEF P_Mat4 +p_mat4_rotate_y(float rad) +{ + P_Mat4 res = p_mat4_identity(); + float c = p_cosf(rad); + float s = p_sinf(rad); + res.m[0] = c; + res.m[2] = -s; + res.m[8] = s; + res.m[10] = c; + return res; +} + +PODEF P_Mat4 +p_mat4_rotate_y_by(P_Mat4 m, float rad) +{ + return p_mat4_mul(m, p_mat4_rotate_y(rad)); +} + +PODEF P_Mat4 +p_mat4_rotate_z(float rad) +{ + P_Mat4 res = p_mat4_identity(); + float c = p_cosf(rad); + float s = p_sinf(rad); + res.m[0] = c; + res.m[1] = s; + res.m[4] = -s; + res.m[5] = c; + return res; +} + +PODEF P_Mat4 +p_mat4_perspective(float fov_rad, float aspect, float near_z, float far_z) +{ + P_Mat4 res = {0}; + float tan_half_fov = p_sinf(fov_rad * 0.5f) / p_cosf(fov_rad * 0.5f); + + res.m[0] = 1.0f / (aspect * tan_half_fov); + res.m[5] = 1.0f / tan_half_fov; + res.m[10] = far_z / (near_z - far_z); + res.m[11] = -1.0f; + res.m[14] = (near_z * far_z) / (near_z - far_z); + return res; +} +/* --- End of p_math.c --- */ +#endif + +#ifdef P_MODULE_ALLOC +/* --- Start of p_allocators.c --- */ +PODEF bool +is_power_of_two(uintptr x) { + return (x & (x-1)) == 0; +} + +PODEF uintptr +align_forward(uintptr ptr, usize align) +{ + uintptr p, a, remainder; + assert(is_power_of_two(align)); + + p = ptr; + a = (uintptr)align; + remainder = p & (a-1); + + return (remainder != 0) ? p + a - remainder : p; +} + +PODEF void +p_arena_create(Arena *a, void *backing_buffer, usize backing_buffer_length) +{ + a->buf = (unsigned char *)backing_buffer; + a->buf_len = backing_buffer_length; + a->curr_offset = 0; + a->prev_offset = 0; +} + +PODEF void* +p_arena_alloc_align(Arena *a, usize s, usize align) +{ + + // Align 'curr_offset' forward to the specified alignment + uintptr curr_ptr = (uintptr)a->buf + (uintptr)a->curr_offset; + uintptr offset = align_forward(curr_ptr, align); + offset -= (uintptr)a->buf; // Change to relative offset + + // Check to see if the backing memory has space left + if (offset+s <= a->buf_len) { + void *ptr = &a->buf[offset]; + a->prev_offset = offset; + a->curr_offset = offset+s; + + memset(ptr, 0, s); + return ptr; + } + + return NULL; +} + +PODEF void* +p_arena_alloc(Arena *a, usize s) { + return p_arena_alloc_align(a, s, DEFAULT_ALIGNMENT); +} + +PODEF void* +p_arena_resize_align(Arena *a, void *old_memory, usize old_size, usize new_size, usize align) +{ + assert(is_power_of_two(align)); + unsigned char *old_mem = (unsigned char *)old_memory; + + if (old_mem == NULL || old_size == 0) { + return p_arena_alloc_align(a, new_size, align); + } else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) { + if (a->buf+a->prev_offset == old_mem) { + a->curr_offset = a->prev_offset + new_size; + if (new_size > old_size) { + // Zero the new memory by default + memset(&a->buf[a->curr_offset], 0, new_size-old_size); + } + return old_memory; + } else { + void *new_memory = p_arena_alloc_align(a, new_size, align); + size_t copy_size = old_size < new_size ? old_size : new_size; + memmove(new_memory, old_memory, copy_size); + return new_memory; + } + + } else { + assert(0 && "Memory is out of bounds of the buffer in this arena"); + return NULL; + } +} + +PODEF void* +p_arena_resize(Arena *a, void *old_memory, usize old_size, usize new_size) +{ + return p_arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT); +} + +PODEF void +p_arena_free_all(Arena *a) +{ + a->curr_offset = 0; + a->prev_offset = 0; +} + +PODEF void +p_arena_destroy(Arena* a) +{ + a->buf_len = 0; + a->curr_offset = 0; + a->prev_offset = 0; +} + +PODEF void +p_stack_create(Stack *s, void *backing_buffer, usize backing_buffer_length) +{ + s->buf = (unsigned char*) backing_buffer; + s->buf_len = backing_buffer_length; + s->offset = 0; +} + +PODEF usize +calc_padding_with_header(uintptr ptr, uintptr alignment, usize header_size) +{ + uintptr p, a, modulo, padding, needed_space; + + assert(is_power_of_two(alignment)); + + p = ptr; + a = alignment; + modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two + + padding = 0; + needed_space = 0; + + if (modulo != 0) { // Same logic as 'align_forward' + padding = a - modulo; + } + + needed_space = (uintptr)header_size; + + if (padding < needed_space) { + needed_space -= padding; + + if ((needed_space & (a-1)) != 0) { + padding += a * (1+(needed_space/a)); + } else { + padding += a * (needed_space/a); + } + } + + return (usize)padding; +} + +PODEF void* +p_stack_alloc(Stack *s, usize len, usize alignment) +{ + uintptr curr_addr, next_addr; + usize padding; + Stack_Allocation_Header *header; + + assert(is_power_of_two(alignment)); + + if (alignment > 128) { + // As the padding is 8 bits (1 byte), the largest alignment that can + // be used is 128 bytes + alignment = 128; + } + + curr_addr = (uintptr)s->buf + (uintptr)s->offset; + padding = calc_padding_with_header(curr_addr, (uintptr)alignment, sizeof(Stack_Allocation_Header)); + if (s->offset + padding + len > s->buf_len) { + // Stack allocator is out of memory + return NULL; + } + s->offset += padding; + + next_addr = curr_addr + (uintptr)padding; + header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header)); + header->padding = (u8)padding; + + s->offset += len; + + return memset((void *)next_addr, 0, len); +} + +PODEF void +p_stack_free(Stack *s, void *ptr) +{ + if (ptr != NULL) { + uintptr start, end, curr_addr; + Stack_Allocation_Header *header; + usize prev_offset; + + start = (uintptr)s->buf; + end = start + (uintptr)s->buf_len; + curr_addr = (uintptr)ptr; + + if (!(start <= curr_addr && curr_addr < end)) { + assert(0 && "Out of bounds memory address passed to stack allocator (free)"); + return; + } + + if (curr_addr >= start+(uintptr)s->offset) { + // Allow double frees + return; + } + + header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header)); + prev_offset = (usize)(curr_addr - (uintptr)header->padding - start); + + s->offset = prev_offset; + } +} + +PODEF void +p_stack_free_all(Stack* s) +{ + s->offset = 0; +} + +PODEF void +p_stack_destroy(Stack* s) +{ + s->buf = 0; + s->buf_len = 0; + s->offset = 0; +} + +PODEF void +p_pool_create(Pool *p, void *buf, usize buf_len, usize chunk_size, usize chunk_align) +{ + /* align start */ + uintptr start = (uintptr) buf; + uintptr aligned = align_forward(start, chunk_align); + buf_len -= (aligned - start); + + /* align chunk usize */ + chunk_size = (usize) align_forward(chunk_size, chunk_align); + + assert(chunk_size >= sizeof(Pool_Free_Node) && "Chunk usize is too small"); + assert(buf_len >= chunk_size && "Backing buffer length is smaller than the chunk size"); + + p->buf = (unsigned char *) buf; + p->pool_size = buf_len; + p->chunk_size = chunk_size; + p->head = NULL; + + p_pool_free_all(p); +} + +PODEF void* +p_pool_alloc(Pool *p) +{ + Pool_Free_Node *node = p->head; + + assert(node != NULL && "Pool allocator has no free memory"); + + p->head = p->head->next; + + return memset(node, 0 , p->chunk_size); +} + +PODEF void +p_pool_free(Pool *p, void *ptr) +{ + Pool_Free_Node *node; + + void *start = p->buf; + void *end = &p->buf[p->pool_size]; + + if (ptr == NULL) { + return; + } + + if (!(start <= ptr && ptr < end)) { + assert(0 && "Memory is out of bounds of the buffer in this pool"); + return; + } + + node = (Pool_Free_Node *)ptr; + node->next = p->head; + p->head = node; +} + +PODEF void +p_pool_free_all(Pool *p) +{ + usize chunk_count = p->pool_size / p->chunk_size; + usize i; + + for (i = 0; i < chunk_count; i++) { + void *ptr = &p->buf[i * p->chunk_size]; + Pool_Free_Node *node = (Pool_Free_Node *)ptr; + + // Push free node onto thte free list + node->next = p->head; + p->head = node; + } +} + +PODEF void +p_pool_destroy(Pool *p) +{ + p->buf = NULL; + p->pool_size = 0; + p->chunk_size = 0; + p->head = NULL; +} + +/* --- End of p_allocators.c --- */ +#endif + +#define P_MAX_PRINTF_SIZE 1024 +PODEF void +p_log_printf(P_Log_Level level, const char* src, ...) +{ + static char out_message[P_MAX_PRINTF_SIZE]; + static u32 offset = P_PREFIX_LEN + 1; + + memcpy(out_message, p_prefix[level], P_PREFIX_LEN); /* prefixes have a fixed len */ + out_message[P_PREFIX_LEN] = ' '; /* write space */ + + __builtin_va_list arg_ptr; + va_start(arg_ptr, src); + int len = vsnprintf(out_message+offset, P_MAX_PRINTF_SIZE-offset, src, arg_ptr); // append message + va_end(arg_ptr); + + if (len < 0) len = 0; + usize total_len = offset + len; + if (total_len > P_MAX_PRINTF_SIZE) { + total_len = P_MAX_PRINTF_SIZE; + } + + p_stdout(out_message, total_len); + p_stdout("\n", 1); +} + +#endif // PODIUM_IMPLEMENTATION diff --git a/archive/Podium/podium_header.h b/archive/Podium/podium_header.h new file mode 100644 index 0000000..6407f62 --- /dev/null +++ b/archive/Podium/podium_header.h @@ -0,0 +1,258 @@ +/* =========================================================================== + * PODIUM - Kitchen Sink - Copyright (c) 2026 Vasco Alves + * --------------------------------------------------------------------------- + * To stand on the shoulders of giants... Giants must stand still!" + * --------------------------------------------------------------------------- + * PREFIX: P_ (types) or p_ (functions & variables) + * --------------------------------------------------------------------------- + * COMPILE TIME FLAGS + * ._________________________________________________________________________, + * | Name | Description | + * |-----------------|-------------------------------------------------------| + * | PODIUM_WIN32 | Used to explicitly compile for Windows | + * | PODIUM_LINUX | Used to explicitly compile for Linux and FreeBSD. | + * '-------------------------------------------------------------------------' + * MODULES + * ._________________________________________________________________________, + * | Name | Description | + * |-----------------|-------------------------------------------------------| + * | P_MODULE_VULKAN | Gives access to vulkan related utilities. | + * | P_MODULE_STRING | Include string manipulation utilities. | + * | P_MODULE_MATH | Trigonometry and linear algebra functions. | + * | P_MODULE_ALLOC | Memory allocators: Arena, Stack & Pool. | + * '-------------------------------------------------------------------------' + * =========================================================================== */ + +#ifndef _PODIUM_H_ +#define _PODIUM_H_ + +#define PODIUM_MAJOR "0" // needs more revisions and use to reach 1.0 status +#define PODIUM_MINOR "2" // vulkan module +#define PODIUM_PATCH "2" // gamepad + +#define POAPI static inline +#define PODEF static inline + +/* + * Auto-detect platforms. + */ + +/* Windows */ +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) + #ifndef PODIUM_WIN32 + #define PODIUM_WIN32 + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif +/* Apple */ +#elif defined(__APPLE__) || defined(__MACH__) + #include + #ifndef PODIUM_APPLE + #define PODIUM_APPLE + #endif + #if TARGET_OS_IPHONE + #define PODIUM_IOS + #else + #define PODIUM_MACOS + #endif +/* Linux / BSD */ +#elif defined(__linux__) || defined(__FreeBSD__) + #ifndef PODIUM_LINUX + #define PODIUM_LINUX + #endif + #ifndef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 200112L + #endif +#endif + +#include +#include +#include +#include /* memcpy */ +#include /* vsnprintf */ + +/* + * NOTE: The comments are used by the build + * script to know what includes to inline into the + * final single header file! + */ + +#include "p_types.h" // +#include "p_log.h" // +#include "p_ds.h" // + +/* + * Main Platform Types (Keyboard, Events, Context, etc.) + */ + +typedef enum { + P_KEYMOD_ALT = 0, + P_KEYMOD_SHIFT, + P_KEYMOD_CTRL, + P_KEYMOD_CAPS, +} P_KeyMod; + +typedef enum { + P_KEY_UNKNOWN = 0, + P_KEY_UP, P_KEY_DOWN, P_KEY_LEFT, P_KEY_RIGHT, + P_KEY_SPACE, P_KEY_ESCAPE, P_KEY_ENTER, + P_KEY_A, P_KEY_B, P_KEY_C, P_KEY_D, P_KEY_E, P_KEY_F, P_KEY_G, P_KEY_H, P_KEY_I, + P_KEY_J, P_KEY_K, P_KEY_L, P_KEY_M, P_KEY_N, P_KEY_O, P_KEY_P, P_KEY_Q, P_KEY_R, + P_KEY_S, P_KEY_T, P_KEY_U, P_KEY_V, P_KEY_W, P_KEY_X, P_KEY_Y, P_KEY_Z, +} P_KeyCode; + +/* Platform event type */ +typedef enum { + P_EVENT_NONE = 0, + P_EVENT_KEY_DOWN, + P_EVENT_KEY_UP, + P_EVENT_WINDOW_CLOSE, + P_EVENT_WINDOW_RESIZE, + P_EVENT_POINTER, + P_EVENT_CONTROLLER_CONNECTED, + P_EVENT_CONTROLLER_DISCONNECTED, +} P_EvenType; + +typedef enum { + P_POINTER_MOVED = 0, + P_POINTER_PRESSED, + P_POINTER_RELEASED +} P_PointerState; + +typedef struct { + P_EvenType type; + union { + struct {P_KeyCode key; P_KeyMod mod; } key; + struct { u32 width, height; } resize; + struct { P_PointerState state; u32 button; u32 x, y; } pointer; + }; +} P_Event; + +/* + * Platform dependent functions to be implemented by each layer. + */ + +POAPI u64 p_get_time(void); // Get time in nanoseconds. +POAPI void p_sleep_ns(i64 ns); // Sleep for nanoseconds. + +POAPI void p_stdout(void *msg, usize bytes); // Print buffer to stdout. +PODEF void p_log_printf(P_Log_Level level, const char* src, ...); // Print buffer to stdout. + + +POAPI void* p_file_alloc(const char *path, unsigned long *buf_size); +POAPI bool p_file_exists(const char *path); // Check if file exists. +POAPI usize p_file_size(const char *path); // Check file size. +POAPI void p_file_load(const char *path, void *buf_ptr, unsigned long buf_size); // Load file to buffer. +PODEF void p_file_write(const char *path, void *buf_ptr, unsigned long buf_size); // Write buffer to file. + +typedef struct P_Window_Impl P_Window; +POAPI bool p_window_open(P_Window *win, int width, int height, const char *title); // Opens a window. Returns false on failure. +POAPI void p_window_close(P_Window *win); // Close window. +POAPI bool p_window_is_open(P_Window *win); // Check if window is open. +POAPI void p_window_size(P_Window *win, int *window_width, int *window_height); // Get window size. +POAPI void p_window_draw(P_Window *win, u32 *pixels, int width, int height); // Draw directly to the window's buffer. Normally much slower than using the GPU. +POAPI bool p_window_poll_event(P_Window *win, P_Event *ev); // Returns true while there are events to poll. Write event data to a pointer. + +POAPI void p_audio_init(const int sample_rate, const int channels, const char *name, const char *desc); +POAPI void p_audio_quit(); +POAPI void p_audio_write(const i16 *samples, usize count); + +/* + * The Vulkan Module is platform specific + */ + +#ifdef P_MODULE_VULKAN +#include +POAPI char** p_vulkan_get_extensions(); +POAPI bool p_vulkan_create_surface(P_Window *window, VkInstance instance, const VkAllocationCallbacks* allocator, VkSurfaceKHR* out_surface); +#endif + +/* + * The Remaining Modules are PLATFORM INDEPENDENT! + */ + +#ifdef P_MODULE_STRING +#include // +#endif + +#ifdef P_MODULE_MATH +#include // +#endif + +#ifdef P_MODULE_ALLOC +#include // +#endif + +#endif /* _PODIUM_H_ */ + +#ifdef PODIUM_IMPLEMENTATION +#undef PODIUM_IMPLEMENTATION + +#if defined(PODIUM_SDL) +/* #include "p_platform_sdl.c" */ +#elif defined(PODIUM_WIN32) +#include "p_platform_win32.c" // +#elif defined(PODIUM_LINUX) +#include "p_platform_linux.c" // +#endif /* linux */ + + +POAPI void* p_file_alloc(const char *path, unsigned long *buf_size) { + if (p_file_exists(path) == false) { + return NULL; + } + + usize file_size = p_file_size(path); + *buf_size = file_size; + + void *ptr = malloc(file_size); + if (ptr) { + p_file_load(path, ptr, file_size); // Load file to buffer. + } + return ptr; +} + + +/* + * Import Module Code + */ + +#ifdef P_MODULE_STRING +#include // +#endif + +#ifdef P_MODULE_MATH +#include // +#endif + +#ifdef P_MODULE_ALLOC +#include // +#endif + +#define P_MAX_PRINTF_SIZE 1024 +PODEF void +p_log_printf(P_Log_Level level, const char* src, ...) +{ + static char out_message[P_MAX_PRINTF_SIZE]; + static u32 offset = P_PREFIX_LEN + 1; + + memcpy(out_message, p_prefix[level], P_PREFIX_LEN); /* prefixes have a fixed len */ + out_message[P_PREFIX_LEN] = ' '; /* write space */ + + __builtin_va_list arg_ptr; + va_start(arg_ptr, src); + int len = vsnprintf(out_message+offset, P_MAX_PRINTF_SIZE-offset, src, arg_ptr); // append message + va_end(arg_ptr); + + if (len < 0) len = 0; + usize total_len = offset + len; + if (total_len > P_MAX_PRINTF_SIZE) { + total_len = P_MAX_PRINTF_SIZE; + } + + p_stdout(out_message, total_len); + p_stdout("\n", 1); +} + +#endif // PODIUM_IMPLEMENTATION diff --git a/build.c b/build.c index b07404d..d111efe 100644 --- a/build.c +++ b/build.c @@ -1,33 +1,11 @@ #define POOF_IMPLEMENTATION -#include "../Poof/poof.h" - -void Peak() { - Poof_Cmd cmd = {0}; - poof_cmd_append(&cmd, "python", "bundle_header.py", "src/peak_header.h"); - poof_cmd_run(&cmd); - - poof_copy_file("Peak/src/peak_header.h.out", "Peak/peak.h"); - - Poof_CC cc = {0}; - poof_cc_init(&cc, POOF_CC_GCC | POOF_CC_CLANG, POOF_TARGET_HOST); - cc.debug_mode = true; - cc.optimization = POOF_O0; - cc.output = "Peak/demos/demo"; - poof_cc_append(&cc.inputs, "Peak/demos/demo.c"); - poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations"); - poof_cc_append(&cc.libs, "m"); - poof_cc_run(&cc); - - cc.debug_mode = true; - cc.optimization = POOF_O0; - cc.output = "Peak/demos/demo_main"; - poof_cc_append(&cc.inputs, "Peak/demos/demo_replace_main.c"); - poof_cc_append(&cc.extra_flags, "-std=c99", "-Wall", "-Wno-deprecated-declarations"); - poof_cc_append(&cc.libs, "m"); - poof_cc_run(&cc); -} +#include "Poof/poof.h" int main(int argc, char **argv) { POOF_GO_REBUILD_URSELF(argc, argv); - Peak(); + + Poof_Cmd cmd = {0}; + poof_cmd_append(&cmd, "./Peak/build"); + poof_cmd_run(&cmd); + return 0; } -- cgit v1.3.1