Rename to sigil-desktop 0.10.0: (sigil desktop), a C surface, a Guix+Nix resolver
The package is sigil-desktop, the library (sigil desktop); (sigil app) stays for this release as a plain re-export and goes in 0.11. app.c is desktop.c ((sigil desktop), SIGIL_DESKTOP_ variables, sigil-desktop: log prefix); vendor/sokol and the sapp_ compatibility file are gone, nothing sokol remains here.
include/sigil-desktop.h gives sibling packages the framebuffer size, a GL proc loader (glfwGetProcAddress) and the platform name; sigil-graphics 0.11.4 builds its swapchain and GL loader on it and stops linking libGL.
dl-search.c is table-driven and walks every row on every machine (Guix and Nix coexist): SIGIL_LIBRARY_PATH, then the active environment ($GUIX_ENVIRONMENT/lib, nix-shell's buildInputs and nativeBuildInputs each + /lib under IN_NIX_SHELL, $NIX_LD_LIBRARY_PATH), then the user profiles (~/.guix-home/profile/lib, ~/.guix-profile/lib, ~/.nix-profile/lib, /etc/profiles/per-user/$USER/lib), then the system ones (/run/current-system/profile/lib, /run/current-system/sw/lib). First hit wins; the trace names the row and whether /gnu/store and /nix/store exist. test/dl-search-test.sh proves it against fake Guix and Nix profiles in a temp HOME (and went red when two rows were swapped).
README (new) and the 0.10.0 CHANGELOG entry.
CHANGELOG.md | 65 +
README.md | 103 ++
include/sigil-desktop.h | 34 +
package.sgl | 49 +-
src/c/{app-internal.h => desktop-internal.h} | 45 +-
src/c/{app.c => desktop.c} | 285 +--
src/c/dl-search.c | 190 +-
src/c/sokol-compat.c | 60 -
src/sigil/app.sgl | 92 +-
src/sigil/desktop.sgl | 93 +
test/dl-search-probe.c | 23 +
test/dl-search-test.sh | 56 +
vendor/sokol/sokol_app.h | 14047 -------------------------------------------------------------------------------------------------------------------------------------------------
vendor/sokol/sokol_log.h | 334 ----
vendor/sokol/sokol_time.h | 319 ----
15 files changed, 727 insertions(+), 15068 deletions(-)CHANGELOG.mdmodified
The format is based on [Keep a Changelog](https://keepachangelog.com/),and this project adheres to [Semantic Versioning](https://semver.org/).## [0.10.0] - unreleasedThe package is now **sigil-desktop**, and the window is GLFW instead ofsokol_app.### Changed- **Native Wayland on Linux.** GLFW 3.5.1 is vendored as source under `vendor/glfw` (no local patches, `VENDOR.md` has the pin and inventory). One binary runs as a Wayland client or under X11, picked at startup; `SIGIL_DESKTOP_PLATFORM=wayland|x11` forces it. Under sokol_app the Linux window was always X11 (XWayland on a Wayland desktop).- **No platform libraries linked.** GLFW loads libwayland-client, libxkbcommon, libX11 and friends, libEGL and libGL at run time; the Linux `link-flags` are empty (were `-lX11 -lXi -lXcursor`). A game's NEEDED list loses those three.- **Framebuffer pixels follow the output scale.** On a Wayland output at scale 2 a window asked for at 800x600 has a 1600x1200 framebuffer; `frame-width`/`frame-height` report it and `mouse-x`/`mouse-y` are in the same pixels. Fractional scales work. A program with hard-coded pixel coordinates should lay out from `frame-width` and `frame-height`. On X11 nothing changes.- The library is `(sigil desktop)`. `(sigil app)` remains as a re-export for this release only.- `app-run` raises `app-run: could not open a window` (exit 1) when no display platform or GL context is available; sokol_app aborted.- `(quit-requested?)` answers `#t` inside the frame the close button was pressed in, not only in cleanup.- The frame clock restarts after the init procedure, so a long asset load is not the first frame's delta time.- GL 4.3 core is requested on Linux and Windows and 4.1 on macOS, as before; the window has no default icon (sokol_app drew one).### Added- `include/sigil-desktop.h`: `sigil_desktop_framebuffer_size`, `sigil_desktop_gl_get_proc_address`, `sigil_desktop_platform`, the C surface sigil-graphics builds its swapchain and GL loader on.- `src/c/dl-search.c`: a `dlopen` that finds runtime libraries in Guix and Nix profile directories (`SIGIL_LIBRARY_PATH`, `$GUIX_ENVIRONMENT/lib`, nix-shell inputs, `$NIX_LD_LIBRARY_PATH`, the user and system profiles), with `test/dl-search-test.sh`.- `guix/run-wayland.scm`, `guix/run-x11.scm`, `guix/build.scm`.- `SIGIL_DESKTOP_VERBOSE=1` for startup, resize and library-resolution diagnostics.### Removed- `vendor/sokol` (sokol_app, sokol_time, sokol_log) and the `sapp_*` symbols. sigil-graphics 0.11.4 no longer links them.### Requires- Sigil 0.20+ as before. Building on Guix needs the X11 and Wayland headers in `guix/build.scm` (libx11, libxcursor, libxrandr, libxinerama, libxi, libxext, xorgproto, wayland, libxkbcommon).### Upgrade notes- Relock to `^0.10.0`, delete the consumer's `build/` once (a stale `sokol-app.o` otherwise stays in the archive), then rename `(sigil app)` imports to `(sigil desktop)` before 0.11.- The Codeberg repository is renamed `sigil/sigil-app` to `sigil/sigil-desktop`; the old URL redirects.## [0.9.0] - 2026-06-25### ChangedREADME.mdadded
# sigil-desktopThe desktop host layer for Sigil games and interactive programs: a windowwith an OpenGL context, keyboard and mouse state, a frame clock, and aquit request, behind the `(sigil desktop)` library. Drawing is[sigil-graphics](https://codeberg.org/sigil/sigil-graphics), which buildson this package; sound is[sigil-audio](https://codeberg.org/sigil/sigil-audio).The window comes from [GLFW](https://www.glfw.org) 3.5.1, vendored assource under `vendor/glfw` (no local patches; see `vendor/glfw/VENDOR.md`).On Linux one binary runs as a native Wayland client or under X11, pickedwhen the program starts; on Windows it is a Win32 window; the macOS filesare vendored and not yet built. Every platform library (libwayland-client,libxkbcommon, libX11, libEGL, libGL, ...) is loaded at run time by GLFW,so a game links none of them.This package was `sigil-app` through 0.9.x, when the window was sokol_app(X11 only on Linux). The `(sigil app)` library still exists in 0.10 as are-export of `(sigil desktop)` so a consumer can relock first and renameits imports second; it goes away in 0.11.## Use```scheme(import (sigil desktop) (sigil graphics))(define (game) (gfx-setup) (let loop () (let ((dt (wait-frame))) (begin-frame) (clear-screen 0.0 0.0 0.0) ;; draw with (frame-width) x (frame-height) pixels to work with (end-frame) (cond ((key-pressed? 'escape) (request-quit)) ((quit-requested?) #t) (else (loop))))) (gfx-shutdown))(run-game "My Game" 960 720 game)````run-game` runs the thunk in a coroutine; `(wait-frame)` yields to thehost and returns the frame's delta time in seconds when the next framestarts. Below it, `app-run` takes init, frame and cleanup proceduresdirectly.The exports: `app-run`, `run-game`, `wait-frame`, `frame-width`,`frame-height`, `frame-time`, `time-elapsed`, `mouse-x`, `mouse-y`,`mouse-down?`, `mouse-pressed?`, `mouse-released?`, `key-down?`,`key-pressed?`, `key-released?`, `quit-requested?`, `request-quit`.Sizes and positions are framebuffer pixels: on a scaled Wayland output awindow asked for at 960x720 has a framebuffer of 1920x1440 at scale 2 (or1440x1080 at 1.5), `frame-width` says so, and `mouse-x`/`mouse-y` are inthe same pixels. Key symbols: `a`..`z`, `0`..`9`, `space`, `return` (or`enter`), `escape` (or `esc`), `tab`, `backspace`, `left`, `right`, `up`,`down`, `shift`, `ctrl` (or `control`), `alt`. Mouse buttons: `left`,`right`, `middle`. `key-pressed?` is true for the one frame after the keywent down (a held key repeating does not re-fire it); `key-down?` reportsthe state after that frame's events.## Environment- `SIGIL_DESKTOP_PLATFORM=wayland|x11|win32|cocoa` forces the platform. Otherwise GLFW follows `XDG_SESSION_TYPE` and `WAYLAND_DISPLAY`/`DISPLAY`, trying Wayland before X11.- `SIGIL_DESKTOP_VERBOSE=1` prints the platform, window and framebuffer sizes and content scale on startup and on every resize, and which directory each runtime library was found in when the loader needed help.- `SIGIL_LIBRARY_PATH=dir:dir` adds directories the runtime loader looks in for the platform libraries (see below).## Guix and NixGLFW loads its platform libraries by bare soname, and on Guix and Nix aprofile's `lib/` is on no default loader path. `src/c/dl-search.c`defines `dlopen` in the executable: it tries the name as the loader would,and only when that fails looks under, in order, `SIGIL_LIBRARY_PATH`,`$GUIX_ENVIRONMENT/lib`, `$buildInputs`/`$nativeBuildInputs` (each `/lib`,inside a nix-shell), `$NIX_LD_LIBRARY_PATH`, `~/.guix-home/profile/lib`,`~/.guix-profile/lib`, `~/.nix-profile/lib`,`/etc/profiles/per-user/$USER/lib`, `/run/current-system/profile/lib`,`/run/current-system/sw/lib`. Every row is walked on every machine (Guixand Nix can coexist); the first hit wins and `SIGIL_DESKTOP_VERBOSE` sayswhich. `test/dl-search-test.sh` exercises it against fake profiles.Manifests under `guix/`: `run-wayland.scm` (mesa, wayland, libxkbcommon)and `run-x11.scm` (mesa, libx11) are what a game needs at run time;`build.scm` is the headers a build needs. Keep `libglvnd` out of a runmanifest: Guix's mesa is built without it and a vendorless `libGLX.so.0`breaks GLX.## Building```shguix shell -m guix/build.scm -- sh -c 'CPATH=$GUIX_ENVIRONMENT/include sigil build'sigil build --config windows-amd64 # cross, through zig```The C surface other packages use is `include/sigil-desktop.h`(framebuffer size, GL proc loader, platform name).include/sigil-desktop.hadded
/* * sigil-desktop.h - the C surface of sigil-desktop for sibling packages * * sigil-graphics builds its sokol_gfx environment and swapchain from * these, and loads GL through the proc loader instead of linking libGL. * Everything here is valid only while app-run is running (between the * init callback and the cleanup callback). */#ifndef SIGIL_DESKTOP_H#define SIGIL_DESKTOP_H#ifdef __cplusplusextern "C" {#endif/* The window's framebuffer size in pixels: what GL draws into and what * frame-width / frame-height report. 1x1 before the window exists. */void sigil_desktop_framebuffer_size(int *width, int *height);/* The address of a GL or GL extension function for the current context * (glfwGetProcAddress), or NULL. Valid once the window exists; GL is * loaded by GLFW at run time, nothing links it. */void *sigil_desktop_gl_get_proc_address(const char *name);/* The platform GLFW picked: "wayland", "x11", "win32", "cocoa", or * "none" before app-run. */const char *sigil_desktop_platform(void);#ifdef __cplusplus}#endif#endif /* SIGIL_DESKTOP_H */package.sglmodified
;;; sigil-app - Desktop host layer for Sigil;;; sigil-desktop - Desktop host layer for Sigil (window, GL context, input,;;; frame clock, quit). Named sigil-app through 0.9.x, when it wrapped;;; sokol_app.;;;;;; Window, GL context, input, frame clock and quit for interactive;;; applications, over GLFW (vendored under vendor/glfw). On Linux one (else '())));; dl-search.c interposes dlopen so GLFW's runtime loading finds libraries;; in Guix profile directories; it is Linux-only by design (see the file).(define (app-sources os) (append '("src/c/app.c" "src/c/sokol-compat.c");; in Guix and Nix profile directories; Linux-only by design (see the file).(define (desktop-sources os) (append '("src/c/desktop.c") (if (eq? os 'linux) '("src/c/dl-search.c") '())))(define app-include-dirs '("vendor/glfw/include" "vendor/sokol" "src/c"))(define desktop-include-dirs '("include" "vendor/glfw/include"))(define app-flags(define desktop-flags '("-std=c99" "-D_GNU_SOURCE" "-Wno-unused-parameter"));; GLFW's own warning set is -Wall; its sources are compiled as they;; defines follow the config's target rather than the host (the same;; reason link-flags below is a lambda). Both steps register their;; objects on the context; create-static-library archives them all.(define (compile-app ctx)(define (compile-desktop ctx) ((compile-c-sources sources: (app-sources (target-os ctx)) include-dirs: app-include-dirs flags: (with-sigil-c-flags app-flags)) sources: (desktop-sources (target-os ctx)) include-dirs: desktop-include-dirs flags: (with-sigil-c-flags desktop-flags)) ctx))(define (compile-glfw ctx) ctx)))(package name: "sigil-app" version: "0.9.0" name: "sigil-desktop" version: "0.10.0" sigil: "0.20" description: "Desktop host layer for Sigil (window, GL context, input, frame clock) over GLFW" url: "https://codeberg.org/sigil/sigil-app" ;; The repository is being renamed sigil/sigil-app -> sigil/sigil-desktop on ;; Codeberg (in place; the old name redirects). Until then consumers keep ;; from-git url: "codeberg:sigil/sigil-app". url: "https://codeberg.org/sigil/sigil-desktop" license: "BSD-3-Clause" authors: (list "David Wilson <david@daviwil.com>") libraries: (list (library name: 'sigil-app name: 'sigil-desktop ;; sigil-build reads this list only as a "has native code" test and ;; for the include dirs; the build task below compiles the real, ;; target-dependent list (these two plus dl-search.c on Linux plus ;; target-dependent list (this plus dl-search.c on Linux plus ;; the GLFW backend files). Keep it to files every target compiles. c-sources: '("src/c/app.c" "src/c/sokol-compat.c") c-include-dirs: '("vendor/glfw/include" "vendor/sokol" "src/c") native-init: "sigil__init_sigil_app_module" c-sources: '("src/c/desktop.c") c-include-dirs: '("include" "vendor/glfw/include") native-init: "sigil__init_sigil_desktop_module" ;; Platform linker flags. ;; ;; Linux: none. GLFW dlopens libwayland-client, libwayland-egl, tasks: (list (task name: 'build description: "Build sigil-app" description: "Build sigil-desktop" steps: (list compile-app compile-desktop compile-glfw (create-static-library name: "sigil-app") (create-static-library name: "sigil-desktop") (compile-sigil-modules sources: "src/**/*.sgl" output-dir: (config-output-subdir "lib"))))))src/c/app-internal.h → src/c/desktop-internal.hrenamed
/* * app-internal.h - Internal header for the Sigil desktop host layer * desktop-internal.h - Internal header for the Sigil desktop host layer * * Application state for the window, GL context, input, frame clock and * quit handling. The host is GLFW (vendored under vendor/glfw); the * and every platform library is loaded at runtime by GLFW itself. */#ifndef SIGIL_APP_INTERNAL_H#define SIGIL_APP_INTERNAL_H#ifndef SIGIL_DESKTOP_INTERNAL_H#define SIGIL_DESKTOP_INTERNAL_H#include <sigil/sigil.h>#include <stdbool.h>/* GLFW_KEY_LAST is 348; sokol_app's table was 512 and the key symbols * only ever map into the GLFW range, so the same size is kept. */#define SIGIL_APP_MAX_KEYS 512#define SIGIL_APP_MAX_MOUSE_BUTTONS 3#define SIGIL_DESKTOP_MAX_KEYS 512#define SIGIL_DESKTOP_MAX_MOUSE_BUTTONS 3struct GLFWwindow; double time_elapsed; /* Seconds since app start */ /* Input state - current frame */ bool keys_down[SIGIL_APP_MAX_KEYS]; bool keys_pressed[SIGIL_APP_MAX_KEYS]; bool keys_released[SIGIL_APP_MAX_KEYS]; bool keys_down[SIGIL_DESKTOP_MAX_KEYS]; bool keys_pressed[SIGIL_DESKTOP_MAX_KEYS]; bool keys_released[SIGIL_DESKTOP_MAX_KEYS]; bool mouse_buttons[SIGIL_APP_MAX_MOUSE_BUTTONS]; bool mouse_pressed[SIGIL_APP_MAX_MOUSE_BUTTONS]; bool mouse_released[SIGIL_APP_MAX_MOUSE_BUTTONS]; bool mouse_buttons[SIGIL_DESKTOP_MAX_MOUSE_BUTTONS]; bool mouse_pressed[SIGIL_DESKTOP_MAX_MOUSE_BUTTONS]; bool mouse_released[SIGIL_DESKTOP_MAX_MOUSE_BUTTONS]; /* Cursor position in window (screen) units as GLFW reports it; * mouse-x / mouse-y scale it into framebuffer pixels on read. */ double cursor_x; /* Quit handling */ bool quit_requested;} AppState;} DesktopState;/* Global state - initialized by app module */extern AppState *g_app;extern DesktopState *g_desktop;/* Initialize/shutdown app state */void sigil_app_state_init(SigilVM *vm);void sigil_app_state_shutdown(void);void sigil_desktop_state_init(SigilVM *vm);void sigil_desktop_state_shutdown(void);/* Input handling helpers */void sigil_app_clear_frame_input(void);int sigil_app_key_code_from_symbol(SigilVM *vm, Value sym);void sigil_desktop_clear_frame_input(void);int sigil_desktop_key_code_from_symbol(SigilVM *vm, Value sym);/* Framebuffer size in pixels (1x1 before the window exists), for the * sokol_app compatibility shim in sokol-compat.c. */int sigil_app_framebuffer_width(void);int sigil_app_framebuffer_height(void);/* Framebuffer size in pixels (1x1 before the window exists). */int sigil_desktop_framebuffer_width(void);int sigil_desktop_framebuffer_height(void);#if defined(__linux__)/* dl-search.c: the directories the interposed dlopen falls back to. */const char *sigil_app_dlopen_search_summary(void);const char *sigil_desktop_dlopen_search_summary(void);#endif#endif /* SIGIL_APP_INTERNAL_H */#endif /* SIGIL_DESKTOP_INTERNAL_H */src/c/app.c → src/c/desktop.crenamed
/* * app.c - Sigil App Module * desktop.c - the (sigil desktop) module * * The desktop host layer: window, GL context, input, frame clock and quit, * over GLFW (vendored under vendor/glfw). One binary runs as a native * platform libraries (libwayland-client, libxkbcommon, libX11, libGL/EGL, * ...) with dlopen, so nothing platform-specific is linked. * * The exported Sigil API is the one sokol_app provided before: app-run, * The exported Sigil API is the one (sigil app) had over sokol_app: app-run, * frame-*, mouse-*, key-*, quit-requested?, request-quit. */#include "app-internal.h"#include "desktop-internal.h"#include "sigil-desktop.h"#include "sigil-internal.h"/* No GL header: this layer never calls GL itself; sokol_gfx (in#include <string.h>/* Global app state */AppState *g_app = NULL;DesktopState *g_desktop = NULL;/* * Initialize app state */void sigil_app_state_init(SigilVM *vm)void sigil_desktop_state_init(SigilVM *vm){ if (g_app) return; if (g_desktop) return; g_app = calloc(1, sizeof(AppState)); g_app->vm = vm; g_app->init_callback = SIGIL_FALSE; g_app->frame_callback = SIGIL_FALSE; g_app->cleanup_callback = SIGIL_FALSE; g_app->fb_width = 1; g_app->fb_height = 1; g_app->win_width = 1; g_app->win_height = 1; g_desktop = calloc(1, sizeof(DesktopState)); g_desktop->vm = vm; g_desktop->init_callback = SIGIL_FALSE; g_desktop->frame_callback = SIGIL_FALSE; g_desktop->cleanup_callback = SIGIL_FALSE; g_desktop->fb_width = 1; g_desktop->fb_height = 1; g_desktop->win_width = 1; g_desktop->win_height = 1;}void sigil_app_state_shutdown(void)void sigil_desktop_state_shutdown(void){ if (g_app) { free(g_app); g_app = NULL; if (g_desktop) { free(g_desktop); g_desktop = NULL; }}/* * Clear per-frame input state */void sigil_app_clear_frame_input(void)void sigil_desktop_clear_frame_input(void){ if (!g_app) return; memset(g_app->keys_pressed, 0, sizeof(g_app->keys_pressed)); memset(g_app->keys_released, 0, sizeof(g_app->keys_released)); memset(g_app->mouse_pressed, 0, sizeof(g_app->mouse_pressed)); memset(g_app->mouse_released, 0, sizeof(g_app->mouse_released)); if (!g_desktop) return; memset(g_desktop->keys_pressed, 0, sizeof(g_desktop->keys_pressed)); memset(g_desktop->keys_released, 0, sizeof(g_desktop->keys_released)); memset(g_desktop->mouse_pressed, 0, sizeof(g_desktop->mouse_pressed)); memset(g_desktop->mouse_released, 0, sizeof(g_desktop->mouse_released));}int sigil_app_framebuffer_width(void)int sigil_desktop_framebuffer_width(void){ return (g_app && g_app->fb_width > 0) ? g_app->fb_width : 1; return (g_desktop && g_desktop->fb_width > 0) ? g_desktop->fb_width : 1;}int sigil_app_framebuffer_height(void)int sigil_desktop_framebuffer_height(void){ return (g_app && g_app->fb_height > 0) ? g_app->fb_height : 1; return (g_desktop && g_desktop->fb_height > 0) ? g_desktop->fb_height : 1;}/* * digits, space, return/enter, escape/esc, tab, backspace, the arrows, * shift, ctrl/control, alt. */int sigil_app_key_code_from_symbol(SigilVM *vm, Value sym)int sigil_desktop_key_code_from_symbol(SigilVM *vm, Value sym){ (void)vm; if (!sigil_is_symbol(sym)) return GLFW_KEY_UNKNOWN;/* Check for VM errors and print them */static void check_vm_error(const char *context){ if (!g_app) return; const char *err = sigil_error_message(g_app->vm); if (!g_desktop) return; const char *err = sigil_error_message(g_desktop->vm); if (err) { fprintf(stderr, "Scheme error in %s: %s\n", context, err); sigil_error_clear(g_app->vm); g_app->quit_requested = true; if (g_app->window) glfwSetWindowShouldClose(g_app->window, GLFW_TRUE); sigil_error_clear(g_desktop->vm); g_desktop->quit_requested = true; if (g_desktop->window) glfwSetWindowShouldClose(g_desktop->window, GLFW_TRUE); }}static void glfw_error_callback(int code, const char *description){ fprintf(stderr, "sigil-app: GLFW error 0x%X: %s\n", code, description); fprintf(stderr, "sigil-desktop: GLFW error 0x%X: %s\n", code, description);}static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods){ (void)window; (void)scancode; (void)mods; if (!g_app) return; if (key < 0 || key >= SIGIL_APP_MAX_KEYS) return; if (!g_desktop) return; if (key < 0 || key >= SIGIL_DESKTOP_MAX_KEYS) return; if (action == GLFW_PRESS || action == GLFW_REPEAT) { /* A repeat is a held key, not a new press: pressed? fires once. */ if (!g_app->keys_down[key]) { g_app->keys_pressed[key] = true; if (!g_desktop->keys_down[key]) { g_desktop->keys_pressed[key] = true; } g_app->keys_down[key] = true; g_desktop->keys_down[key] = true; } else if (action == GLFW_RELEASE) { g_app->keys_down[key] = false; g_app->keys_released[key] = true; g_desktop->keys_down[key] = false; g_desktop->keys_released[key] = true; }}static void mouse_button_callback(GLFWwindow *window, int button, int action, int mods){ (void)window; (void)mods; if (!g_app) return; if (button < 0 || button >= SIGIL_APP_MAX_MOUSE_BUTTONS) return; if (!g_desktop) return; if (button < 0 || button >= SIGIL_DESKTOP_MAX_MOUSE_BUTTONS) return; if (action == GLFW_PRESS) { if (!g_app->mouse_buttons[button]) { g_app->mouse_pressed[button] = true; if (!g_desktop->mouse_buttons[button]) { g_desktop->mouse_pressed[button] = true; } g_app->mouse_buttons[button] = true; g_desktop->mouse_buttons[button] = true; } else if (action == GLFW_RELEASE) { g_app->mouse_buttons[button] = false; g_app->mouse_released[button] = true; g_desktop->mouse_buttons[button] = false; g_desktop->mouse_released[button] = true; }}static void cursor_pos_callback(GLFWwindow *window, double x, double y){ (void)window; if (!g_app) return; g_app->cursor_x = x; g_app->cursor_y = y; if (!g_desktop) return; g_desktop->cursor_x = x; g_desktop->cursor_y = y;}static void window_close_callback(GLFWwindow *window){ (void)window; if (!g_app) return; if (!g_desktop) return; /* The window manager asked; the game sees (quit-requested?) => #t * and the loop ends after this frame, as with sokol_app's * QUIT_REQUESTED event. */ g_app->quit_requested = true; g_desktop->quit_requested = true;}static void framebuffer_size_callback(GLFWwindow *window, int width, int height){ if (!g_app) return; g_app->fb_width = width; g_app->fb_height = height; if (getenv("SIGIL_APP_VERBOSE")) { if (!g_desktop) return; g_desktop->fb_width = width; g_desktop->fb_height = height; if (getenv("SIGIL_DESKTOP_VERBOSE")) { float sx = 1.0f, sy = 1.0f; glfwGetWindowContentScale(window, &sx, &sy); fprintf(stderr, "sigil-app: framebuffer=%dx%d window=%dx%d content-scale=%.2fx%.2f\n", width, height, g_app->win_width, g_app->win_height, sx, sy); fprintf(stderr, "sigil-desktop: framebuffer=%dx%d window=%dx%d content-scale=%.2fx%.2f\n", width, height, g_desktop->win_width, g_desktop->win_height, sx, sy); }}static void window_size_callback(GLFWwindow *window, int width, int height){ (void)window; if (!g_app) return; g_app->win_width = width; g_app->win_height = height; if (!g_desktop) return; g_desktop->win_width = width; g_desktop->win_height = height;}/* Cursor position scaled from window units into framebuffer pixels, so * system whatever the output scale is. */static double cursor_to_fb_x(void){ if (!g_app || g_app->win_width <= 0) return 0.0; return g_app->cursor_x * ((double)g_app->fb_width / (double)g_app->win_width); if (!g_desktop || g_desktop->win_width <= 0) return 0.0; return g_desktop->cursor_x * ((double)g_desktop->fb_width / (double)g_desktop->win_width);}static double cursor_to_fb_y(void){ if (!g_app || g_app->win_height <= 0) return 0.0; return g_app->cursor_y * ((double)g_app->fb_height / (double)g_app->win_height); if (!g_desktop || g_desktop->win_height <= 0) return 0.0; return g_desktop->cursor_y * ((double)g_desktop->fb_height / (double)g_desktop->win_height);}/* ============================================================/* * GLFW picks Wayland when XDG_SESSION_TYPE=wayland and WAYLAND_DISPLAY is * set, X11 when XDG_SESSION_TYPE=x11 and DISPLAY is set, and otherwise * tries Wayland then X11. SIGIL_APP_PLATFORM=wayland|x11 forces one, for * tries Wayland then X11. SIGIL_DESKTOP_PLATFORM=wayland|x11 forces one, for * tests that run under a private compositor or X server and for users * whose session variables lie. */static void apply_platform_hint(void){ const char *want = getenv("SIGIL_APP_PLATFORM"); const char *want = getenv("SIGIL_DESKTOP_PLATFORM"); if (!want || !*want) return; if (strcmp(want, "wayland") == 0) { glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_WAYLAND); } else if (strcmp(want, "cocoa") == 0) { glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_COCOA); } else { fprintf(stderr, "sigil-app: SIGIL_APP_PLATFORM=%s is not one of wayland, x11, win32, cocoa; ignored\n", want); fprintf(stderr, "sigil-desktop: SIGIL_DESKTOP_PLATFORM=%s is not one of wayland, x11, win32, cocoa; ignored\n", want); }} }}/* ============================================================ * PUBLIC C SURFACE (include/sigil-desktop.h) * ============================================================ */void sigil_desktop_framebuffer_size(int *width, int *height){ if (width) *width = sigil_desktop_framebuffer_width(); if (height) *height = sigil_desktop_framebuffer_height();}void *sigil_desktop_gl_get_proc_address(const char *name){ if (!g_desktop || !g_desktop->window || !name) return NULL; return (void *)glfwGetProcAddress(name);}const char *sigil_desktop_platform(void){ if (!g_desktop || !g_desktop->window) return "none"; return platform_name(glfwGetPlatform());}/* ============================================================ * MAIN LOOP * ============================================================ */ apply_platform_hint(); if (!glfwInit()) { fprintf(stderr, "sigil-app: glfwInit failed; no usable display platform\n"); fprintf(stderr, "sigil-desktop: glfwInit failed; no usable display platform\n"); return false; } GLFWwindow *window = glfwCreateWindow(width, height, title, NULL, NULL); if (!window) { fprintf(stderr, "sigil-app: window creation failed (platform %s)\n", fprintf(stderr, "sigil-desktop: window creation failed (platform %s)\n", platform_name(glfwGetPlatform())); glfwTerminate(); return false; } g_app->window = window; g_desktop->window = window; glfwMakeContextCurrent(window); glfwSwapInterval(1); glfwGetFramebufferSize(window, &g_app->fb_width, &g_app->fb_height); glfwGetWindowSize(window, &g_app->win_width, &g_app->win_height); glfwGetCursorPos(window, &g_app->cursor_x, &g_app->cursor_y); glfwGetFramebufferSize(window, &g_desktop->fb_width, &g_desktop->fb_height); glfwGetWindowSize(window, &g_desktop->win_width, &g_desktop->win_height); glfwGetCursorPos(window, &g_desktop->cursor_x, &g_desktop->cursor_y); if (getenv("SIGIL_APP_VERBOSE")) { if (getenv("SIGIL_DESKTOP_VERBOSE")) { float sx = 1.0f, sy = 1.0f; glfwGetWindowContentScale(window, &sx, &sy); fprintf(stderr, "sigil-app: platform=%s window=%dx%d framebuffer=%dx%d content-scale=%.2fx%.2f gl=%d.%d\n", fprintf(stderr, "sigil-desktop: platform=%s window=%dx%d framebuffer=%dx%d content-scale=%.2fx%.2f gl=%d.%d\n", platform_name(glfwGetPlatform()), g_app->win_width, g_app->win_height, g_app->fb_width, g_app->fb_height, sx, sy, g_desktop->win_width, g_desktop->win_height, g_desktop->fb_width, g_desktop->fb_height, sx, sy, glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR), glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR));#if defined(__linux__) fprintf(stderr, "sigil-app: dlopen fallback dirs: %s\n", sigil_app_dlopen_search_summary()); fprintf(stderr, "sigil-desktop: dlopen fallback dirs: %s\n", sigil_desktop_dlopen_search_summary());#endif } glfwSetWindowSizeCallback(window, window_size_callback); /* init */ g_app->last_time = glfwGetTime(); if (!sigil_is_false(g_app->init_callback)) { sigil_apply0(g_app->vm, g_app->init_callback); g_desktop->last_time = glfwGetTime(); if (!sigil_is_false(g_desktop->init_callback)) { sigil_apply0(g_desktop->vm, g_desktop->init_callback); check_vm_error("init"); } /* frames; the clock restarts after init so a long asset load is not * frame 1's dt (sokol_app handed the load time to the first frame). */ g_app->last_time = glfwGetTime(); g_desktop->last_time = glfwGetTime(); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); double now = glfwGetTime(); g_app->frame_time = now - g_app->last_time; g_app->time_elapsed += g_app->frame_time; g_app->last_time = now; g_desktop->frame_time = now - g_desktop->last_time; g_desktop->time_elapsed += g_desktop->frame_time; g_desktop->last_time = now; if (!sigil_is_false(g_app->frame_callback)) { Value dt = sigil_flonum(g_app->frame_time); sigil_apply1(g_app->vm, g_app->frame_callback, dt); if (!sigil_is_false(g_desktop->frame_callback)) { Value dt = sigil_flonum(g_desktop->frame_time); sigil_apply1(g_desktop->vm, g_desktop->frame_callback, dt); check_vm_error("frame"); } glfwSwapBuffers(window); /* Clear per-frame input state for next frame */ sigil_app_clear_frame_input(); sigil_desktop_clear_frame_input(); } /* cleanup */ if (!sigil_is_false(g_app->cleanup_callback)) { sigil_apply0(g_app->vm, g_app->cleanup_callback); if (!sigil_is_false(g_desktop->cleanup_callback)) { sigil_apply0(g_desktop->vm, g_desktop->cleanup_callback); check_vm_error("cleanup"); } g_app->window = NULL; g_desktop->window = NULL; glfwDestroyWindow(window); glfwTerminate(); return true; } /* Initialize app state */ sigil_app_state_init(vm); if (g_app->window) { sigil_desktop_state_init(vm); if (g_desktop->window) { sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "app-run: already running; app-run does not nest"); return SIGIL_UNDEFINED; } /* Store callbacks */ g_app->init_callback = args[0]; g_app->frame_callback = args[1]; g_app->cleanup_callback = args[2]; g_desktop->init_callback = args[0]; g_desktop->frame_callback = args[1]; g_desktop->cleanup_callback = args[2]; /* Parse optional arguments */ const char *title = "Sigil App"; bool ran = run_main_loop(title, width, height); /* Cleanup */ sigil_app_state_shutdown(); sigil_desktop_state_shutdown(); if (!ran) { sigil__vm_error(vm, SIGIL_ERR_RUNTIME, "app-run: could not open a window (see the sigil-app: lines above)"); "app-run: could not open a window (see the sigil-desktop: lines above)"); return SIGIL_UNDEFINED; } return SIGIL_NIL;static Value native_frame_width(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; return sigil_fixnum(sigil_app_framebuffer_width()); return sigil_fixnum(sigil_desktop_framebuffer_width());}/*static Value native_frame_height(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; return sigil_fixnum(sigil_app_framebuffer_height()); return sigil_fixnum(sigil_desktop_framebuffer_height());}/*static Value native_frame_time(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_app) return sigil_flonum(0.0); return sigil_flonum(g_app->frame_time); if (!g_desktop) return sigil_flonum(0.0); return sigil_flonum(g_desktop->frame_time);}/*static Value native_time_elapsed(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_app) return sigil_flonum(0.0); return sigil_flonum(g_app->time_elapsed); if (!g_desktop) return sigil_flonum(0.0); return sigil_flonum(g_desktop->time_elapsed);}/*{Showing the first 500 of 606 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
src/c/dl-search.cmodified
* GLFW loads every platform library at run time by bare soname: * dlopen("libwayland-client.so.0"), dlopen("libX11.so.6"), dlopen("libEGL.so.1"). * The dynamic loader finds such a name through LD_LIBRARY_PATH, the * caller's RUNPATH and the system default directories. On Guix none of * those name a profile's lib directory: a Guix profile does not set * LD_LIBRARY_PATH, and Guix's own packages are patched to dlopen absolute * store paths, which a user's binary cannot be. * caller's RUNPATH and the system default directories. On Guix and Nix none * of those name a profile's lib directory: the profile sets no * LD_LIBRARY_PATH, and the distribution's own packages are patched to * dlopen absolute store paths, which a user's binary cannot be. * * This file defines dlopen in the executable. The executable's symbols come * first in the lookup order, so every dlopen in the process (GLFW's in * posix_module.c, and any other loader compiled into the program) resolves * here. It tries the name as given first, so on a system where the loader * already finds the library nothing changes. When that fails and the name * is a bare soname (no slash), it retries the name joined to each directory * of: * * SIGIL_LIBRARY_PATH colon separated, the user's override * $GUIX_ENVIRONMENT/lib set by `guix shell` * $HOME/.guix-home/profile/lib Guix Home * $HOME/.guix-profile/lib the user profile * /run/current-system/profile/lib Guix System * * The real dlopen is reached with dlsym(RTLD_NEXT, "dlopen"). A library * found this way loads its own dependencies through its RUNPATH, which on * Guix names their store paths, so one absolute path is enough. * is a bare soname (no slash), it retries the name under each entry of the * table below, in order. The real dlopen is reached with * dlsym(RTLD_NEXT, "dlopen"). A library found this way loads its own * dependencies through its RUNPATH, which on Guix and Nix names their * store paths, so one absolute path is enough. * * A NEEDED entry (a library linked with -l) is resolved by the loader * before main runs and cannot be helped here; only dlopen is covered. * * SIGIL_APP_VERBOSE=1 prints every fallback that succeeds. * SIGIL_DESKTOP_VERBOSE=1 prints, for every fallback that succeeds, the * soname, the table entry and the path, and whether /gnu/store and * /nix/store exist on this machine. * * Linux only: on macOS the two-level namespace binds libraries to * libSystem's dlopen, and on Windows LoadLibrary has its own search order.#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/stat.h>/* ---- the search table ------------------------------------------------ */typedef enum { ENTRY_ENV_COLON_DIRS, /* env var: colon-separated directories */ ENTRY_ENV_SPACE_ROOTS, /* env var: space-separated roots, suffix appended */ ENTRY_ENV_ROOT, /* env var: one root, suffix appended */ ENTRY_HOME, /* $HOME + suffix */ ENTRY_USER, /* prefix + $USER + suffix */ ENTRY_DIR /* a fixed directory */} entry_kind;typedef struct { entry_kind kind; const char *name; /* env var, prefix, or the directory itself */ const char *suffix; /* appended to each root (may be "") */ const char *only_if; /* env var that must be set for the row to apply, or NULL */} search_entry;/* One row per source of library directories; adding a distribution is a * row. Every row is walked on every machine (Guix and Nix coexist: David * runs Nix inside Guix), so there is no "which distro" branch and a * library may come from a Nix profile while its own dependency, resolved * by RUNPATH, comes from a Guix one. Order: the user's explicit override, * the active environment (guix shell, nix-shell, nix-ld), then the user * profiles, then the system profiles, Guix and Nix interleaved. When two * rows offer the same soname the first hit wins and the trace names it. */static const search_entry SEARCH_TABLE[] = { /* explicit override */ { ENTRY_ENV_COLON_DIRS, "SIGIL_LIBRARY_PATH", "", NULL }, /* the active environment */ { ENTRY_ENV_ROOT, "GUIX_ENVIRONMENT", "/lib", NULL }, /* guix shell */ { ENTRY_ENV_SPACE_ROOTS, "buildInputs", "/lib", "IN_NIX_SHELL" }, /* nix-shell */ { ENTRY_ENV_SPACE_ROOTS, "nativeBuildInputs", "/lib", "IN_NIX_SHELL" }, { ENTRY_ENV_COLON_DIRS, "NIX_LD_LIBRARY_PATH", "", NULL }, /* nix-ld */ /* user profiles */ { ENTRY_HOME, "", "/.guix-home/profile/lib", NULL }, /* Guix Home */ { ENTRY_HOME, "", "/.guix-profile/lib", NULL }, /* guix package */ { ENTRY_HOME, "", "/.nix-profile/lib", NULL }, /* nix-env, nix profile */ { ENTRY_USER, "/etc/profiles/per-user/", "/lib", NULL }, /* NixOS per-user */ /* system profiles */ { ENTRY_DIR, "/run/current-system/profile/lib", "", NULL }, /* Guix System */ { ENTRY_DIR, "/run/current-system/sw/lib", "", NULL }, /* NixOS */};#define SEARCH_TABLE_LEN (sizeof SEARCH_TABLE / sizeof SEARCH_TABLE[0])/* ---- the real dlopen ------------------------------------------------- */typedef void *(*sigil_dlopen_fn)(const char *, int); return fn;}static void *try_dir(sigil_dlopen_fn real, const char *dir, size_t dirlen,static int dir_exists(const char *path){ struct stat st; return stat(path, &st) == 0 && S_ISDIR(st.st_mode);}static void trace_found(const search_entry *e, const char *name, const char *path){ if (!getenv("SIGIL_DESKTOP_VERBOSE")) return; const char *label = (e->kind == ENTRY_DIR) ? e->name : (e->kind == ENTRY_HOME) ? "HOME" : (e->kind == ENTRY_USER) ? "USER" : e->name; fprintf(stderr, "sigil-desktop: dlopen %s: found via %s%s as %s (/gnu/store %s, /nix/store %s)\n", name, label, e->suffix, path, dir_exists("/gnu/store") ? "present" : "absent", dir_exists("/nix/store") ? "present" : "absent");}/* dir[0..dirlen) + suffix + "/" + name, through the real dlopen */static void *try_dir(sigil_dlopen_fn real, const search_entry *e, const char *dir, size_t dirlen, const char *suffix, const char *name, int flags){ char path[PATH_MAX]; size_t namelen = strlen(name); if (dirlen == 0 || dirlen + 1 + namelen + 1 > sizeof path) return NULL; size_t sufflen = strlen(suffix), namelen = strlen(name); if (dirlen == 0 || dirlen + sufflen + 1 + namelen + 1 > sizeof path) return NULL; memcpy(path, dir, dirlen); path[dirlen] = '/'; memcpy(path + dirlen + 1, name, namelen + 1); memcpy(path + dirlen, suffix, sufflen); path[dirlen + sufflen] = '/'; memcpy(path + dirlen + sufflen + 1, name, namelen + 1); void *h = real(path, flags); if (h && getenv("SIGIL_APP_VERBOSE")) { fprintf(stderr, "sigil-app: dlopen %s: found as %s\n", name, path); } if (h) trace_found(e, name, path); return h;}/* dir may be a colon-separated list */static void *try_list(sigil_dlopen_fn real, const char *list, const char *name, int flags)/* every item of a separated list */static void *try_list(sigil_dlopen_fn real, const search_entry *e, const char *list, char sep, const char *name, int flags){ if (!list) return NULL; while (*list) { const char *colon = strchr(list, ':'); size_t len = colon ? (size_t)(colon - list) : strlen(list); void *h = try_dir(real, list, len, name, flags); const char *end = strchr(list, sep); size_t len = end ? (size_t)(end - list) : strlen(list); void *h = try_dir(real, e, list, len, e->suffix, name, flags); if (h) return h; if (!colon) break; list = colon + 1; if (!end) break; list = end + 1; } return NULL;}static void *try_under(sigil_dlopen_fn real, const char *base, const char *suffix, const char *name, int flags)static void *try_entry(sigil_dlopen_fn real, const search_entry *e, const char *name, int flags){ char dir[PATH_MAX]; if (!base || !*base) return NULL; int n = snprintf(dir, sizeof dir, "%s%s", base, suffix); if (n <= 0 || (size_t)n >= sizeof dir) return NULL; return try_dir(real, dir, (size_t)n, name, flags); if (e->only_if && !getenv(e->only_if)) return NULL; const char *v; switch (e->kind) { case ENTRY_ENV_COLON_DIRS: return try_list(real, e, getenv(e->name), ':', name, flags); case ENTRY_ENV_SPACE_ROOTS: return try_list(real, e, getenv(e->name), ' ', name, flags); case ENTRY_ENV_ROOT: v = getenv(e->name); return v ? try_dir(real, e, v, strlen(v), e->suffix, name, flags) : NULL; case ENTRY_HOME: v = getenv("HOME"); return v ? try_dir(real, e, v, strlen(v), e->suffix, name, flags) : NULL; case ENTRY_USER: { v = getenv("USER"); if (!v || !*v) return NULL; char root[PATH_MAX]; int n = snprintf(root, sizeof root, "%s%s", e->name, v); if (n <= 0 || (size_t)n >= sizeof root) return NULL; return try_dir(real, e, root, (size_t)n, e->suffix, name, flags); } case ENTRY_DIR: return try_dir(real, e, e->name, strlen(e->name), e->suffix, name, flags); } return NULL;}/* ---- the interposed dlopen ------------------------------------------- */void *dlopen(const char *name, int flags){ sigil_dlopen_fn real = real_dlopen(); if (!real) { /* No next dlopen: only a fully static link gets here; say so, since * GLFW then reports the library as missing. */ fprintf(stderr, "sigil-app: dlopen(%s): no underlying dlopen (static link?)\n", name ? name : "(null)"); fprintf(stderr, "sigil-desktop: dlopen(%s): no underlying dlopen (static link?)\n", name ? name : "(null)"); return NULL; } void *h = real(name, flags); if (h || !name || strchr(name, '/')) return h; /* A bare soname the loader could not find: the profile directories. */ const char *home = getenv("HOME"); h = try_list(real, getenv("SIGIL_LIBRARY_PATH"), name, flags); if (!h) h = try_under(real, getenv("GUIX_ENVIRONMENT"), "/lib", name, flags); if (!h) h = try_under(real, home, "/.guix-home/profile/lib", name, flags); if (!h) h = try_under(real, home, "/.guix-profile/lib", name, flags); if (!h) h = try_dir(real, "/run/current-system/profile/lib", strlen("/run/current-system/profile/lib"), name, flags); if (h) return h; /* A bare soname the loader could not find: the table, in order. */ for (size_t i = 0; i < SEARCH_TABLE_LEN; i++) { h = try_entry(real, &SEARCH_TABLE[i], name, flags); if (h) return h; } /* Nothing found: repeat the plain call so dlerror() names the soname * the caller asked for, not the last directory tried. */ return real(name, flags);}/* Referenced from app.c so this object is always linked ahead of libc,/* Referenced from desktop.c so this object is always linked ahead of libc, * whatever order the linker meets the archive members in. */const char *sigil_app_dlopen_search_summary(void)const char *sigil_desktop_dlopen_search_summary(void){ return "SIGIL_LIBRARY_PATH, $GUIX_ENVIRONMENT/lib, ~/.guix-home/profile/lib, " "~/.guix-profile/lib, /run/current-system/profile/lib"; return "SIGIL_LIBRARY_PATH; $GUIX_ENVIRONMENT/lib; $buildInputs/*/lib and " "$nativeBuildInputs/*/lib under IN_NIX_SHELL; $NIX_LD_LIBRARY_PATH; ~/.guix-home/profile/lib; " "~/.guix-profile/lib; ~/.nix-profile/lib; /etc/profiles/per-user/$USER/lib; " "/run/current-system/profile/lib; /run/current-system/sw/lib";}src/c/sokol-compat.cdeleted
/* * sokol-compat.c - the sokol_app symbols sigil-graphics still links against * * The window no longer comes from sokol_app (see app.c: it is GLFW now), but * sigil-graphics' vendored sokol_glue.h calls sapp_get_environment() and * sapp_get_swapchain(), its graphics.c calls sapp_width()/sapp_height(), and * every sokol_gfx logger points at slog_func from sokol_log.h. This file * implements exactly those five symbols over the GLFW host state so a * consumer relocks sigil-app and nothing else changes. The types come from * the vendored sokol_app.h, included for declarations only (no SOKOL_IMPL), * so the struct layouts match what sigil-graphics compiled against. * * The values are the ones sokol_app's GL backend reported: RGBA8 colour, * a combined depth-stencil buffer (GLFW's default framebuffer is 24/8), no * MSAA, GL framebuffer 0. */#include "app-internal.h"#include <string.h>/* Order is load-bearing: sokol_log.h with its IMPL defines SOKOL_API_IMPL, * which the sapp_* definitions below use; sokol_app.h then contributes * declarations only. */#define SOKOL_LOG_IMPL#include "sokol_log.h"#include "sokol_app.h"SOKOL_API_IMPL int sapp_width(void){ return sigil_app_framebuffer_width();}SOKOL_API_IMPL int sapp_height(void){ return sigil_app_framebuffer_height();}SOKOL_API_IMPL sapp_environment sapp_get_environment(void){ sapp_environment env; memset(&env, 0, sizeof(env)); env.defaults.color_format = SAPP_PIXELFORMAT_RGBA8; env.defaults.depth_format = SAPP_PIXELFORMAT_DEPTH_STENCIL; env.defaults.sample_count = 1; return env;}SOKOL_API_IMPL sapp_swapchain sapp_get_swapchain(void){ sapp_swapchain sc; memset(&sc, 0, sizeof(sc)); sc.width = sigil_app_framebuffer_width(); sc.height = sigil_app_framebuffer_height(); sc.sample_count = 1; sc.color_format = SAPP_PIXELFORMAT_RGBA8; sc.depth_format = SAPP_PIXELFORMAT_DEPTH_STENCIL; sc.gl.framebuffer = 0; return sc;}src/sigil/app.sglmodified
;;; (sigil app) - Application Framework;;; (sigil app) - the old name of (sigil desktop);;;;;; Provides windowing, input handling, and application lifecycle management.;;; Native functions are registered by app.c, this module provides re-exports;;; and higher-level utilities.;;; DEPRECATED since sigil-desktop 0.10.0 and kept for one release so a;;; consumer can relock first and rename its imports second. Every binding;;; is (sigil desktop)'s; nothing lives here. Change;;;;;; The frame-loop macro provides a coroutine-based game loop where Scheme;;; appears to own the main loop while C (Sokol) actually controls execution.;;; (import (sigil app));;;;;; to;;;;;; (import (sigil desktop));;;;;; before 0.11.0, when this library goes away.(define-library (sigil app) (import (sigil core) (sigil coroutines)) (import (sigil desktop)) (export ;; Native functions (re-exported) app-run frame-width frame-height frame-time time-elapsed mouse-down? mouse-pressed? mouse-released? key-down? key-pressed? key-released? quit-requested? request-quit ;; Cooperative frame scheduling run-game wait-frame) (begin ;; Native functions are automatically available after native module init. ;; ========== Cooperative Frame Scheduler ========== ;;; ;;; The frame-loop pattern inverts control so Scheme code reads naturally: ;;; ;;; (run-game "My Game" 800 600 ;;; (lambda () ;;; ;; init code here ;;; (let loop () ;;; (let ((dt (wait-frame))) ;;; ;; frame code here using dt ;;; (unless (quit-requested?) ;;; (loop)))))) ;;; ;;; Behind the scenes, (wait-frame) yields to C, which resumes the ;;; coroutine on the next frame with the delta time. ;; Global coroutine for the current game loop (define *game-coroutine* #f) ;; Yield point - returns delta time when resumed (define (wait-frame) (: -> number?) (send 'frame-complete)) ;; Internal frame callback - resumes the game coroutine (define (frame-tick dt) (when *game-coroutine* (if (coroutine-done? *game-coroutine*) (request-quit) (coroutine-send *game-coroutine* dt)))) ;; Stored game thunk - will be converted to coroutine in init callback (define *game-thunk* #f) ;; Internal init callback - creates and starts the game coroutine (define (game-init) (when *game-thunk* ;; Create the game coroutine now that Sokol is initialized (set! *game-coroutine* (coroutine ;; Call the user's game thunk (*game-thunk*) ;; When thunk returns, game is done 'game-finished)) ;; Run init code up to first (wait-frame) (when (not (coroutine-done? *game-coroutine*)) (coroutine-send *game-coroutine* 0.0)))) ;; Run a game with the coroutine-based loop ;; game-thunk is called inside a coroutine and can use (wait-frame) (define (run-game title width height game-thunk) (: string? integer? integer? procedure? -> void?) ;; Store the thunk for the init callback (set! *game-thunk* game-thunk) ;; Run the app - init callback will start the coroutine (app-run game-init ; init - creates and starts coroutine frame-tick ; frame - resumes coroutine (lambda () ; cleanup (set! *game-coroutine* #f) (set! *game-thunk* #f)) title width height)))) wait-frame))src/sigil/desktop.sgladded
;;; (sigil desktop) - the desktop host layer;;;;;; Provides windowing, input handling, and application lifecycle management.;;; Native functions are registered by desktop.c (over GLFW), this module provides re-exports;;; and higher-level utilities.;;;;;; The frame-loop macro provides a coroutine-based game loop where Scheme;;; appears to own the main loop while C (GLFW) actually controls execution.(define-library (sigil desktop) (import (sigil core) (sigil coroutines)) (export ;; Native functions (re-exported) app-run frame-width frame-height frame-time time-elapsed mouse-x mouse-y mouse-down? mouse-pressed? mouse-released? key-down? key-pressed? key-released? quit-requested? request-quit ;; Cooperative frame scheduling run-game wait-frame) (begin ;; Native functions are automatically available after native module init. ;; ========== Cooperative Frame Scheduler ========== ;;; ;;; The frame-loop pattern inverts control so Scheme code reads naturally: ;;; ;;; (run-game "My Game" 800 600 ;;; (lambda () ;;; ;; init code here ;;; (let loop () ;;; (let ((dt (wait-frame))) ;;; ;; frame code here using dt ;;; (unless (quit-requested?) ;;; (loop)))))) ;;; ;;; Behind the scenes, (wait-frame) yields to C, which resumes the ;;; coroutine on the next frame with the delta time. ;; Global coroutine for the current game loop (define *game-coroutine* #f) ;; Yield point - returns delta time when resumed (define (wait-frame) (: -> number?) (send 'frame-complete)) ;; Internal frame callback - resumes the game coroutine (define (frame-tick dt) (when *game-coroutine* (if (coroutine-done? *game-coroutine*) (request-quit) (coroutine-send *game-coroutine* dt)))) ;; Stored game thunk - will be converted to coroutine in init callback (define *game-thunk* #f) ;; Internal init callback - creates and starts the game coroutine (define (game-init) (when *game-thunk* ;; Create the game coroutine now that the window is up (set! *game-coroutine* (coroutine ;; Call the user's game thunk (*game-thunk*) ;; When thunk returns, game is done 'game-finished)) ;; Run init code up to first (wait-frame) (when (not (coroutine-done? *game-coroutine*)) (coroutine-send *game-coroutine* 0.0)))) ;; Run a game with the coroutine-based loop ;; game-thunk is called inside a coroutine and can use (wait-frame) (define (run-game title width height game-thunk) (: string? integer? integer? procedure? -> void?) ;; Store the thunk for the init callback (set! *game-thunk* game-thunk) ;; Run the app - init callback will start the coroutine (app-run game-init ; init - creates and starts coroutine frame-tick ; frame - resumes coroutine (lambda () ; cleanup (set! *game-coroutine* #f) (set! *game-thunk* #f)) title width height))))test/dl-search-probe.cadded
/* dl-search-probe.c - links dl-search.c and reports which file a soname * resolved to. Driven by test/dl-search-test.sh. * usage: dl-search-probe <soname> exit 0 and print the path on success */#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif#include <dlfcn.h>#include <link.h>#include <stdio.h>const char *sigil_desktop_dlopen_search_summary(void);int main(int argc, char **argv){ if (argc != 2) { fprintf(stderr, "usage: %s <soname>\n", argv[0]); return 2; } void *h = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL); if (!h) { printf("UNRESOLVED %s: %s\n", argv[1], dlerror()); return 1; } struct link_map *lm = NULL; if (dlinfo(h, RTLD_DI_LINKMAP, &lm) == 0 && lm) printf("RESOLVED %s -> %s\n", argv[1], lm->l_name); else printf("RESOLVED %s\n", argv[1]); (void)sigil_desktop_dlopen_search_summary; return 0;}test/dl-search-test.shadded
#!/bin/sh# dl-search-test.sh - the interposed dlopen against fake Guix and Nix profiles.## Builds a probe from src/c/dl-search.c, a throwaway shared object, and a# temp HOME holding an empty ~/.guix-home/profile/lib and a# ~/.nix-profile/lib that has the object; then asserts, in order:# 1. the plain loader cannot find the soname (the control: the fallback# is what resolves it, not LD_LIBRARY_PATH or the cache)# 2. it resolves, from the Nix profile, with the Guix row walked first# and empty (both distributions' rows are live at once)# 3. with a copy in the Guix profile too, the Guix row wins (first hit)# and the trace names it# 4. IN_NIX_SHELL + buildInputs resolves ahead of every profile# 5. SIGIL_LIBRARY_PATH resolves ahead of everything# Any assertion failing exits 1. Needs a C compiler: $CC, or the pinned# zig under ~/.sigil/toolchain/zig, or cc.set -ucd "$(dirname "$0")/.." || exit 2if [ -n "${CC:-}" ]; then :elif [ -x "$HOME/.sigil/toolchain/zig/zig" ]; then CC="$HOME/.sigil/toolchain/zig/zig cc"elif command -v cc >/dev/null 2>&1; then CC=ccelse echo "SETUP-FAILED: no C compiler (set CC)"; exit 2; fiT=$(mktemp -d) || exit 2trap 'rm -rf "$T"' EXITFAKE=libsigildlsearchfake.so.7printf 'int sigil_fake_answer(void) { return 42; }\n' > "$T/fake.c"$CC -shared -fPIC -o "$T/$FAKE" "$T/fake.c" || { echo "SETUP-FAILED: cannot build the fake library"; exit 2; }$CC -o "$T/probe" test/dl-search-probe.c src/c/dl-search.c -ldl || { echo "SETUP-FAILED: cannot build the probe"; exit 2; }H="$T/home"; mkdir -p "$H/.guix-home/profile/lib" "$H/.nix-profile/lib" "$T/nixstore/foo-1.0/lib"cp "$T/$FAKE" "$H/.nix-profile/lib/"fails=0pass() { echo "PASS $1: $2"; }fail() { echo "FAIL $1: $2"; fails=$((fails + 1)); }run() { # name, expected-substring-of-stdout, env assignments... name=$1; want=$2; shift 2 out=$(env -i HOME="$H" USER=sigiltest SIGIL_DESKTOP_VERBOSE=1 "$@" "$T/probe" "$FAKE" 2>&1) case "$out" in *"$want"*) pass "$name" "$want";; *) fail "$name" "wanted [$want], got: $out";; esac}# 1. control: HOME with no profile dirs at allmkdir -p "$T/emptyhome"run control "UNRESOLVED $FAKE" HOME="$T/emptyhome"# 2. Nix profile only (the Guix row exists and is empty)run nix-profile "found via HOME/.nix-profile/lib as $H/.nix-profile/lib/$FAKE"# 3. both: Guix Home firstcp "$T/$FAKE" "$H/.guix-home/profile/lib/"run first-hit-wins "found via HOME/.guix-home/profile/lib as $H/.guix-home/profile/lib/$FAKE"# 4. nix-shell inputs beat the profilescp "$T/$FAKE" "$T/nixstore/foo-1.0/lib/"run nix-shell "found via buildInputs/lib as $T/nixstore/foo-1.0/lib/$FAKE" IN_NIX_SHELL=impure buildInputs="$T/nixstore/bar-2.0 $T/nixstore/foo-1.0"# 5. the override beats everythingmkdir -p "$T/override"; cp "$T/$FAKE" "$T/override/"run override "found via SIGIL_LIBRARY_PATH as $T/override/$FAKE" SIGIL_LIBRARY_PATH="/nonexistent:$T/override" IN_NIX_SHELL=impure buildInputs="$T/nixstore/foo-1.0"# the trace names the stores present on this machineout=$(env -i HOME="$H" USER=sigiltest SIGIL_DESKTOP_VERBOSE=1 "$T/probe" "$FAKE" 2>&1)case "$out" in *"(/gnu/store "*", /nix/store "*")"*) pass trace-stores "$(echo "$out" | grep -o '(/gnu/store [a-z]*, /nix/store [a-z]*)' | head -1)";; *) fail trace-stores "no store note in: $out";; esacif [ $fails -eq 0 ]; then echo "GREEN: dl-search"; exit 0; else echo "RED: $fails failed"; exit 1; fivendor/sokol/sokol_app.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL)#define SOKOL_APP_IMPL#endif#ifndef SOKOL_APP_INCLUDED/* sokol_app.h -- cross-platform application wrapper Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_APP_IMPL before you include this file in *one* C or C++ file to create the implementation. In the same place define one of the following to select the 3D-API which should be initialized by sokol_app.h (this must also match the backend selected for sokol_gfx.h if both are used in the same project): #define SOKOL_GLCORE #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_WGPU #define SOKOL_NOAPI Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_WIN32_FORCE_MAIN - define this on Win32 to add a main() entry point SOKOL_WIN32_FORCE_WINMAIN - define this on Win32 to add a WinMain() entry point (enabled by default unless SOKOL_WIN32_FORCE_MAIN or SOKOL_NO_ENTRY is defined) SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function SOKOL_APP_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_APP_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) Optionally define the following to force debug checks and validations even in release mode: SOKOL_DEBUG - by default this is defined if NDEBUG is not defined If sokol_app.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. if SOKOL_WIN32_FORCE_MAIN and SOKOL_WIN32_FORCE_WINMAIN are both defined, it is up to the developer to define the desired subsystem. On Linux, SOKOL_GLCORE can use either GLX or EGL. GLX is default, set SOKOL_FORCE_EGL to override. For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp Portions of the Windows and Linux GL initialization, event-, icon- etc... code have been taken from GLFW (http://www.glfw.org/). iOS onscreen keyboard support 'inspired' by libgdx. Link with the following system libraries: - on macOS: - all backends: Foundation, Cocoa, QuartzCore - with SOKOL_METAL: Metal, MetalKit - with SOKOL_GLCORE: OpenGL - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - on iOS: - all backends: Foundation, UIKit - with SOKOL_METAL: Metal, MetalKit - with SOKOL_GLES3: OpenGLES, GLKit - on Linux: - all backends: X11, Xi, Xcursor, dl, pthread, m - with SOKOL_GLCORE: GL - with SOKOL_GLES3: GLESv2 - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - with EGL: EGL - on Android: GLESv3, EGL, log, android - on Windows: - with MSVC or Clang: library dependencies are defined via `#pragma comment` - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - with MINGW/MSYS2 gcc: - compile with '-mwin32' so that _WIN32 is defined - link with the following libs: -lkernel32 -luser32 -lshell32 - additionally with the GL backend: -lgdi32 - additionally with the D3D11 backend: -ld3d11 -ldxgi On Linux, you also need to use the -pthread compiler and linker option, otherwise weird things will happen, see here for details: https://github.com/floooh/sokol/issues/376 On macOS and iOS, the implementation must be compiled as Objective-C. On Emscripten: - for WebGL2: add the linker option `-s USE_WEBGL2=1` - for WebGPU: compile and link with `--use-port=emdawnwebgpu` (for more exotic situations read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) FEATURE OVERVIEW ================ sokol_app.h provides a minimalistic cross-platform API which implements the 'application-wrapper' parts of a 3D application: - a common application entry function - creates a window and 3D-API context/device with a swapchain surface, depth-stencil-buffer surface and optionally MSAA surface - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL2, WebGPU, NOAPI FEATURE/PLATFORM MATRIX ======================= | Windows | macOS | Linux | iOS | Android | HTML5 --------------------+---------+-------+-------+-------+---------+-------- gl 4.x | YES | YES | YES | --- | --- | --- gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES metal | --- | YES | --- | YES | --- | --- d3d11 | YES | --- | --- | --- | --- | --- webgpu | YES(4) | YES(4)| YES(4)| NO | NO | YES noapi | YES | TODO | TODO | --- | TODO | --- KEY_DOWN | YES | YES | YES | SOME | TODO | YES KEY_UP | YES | YES | YES | SOME | TODO | YES CHAR | YES | YES | YES | YES | TODO | YES MOUSE_DOWN | YES | YES | YES | --- | --- | YES MOUSE_UP | YES | YES | YES | --- | --- | YES MOUSE_SCROLL | YES | YES | YES | --- | --- | YES MOUSE_MOVE | YES | YES | YES | --- | --- | YES MOUSE_ENTER | YES | YES | YES | --- | --- | YES MOUSE_LEAVE | YES | YES | YES | --- | --- | YES TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES TOUCHES_MOVED | --- | --- | --- | YES | YES | YES TOUCHES_ENDED | --- | --- | --- | YES | YES | YES TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES RESIZED | YES | YES | YES | YES | YES | YES ICONIFIED | YES | YES | YES | --- | --- | --- RESTORED | YES | YES | YES | --- | --- | --- FOCUSED | YES | YES | YES | --- | --- | YES UNFOCUSED | YES | YES | YES | --- | --- | YES SUSPENDED | --- | --- | --- | YES | YES | TODO RESUMED | --- | --- | --- | YES | YES | TODO QUIT_REQUESTED | YES | YES | YES | --- | --- | YES IME | TODO | TODO? | TODO | ??? | TODO | ??? key repeat flag | YES | YES | YES | --- | --- | YES windowed | YES | YES | YES | --- | --- | YES fullscreen | YES | YES | YES | YES | YES | YES(3) mouse hide | YES | YES | YES | --- | --- | YES mouse lock | YES | YES | YES | --- | --- | YES set cursor type | YES | YES | YES | --- | --- | YES screen keyboard | --- | --- | --- | YES | TODO | YES swap interval | YES | YES | YES | YES | TODO | YES high-dpi | YES | YES | TODO | YES | YES | YES clipboard | YES | YES | YES | --- | --- | YES MSAA | YES | YES | YES | YES | YES | YES drag'n'drop | YES | YES | YES | --- | --- | YES window icon | YES | YES(1)| YES | --- | --- | YES (1) macOS has no regular window icons, instead the dock icon is changed (2) supported with EGL only (not GLX) (3) fullscreen in the browser not supported on iphones (4) WebGPU on native desktop platforms should be considered experimental and mainly useful for debugging and benchmarking STEP BY STEP ============ --- Add a sokol_main() function to your code which returns a sapp_desc structure with initialization parameters and callback function pointers. This function is called very early, usually at the start of the platform's entry function (e.g. main or WinMain). You should do as little as possible here, since the rest of your code might be called from another thread (this depends on the platform): sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc) { .width = 640, .height = 480, .init_cb = my_init_func, .frame_cb = my_frame_func, .cleanup_cb = my_cleanup_func, .event_cb = my_event_func, ... }; } To get any logging output in case of errors you need to provide a log callback. The easiest way is via sokol_log.h: #include "sokol_log.h" sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc) { ... .logger.func = slog_func, }; } There are many more setup parameters, but these are the most important. For a complete list search for the sapp_desc structure declaration below. DO NOT call any sokol-app function from inside sokol_main(), since sokol-app will not be initialized at this point. The .width and .height parameters are the preferred size of the 3D rendering canvas. The actual size may differ from this depending on platform and other circumstances. Also the canvas size may change at any time (for instance when the user resizes the application window, or rotates the mobile device). You can just keep .width and .height zero-initialized to open a default-sized window (what "default-size" exactly means is platform-specific, but usually it's a size that covers most of, but not all, of the display). All provided function callbacks will be called from the same thread, but this may be different from the thread where sokol_main() was called. .init_cb (void (*)(void)) This function is called once after the application window, 3D rendering context and swap chain have been created. The function takes no arguments and has no return value. .frame_cb (void (*)(void)) This is the per-frame callback, which is usually called 60 times per second. This is where your application would update most of its state and perform all rendering. .cleanup_cb (void (*)(void)) The cleanup callback is called once right before the application quits. .event_cb (void (*)(const sapp_event* event)) The event callback is mainly for input handling, but is also used to communicate other types of events to the application. Keep the event_cb struct member zero-initialized if your application doesn't require event handling. As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks must live in global variables. If keeping state in global variables is not an option, there's an alternative set of callbacks with an additional user_data pointer argument: .user_data (void*) The user-data argument for the callbacks below .init_userdata_cb (void (*)(void* user_data)) .frame_userdata_cb (void (*)(void* user_data)) .cleanup_userdata_cb (void (*)(void* user_data)) .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) The function sapp_userdata() can be used to query the user_data pointer provided in the sapp_desc struct. You can also call sapp_query_desc() to get a copy of the original sapp_desc structure. NOTE that there's also an alternative compile mode where sokol_app.h doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. --- Implement the initialization callback function (init_cb), this is called once after the rendering surface, 3D API and swap chain have been initialized by sokol_app. All sokol-app functions can be called from inside the initialization callback, the most useful functions at this point are: int sapp_width(void) int sapp_height(void) Returns the current width and height of the default framebuffer in pixels, this may change from one frame to the next, and it may be different from the initial size provided in the sapp_desc struct. float sapp_widthf(void) float sapp_heightf(void) These are alternatives to sapp_width() and sapp_height() which return the default framebuffer size as float values instead of integer. This may help to prevent casting back and forth between int and float in more strongly typed languages than C and C++. double sapp_frame_duration(void) Returns the frame duration in seconds averaged over a number of frames to smooth out any jittering spikes. int sapp_color_format(void) int sapp_depth_format(void) The color and depth-stencil pixelformats of the default framebuffer, as integer values which are compatible with sokol-gfx's sg_pixel_format enum (so that they can be plugged directly in places where sg_pixel_format is expected). Possible values are: 23 == SG_PIXELFORMAT_RGBA8 28 == SG_PIXELFORMAT_BGRA8 42 == SG_PIXELFORMAT_DEPTH 43 == SG_PIXELFORMAT_DEPTH_STENCIL int sapp_sample_count(void) Return the MSAA sample count of the default framebuffer. const void* sapp_metal_get_device(void) const void* sapp_metal_get_current_drawable(void) const void* sapp_metal_get_depth_stencil_texture(void) const void* sapp_metal_get_msaa_color_texture(void) If the Metal backend has been selected, these functions return pointers to various Metal API objects required for rendering, otherwise they return a null pointer. These void pointers are actually Objective-C ids converted with a (ARC) __bridge cast so that the ids can be tunneled through C code. Also note that the returned pointers may change from one frame to the next, only the Metal device object is guaranteed to stay the same. const void* sapp_macos_get_window(void) On macOS, get the NSWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted back with a (ARC) __bridge cast. const void* sapp_ios_get_window(void) On iOS, get the UIWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted back with a (ARC) __bridge cast. const void* sapp_d3d11_get_device(void) const void* sapp_d3d11_get_device_context(void) const void* sapp_d3d11_get_render_view(void) const void* sapp_d3d11_get_resolve_view(void); const void* sapp_d3d11_get_depth_stencil_view(void) Similar to the sapp_metal_* functions, the sapp_d3d11_* functions return pointers to D3D11 API objects required for rendering, only if the D3D11 backend has been selected. Otherwise they return a null pointer. Note that the returned pointers to the render-target-view and depth-stencil-view may change from one frame to the next! const void* sapp_win32_get_hwnd(void) On Windows, get the window's HWND, otherwise a null pointer. The HWND has been cast to a void pointer in order to be tunneled through code which doesn't include Windows.h. const void* sapp_x11_get_window(void) On Linux, get the X11 Window, otherwise a null pointer. The Window has been cast to a void pointer in order to be tunneled through code which doesn't include X11/Xlib.h. const void* sapp_x11_get_display(void) On Linux, get the X11 Display, otherwise a null pointer. The Display has been cast to a void pointer in order to be tunneled through code which doesn't include X11/Xlib.h. const void* sapp_wgpu_get_device(void) const void* sapp_wgpu_get_render_view(void) const void* sapp_wgpu_get_resolve_view(void) const void* sapp_wgpu_get_depth_stencil_view(void) These are the WebGPU-specific functions to get the WebGPU objects and values required for rendering. If sokol_app.h is not compiled with SOKOL_WGPU, these functions return null. uint32_t sapp_gl_get_framebuffer(void) This returns the 'default framebuffer' of the GL context. Typically this will be zero. int sapp_gl_get_major_version(void) int sapp_gl_get_minor_version(void) bool sapp_gl_is_gles(void) Returns the major and minor version of the GL context and whether the GL context is a GLES context const void* sapp_android_get_native_activity(void); On Android, get the native activity ANativeActivity pointer, otherwise a null pointer. --- Implement the frame-callback function, this function will be called on the same thread as the init callback, but might be on a different thread than the sokol_main() function. Note that the size of the rendering framebuffer might have changed since the frame callback was called last. Call the functions sapp_width() and sapp_height() each frame to get the current size. --- Optionally implement the event-callback to handle input events. sokol-app provides the following type of input events: - a 'virtual key' was pressed down or released - a single text character was entered (provided as UTF-32 encoded UNICODE code point) - a mouse button was pressed down or released (left, right, middle) - mouse-wheel or 2D scrolling events - the mouse was moved - the mouse has entered or left the application window boundaries - low-level, portable multi-touch events (began, moved, ended, cancelled) - the application window was resized, iconified or restored - the application was suspended or restored (on mobile platforms) - the user or application code has asked to quit the application - a string was pasted to the system clipboard - one or more files have been dropped onto the application window To explicitly 'consume' an event and prevent that the event is forwarded for further handling to the operating system, call sapp_consume_event() from inside the event handler (NOTE that this behaviour is currently only implemented for some HTML5 events, support for other platforms and event types will be added as needed, please open a GitHub ticket and/or provide a PR if needed). NOTE: Do *not* call any 3D API rendering functions in the event callback function, since the 3D API context may not be active when the event callback is called (it may work on some platforms and 3D APIs, but not others, and the exact behaviour may change between sokol-app versions). --- Implement the cleanup-callback function, this is called once after the user quits the application (see the section "APPLICATION QUIT" for detailed information on quitting behaviour, and how to intercept a pending quit - for instance to show a "Really Quit?" dialog box). Note that the cleanup-callback isn't guaranteed to be called on the web and mobile platforms. MOUSE CURSOR TYPE AND VISIBILITY ================================ You can show and hide the mouse cursor with void sapp_show_mouse(bool show) And to get the current shown status: bool sapp_mouse_shown(void) NOTE that hiding the mouse cursor is different and independent from the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when active (MOUSE LOCK is described below). To change the mouse cursor to one of several predefined types, call the function: void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore the standard look. To get the currently active mouse cursor type, call: sapp_mouse_cursor sapp_get_mouse_cursor(void) MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) ================================================ In normal mouse mode, no mouse movement events are reported when the mouse leaves the windows client area or hits the screen border (whether it's one or the other depends on the platform), and the mouse move events (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in framebuffer pixels in the sapp_event items mouse_x and mouse_y, and relative movement in framebuffer pixels in the sapp_event items mouse_dx and mouse_dy. To get continuous mouse movement (also when the mouse leaves the window client area or hits the screen border), activate mouse-lock mode by calling: sapp_lock_mouse(true) When mouse lock is activated, the mouse pointer is hidden, the reported absolute mouse position (sapp_event.mouse_x/y) appears frozen, and the relative mouse movement in sapp_event.mouse_dx/dy no longer has a direct relation to framebuffer pixels but instead uses "raw mouse input" (what "raw mouse input" exactly means also differs by platform). To deactivate mouse lock and return to normal mouse mode, call sapp_lock_mouse(false) And finally, to check if mouse lock is currently active, call if (sapp_mouse_locked()) { ... } Note that mouse-lock state may not change immediately after sapp_lock_mouse(true/false) is called, instead on some platforms the actual state switch may be delayed to the end of the current frame or even to a later frame. The mouse may also be unlocked automatically without calling sapp_lock_mouse(false), most notably when the application window becomes inactive. On the web platform there are further restrictions to be aware of, caused by the limitations of the HTML5 Pointer Lock API: - sapp_lock_mouse(true) can be called at any time, but it will only take effect in a 'short-lived input event handler of a specific type', meaning when one of the following events happens: - SAPP_EVENTTYPE_MOUSE_DOWN - SAPP_EVENTTYPE_MOUSE_UP - SAPP_EVENTTYPE_MOUSE_SCROLL - SAPP_EVENTTYPE_KEY_UP - SAPP_EVENTTYPE_KEY_DOWN - The mouse lock/unlock action on the web platform is asynchronous, this means that sapp_mouse_locked() won't immediately return the new status after calling sapp_lock_mouse(), instead the reported status will only change when the pointer lock has actually been activated or deactivated in the browser. - On the web, mouse lock can be deactivated by the user at any time by pressing the Esc key. When this happens, sokol_app.h behaves the same as if sapp_lock_mouse(false) is called. For things like camera manipulation it's most straightforward to lock and unlock the mouse right from the sokol_app.h event handler, for instance the following code enters and leaves mouse lock when the left mouse button is pressed and released, and then uses the relativeShowing the first 500 of 14048 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/sokol/sokol_log.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL)#define SOKOL_LOG_IMPL#endif#ifndef SOKOL_LOG_INCLUDED/* sokol_log.h -- common logging callback for sokol headers Project URL: https://github.com/floooh/sokol Example code: https://github.com/floooh/sokol-samples Do this: #define SOKOL_IMPL or #define SOKOL_LOG_IMPL before you include this file in *one* C or C++ file to create the implementation. Optionally provide the following defines when building the implementation: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GFX_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) Optionally define the following for verbose output: SOKOL_DEBUG - by default this is defined if NDEBUG is not defined OVERVIEW ======== sokol_log.h provides a default logging callback for other sokol headers. To use the default log callback, just include sokol_log.h and provide a function pointer to the 'slog_func' function when setting up the sokol library: For instance with sokol_audio.h: #include "sokol_log.h" ... saudio_setup(&(saudio_desc){ .logger.func = slog_func }); Logging output goes to stderr and/or a platform specific logging subsystem (which means that in some scenarios you might see logging messages duplicated): - Windows: stderr + OutputDebugStringA() - macOS/iOS/Linux: stderr + syslog() - Emscripten: console.info()/warn()/error() - Android: __android_log_write() On Windows with sokol_app.h also note the runtime config items to make stdout/stderr output visible on the console for WinMain() applications via sapp_desc.win32.console_attach or sapp_desc.win32.console_create, however when running in a debugger on Windows, the logging output should show up on the debug output UI panel. In debug mode, a log message might look like this: [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) such error messages are clickable. In release mode, logging is less verbose as to not bloat the executable with string data, but you still get enough information to identify the type and location of an error: [sspine][error][id:12][line:3472] RULES FOR WRITING YOUR OWN LOGGING FUNCTION =========================================== - must be re-entrant because it might be called from different threads - must treat **all** provided string pointers as optional (can be null) - don't store the string pointers, copy the string data instead - must not return for log level panic LICENSE ======= zlib/libpng license Copyright (c) 2023 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.*/#define SOKOL_LOG_INCLUDED (1)#include <stdint.h>#if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL)#define SOKOL_LOG_API_DECL SOKOL_API_DECL#endif#ifndef SOKOL_LOG_API_DECL#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL)#define SOKOL_LOG_API_DECL __declspec(dllexport)#elif defined(_WIN32) && defined(SOKOL_DLL)#define SOKOL_LOG_API_DECL __declspec(dllimport)#else#define SOKOL_LOG_API_DECL extern#endif#endif#ifdef __cplusplusextern "C" {#endif/* Plug this function into the 'logger.func' struct item when initializing any of the sokol headers. For instance for sokol_audio.h it would look like this: saudio_setup(&(saudio_desc){ .logger = { .func = slog_func } });*/SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data);#ifdef __cplusplus} // extern "C"#endif#endif // SOKOL_LOG_INCLUDED// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████//// >>implementation#ifdef SOKOL_LOG_IMPL#define SOKOL_LOG_IMPL_INCLUDED (1)#ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL#endif#ifndef SOKOL_DEBUG #ifndef NDEBUG #define SOKOL_DEBUG #endif#endif#ifndef SOKOL_ASSERT #include <assert.h> #define SOKOL_ASSERT(c) assert(c)#endif#ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif#endif#ifndef _SOKOL_UNUSED #define _SOKOL_UNUSED(x) (void)(x)#endif// platform detection#if defined(__APPLE__) #define _SLOG_APPLE (1)#elif defined(__EMSCRIPTEN__) #define _SLOG_EMSCRIPTEN (1)#elif defined(_WIN32) #define _SLOG_WINDOWS (1)#elif defined(__ANDROID__) #define _SLOG_ANDROID (1)#elif defined(__linux__) || defined(__unix__) #define _SLOG_LINUX (1)#else#error "sokol_log.h: unknown platform"#endif#include <stdlib.h> // abort#include <stdio.h> // fputs#include <stddef.h> // size_t#if defined(_SLOG_EMSCRIPTEN)#include <emscripten/emscripten.h>#elif defined(_SLOG_WINDOWS)#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN#endif#ifndef NOMINMAX #define NOMINMAX#endif#include <windows.h>#elif defined(_SLOG_ANDROID)#include <android/log.h>#elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE)#include <syslog.h>#endif// size of line buffer (on stack!) in bytes including terminating zero#define _SLOG_LINE_LENGTH (512)_SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) { if (str) { char c; while (((c = *str++) != 0) && (dst < (end - 1))) { *dst++ = c; } } *dst = 0; return dst;}_SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { const size_t max_digits_and_null = 11; if (buf_size < max_digits_and_null) { return 0; } char* p = buf + max_digits_and_null; *--p = 0; do { *--p = '0' + (x % 10); x /= 10; } while (x != 0); return p;}#if defined(_SLOG_EMSCRIPTEN)EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { const str = UTF8ToString(c_str); switch (level) { case 0: console.error(str); break; case 1: console.error(str); break; case 2: console.warn(str); break; default: console.info(str); break; }})#endifSOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { _SOKOL_UNUSED(user_data); const char* log_level_str; switch (log_level) { case 0: log_level_str = "panic"; break; case 1: log_level_str = "error"; break; case 2: log_level_str = "warning"; break; default: log_level_str = "info"; break; } // build log output line char line_buf[_SLOG_LINE_LENGTH]; char* str = line_buf; char* end = line_buf + sizeof(line_buf); char num_buf[32]; if (tag) { str = _slog_append("[", str, end); str = _slog_append(tag, str, end); str = _slog_append("]", str, end); } str = _slog_append("[", str, end); str = _slog_append(log_level_str, str, end); str = _slog_append("]", str, end); str = _slog_append("[id:", str, end); str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); str = _slog_append("]", str, end); // if a filename is provided, build a clickable log message that's compatible with compiler error messages if (filename) { str = _slog_append(" ", str, end); #if defined(_MSC_VER) // MSVC compiler error format str = _slog_append(filename, str, end); str = _slog_append("(", str, end); str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); str = _slog_append("): ", str, end); #else // gcc/clang compiler error format str = _slog_append(filename, str, end); str = _slog_append(":", str, end); str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); str = _slog_append(":0: ", str, end); #endif } else { str = _slog_append("[line:", str, end); str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); str = _slog_append("] ", str, end); } if (message) { str = _slog_append("\n\t", str, end); str = _slog_append(message, str, end); } str = _slog_append("\n\n", str, end); if (0 == log_level) { str = _slog_append("ABORTING because of [panic]\n", str, end); (void)str; } // print to stderr? #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) fputs(line_buf, stderr); #endif // platform specific logging calls #if defined(_SLOG_WINDOWS) OutputDebugStringA(line_buf); #elif defined(_SLOG_ANDROID) int prio; switch (log_level) { case 0: prio = ANDROID_LOG_FATAL; break; case 1: prio = ANDROID_LOG_ERROR; break; case 2: prio = ANDROID_LOG_WARN; break; default: prio = ANDROID_LOG_INFO; break; } __android_log_write(prio, "SOKOL", line_buf); #elif defined(_SLOG_EMSCRIPTEN) slog_js_log(log_level, line_buf); #endif if (0 == log_level) { abort(); }}#endif // SOKOL_LOG_IMPLvendor/sokol/sokol_time.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL)#define SOKOL_TIME_IMPL#endif#ifndef SOKOL_TIME_INCLUDED/* sokol_time.h -- simple cross-platform time measurement Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_TIME_IMPL before you include this file in *one* C or C++ file to create the implementation. Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_TIME_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) If sokol_time.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. void stm_setup(); Call once before any other functions to initialize sokol_time (this calls for instance QueryPerformanceFrequency on Windows) uint64_t stm_now(); Get current point in time in unspecified 'ticks'. The value that is returned has no relation to the 'wall-clock' time and is not in a specific time unit, it is only useful to compute time differences. uint64_t stm_diff(uint64_t new, uint64_t old); Computes the time difference between new and old. This will always return a positive, non-zero value. uint64_t stm_since(uint64_t start); Takes the current time, and returns the elapsed time since start (this is a shortcut for "stm_diff(stm_now(), start)") uint64_t stm_laptime(uint64_t* last_time); This is useful for measuring frame time and other recurring events. It takes the current time, returns the time difference to the value in last_time, and stores the current time in last_time for the next call. If the value in last_time is 0, the return value will be zero (this usually happens on the very first call). uint64_t stm_round_to_common_refresh_rate(uint64_t duration) This oddly named function takes a measured frame time and returns the closest "nearby" common display refresh rate frame duration in ticks. If the input duration isn't close to any common display refresh rate, the input duration will be returned unchanged as a fallback. The main purpose of this function is to remove jitter/inaccuracies from measured frame times, and instead use the display refresh rate as frame duration. NOTE: for more robust frame timing, consider using the sokol_app.h function sapp_frame_duration() Use the following functions to convert a duration in ticks into useful time units: double stm_sec(uint64_t ticks); double stm_ms(uint64_t ticks); double stm_us(uint64_t ticks); double stm_ns(uint64_t ticks); Converts a tick value into seconds, milliseconds, microseconds or nanoseconds. Note that not all platforms will have nanosecond or even microsecond precision. Uses the following time measurement functions under the hood: Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() MacOS/iOS: mach_absolute_time() emscripten: emscripten_get_now() Linux+others: clock_gettime(CLOCK_MONOTONIC) zlib/libpng license Copyright (c) 2018 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.*/#define SOKOL_TIME_INCLUDED (1)#include <stdint.h>#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL)#define SOKOL_TIME_API_DECL SOKOL_API_DECL#endif#ifndef SOKOL_TIME_API_DECL#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL)#define SOKOL_TIME_API_DECL __declspec(dllexport)#elif defined(_WIN32) && defined(SOKOL_DLL)#define SOKOL_TIME_API_DECL __declspec(dllimport)#else#define SOKOL_TIME_API_DECL extern#endif#endif#ifdef __cplusplusextern "C" {#endifSOKOL_TIME_API_DECL void stm_setup(void);SOKOL_TIME_API_DECL uint64_t stm_now(void);SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks);SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time);SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks);SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks);SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks);SOKOL_TIME_API_DECL double stm_us(uint64_t ticks);SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks);#ifdef __cplusplus} /* extern "C" */#endif#endif // SOKOL_TIME_INCLUDED/*-- IMPLEMENTATION ----------------------------------------------------------*/#ifdef SOKOL_TIME_IMPL#define SOKOL_TIME_IMPL_INCLUDED (1)#include <string.h> /* memset */#ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL#endif#ifndef SOKOL_ASSERT #include <assert.h> #define SOKOL_ASSERT(c) assert(c)#endif#ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif#endif#if defined(_WIN32)#ifndef WIN32_LEAN_AND_MEAN#define WIN32_LEAN_AND_MEAN#endif#include <windows.h>typedef struct { uint32_t initialized; LARGE_INTEGER freq; LARGE_INTEGER start;} _stm_state_t;#elif defined(__APPLE__) && defined(__MACH__)#include <mach/mach_time.h>typedef struct { uint32_t initialized; mach_timebase_info_data_t timebase; uint64_t start;} _stm_state_t;#elif defined(__EMSCRIPTEN__)#include <emscripten/emscripten.h>typedef struct { uint32_t initialized; double start;} _stm_state_t;#else /* anything else, this will need more care for non-Linux platforms */#ifdef ESP8266// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined#define CLOCK_MONOTONIC 0#endif#include <time.h>typedef struct { uint32_t initialized; uint64_t start;} _stm_state_t;#endifstatic _stm_state_t _stm;/* prevent 64-bit overflow when computing relative timestamp see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3*/#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))_SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { int64_t q = value / denom; int64_t r = value % denom; return q * numer + r * numer / denom;}#endifSOKOL_API_IMPL void stm_setup(void) { memset(&_stm, 0, sizeof(_stm)); _stm.initialized = 0xABCDABCD; #if defined(_WIN32) QueryPerformanceFrequency(&_stm.freq); QueryPerformanceCounter(&_stm.start); #elif defined(__APPLE__) && defined(__MACH__) mach_timebase_info(&_stm.timebase); _stm.start = mach_absolute_time(); #elif defined(__EMSCRIPTEN__) _stm.start = emscripten_get_now(); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; #endif}SOKOL_API_IMPL uint64_t stm_now(void) { SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); uint64_t now; #if defined(_WIN32) LARGE_INTEGER qpc_t; QueryPerformanceCounter(&qpc_t); now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); #elif defined(__APPLE__) && defined(__MACH__) const uint64_t mach_now = mach_absolute_time() - _stm.start; now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); #elif defined(__EMSCRIPTEN__) double js_now = emscripten_get_now() - _stm.start; now = (uint64_t) (js_now * 1000000.0); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; #endif return now;}SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { if (new_ticks > old_ticks) { return new_ticks - old_ticks; } else { return 1; }}SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { return stm_diff(stm_now(), start_ticks);}SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { SOKOL_ASSERT(last_time); uint64_t dt = 0; uint64_t now = stm_now(); if (0 != *last_time) { dt = stm_diff(now, *last_time); } *last_time = now; return dt;}// first number is frame duration in ns, second number is tolerance in ns,// the resulting min/max values must not overlap!static const uint64_t _stm_refresh_rates[][2] = { { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms { 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms { 0, 0 }, // keep the last element always at zero};SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { uint64_t ns; int i = 0; while (0 != (ns = _stm_refresh_rates[i][0])) { uint64_t tol = _stm_refresh_rates[i][1]; if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { return ns; } i++; } // fallthrough: didn't fit into any buckets return ticks;}SOKOL_API_IMPL double stm_sec(uint64_t ticks) { return (double)ticks / 1000000000.0;}SOKOL_API_IMPL double stm_ms(uint64_t ticks) { return (double)ticks / 1000000.0;}SOKOL_API_IMPL double stm_us(uint64_t ticks) { return (double)ticks / 1000.0;}SOKOL_API_IMPL double stm_ns(uint64_t ticks) { return (double)ticks;}#endif /* SOKOL_TIME_IMPL */