summaryrefslogtreecommitdiff
path: root/Peak
diff options
context:
space:
mode:
Diffstat (limited to 'Peak')
-rw-r--r--Peak/build.c39
-rwxr-xr-xPeak/demos/demobin0 -> 43544 bytes
-rw-r--r--Peak/demos/demo.c38
-rw-r--r--Peak/demos/demo.html19
-rw-r--r--Peak/demos/demo.js2370
-rwxr-xr-xPeak/demos/demo.wasmbin0 -> 30983 bytes
-rwxr-xr-xPeak/demos/demo_runbin0 -> 43912 bytes
-rw-r--r--Peak/demos/demo_run.c (renamed from Peak/demos/demo_replace_main.c)49
-rw-r--r--Peak/peak.h1508
-rw-r--r--Peak/src/p_emscripten.c194
-rw-r--r--Peak/src/p_linux.c495
-rw-r--r--Peak/src/p_win32.c399
-rw-r--r--Peak/src/peak.c296
-rw-r--r--Peak/src/peak_header.h164
-rw-r--r--Peak/src/peak_header.h.out1508
15 files changed, 5291 insertions, 1788 deletions
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
--- /dev/null
+++ b/Peak/demos/demo
Binary files 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 <stdio.h>
-#include <math.h>
-#include <unistd.h>
#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 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>Peak</title>
+ <style>
+ html, body { margin: 0; background: #111; color: #ccc; font: 14px/1.4 sans-serif; }
+ main { padding: 1rem; }
+ canvas { display: block; background: #000; image-rendering: pixelated; outline: none; }
+ </style>
+</head>
+<body>
+ <main>
+ <canvas id="demo" width="400" height="400" tabindex="0"></canvas>
+ </main>
+ <script src="demo.js"></script>
+</body>
+</html>
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<Module>
+// 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
--- /dev/null
+++ b/Peak/demos/demo.wasm
Binary files differ
diff --git a/Peak/demos/demo_run b/Peak/demos/demo_run
new file mode 100755
index 0000000..7a526c1
--- /dev/null
+++ b/Peak/demos/demo_run
Binary files differ
diff --git a/Peak/demos/demo_replace_main.c b/Peak/demos/demo_run.c
index 3aedfaa..f5c2f7f 100644
--- a/Peak/demos/demo_replace_main.c
+++ b/Peak/demos/demo_run.c
@@ -1,8 +1,5 @@
#include <stdio.h>
-#include <math.h>
-#include <unistd.h>
-#define PEAK_REPLACE_MAIN
#define PEAK_IMPLEMENTATION
#include "../peak.h"
@@ -42,26 +39,38 @@ typedef struct {
static AppData data;
-int main(int argc, char**argv) {
- data.msg = "Hello World";
- return PEAK_CONTINUE;
-}
-
-void
-peak_events(PeakEvent ev)
+static int
+peak_run_func(PeakWindow *win, void *userdata)
{
- print_event[ev.type](ev);
- switch (ev.type) {
- case PEAK_EVENT_WINDOW_CLOSE:
- printf("stoppppeeed\n");
- peak_stop();
- break;
+ 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;
}
-void
-peak_tick()
+int
+main(int argc, char**argv)
{
- printf("%s\n",data.msg);
- sleep(1);
+ 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 <assert.h>
-#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
@@ -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;
-/* 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.
-
/* 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);
-
-/*
- * 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 <emscripten.h>
-/*
- * 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.
- */
+typedef struct peak_window_internal_t PeakWindowInternal;
+typedef struct PeakWindow PeakWindow;
-static inline void
-peak__emscripten_loop_step(void *arg)
-{
- (void)arg;
- PeakEvent ev;
- while (peak_poll_events(&ev)) {
- peak_events(ev);
- }
-
- if (running) {
- peak_tick();
- } else {
- emscripten_cancel_main_loop();
- peak_quit();
- }
-}
+/* Public API */
+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);
-#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__)
-#endif
-#endif // PEAK_DONT_REPLACE_MAIN
-
#endif // PEAK_H
#ifdef PEAK_IMPLEMENTATION
#undef PEAK_IMPLEMENTATION
-#if defined(PEAK_WIN32)
-/* --- Start of p_win32.c --- */
-#include <windows.h>
+#if defined(PEAK_WIN32) || defined(PEAK_WEB)
+#define PEAK_Q 64
-void
-peak_platform_window_open()
-{
-}
+typedef struct {
+ unsigned h, n;
+ PeakEvent e[PEAK_Q];
+} PeakQ;
-void
-peak_platform_window_close()
+static void
+peak_q_push(PeakQ *q, PeakEvent ev)
{
+ if (!q || q->n == PEAK_Q)
+ return;
+ q->e[(q->h + q->n++) % PEAK_Q] = ev;
}
-uint32_t*
-peak_platform_window_buffer(size_t *width, size_t *height)
+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
-bool
-peak_platform_epoll(PeakEvent *ev)
-{
-}
-/* --- End of p_win32.c --- */
-#elif defined(PEAK_LINUX)
-/* --- Start of p_linux.c --- */
-#include <dlfcn.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <X11/keysym.h>
-#include <stdbool.h>
+#if defined(PEAK_WIN32)
+/* --- Start of p_win32.c --- */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#define PEAK_X11_LINUX "/usr/lib/libX11.so.6"
+#define PEAK_WIN32_USER32 "user32.dll"
+#define PEAK_WIN32_GDI32 "gdi32.dll"
+#define PEAK_WIN32_CLASS "PeakWindow"
+#define PEAK_WIN32_PROP "Peak"
-#define PEAK_LINUX_DEFAULT_WIDTH 800
-#define PEAK_LINUX_DEFAULT_HEIGHT 600
+#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))
-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_GDI32_API(X) \
+ X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD))
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;
-} PeakX11Api;
+#define X(name, ret, conv, args) ret (conv *name) args;
+ PEAK_USER32_API(X)
+#undef X
+} PeakUser32Api;
-static PeakX11Api peak_x11;
+typedef struct {
+#define X(name, ret, conv, args) ret (conv *name) args;
+ PEAK_GDI32_API(X)
+#undef X
+} PeakGdi32Api;
-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");
+typedef struct {
+ HMODULE user32;
+ HMODULE gdi32;
+ int class_reg;
+} PeakWin32;
- 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;
-}
+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;
+};
-typedef struct {
- Display *display;
- Atom wm_delete_window;
- bool initialized;
-} PeakLinux;
+static PeakWin32 peak_win32;
+static PeakUser32Api peak_user32;
+static PeakGdi32Api peak_gdi32;
-typedef struct {
- Window window;
- GC gfx_ctx; /* GRAPHICS CONTEXT, not garbage collector lol */
-} PeakLinuxWindow;
+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;
+}
-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 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;
+}
static PeakKeyCode
-peak__x11_map_key(KeySym sym)
+peak_internal_win32_key_map(WPARAM vk)
{
- 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 (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;
+ }
}
static PeakKeyMod
-peak__x11_map_mod(unsigned int state)
+peak_internal_win32_mod_map(void)
{
- 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 (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;
}
-void
-peak_platform_window_open()
+static int
+peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height)
{
- 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?");
+ uint32_t *buffer;
- 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");
+ 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;
+}
- /* 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");
+static LRESULT CALLBACK
+peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ struct peak_win32_win *w;
+ PeakEvent ev;
- /* Wrap the buffer in an XImage for XPutImage blitting */
- Visual *visual = DefaultVisual(peak_linux.display, screen);
- int depth = DefaultDepth(peak_linux.display, screen);
+ if (msg == WM_NCCREATE) {
+ CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam;
+ peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams);
+ }
- 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");
+ w = (struct peak_win32_win *)peak_user32.GetPropA(hwnd, PEAK_WIN32_PROP);
+ if (!w)
+ return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam);
- peak_x11.XMapRaised(peak_linux.display, peak_linux_window.window);
- peak_x11.XFlush(peak_linux.display);
+ 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;
- peak_linux.initialized = true;
+ 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;
}
-void
-peak_platform_window_close()
+static void
+peak_platform_quit(void)
{
- if (!peak_linux.initialized) return;
+ if (!peak_win32.class_reg)
+ return;
+ peak_user32.UnregisterClassA(PEAK_WIN32_CLASS, GetModuleHandleA(NULL));
+ peak_win32.class_reg = 0;
+}
- if (peak_linux_ximage) {
- peak_linux_ximage->data = NULL;
- XDestroyImage(peak_linux_ximage);
- peak_linux_ximage = NULL;
- }
+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;
+ }
- free(peak_linux_buffer);
- peak_linux_buffer = NULL;
+ 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;
+ }
- 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);
+ w->hdc = peak_user32.GetDC(w->hwnd);
+ if (!w->hdc) {
+ peak_user32.DestroyWindow(w->hwnd);
+ free(w->buffer);
+ free(w);
+ return intern;
+ }
- peak_linux.initialized = false;
+ peak_user32.ShowWindow(w->hwnd, SW_SHOWNORMAL);
+ intern.w = w;
+ return intern;
}
-uint32_t *
-peak_platform_window_buffer(size_t *width, size_t *height)
+static void
+peak_platform_window_close(PeakWindowInternal *intern)
{
- *width = peak_linux_width;
- *height = peak_linux_height;
- return peak_linux_buffer;
+ 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;
}
-bool
-peak_platform_epoll(PeakEvent *ev)
+static uint32_t *
+peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height)
{
- if (!peak_linux.initialized) return false;
+ 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;
+}
- Display *dpy = peak_linux.display;
+static void
+peak_platform_window_present(PeakWindowInternal *intern)
+{
+ struct peak_win32_win *w = intern ? intern->w : NULL;
+ BITMAPINFO bmi;
- while (peak_x11.XPending(dpy) > 0) {
- XEvent xev;
- peak_x11.XNextEvent(dpy, &xev);
+ if (!w || !w->hwnd || !w->hdc || !w->buffer)
+ return;
- switch (xev.type) {
+ 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);
+}
- 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;
+static bool
+peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev)
+{
+ MSG msg;
+ struct peak_win32_win *w = intern ? intern->w : NULL;
- 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;
- }
+ if (!w || !w->hwnd)
+ return 0;
- 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;
- }
+ 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 --- */
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
- 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;
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
- 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;
+#define PEAK_X11_LINUX "libX11.so.6"
- 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;
+#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))
- default:
- continue;
- }
- }
+typedef struct {
+#define X(name, ret, args) ret (*name) args;
+ PEAK_X11_API(X)
+#undef X
+} PeakX11Api;
- 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;
-}
-/* --- End of p_linux.c --- */
-#elif defined(PEAK_WEB)
-/* --- Start of p_emscripten.c --- */
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
+typedef struct {
+ Display *display;
+ Atom wm_delete_window;
+} PeakLinux;
-#define PEAK_CANVAS "#PeakCanvas"
+struct peak_window_internal_t {
+ Window window;
+ GC gfx_ctx;
+ XImage *ximage;
+ uint32_t *buffer;
+ uint32_t width;
+ uint32_t height;
+};
-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);
+static PeakLinux peak_linux;
+static PeakX11Api peak_x11;
-void
-peak_platform_window_open()
+static int
+peak_internal_x11_load(void *handle)
{
- 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);
+#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;
}
-void
-peak_platform_window_close()
+static PeakKeyCode
+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;
+ }
}
-uint32_t*
-peak_platform_window_buffer(size_t *width, size_t *height)
+static PeakKeyMod
+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;
}
-bool
-peak_platform_epoll(PeakEvent *ev)
+static Bool
+peak_internal_x11_window_match(Display *dpy, XEvent *ev, XPointer arg)
{
- return 0;
+ (void)dpy;
+ return ev->xany.window == *(Window *)arg;
}
-/* --- 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 int
+peak_linux_buffer(PeakWindowInternal *w, uint32_t width, uint32_t height)
+{
+ int screen;
-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;
+ 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;
-void
-peak__put_pixel(int x, int y, uint32_t color)
-{
- 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;
+ 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__lerp_color(uint32_t c0, uint32_t c1, float t)
+static int
+peak_platform_init(void)
{
- if (t <= 0.0f) return c0;
- if (t >= 1.0f) return c1;
+ void *handle;
- 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;
+ 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;
}
-float
-peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy)
+static void
+peak_platform_quit(void)
{
- return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax);
+ if (!peak_linux.display)
+ return;
+ peak_x11.XCloseDisplay(peak_linux.display);
+ peak_linux.display = 0;
}
-void
-peak_init(void)
+static PeakWindowInternal
+peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags)
{
- 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;
+ 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;
}
-void
-peak_quit(void)
+static void
+peak_platform_window_close(PeakWindowInternal *w)
{
- peak_platform_window_close();
+ 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};
}
-void
-peak_extensions(void)
+static uint32_t *
+peak_platform_window_buffer(PeakWindowInternal *w, size_t *width, size_t *height)
{
- // TODO
+ if (!w || !w->window) {
+ *width = 0;
+ *height = 0;
+ return NULL;
+ }
+ *width = w->width;
+ *height = w->height;
+ return w->buffer;
}
-bool
-peak_poll_events(PeakEvent *ev)
+static void
+peak_platform_window_present(PeakWindowInternal *w)
{
- return peak_platform_epoll(ev);
+ 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);
}
-void
-peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height)
+static bool
+peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev)
{
- 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;
+ XEvent xev;
- 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 (!w || !w->window || !peak_linux.display)
+ return 0;
- if (start_x >= end_x) continue;
+ 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 <emscripten.h>
+#include <emscripten/html5.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
- size_t src_offset_x = (size_t)(start_x - offset_x);
- size_t copy_pixels = (size_t)(end_x - start_x);
+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();
+});
- memcpy(&peak_window_buffer[target_y * peak_window_width + start_x], &rgba[y * width + src_offset_x], copy_pixels * sizeof(uint32_t));
- }
+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_clip(int x, int y, size_t w, size_t h)
+static PeakKeyCode
+peak_web_key_map(const char *code)
{
- peak_clip_x = x;
- peak_clip_y = y;
- peak_clip_width = w;
- peak_clip_height = h;
+ 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_clip_reset(void)
+static EM_BOOL
+peak_web_key(int type, const EmscriptenKeyboardEvent *e, void *ud)
{
- peak_clip_x = 0;
- peak_clip_y = 0;
- peak_clip_width = peak_window_width;
- peak_clip_height = peak_window_height;
+ 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;
}
-void
-peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color)
+static EM_BOOL
+peak_web_mouse(int type, const EmscriptenMouseEvent *e, void *ud)
{
- 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;
+ 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;
+}
- 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;
- }
- }
+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);
}
-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)
+static int
+peak_platform_init(void)
{
- 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);
+ return 1;
+}
- 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);
- }
- }
+static void
+peak_platform_quit(void)
+{
}
-void
-peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color)
+static PeakWindowInternal
+peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags)
{
- 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);
+ PeakWindowInternal intern = {0};
+ struct peak_web_win *w;
- float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2);
- if (area == 0.0f) return;
+ (void)flags;
- 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);
+ 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;
- if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) {
- peak__put_pixel(x, y, color);
- }
- }
- }
+ peak_web_dom_open(w->name, (int)width, (int)height);
+
+ peak_web_listen(w, 1);
+ intern.w = w;
+ return intern;
}
-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)
+static void
+peak_platform_window_close(PeakWindowInternal *intern)
{
- 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);
+ struct peak_web_win *w;
+ if (!intern || !intern->w)
+ return;
+ w = intern->w;
+ peak_web_listen(w, 0);
+ free(w);
+ intern->w = NULL;
+}
- float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2);
- if (area == 0.0f) return;
+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;
+}
- 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;
+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);
+}
- 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);
- }
- }
- }
+static bool
+peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *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_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color)
+/* 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;
+};
+
+
+/* --- Start of peak.c --- */
+static uint32_t *
+peak_window_sync(PeakWindow *win, size_t *width, size_t *height)
{
- 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;
+ 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;
+}
- int half_w = (int)width / 2;
+int
+peak_init(void)
+{
+ return peak_platform_init();
+}
- 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_quit(void)
+{
+ peak_platform_quit();
+}
+
+PeakWindow
+peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags)
+{
+ 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_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1)
+peak_window_close(PeakWindow *win)
{
- 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;
+ win->running = 0;
+ peak_platform_window_close(&win->internal);
+ win->buffer = NULL;
+ win->width = 0;
+ win->height = 0;
+ win->bufsize = 0;
+}
- int orig_x0 = x0, orig_y0 = y0;
- int half_w = (int)width / 2;
+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;
+}
- 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);
+uint32_t *
+peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height)
+{
+ return peak_window_sync(win, width, height);
+}
- 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);
- }
- }
+void
+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;
+}
- 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_window_present(PeakWindow *win)
+{
+ if (win) peak_platform_window_present(&win->internal);
+}
+
+#ifdef PEAK_WEB
+#include <emscripten.h>
+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 <emscripten.h>
+#include <emscripten/html5.h>
#include <stdlib.h>
#include <string.h>
-#include <stdbool.h>
+#include <stdint.h>
-#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 <dlfcn.h>
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
+
#include <X11/Xlib.h>
-#include <X11/Xutil.h>
#include <X11/Xatom.h>
+#include <X11/Xutil.h>
#include <X11/keysym.h>
-#include <stdbool.h>
+#include <dlfcn.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#define PEAK_X11_LINUX "/usr/lib/libX11.so.6"
+#define PEAK_X11_LINUX "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_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;
+ int screen;
- if (peak_linux_ximage) {
- peak_linux_ximage->data = NULL;
- XDestroyImage(peak_linux_ximage);
- peak_linux_ximage = NULL;
- }
+ 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;
- free(peak_linux_buffer);
- peak_linux_buffer = NULL;
+ 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;
+}
- 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 int
+peak_platform_init(void)
+{
+ void *handle;
- peak_linux.initialized = false;
+ 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;
}
-uint32_t *
-peak_platform_window_buffer(size_t *width, size_t *height)
+static void
+peak_platform_quit(void)
{
- *width = peak_linux_width;
- *height = peak_linux_height;
- return peak_linux_buffer;
+ if (!peak_linux.display)
+ return;
+ peak_x11.XCloseDisplay(peak_linux.display);
+ peak_linux.display = 0;
}
-bool
-peak_platform_epoll(PeakEvent *ev)
+static PeakWindowInternal
+peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags)
{
- if (!peak_linux.initialized) return false;
-
- Display *dpy = peak_linux.display;
+ PeakWindowInternal w = {0};
+ int screen;
- while (peak_x11.XPending(dpy) > 0) {
- XEvent xev;
- peak_x11.XNextEvent(dpy, &xev);
+ (void)flags;
+ if (!peak_linux.display && !peak_platform_init())
+ return w;
- switch (xev.type) {
+ 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);
- 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;
+ 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};
+ }
- 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;
- }
+ peak_x11.XMapRaised(peak_linux.display, w.window);
+ peak_x11.XFlush(peak_linux.display);
+ return w;
+}
- 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;
- }
+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 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 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;
+}
- 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_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);
+}
- 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 bool
+peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev)
+{
+ XEvent xev;
- default:
- continue;
- }
- }
+ if (!w || !w->window || !peak_linux.display)
+ return 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);
- peak_x11.XFlush(dpy);
- return false;
+ 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 <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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)
-{
- 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 uint32_t *
+peak_window_sync(PeakWindow *win, size_t *width, size_t *height)
{
- 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;
+ 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;
}
-float
-peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy)
+int
+peak_init(void)
{
- 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_window_clear(PeakWindow *win, float r, float g, float b, float a)
{
- peak_clip_x = x;
- peak_clip_y = y;
- peak_clip_width = w;
- peak_clip_height = h;
+ 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 <emscripten.h>
+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)
+peak_window_run(PeakWindow *win, int (*peak_tick)(PeakWindow *win, void *userdata), void *userdata)
{
- 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);
- }
- }
+ 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 <assert.h>
-#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
@@ -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 <emscripten.h>
-/*
- * 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" // <REPLACE>
@@ -235,6 +182,19 @@ peak__emscripten_loop_step(void *arg)
#include "p_emscripten.c" // <REPLACE>
#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" // <REPLACE>
#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 <assert.h>
-#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
@@ -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;
-/* 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.
-
/* 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);
-
-/*
- * 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 <emscripten.h>
-/*
- * 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.
- */
+typedef struct peak_window_internal_t PeakWindowInternal;
+typedef struct PeakWindow PeakWindow;
-static inline void
-peak__emscripten_loop_step(void *arg)
-{
- (void)arg;
- PeakEvent ev;
- while (peak_poll_events(&ev)) {
- peak_events(ev);
- }
-
- if (running) {
- peak_tick();
- } else {
- emscripten_cancel_main_loop();
- peak_quit();
- }
-}
+/* Public API */
+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);
-#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__)
-#endif
-#endif // PEAK_DONT_REPLACE_MAIN
-
#endif // PEAK_H
#ifdef PEAK_IMPLEMENTATION
#undef PEAK_IMPLEMENTATION
-#if defined(PEAK_WIN32)
-/* --- Start of p_win32.c --- */
-#include <windows.h>
+#if defined(PEAK_WIN32) || defined(PEAK_WEB)
+#define PEAK_Q 64
-void
-peak_platform_window_open()
-{
-}
+typedef struct {
+ unsigned h, n;
+ PeakEvent e[PEAK_Q];
+} PeakQ;
-void
-peak_platform_window_close()
+static void
+peak_q_push(PeakQ *q, PeakEvent ev)
{
+ if (!q || q->n == PEAK_Q)
+ return;
+ q->e[(q->h + q->n++) % PEAK_Q] = ev;
}
-uint32_t*
-peak_platform_window_buffer(size_t *width, size_t *height)
+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
-bool
-peak_platform_epoll(PeakEvent *ev)
-{
-}
-/* --- End of p_win32.c --- */
-#elif defined(PEAK_LINUX)
-/* --- Start of p_linux.c --- */
-#include <dlfcn.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#include <X11/keysym.h>
-#include <stdbool.h>
+#if defined(PEAK_WIN32)
+/* --- Start of p_win32.c --- */
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#define PEAK_X11_LINUX "/usr/lib/libX11.so.6"
+#define PEAK_WIN32_USER32 "user32.dll"
+#define PEAK_WIN32_GDI32 "gdi32.dll"
+#define PEAK_WIN32_CLASS "PeakWindow"
+#define PEAK_WIN32_PROP "Peak"
-#define PEAK_LINUX_DEFAULT_WIDTH 800
-#define PEAK_LINUX_DEFAULT_HEIGHT 600
+#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))
-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_GDI32_API(X) \
+ X(StretchDIBits, int, WINAPI, (HDC, int, int, int, int, int, int, int, int, const void *, const BITMAPINFO *, UINT, DWORD))
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;
-} PeakX11Api;
+#define X(name, ret, conv, args) ret (conv *name) args;
+ PEAK_USER32_API(X)
+#undef X
+} PeakUser32Api;
-static PeakX11Api peak_x11;
+typedef struct {
+#define X(name, ret, conv, args) ret (conv *name) args;
+ PEAK_GDI32_API(X)
+#undef X
+} PeakGdi32Api;
-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");
+typedef struct {
+ HMODULE user32;
+ HMODULE gdi32;
+ int class_reg;
+} PeakWin32;
- 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;
-}
+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;
+};
-typedef struct {
- Display *display;
- Atom wm_delete_window;
- bool initialized;
-} PeakLinux;
+static PeakWin32 peak_win32;
+static PeakUser32Api peak_user32;
+static PeakGdi32Api peak_gdi32;
-typedef struct {
- Window window;
- GC gfx_ctx; /* GRAPHICS CONTEXT, not garbage collector lol */
-} PeakLinuxWindow;
+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;
+}
-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 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;
+}
static PeakKeyCode
-peak__x11_map_key(KeySym sym)
+peak_internal_win32_key_map(WPARAM vk)
{
- 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 (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;
+ }
}
static PeakKeyMod
-peak__x11_map_mod(unsigned int state)
+peak_internal_win32_mod_map(void)
{
- 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 (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;
}
-void
-peak_platform_window_open()
+static int
+peak_win32_buffer(struct peak_win32_win *w, uint32_t width, uint32_t height)
{
- 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?");
+ uint32_t *buffer;
- 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");
+ 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;
+}
- /* 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");
+static LRESULT CALLBACK
+peak_internal_win32_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ struct peak_win32_win *w;
+ PeakEvent ev;
- /* Wrap the buffer in an XImage for XPutImage blitting */
- Visual *visual = DefaultVisual(peak_linux.display, screen);
- int depth = DefaultDepth(peak_linux.display, screen);
+ if (msg == WM_NCCREATE) {
+ CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam;
+ peak_user32.SetPropA(hwnd, PEAK_WIN32_PROP, cs->lpCreateParams);
+ }
- 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");
+ w = (struct peak_win32_win *)peak_user32.GetPropA(hwnd, PEAK_WIN32_PROP);
+ if (!w)
+ return peak_user32.DefWindowProcA(hwnd, msg, wparam, lparam);
- peak_x11.XMapRaised(peak_linux.display, peak_linux_window.window);
- peak_x11.XFlush(peak_linux.display);
+ 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;
- peak_linux.initialized = true;
+ 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;
}
-void
-peak_platform_window_close()
+static void
+peak_platform_quit(void)
{
- if (!peak_linux.initialized) return;
+ if (!peak_win32.class_reg)
+ return;
+ peak_user32.UnregisterClassA(PEAK_WIN32_CLASS, GetModuleHandleA(NULL));
+ peak_win32.class_reg = 0;
+}
- if (peak_linux_ximage) {
- peak_linux_ximage->data = NULL;
- XDestroyImage(peak_linux_ximage);
- peak_linux_ximage = NULL;
- }
+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;
+ }
- free(peak_linux_buffer);
- peak_linux_buffer = NULL;
+ 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;
+ }
- 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);
+ w->hdc = peak_user32.GetDC(w->hwnd);
+ if (!w->hdc) {
+ peak_user32.DestroyWindow(w->hwnd);
+ free(w->buffer);
+ free(w);
+ return intern;
+ }
- peak_linux.initialized = false;
+ peak_user32.ShowWindow(w->hwnd, SW_SHOWNORMAL);
+ intern.w = w;
+ return intern;
}
-uint32_t *
-peak_platform_window_buffer(size_t *width, size_t *height)
+static void
+peak_platform_window_close(PeakWindowInternal *intern)
{
- *width = peak_linux_width;
- *height = peak_linux_height;
- return peak_linux_buffer;
+ 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;
}
-bool
-peak_platform_epoll(PeakEvent *ev)
+static uint32_t *
+peak_platform_window_buffer(PeakWindowInternal *intern, size_t *width, size_t *height)
{
- if (!peak_linux.initialized) return false;
+ 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;
+}
- Display *dpy = peak_linux.display;
+static void
+peak_platform_window_present(PeakWindowInternal *intern)
+{
+ struct peak_win32_win *w = intern ? intern->w : NULL;
+ BITMAPINFO bmi;
- while (peak_x11.XPending(dpy) > 0) {
- XEvent xev;
- peak_x11.XNextEvent(dpy, &xev);
+ if (!w || !w->hwnd || !w->hdc || !w->buffer)
+ return;
- switch (xev.type) {
+ 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);
+}
- 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;
+static bool
+peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *ev)
+{
+ MSG msg;
+ struct peak_win32_win *w = intern ? intern->w : NULL;
- 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;
- }
+ if (!w || !w->hwnd)
+ return 0;
- 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;
- }
+ 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 --- */
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
- 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;
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
- 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;
+#define PEAK_X11_LINUX "libX11.so.6"
- 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;
+#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))
- default:
- continue;
- }
- }
+typedef struct {
+#define X(name, ret, args) ret (*name) args;
+ PEAK_X11_API(X)
+#undef X
+} PeakX11Api;
- 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;
-}
-/* --- End of p_linux.c --- */
-#elif defined(PEAK_WEB)
-/* --- Start of p_emscripten.c --- */
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
+typedef struct {
+ Display *display;
+ Atom wm_delete_window;
+} PeakLinux;
-#define PEAK_CANVAS "#PeakCanvas"
+struct peak_window_internal_t {
+ Window window;
+ GC gfx_ctx;
+ XImage *ximage;
+ uint32_t *buffer;
+ uint32_t width;
+ uint32_t height;
+};
-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);
+static PeakLinux peak_linux;
+static PeakX11Api peak_x11;
-void
-peak_platform_window_open()
+static int
+peak_internal_x11_load(void *handle)
{
- 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);
+#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;
}
-void
-peak_platform_window_close()
+static PeakKeyCode
+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;
+ }
}
-uint32_t*
-peak_platform_window_buffer(size_t *width, size_t *height)
+static PeakKeyMod
+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;
}
-bool
-peak_platform_epoll(PeakEvent *ev)
+static Bool
+peak_internal_x11_window_match(Display *dpy, XEvent *ev, XPointer arg)
{
- return 0;
+ (void)dpy;
+ return ev->xany.window == *(Window *)arg;
}
-/* --- 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 int
+peak_linux_buffer(PeakWindowInternal *w, uint32_t width, uint32_t height)
+{
+ int screen;
-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;
+ 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;
-void
-peak__put_pixel(int x, int y, uint32_t color)
-{
- 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;
+ 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__lerp_color(uint32_t c0, uint32_t c1, float t)
+static int
+peak_platform_init(void)
{
- if (t <= 0.0f) return c0;
- if (t >= 1.0f) return c1;
+ void *handle;
- 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;
+ 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;
}
-float
-peak__edge_cross(float ax, float ay, float bx, float by, float cx, float cy)
+static void
+peak_platform_quit(void)
{
- return (cx - ax) * (by - ay) - (cy - ay) * (bx - ax);
+ if (!peak_linux.display)
+ return;
+ peak_x11.XCloseDisplay(peak_linux.display);
+ peak_linux.display = 0;
}
-void
-peak_init(void)
+static PeakWindowInternal
+peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags)
{
- 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;
+ 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;
}
-void
-peak_quit(void)
+static void
+peak_platform_window_close(PeakWindowInternal *w)
{
- peak_platform_window_close();
+ 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};
}
-void
-peak_extensions(void)
+static uint32_t *
+peak_platform_window_buffer(PeakWindowInternal *w, size_t *width, size_t *height)
{
- // TODO
+ if (!w || !w->window) {
+ *width = 0;
+ *height = 0;
+ return NULL;
+ }
+ *width = w->width;
+ *height = w->height;
+ return w->buffer;
}
-bool
-peak_poll_events(PeakEvent *ev)
+static void
+peak_platform_window_present(PeakWindowInternal *w)
{
- return peak_platform_epoll(ev);
+ 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);
}
-void
-peak_blit(int offset_x, int offset_y, const uint32_t *rgba, size_t width, size_t height)
+static bool
+peak_platform_epoll(PeakWindowInternal *w, PeakEvent *ev)
{
- 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;
+ XEvent xev;
- 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 (!w || !w->window || !peak_linux.display)
+ return 0;
- if (start_x >= end_x) continue;
+ 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 <emscripten.h>
+#include <emscripten/html5.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
- size_t src_offset_x = (size_t)(start_x - offset_x);
- size_t copy_pixels = (size_t)(end_x - start_x);
+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();
+});
- memcpy(&peak_window_buffer[target_y * peak_window_width + start_x], &rgba[y * width + src_offset_x], copy_pixels * sizeof(uint32_t));
- }
+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_clip(int x, int y, size_t w, size_t h)
+static PeakKeyCode
+peak_web_key_map(const char *code)
{
- peak_clip_x = x;
- peak_clip_y = y;
- peak_clip_width = w;
- peak_clip_height = h;
+ 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_clip_reset(void)
+static EM_BOOL
+peak_web_key(int type, const EmscriptenKeyboardEvent *e, void *ud)
{
- peak_clip_x = 0;
- peak_clip_y = 0;
- peak_clip_width = peak_window_width;
- peak_clip_height = peak_window_height;
+ 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;
}
-void
-peak_draw_rectangle(int x, int y, size_t w, size_t h, uint32_t color)
+static EM_BOOL
+peak_web_mouse(int type, const EmscriptenMouseEvent *e, void *ud)
{
- 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;
+ 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;
+}
- 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;
- }
- }
+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);
}
-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)
+static int
+peak_platform_init(void)
{
- 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);
+ return 1;
+}
- 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);
- }
- }
+static void
+peak_platform_quit(void)
+{
}
-void
-peak_draw_triangle(int x0, int y0, int x1, int y1, int x2, int y2, uint32_t color)
+static PeakWindowInternal
+peak_platform_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags)
{
- 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);
+ PeakWindowInternal intern = {0};
+ struct peak_web_win *w;
- float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2);
- if (area == 0.0f) return;
+ (void)flags;
- 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);
+ 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;
- if ((w0 >= 0 && w1 >= 0 && w2 >= 0) || (w0 <= 0 && w1 <= 0 && w2 <= 0)) {
- peak__put_pixel(x, y, color);
- }
- }
- }
+ peak_web_dom_open(w->name, (int)width, (int)height);
+
+ peak_web_listen(w, 1);
+ intern.w = w;
+ return intern;
}
-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)
+static void
+peak_platform_window_close(PeakWindowInternal *intern)
{
- 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);
+ struct peak_web_win *w;
+ if (!intern || !intern->w)
+ return;
+ w = intern->w;
+ peak_web_listen(w, 0);
+ free(w);
+ intern->w = NULL;
+}
- float area = peak__edge_cross((float)x0, (float)y0, (float)x1, (float)y1, (float)x2, (float)y2);
- if (area == 0.0f) return;
+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;
+}
- 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;
+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);
+}
- 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);
- }
- }
- }
+static bool
+peak_platform_epoll(PeakWindowInternal *intern, PeakEvent *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_draw_line(int x0, int y0, int x1, int y1, size_t width, uint32_t color)
+/* 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;
+};
+
+
+/* --- Start of peak.c --- */
+static uint32_t *
+peak_window_sync(PeakWindow *win, size_t *width, size_t *height)
{
- 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;
+ 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;
+}
- int half_w = (int)width / 2;
+int
+peak_init(void)
+{
+ return peak_platform_init();
+}
- 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_quit(void)
+{
+ peak_platform_quit();
+}
+
+PeakWindow
+peak_window_open(const char *name, uint32_t width, uint32_t height, uint32_t flags)
+{
+ 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_line_gradient(int x0, int y0, int x1, int y1, size_t width, uint32_t c0, uint32_t c1)
+peak_window_close(PeakWindow *win)
{
- 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;
+ win->running = 0;
+ peak_platform_window_close(&win->internal);
+ win->buffer = NULL;
+ win->width = 0;
+ win->height = 0;
+ win->bufsize = 0;
+}
- int orig_x0 = x0, orig_y0 = y0;
- int half_w = (int)width / 2;
+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;
+}
- 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);
+uint32_t *
+peak_window_backbuffer(PeakWindow *win, size_t *width, size_t *height)
+{
+ return peak_window_sync(win, width, height);
+}
- 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);
- }
- }
+void
+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;
+}
- 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_window_present(PeakWindow *win)
+{
+ if (win) peak_platform_window_present(&win->internal);
+}
+
+#ifdef PEAK_WEB
+#include <emscripten.h>
+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