A portable C library providing common data structures, networking, GUI widgets, 2D/3D helpers, and more. Designed to be used as a monolith library or as a collection of individual source files dropped into your project.
- Logging system with configurable levels and file output (
n_log) - No-duplicate logging to console, file, or syslog (
n_nodup_log) - Dynamic strings with formatting helpers (
n_str) - Generic linked lists (
n_list) - Hash tables (
n_hash) - Thread pools (
n_thread_pool) - Stack data structure (
n_stack) - Tree data structure (
n_trees) - Base64 encoding / decoding (
n_base64) - Vigenere cipher encoding / decoding (
n_crypto) - Enum-to-string macro helpers (
n_enum) - Signal handling helpers (
n_signals) - Exception-like macros (
n_exceptions) - Common macros and typedefs (
n_common) - File helpers (
n_files) - Time / timer utilities (
n_time) - Hexadecimal encode / decode helpers (
n_hex) - Cryptographically secure random bytes and hex tokens (
n_random) - Randomness / entropy metrics for byte samples, token-randomness analysis (
n_entropy) - Lightweight HTML/XML extraction: links, forms, and sitemap URLs (
n_html) - Lexical pretty-printers for JSON, XML/HTML, and JavaScript text (
n_pretty, JSON needs cJSON) - Domain-aware HTTP cookie jar: Set-Cookie parsing and Cookie header building (
n_cookies) - HTTP/2 (RFC 7540) wire framing plus HPACK header compression (
n_http2) - Minimal DNS message helpers: parse a query question, build an A-record response (
n_dns) - zlib compression helpers (
n_zlib), vendored underexternal/zlib/, built into the library - LZ4 block-compression helpers (
n_lz4), vendored underexternal/lz4/, built unconditionally and used as an opt-in network compression backend
- TCP / UDP network engine with optional SSL (
n_network) - HTTP CONNECT, HTTPS CONNECT, and SOCKS5 proxy tunneling (
n_network) - WebSocket client handshake and framing (
n_network) - Server-Sent Events (SSE) client (
n_network) - Network message framing (
n_network_msg) - Parallel accept pool, nginx-style multi-threaded accept (
n_network_accept_pool) - Single-threaded epoll reactor as an opt-in alternative to the per-connection thread engine (
n_reactor, Linux/Android only) - Clock synchronization estimator for networked games (
n_clock_sync) - URL parsing and canonicalization helpers (
n_url_canonicalize, inn_network) - Per-connection compression backend (
netw_set_compression_mode):NETW_COMPRESS_NONE/_ZLIB/_LZ4. The wire layout is self-describing, so the two ends can run different codecs and still interop. - Message digests, HMAC, PBKDF2, and AES-256-GCM AEAD over the OpenSSL EVP API (
n_digest) - X.509 helpers: self-signed CA generation and per-host leaf minting (
n_x509)
- PCRE2 regex wrapper (
n_pcre) - Configuration file parser (
n_config_file) - Boolean query language over caller-named fields (
n_query)
- GUI widget system with pseudo-windows (
n_gui) -- buttons, toggle buttons, horizontal & vertical sliders, text areas, checkboxes, scrollbars, listboxes, radio lists, combo boxes, labels/hyperlinks, images, dropdown menus, frameless windows, disabled/hidden widgets, auto-scrollbar windows, resizable windows, global display scrollbars, cross-platform DPI scale detection, and optional bitmap skinning for all widgets and windows - Allegro 5 input and display helpers (
n_allegro5) - Isometric engine with height segments, depth-sorted object rendering, occlusion
detection with clipped ghost overlay, 2D camera, multiple projection presets, and
terrain transitions, with optional cross-chunk neighbor arrays so transition masks
span chunk edges without water-clamp seams (
n_iso_engine) - A* pathfinding (
n_astar) - Dead reckoning / prediction (
n_dead_reckoning) - Trajectory helpers (
n_trajectory) - AABB collision (
n_aabb) - Particle system (
n_particles) - Fluid dynamics simulation (
n_fluids) - 3D helpers (
n_3d) - Animation helpers (
n_anim) - Game environment utilities (
n_games) - Network-oriented user handling (
n_user)
- Security audit and hardened HTTPS example (
SSL_SECURITY.md) - TLS 1.2+ enforcement, strong cipher suites, HSTS, path traversal protection
- Persistent server scripts with auto-restart (
serve_ssl.sh,serve_ssl_hardened.sh)
- Avro binary format encoding/decoding with JSON conversion (
n_avro) - Small JSON helper, a thin wrapper over the vendored cJSON (
n_json)
- Git repository operations via libgit2 (
n_git), open/init/close repos, stage/unstage files, commit, log, diff, checkout, branch management, push/pull with auth (token, basic, SSH), fetch from remotes, ahead/behind counts, per-repo operation status reporting
- Kafka consumer / producer wrappers (
n_kafka, requires librdkafka): optional retry-after-timeout for errored events, transactional producing when atransactional.idis configured, and optional move-on-ack of produced files to asendeddirectory (n_kafka_set_sended_dir) instead of deleting them - cJSON integration (included as git subtree)
Most core modules compile with only pthreads and a C17 compiler. The zlib and LZ4 codecs are vendored as git subtrees and built into the library, no system -lz dependency.
| Dependency | Required for | How to get it |
|---|---|---|
| gcc / make | Building | Your system package manager |
| pthreads | Core (threads, network) | Usually bundled with your C toolchain |
| zlib | Compression helpers (n_zlib) |
Included as git subtree under external/zlib/ (no system package needed) |
| LZ4 | LZ4 block compression helpers (n_lz4) |
Included as git subtree under external/lz4/ (no system package needed) |
| OpenSSL | SSL networking | apt install libssl-dev / pacman -S openssl / brew install openssl |
| libpcre2 | Regex module | apt install libpcre2-dev / pacman -S pcre2 |
| Allegro 5 | GUI, isometric, particles, etc. | apt install liballegro5-dev liballegro-acodec5-dev liballegro-audio5-dev liballegro-image5-dev liballegro-primitives5-dev liballegro-ttf5-dev liballegro-font5-dev |
| cJSON | JSON parsing, Avro support | Included as git subtree (no compilation needed) |
| librdkafka | Kafka wrappers | Included as git subtree (make integrate-deps to compile) or apt install librdkafka-dev |
| libgit2 | Git operations module | apt install libgit2-dev / pacman -S libgit2 / brew install libgit2 |
| doxygen + graphviz | Documentation | apt install doxygen graphviz |
| Platform | Compiler | Notes |
|---|---|---|
| Linux (x86_64, aarch64) | gcc / clang | Primary development platform |
| Windows (MinGW-w64) | gcc (MinGW) | Cross-compile from Linux or native MinGW shell |
| Android | NDK clang | Via Allegro Android toolchain |
| Solaris | gcc | Legacy support |
cd my_project_dir
git clone --recurse-submodules git@github.com:gullradriel/nilorea-library.git
cd nilorea-library
make # builds the static + shared library
make examples # builds all example programs
# or
make all # library + examples in one stepmake DEBUG=1 clean all # ASan + UBSan + extra warnings
make DEBUG=1 DEBUG_THREADS=1 clean all # ThreadSanitizer insteadConvenience targets wrap the above:
make asan # alias for: make DEBUG=1 clean all
make tsan # alias for: make DEBUG=1 DEBUG_THREADS=1 clean all
make asan-test # build with ASan, then run examples/run_tests.sh
make tsan-test # build with TSAN, then run examples/run_tests.shmake asan-test # or: make tsan-test
# equivalent to:
make DEBUG=1 clean all
cd examples && bash run_tests.shThe test runner executes all non-GUI examples and checks for
ASan/LSan/TSan reports. It verifies that binaries were compiled with
DEBUG=1 before running (exits with an error otherwise). Network tests
(TCP, SSL, accept pool) run both server and client through the
sanitizer. Suppressions for system library leaks (Mesa, OpenSSL) are
loaded automatically from examples/lsan-suppressions.cfg.
Use FORCE_NO_* flags to disable auto-detected optional dependencies:
make FORCE_NO_ALLEGRO=1 # disable Allegro 5 GUI/game modules
make FORCE_NO_OPENSSL=1 # disable SSL networking
make FORCE_NO_KAFKA=1 # disable Kafka integration
make FORCE_NO_PCRE=1 # disable PCRE2 regex support
make FORCE_NO_CJSON=1 # disable cJSON support
make FORCE_NO_LIBGIT2=1 # disable libgit2 Git operations# Install MinGW-w64 toolchain
apt install gcc-mingw-w64-x86-64
# Build with the MINGW flag
make CC=x86_64-w64-mingw32-gcc MINGW=1 clean all- Set up the Android NDK and Allegro 5 Android build as described in the Allegro Android docs.
- Point your
CCto the NDK clang and set appropriateCFLAGS/LDFLAGS. - Build with
make.
make doc
# or directly:
doxygen Doxyfilemake check # cppcheck (gating) + scan-build (informational)
make check SCAN_BUILD_STATUS_BUGS=1 # also fail on scan-build findingsBoth cppcheck and scan-build (from the clang-tools package on
Debian/Ubuntu) must be in PATH; the target reports a clear error and
exits non-zero if either binary is missing. Override the resolved tools
with CPPCHECK=... / SCAN_BUILD=... on the command line if needed.
Vendored third-party translation units under external/ (zlib, LZ4,
cJSON) are pre-built without scan-build interception, then
scan-build drives only the src/ rebuild. Their headers are also
included via -isystem so warnings emitted from inside them when our
src/*.c #includes them are suppressed at the source. Result:
analyzer and compiler diagnostics come exclusively from code we own.
make update-deps # update cJSON and librdkafka subtrees (needs git subtree)
make integrate-deps # compile librdkafka, copy libs to the right directories
make clean ; make all # full fresh buildAll examples live in the examples/ directory and are built by make examples.
| Example | Description | Requires |
|---|---|---|
ex_gui |
Full GUI demo: all widget types, dropdown menus, toggle buttons, vertical sliders, frameless windows, disabled/hidden widgets, auto-scrollbars, resizable windows, global display scrollbars, DPI detection, bitmap-skinned containers (listbox/radiolist/combobox/dropmenu/textarea), titlebar button bitmaps, focused-keycode bindings, layout save/load JSON | Allegro 5 |
ex_gui_particles |
Particle system with real-time info overlay | Allegro 5 |
ex_gui_dictionary |
Dictionary search app (text input, listbox, labels) | Allegro 5, PCRE2 |
ex_gui_isometric |
Isometric map editor with dead reckoning | Allegro 5 |
ex_gui_network |
TCP chat application with GUI | Allegro 5, OpenSSL |
ex_gui_custom |
Owner-draw custom widget (paint callback) demo | Allegro 5 |
ex_gui_datagrid |
Datagrid rows, numeric sort, selection, right-click context menu | Allegro 5 |
ex_gui_dropmenu |
Dropdown menu entry, label, and clear regression (headless) | Allegro 5 |
ex_gui_hexview |
Hex view data round-trip regression (headless) | Allegro 5 |
ex_gui_kvtable |
KV table adaptive-resize regression | Allegro 5 |
ex_gui_detach |
Interactive native-window demo: per-panel detach/attach buttons, the three N_GUI_DETACH_* modes side by side, layout save/load |
Allegro 5 |
ex_gui_multiwin |
Native (detached) window regression: display routing, shared font/bitmap, clipboard, layout persistence (skips itself without a display) | Allegro 5 |
ex_gui_placeholder |
Textarea placeholder/hint set, replace, and clear (headless) | Allegro 5 |
ex_gui_progressbar |
Progress bar value set/get and clamping regression (headless) | Allegro 5 |
ex_gui_reentrant |
n_gui_process_event re-entrancy regression (headless) |
Allegro 5 |
ex_gui_splitpane |
Split pane ratio and clamping regression (headless) | Allegro 5 |
ex_gui_syntaxview |
Syntax view line-count, highlight modes, and selection/copy regression (headless) | Allegro 5 |
ex_gui_zorder |
Topmost-overlapping-widget mouse dispatch regression (headless) | Allegro 5 |
ex_fluid |
Fluid dynamics simulation | Allegro 5 |
ex_trajectory |
Trajectory / ballistic helpers demo | Allegro 5 |
ex_common |
Common macros and helpers demo | - |
ex_exceptions |
Exception handling demo | - |
ex_hash |
Hash table demo | - |
ex_list |
Linked list demo | - |
ex_log |
Logging system demo | - |
ex_nstr |
String helpers demo | - |
ex_base64 |
Base64 encoding / decoding demo | - |
ex_base64_encode |
Base64 encoding demo (alternate binary built from the ex_base64 source) |
- |
ex_hex |
Hexadecimal encode/decode round-trips and error paths | - |
ex_random |
Secure random bytes and hex token generation | - |
ex_entropy |
Randomness / entropy metrics regression | - |
ex_html |
HTML link, form, and sitemap extraction | - |
ex_cookies |
Cookie jar: Set-Cookie parsing, domain/path/secure/expiry matching | - |
ex_http2 |
HTTP/2 frame codec, SETTINGS, and HPACK (RFC 7541 vectors) | - |
ex_dns |
DNS query parsing and A-record response building | - |
ex_url |
URL canonicalization (scheme/host case, default port, dot segments) | - |
ex_crypto |
Vigenere cipher encryption demo | - |
ex_file |
File operations demo | - |
ex_zlib |
Zlib compression / decompression demo | - |
ex_stack |
Stack data structure demo | - |
ex_trees |
Tree data structure demo | - |
ex_threads |
Thread pool demo | - |
ex_network |
Network client/server demo | OpenSSL |
ex_network_mock |
Mock HTTP server for testing (serves canned responses) | OpenSSL |
ex_network_proxy |
HTTP/HTTPS CONNECT and SOCKS5 proxy tunneling demo | OpenSSL |
ex_network_ssl |
SSL network demo | OpenSSL |
ex_network_ssl_hardened |
Hardened HTTPS server (TLS 1.2+, security headers, path traversal protection) | OpenSSL |
ex_network_ws |
WebSocket client demo | OpenSSL |
ex_network_sse |
Server-Sent Events (SSE) client demo | OpenSSL |
ex_network_sni |
SNI accept: per-host leaf minting, client-side trust evaluation | OpenSSL |
ex_digest |
MD5/SHA/HMAC/PBKDF2 and constant-time-compare vectors | OpenSSL |
ex_x509 |
CA generation and per-host leaf minting | OpenSSL |
ex_network_reactor |
Single-threaded epoll reactor demo (n_reactor + netw_accept_into_reactor), Linux/Android only |
- |
ex_accept_pool_server |
Accept pool server: single-inline, single-pool, and pooled accept modes | - |
ex_accept_pool_client |
Accept pool client: stress-tests the server with concurrent connections | - |
ex_pcre |
PCRE regex demo | PCRE2 |
ex_configfile |
Config file parser demo | PCRE2 |
ex_query |
Boolean query language: operators, AND/OR/NOT, error paths | PCRE2 |
ex_clock_sync |
Clock synchronization over UDP (offset + RTT estimation) | OpenSSL |
ex_signals |
Signal handler demo | - |
ex_iso_astar |
A* pathfinding on isometric map | - |
ex_avro |
Avro binary encoding/decoding with JSON round-trip | cJSON |
ex_json |
JSON wrapper: parse, typed getters, arrays, and file parsing | cJSON |
ex_pretty |
JSON, XML/HTML, and JavaScript pretty-printing regression | cJSON |
ex_kafka |
Kafka event streaming demo | librdkafka, cJSON, PCRE2 |
ex_git |
Git repository operations demo (init, stage, commit, log, diff, branch) | libgit2 |
ex_monolith |
Monolith build test (all modules linked) | All |
The n_gui module provides a retained-mode widget system with pseudo-windows,
built on top of Allegro 5 primitives and fonts.
- Button (regular + toggle + bitmap)
- Slider (horizontal + vertical, value or percentage mode, configurable step with snap, mouse scroll + keyboard support, custom
printf-style value readout format, toggleable value label) - Text area (single-line + multiline with cursor)
- Checkbox
- Scrollbar (horizontal + vertical, rect or rounded)
- Listbox (none / single / multi select)
- Radio list
- Combo box (dropdown selector with scrollbar, auto-scroll to selected item on open, optional auto-width to fit longest item)
- Label (static text, left/center/right/justified, optional hyperlink)
- Image (fit / stretch / center)
- Dropdown menu (static + dynamic entries, rebuilt on open, scrollbar when entries overflow)
- Split pane (horizontal / vertical, draggable divider with ratio clamping)
- Hex view (read-only hexadecimal dump of a byte buffer)
- Syntax view (read-only source viewer with highlighting modes: plain, HTTP, JSON, XML, YAML, JavaScript; text selection/copy, scroll-to-offset)
- Datagrid (columns, sortable rows, selection, right-click context callback)
- Progress bar (value set/get with clamping)
- Custom (owner-draw widget with paint callback)
- KV table helper (
n_gui_kvtable_*, key/value rows with per-row remove buttons, built on core widgets)
- Draggable title bar
- Minimise (title bar only)
- Frameless (no title bar, drag from body area)
- Fixed position (disable dragging)
- Resizable (drag handle at bottom-right)
- Auto-scrollbar (vertical + horizontal scrollbars appear when content overflows)
- Auto-size (fit window to widget extents)
- Z-ordering (click to raise, always-on-top, always-behind, fixed z-value)
- Show/hide via dropdown menu
- Adaptive resize (per-window policies: none / move / scale)
- Native window (detach a pop-up into a real OS window, see below)
Any pseudo-window can be promoted to a real OS window, so a pop-up that the user wants to keep on a second monitor stops being confined to the host's display. Pop-ups keep working exactly as before, detaching is opt-in per window.
n_gui_set_event_queue(gui, queue); /* once, before detaching */
n_gui_window_detach(gui, win_id, N_GUI_DETACH_RESIZABLE);
...
while (running) {
while (al_get_next_event(queue, &ev))
n_gui_process_event(gui, ev); /* routes on the event's display */
al_set_target_backbuffer(display);
n_gui_draw(gui); /* main display, pop-ups only */
al_flip_display();
n_gui_draw_detached(gui); /* native windows: draws and flips */
}A detached window is drawn frameless at the origin of its own display: the
window manager supplies the title bar, so dragging, resizing, minimising and
closing are handled by the OS. ALLEGRO_EVENT_DISPLAY_CLOSE runs the window's
close callback (n_gui_window_set_close_callback) exactly like the pop-up close
button does, and the display is released at the next n_gui_draw_detached,
never from inside event processing.
n_gui_window_attach()turns it back into a pop-up with its original geometry.- Closing a detached window takes the OS window down but remembers the choice, so
n_gui_open_window()brings the native window back rather than a pop-up. n_gui_save_layout_json()persists the detached state, the native size and the desktop position; loading re-creates the native window when the context already has an event queue.N_GUI_DETACH_RESIZABLElets the user resize the OS window,N_GUI_DETACH_SCALE_CONTENTadditionally scales the widgets with it.n_gui_window_set_native_icons()gives the OS window a taskbar icon.
N_GUI_DETACH_OWN_CHROME creates the native window frameless and keeps the
window drawing its own title bar, so a detached window looks and skins exactly
like the pop-up it came from instead of picking up the desktop theme:
n_gui_window_set_flags(gui, win_id, N_GUI_WIN_RESIZABLE | N_GUI_WIN_BTN_ALL);
n_gui_window_detach(gui, win_id, N_GUI_DETACH_OWN_CHROME);The chrome then drives the OS window: dragging the title bar moves it, the
resize grip calls al_resize_display(), minimise shrinks it to the title bar
(Allegro's ALLEGRO_MINIMIZED is read-only, so this is the portable mapping of
"title bar only"), maximise toggles ALLEGRO_MAXIMIZED with a monitor-work-area
fallback, and close takes the window down. The window's min_w/min_h are also
pushed to the window manager via al_set_window_constraints().
Frameless windows are the less well trodden path on some window managers, which is why the OS chrome remains the default.
examples/ex_gui_detach.c is an interactive demo of all of this: three panels,
one per detach mode, each with a button that promotes it to an OS window and
back. -a detaches everything at startup and -q SECONDS quits on its own, so
it also works as a sanitizer smoke test.
ALLEGRO_EVENT_DISPLAY_HALT_DRAWING / RESUME_DRAWING are acknowledged per
native window and n_gui_draw_detached() skips a window whose surface the OS has
taken away (Android) or that the window manager has iconified.
Caveats: the virtual canvas, global scroll and global scrollbars apply to the
main display only, and n_gui_wants_mouse() keeps answering for the main
display. Fonts and bitmaps are shared across displays by Allegro, so they must
outlive every native window (ex_gui_multiwin checks this holds). Window
constraints, ALLEGRO_FRAMELESS and ALLEGRO_MAXIMIZED are hints the window
manager may ignore; N_GUI's own bookkeeping stays consistent either way.
Two context-level resize modes control how windows respond to display size changes:
N_GUI_RESIZE_VIRTUAL(default): fixed virtual canvas, a uniform transform scales all coordinates identically, the existing behaviour.N_GUI_RESIZE_ADAPTIVE: the virtual canvas is disabled and each window follows its own per-window resize policy:N_GUI_WIN_RESIZE_NONE, window stays at its absolute position and size.N_GUI_WIN_RESIZE_MOVE, window repositions proportionally but keeps its pixel size.N_GUI_WIN_RESIZE_SCALE, window repositions and resizes proportionally; child widgets scale with it.
/* after creating all windows: */
n_gui_set_resize_mode(gui, N_GUI_RESIZE_ADAPTIVE);
/* assign policies per window */
n_gui_window_set_resize_policy(gui, win_menu, N_GUI_WIN_RESIZE_SCALE);
n_gui_window_set_resize_policy(gui, win_buttons, N_GUI_WIN_RESIZE_MOVE);
n_gui_window_set_resize_policy(gui, win_fixed, N_GUI_WIN_RESIZE_NONE);When the user drags or resizes a window, the new position/size becomes the reference for future adaptive resizes.
- Tab / Shift+Tab cycles focus between widgets in a window (wraps around)
- Sliders: Left/Right (horizontal) or Up/Down (vertical) adjust by one step; Home/End jump to min/max
- Listbox / Radiolist / Combobox: Up/Down selects previous/next item; Home/End jump to first/last
- Checkbox: Space/Enter toggles
- Scrollbar: Arrow keys scroll; Home/End jump to start/end
- Text areas: Ctrl+Tab inserts a literal tab; plain Tab moves focus
- Global (
n_gui_button_set_keycode): fires when the bound key is pressed and no interactive widget has focus. Single-line textareas let Enter pass through for backward compatibility. - Focused (
n_gui_button_set_keycode_focused): fires only when the button itself or one of the listed source widgets has focus. This allows e.g. Enter in a URL textarea to trigger a Send button without interfering with Enter in other textareas. Source widgets are specified as an array of widget IDs (up toN_GUI_KEY_SOURCES_MAX). - Visual feedback: When a button is activated through its keybind, it briefly renders in its pressed (active) visual state, same colours/bitmaps as a mouse click, so keyboard activation is as perceivable as a pointer click.
Widgets and windows inherit ctx->default_theme. Two helpers simplify
bulk theme changes:
// Set a new default theme and auto-sync scrollbar colors
n_gui_set_default_theme(gui, my_theme);
// Push the default theme to every existing window and widget
n_gui_reset_all_widget_themes(gui);
// Re-apply individual overrides after the reset
n_gui_set_widget_theme(gui, accent_btn_id, accent_theme);n_gui_set_default_theme() derives scrollbar track/thumb colors from the
theme's bg_normal and border_normal fields so scrollbars stay in sync
with the active palette.
- Enabled/Disabled, disabled widgets are drawn dimmed and ignore all input
- Visible/Hidden, hidden widgets are removed from drawing and hit testing
When the total GUI bounding box exceeds the display/viewport size, global scrollbars automatically appear at the edges. This works dynamically with resizable Allegro displays:
// On startup:
n_gui_set_display_size(gui, (float)display_w, (float)display_h);
// On ALLEGRO_EVENT_DISPLAY_RESIZE:
al_acknowledge_resize(display);
n_gui_set_display_size(gui, (float)al_get_display_width(display),
(float)al_get_display_height(display));Cross-platform DPI detection works on Linux, Windows, and Android:
// Auto-detect from display (compares framebuffer to logical window size)
float scale = n_gui_detect_dpi_scale(gui, display);
// Or set manually
n_gui_set_dpi_scale(gui, 1.5f);
// Query at any time
float current_scale = n_gui_get_dpi_scale(gui);How it works:
- Compares the physical pixel size (backbuffer bitmap) to the logical window size. On HiDPI displays these differ (e.g. 2x on Retina, 1.25x at 125% Windows scaling).
- On Android, uses the display DPI relative to the 160 DPI baseline.
- The detected scale is stored in
ctx->dpi_scaleand can be used by the application to scale fonts, widget sizes, etc.
On Windows, when using per-monitor DPI awareness (e.g. laptop at 125%, external monitor at 100%), the OS changes the effective DPI when the window is moved between monitors. This can cause the window content to appear clipped by the scaling difference (e.g. 25% clipping when moving from 125% to 100%).
Recommended solution:
-
Enable per-monitor DPI awareness via your application manifest or by calling
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)before creating the display. -
Handle
ALLEGRO_EVENT_DISPLAY_RESIZE(which Allegro fires when Windows sendsWM_DPICHANGED) and update the display size:case ALLEGRO_EVENT_DISPLAY_RESIZE: al_acknowledge_resize(display); n_gui_set_display_size(gui, (float)al_get_display_width(display), (float)al_get_display_height(display)); n_gui_detect_dpi_scale(gui, display); break;
-
Scale your fonts using the detected DPI factor:
float scale = n_gui_get_dpi_scale(gui); int font_size = (int)(13.0f * scale); ALLEGRO_FONT* font = al_load_ttf_font("font.ttf", font_size, 0);
-
Use
ALLEGRO_RESIZABLEdisplay flag so the window can be resized by the OS during DPI changes.
The n_gui module already handles ALLEGRO_EVENT_DISPLAY_SWITCH_OUT to reset
all drag/resize states, preventing GUI windows from being unintentionally
moved or resized when the OS window changes focus during monitor transitions.
Full API documentation is generated with Doxygen. Run make doc and open
docs/html/index.html in your browser.
- Issues: https://github.com/gullradriel/nilorea-library/issues
- Documentation: Run
make docto generate the full API reference - Examples: See the
examples/directory for working code samples
This project is licensed under the Apache License, Version 2.0, see the LICENSE file for the full text and the NOTICE file for attribution.
SPDX-License-Identifier: Apache-2.0
Copyright (C) 2005-2026 Castagnier Mickael
Releases made before the relicensing remain available under the GNU General Public License v3.0 or later; that grant is not withdrawn.
Vendored third-party code under external/ keeps its own upstream license and is not covered by the Apache License. See NOTICE for the per-component breakdown.