Spec M32 — examples/desktop: composing windows with GTK4 from mc (owner's test, 2026-09-03)
Goal: prove that an mc program (and then a taught UI language) can drive a desktop toolkit to
compose windows. Toolkit: GTK4 via Homebrew (/opt/homebrew/lib/libgtk-4.1.dylib plus GLib/GObject/
GIO), chosen because its C API uses only integers, pointers and callbacks — everything mc has today
(extern, #dylib/[libs]+[externs], &fn, callp). AppKit through objc_msgSend follows
after M24 (NSRect travels in v0..v3).
Part A — plain mc binding (examples/desktop/main.mc, lib/gtk.mc) #
lib/gtk.mc:externdeclarations for the subset used:g_application_new,g_application_run,g_signal_connect_data,g_object_unref,gtk_application_new,gtk_application_window_new,gtk_window_set_title,gtk_window_set_default_size,gtk_window_present,gtk_window_close,gtk_window_set_transient_for,gtk_box_new,gtk_box_append,gtk_label_new,gtk_label_set_text,gtk_label_get_text,gtk_button_new_with_label,gtk_entry_new,gtk_editable_get_text,gtk_editable_set_text,gtk_widget_set_margin_*,gtk_header_bar_new,gtk_window_set_titlebar,gtk_list_box_new,gtk_list_box_append,gtk_check_button_new_with_label,gtk_check_button_get_active, constants (GTK_ORIENTATION_*,G_APPLICATION_DEFAULT_FLAGS).mc.tomlmaps prefixes to libraries:gtk_*-> gtk4,g_signal_*/g_object_*-> gobject,g_application_*-> gio (first real use of[externs]prefix mapping with several dylibs).main.mc: an app with a main window (header bar, a counter label with+/-buttons, an entry whose text is echoed to a label on Enter, a list box that grows with entries) and a second window opened by a button (transient dialog with a close button). Callbacks aremcfunctions passed with&fntog_signal_connect_data; state lives in globals or in a small arena struct passed asuser_data.--self-test: builds the widget tree without running the main loop, invokes the callbacks directly (on_plus,on_entry_activatewith the entry set programmatically), prints the label texts and exits 0 — the CI-safe test (check-desktopskips when GTK4 is not installed).- Manual/visual acceptance: the architect runs the app and captures the windows with the desktop
tooling (
computer-use), attaching screenshots to the report. CI never opens a window: the GitHub runners have no GTK4, socheck-desktopprints a skip line there; the self-test is the only automated check and it needs no display server (owner, 2026-09-03).
Part B — a UI language taught by the surface (examples/desktop/ui.mc, .ui files) #
Using the lx machinery (M22) or a small dedicated module: declarative composition lowered to Part A:
window "Counter" size 360 200 {
vbox {
label count "0";
hbox { button "-" -> on_minus; button "+" -> on_plus; }
entry name placeholder "your name" -> on_name;
}
}
fn on_plus() { count.text = itoa(state.n += 1); }
- Handlers are ordinary functions; the module generates the
g_signal_connect_datacalls and the widget globals; nested containers by recursion in thesyntax_stmt("window")handler. - Same
--self-testidea; the same app expressed in the DSL must produce the same self-test output.
Acceptance #
mc build examples/desktop(Part A) andmc build examples/desktop/ui(Part B) produce signed--exebinaries linked to gtk4/gobject/gio;otool -Lshows the four dylibs; both self-tests print the same expected lines;check-desktopinmake checkruns the self-tests whenpkg-config --exists gtk4, otherwise prints a skip line.- Visual: screenshots of the main and secondary windows (light and dark if the toolkit follows the system), attached to the milestone report by the architect.
- Linux: the same source builds for linux/arm64 with the Alpine
gtk4-devsysroot in Docker when the[target]is linux (headless run limited to--self-test).