An idiomatic Odin rewrite of the state-mode graphical user interface toolkit SMGUI.
The original ANSI C project is pinned as the reference-c git
submodule. It remains the behavioral reference for API, event, and framebuffer
parity tests.
Install Nix and direnv, then clone and enter the repository:
git clone --recurse-submodules <repository-url>
cd smgui
direnv allow
make check
make runDirenv loads the pinned Nix development shell from flake.nix, including
Odin, the C toolchain, GLFW, and native graphics dependencies. After changing
flake.nix or flake.lock, approve it again with direnv allow.
Use make help to list Odin and reference-C build targets. make run launches
the Odin migration target derived from
reference-c/docs/screen1.png; make c-run
launches the original C widgets example behind that screenshot.
examples— visual/manual migration examples (make run)smgui/ui.odin— backend-independent interface and implementation targetsmgui/{sdl2,sdl3,glfw,raylib,sokol}— presentation and event adapterssmgui/widgets— optional custom widgetspsf2,ssfn,spritesheet— fixed, scalable, and Aseprite sprite-sheet font packagesscripts/aseprite-theme,themes— Aseprite theme conversion and generated SMGUI theme bundlesreference-c— pinned original implementationtests/parity— behavioral and framebuffer parity harness
The Odin interface intentionally uses slices, enums, bit sets, and typed errors rather than preserving C source compatibility.
The default commands show the migration target and its original reference:
make run # Odin target with Raylib
make c-run # upstream reference-c/examples/widgets.cThis target stays intentionally visible while parity work proceeds, making cross-form layout gaps and missing controls obvious. For exact paired form trees used by framebuffer comparison, run:
make smoke-odin
make smoke-cOther examples and backends remain selectable explicitly:
make run EXAMPLE=basic BACKEND=raylib
make run BACKEND=sokol # same smoke forms via Sokol
make c-example-run C_EXAMPLE=helloworldBoth make run commands render the same examples/smoke form tree. The Sokol
adapter uses sokol-odin and owns the platform loop, so
Sokol applications call smgui/sokol.run with their context, forms, and
optional init/frame hooks. make sokol-libs builds its native libraries;
normal make check and Sokol builds do this automatically.
Set SMGUI_SKIN to apply a packed PNG skin to the smoke application with either
backend. The bundled reference skin is a ready-to-use example:
SMGUI_SKIN=reference-c/examples/skin.png make run
SMGUI_SKIN=reference-c/examples/skin.png make run BACKEND=sokolThe smoke example also offers the bundled proportional Aseprite fonts without changing the PSF2 parity default:
SMGUI_FONT=aseprite make run
SMGUI_FONT=aseprite-mini make runBoth are rendered at nearest-neighbour scale 2. Their assets are licensed under
CC BY 4.0; see spritesheet/fonts/LICENSE.txt.
The generated Catppuccin Mocha colors and skin can be selected together:
SMGUI_THEME=catppuccin-mocha SMGUI_FONT=aseprite make runA native .Panel Form renders named Panel_Style nine-slices in normal layout
order and clips its children. panel_padding defines independent content insets,
panel_background fills beneath the nine-slice center without affecting layout,
and panel_content = .Transparent clears the full nine-slice center for a
host-rendered canvas, independently of child padding. Buttons retain the default
4px horizontal content padding unless button_horizontal_padding is set. Set
button_fixed_size to preserve explicit button dimensions when oversized icon
bounds should not drive layout. Set button_style to a normal/focused
Panel_Style pair to replace the fixed-skin button chrome with named
nine-slices; hover, pressed, and selected states use the focused slice. Adding .Focused selects the focused slice without turning
the Panel into a popup or overlay. Generated theme bundles expose style_pair
for constructing these styles from Aseprite part names.
set_png_skin(ctx, png, scale) optionally scales packed skin graphics by a
positive integer at load time using nearest-neighbor sampling; scale defaults
to 1. Generated bundles provide matching scale parameters for their skin,
style atlas, and icon atlas.
See scripts/aseprite-theme for converting other
Aseprite themes into separate skin, icon-atlas, nine-slice style-atlas, and
color/attribute outputs.
The skin file must use SMGUI's packed skin-atlas format: one sprite per skin
image in
the documented order, with its rectangles stored in the PNG comment metadata.
An unset or empty SMGUI_SKIN keeps the default color theme.
Run completed small framebuffer fixtures, or generate the large smoke fixture:
make parity # expected-green element/state fixtures
make parity-case CASE=empty
make skin-parity # representative C/Odin fixtures with the bundled skin
make parity-fuzz FUZZ_SEED=1 FUZZ_CASES=20
make smoke-images
make smoke-compare # non-zero until smoke parity is reachedSee tests/manual/README.md for covered controls and the comparison procedure.
BACKEND is part of the build interface now; additional values will become
available as their adapters are migrated.