cosmicnim
libcosmic behind a small C ABI, driven from Nim.
libcosmic's API is generics, traits and closures, so it has no C ABI of its own —
building libcosmic itself as a cdylib exports nothing. The cosmic_ffi crate
therefore defines the C surface: it depends on libcosmic as a normal Rust
crate and exports a handful of functions.
cosmic_ffi/ Rust cdylib -> libcosmic_ffi.so + cosmic_ffi.h
nim/ Nim bindings (cosmic.nim) and the demo app (app.nim)
justfile build / run / fetch tasks
The contract
The host owns the application state and the shape of the window. Once per
frame libcosmic calls back through on_view with an opaque builder, and the
host describes its widget tree by calling the builder functions. Widget memory
never crosses the boundary — the host only ever issues calls, and the builder
handle is dead the moment on_view returns.
Interaction runs the other way. A button carries an id the host chose; pressing
it calls on_press with that id, the host mutates its own state, and the next
on_view reflects it.
typedef void (*cosmic_on_view)(void *ctx, CosmicBuilder *b);
typedef void (*cosmic_on_press)(void *ctx, int32_t id);
int32_t cosmic_run(const CosmicConfig *config); /* blocks until closed */
Because the tree is rebuilt every frame, it can depend on state: a button
disappears, or goes inert, simply by not being described that way this time
round. See cosmic_ffi.h for the full list of containers, leaves and
attributes.
cosmic_run must be called from the main thread, and ctx is only ever
touched from that thread.
From Nim
Containers are block templates, so the source has the same shape as the window:
proc onView(ctx: pointer; b: Builder) {.cdecl.} =
let c = cast[ptr Counter](ctx)
b.container:
b.fill(); b.alignCenter(); b.spacing(space(SpaceM))
b.text($c.value, TextTitle1)
b.row:
b.spacing(space(SpaceS))
b.button("−", IdDec)
b.button("Reset", IdReset, ButtonDestructive, enabled = c.value != 0)
b.button("+", IdInc, ButtonSuggested)
Running it
The demo needs nim/libcosmic_ffi.so. Take it from a release:
just fetch # newest v* tag on the remote
just fetch v0.1.2 # or a specific one
just run
or build it yourself — the cold build is long, which is what the release CI is
for:
just build
just run
just check type-checks the Nim without needing the library at all, since the
binding loads it lazily.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
|