A Gtk4 binding for GNU Modula.

Eric Streit 963a0b6faf feat: C shim for GObject subclassing (custom GtkWidget / GdkPaintable) 3 hari lalu
examples 963a0b6faf feat: C shim for GObject subclassing (custom GtkWidget / GdkPaintable) 3 hari lalu
lib 963a0b6faf feat: C shim for GObject subclassing (custom GtkWidget / GdkPaintable) 3 hari lalu
src 963a0b6faf feat: C shim for GObject subclassing (custom GtkWidget / GdkPaintable) 3 hari lalu
tests 963a0b6faf feat: C shim for GObject subclassing (custom GtkWidget / GdkPaintable) 3 hari lalu
tools 0418ccb46e m2-GTK4 v1.0.0: GNU Modula-2 bindings for GTK4 (+ libadwaita) 3 hari lalu
.gitignore 0418ccb46e m2-GTK4 v1.0.0: GNU Modula-2 bindings for GTK4 (+ libadwaita) 3 hari lalu
Makefile 963a0b6faf feat: C shim for GObject subclassing (custom GtkWidget / GdkPaintable) 3 hari lalu
README.md 963a0b6faf feat: C shim for GObject subclassing (custom GtkWidget / GdkPaintable) 3 hari lalu

README.md

m2-GTK4

GNU Modula-2 bindings for GTK4 and libadwaita, for gm2 (GCC 16 / GNU Modula-2) on Linux.

m2-GTK4 is a hand-written set of DEFINITION MODULE FOR "C" bindings — one module per GTK/GIO/libadwaita class — plus a small pure-Modula-2 helper layer, a test suite, runnable examples, and a generator that can emit broad binding coverage from GObject-Introspection .gir XML.

It is compiled in the ISO dialect (-fiso).

Verified with gm2 16.0.1 (experimental), GTK 4.18.6 and libadwaita 1.7.6.

Features

  • 123 hand-written binding modules across GLib, GObject, Gio, GTK4 and libadwaita — windows, widgets, inputs, containers, model-backed views, actions/menus/accelerators, dialogs, CSS, drawing (Cairo/Pango), gestures, settings, and more.
  • Pure-Modula-2 helpers: GtkUtils (string/number conversion, Connect, SetAccel, ownership and GError helpers) and GtkClosures (per-connection signal contexts with cleanup).
  • 28 tests and 20 runnable examples.
  • Generator + verifier: tools/gir2def.py turns a .gir file into .def files; tools/gir_verify.py checks every generated identifier against the installed library. Verified clean across 8602 identifiers.

Requirements

  • gm2 (tested: 16.0.1 experimental)
  • GTK4 development files (pkg-config --cflags --libs gtk4, tested 4.18.6)
  • libadwaita development files (pkg-config --cflags --libs libadwaita-1, tested 1.7.6) — optional; enables the Adw* modules, test_adw and the adw example. The build detects them automatically.
export PATH=$HOME/bin/Modula2/Gm2/bin:$PATH

Build and test

make                 # build all tests and examples into build/
./tests/run_tests.sh # run the test suite

make check is make tests plus the runner script. GUI tests are skipped automatically when neither DISPLAY nor WAYLAND_DISPLAY is set. The runner also compiles a private GSettings schema into build/schemas so test_gio can exercise GSettings without touching real user settings.

Run an example:

./build/examples/hello
./build/examples/adw

Quick start

MODULE hello ;

FROM Gio IMPORT g_application_run;
FROM GtkApplication IMPORT gtk_application_new;
FROM GtkWindow IMPORT gtk_application_window_new, gtk_window_set_title,
   gtk_window_set_default_size, gtk_window_set_child, gtk_window_present;
FROM GtkLabel IMPORT gtk_label_new;
FROM GtkUtils IMPORT Connect;
FROM SYSTEM IMPORT ADDRESS, ADR;

PROCEDURE OnActivate (application: ADDRESS; data: ADDRESS);
VAR window, label: ADDRESS;
BEGIN
   window := gtk_application_window_new(application);
   gtk_window_set_title(window, "Hello");
   gtk_window_set_default_size(window, 360, 120);
   label := gtk_label_new("Hello, GNU Modula-2 + GTK4!");
   gtk_window_set_child(window, label);
   gtk_window_present(window)
END OnActivate;

VAR app: ADDRESS;
BEGIN
   app := gtk_application_new("org.example.Hello", 0);
   Connect(app, "activate", ADR(OnActivate), NIL);
   g_application_run(app, 0, NIL)
END hello.

Compile a program against the bindings (the helper objects must be built first):

gm2 -fiso -Isrc -Ilib myprog.mod \
    build/objs/GtkUtils.o build/objs/GtkClosures.o -o myprog \
    $(pkg-config --cflags --libs gtk4)

Examples

Example Shows
hello the canonical application + window + label
counter button signal updating a label
inputs entry, check button, switch, scale
containers grid, stack/switcher, notebook, scrolled window
actions menu bar + app/window actions + accelerators
headerbar header bar, menu button, connected button
editor search entry, text view, drop-down, spin button
layouts frame, expander, paned, overlay, list box
files native file dialog → GFile
views GtkListView / GtkGridView / GtkColumnView from a store
builder load a window from a .ui file with GtkBuilder
feedback progress bar, spinner, separator, image
styling CSS provider and per-widget CSS classes
popover popover and menu popover
dialogs GtkAlertDialog and GtkColorDialog
drawing GtkDrawingArea + Cairo + Pango
demo searchable demo browser (filter model + stack of pages)
gestures click/drag gestures on a drawing area
customwidget custom GtkWidget and GdkPaintable via the C shim
adw libadwaita preferences app (toolbar view, rows, toast, about)

Module reference

Core runtime

Module C header Contents
GLib <glib.h> base types, main loop, timeouts, memory, clock
GObject <glib-object.h> ref/unref, GType, g_object_new, signals, properties
Gio <gio/gapplication.h> GApplication flags, run/quit/hold
GVariant <glib/gvariant.h> boolean/string/int32 values, refcount
GError <glib/gerror.h> error value (message/domain/code)
GioError <gio/gioerror.h> GIO error domain and codes
Gtk <gtk/gtk.h> gtk_init, version helpers
Gdk <gdk/gdkdisplay.h> default display, GdkRGBA
GtkEnums <gtk/gtkenums.h> orientation, alignment, wrap, selection, position

GIO objects and models

Module Contents
GListModel, GListStore indexed models; in-memory store
GtkStringList, GtkStringObject string model / item
GMenu, GMenuModel, GSimpleAction, GAction, GActionMap menus and actions
GtkFilterListModel, GtkCustomFilter, GtkSortListModel, GtkCustomSorter M2-callback filter/sort models
GtkMultiSelection, GtkBitset multiple selection and bitsets
GtkTreeListModel, GtkTreeListRow, GtkTreeExpander tree models and expanders
GtkDirectoryList, GtkBookmarkList directory / bookmark models
GFile, GtkFileDialog file handles and the native dialog
GSettings, GSettingsSchema typed settings and schema lookup

GTK widgets and views

Module Contents
GtkApplication, GtkApplicationWindow, GtkWindow, GtkWidget application and windows
GtkBox, GtkGrid, GtkFrame, GtkExpander, GtkPaned, GtkOverlay, GtkNotebook, GtkStack, GtkStackSwitcher, GtkScrolledWindow layout containers
GtkLabel, GtkButton, GtkCheckButton, GtkSwitch, GtkScale, GtkRange, GtkSpinButton, GtkEntry, GtkEditable, GtkSearchEntry, GtkDropDown controls
GtkTextView, GtkTextBuffer, GtkTextIter multi-line text
GtkAdjustment, GtkProgressBar, GtkSpinner, GtkSeparator, GtkImage value model and feedback/decoration widgets
GtkHeaderBar, GtkMenuButton, GtkPopoverMenuBar window chrome and menus
GtkPopover, GtkPopoverMenu popovers and menu popovers
GtkBuilder load widget trees from .ui XML (with GError)
GtkAlertDialog, GtkColorDialog modern alert and colour dialogs
GtkCssProvider, GtkStyleContext CSS styling for the display
GtkStackSidebar, GtkSearchBar, GtkRevealer, GtkAspectFrame sidebar/search/animation/layout helpers
GtkFlowBox, GtkFixed, GtkPicture, GtkToggleButton, GtkLinkButton, GtkCalendar, GtkLevelBar, GtkScrollbar reflowing grid, fixed/abs, image, toggle/link, date, level, scrollbar
GtkEventController, GtkGestures event controllers and gestures (click/drag/swipe/zoom/rotate/pan/long-press)
GtkDrawingArea + Cairo + Pango + PangoCairo custom drawing with Cairo and text layout
GtkListBox, GtkListView, GtkGridView, GtkColumnView, GtkColumnViewColumn, GtkColumnViewCell, GtkListItem, GtkSignalListItemFactory, GtkSelectionModel, GtkSingleSelection, GtkNoSelection lists, grids and tables

libadwaita

Module Contents
AdwApplication, AdwApplicationWindow Adwaita application and window
AdwHeaderBar, AdwToolbarView header bar and toolbar view
AdwPreferencesPage, AdwPreferencesGroup, AdwPreferencesRow preferences layout
AdwActionRow, AdwSwitchRow, AdwEntryRow, AdwComboRow, AdwExpanderRow settings rows
AdwStatusPage, AdwBanner, AdwClamp status/banner/clamp
AdwToast, AdwToastOverlay transient notifications
AdwAboutWindow, AdwMessageDialog about and message dialogs

Helpers and the C shim (lib/)

Module Contents
GtkUtils CStrToM2, StrAppend, IntToStr, Connect, SetAccel, Unref, Free, ErrorMessage, ClearError
GtkClosures Connect2/Connect3 (and owned variants) for per-connection signal state
M2GtkShim (lib/m2gtkshim.c) C shim for GObject subclassing: a custom GtkWidget and GdkPaintable whose snapshot vfuncs hand a Cairo context to Modula-2

Signals, ownership and errors

GObject.g_signal_connect_data() is the real function behind the g_signal_connect() macro. For the common case use the helpers:

Connect(button, "clicked", ADR(OnClicked), NIL);          (* GtkUtils *)
Connect2(row, "activated", OnActivate, state);            (* GtkClosures: own state *)
Connect3(dropdown, "notify::selected", OnSelected, NIL);  (* 3-argument form *)

Modula-2 has no destructors, so follow the C transfer rules:

  • GObject handles → GtkUtils.Unref (g_object_unref);
  • strings from getters (g_file_get_basename, g_settings_get_string, …) → GtkUtils.Free (g_free);
  • GError values → GtkUtils.ClearError, and read them with GtkUtils.ErrorMessage;
  • GVariant from g_variant_get_* / g_action_get_state → g_variant_unref, while g_variant_new_* are floating.

Dialect and binding notes

The build uses the ISO dialect (-fiso). Things worth knowing:

  • CAST and NEW/DISPOSE (with Storage.ALLOCATE/DEALLOCATE) come from ISO, not PIM.
  • EXPORT UNQUALIFIED makes the C symbol match the Modula-2 name.
  • Formal parameters in a procedure type are unnamed: GSourceFunc = PROCEDURE (ADDRESS) : INTEGER;.
  • An ignorable result is declared : [ T ]; otherwise ignoring it is an error.
  • gm2 REAL is C double; SHORTREAL is float; avoid LONGREAL.
  • Pass a procedure directly where the parameter is a procedure type, and ADR(proc) where the parameter is ADDRESS/GCallback.
  • ARRAY OF CHAR maps to char *; VAR parameters map to T *; opaque handles travel as ADDRESS.
  • Inside comments, avoid *) and (* (write void ptr, not void *).
  • A large opaque struct always passed by address (e.g. GtkTextIter, 80 bytes) can be modelled as ARRAY [0..N] OF ADDRESS.

Generator (broad coverage)

For breadth beyond the hand-written modules, tools/gir2def.py emits FOR "C" .def files from GObject-Introspection .gir XML:

make gir                  # Gtk-4.0 -> gen/
make gir GIR=Adw-1        # any namespace
make gir-check            # generate, parse every module, link a sample
make gir-verify           # check every identifier exists in the library

It produces one module per class/interface/record, namespace functions in <Namespace>Functions.def, and enums in <Namespace>Enums.def; procedure names are the exact GIR c:identifier. Across Gtk/GLib/Gio/Adw, 633 generated modules parse under gm2 -fiso and all 8602 identifiers resolve in the installed libraries. Output goes to gen/ (git-ignored) and is meant to be cherry-picked, not to replace src/. See tools/README.md for the type mapping and limits.

Repository layout

m2-GTK4/
  src/        FOR "C" .def bindings (one per GTK/GIO/Adw object)
  lib/        helpers (GtkUtils, GtkClosures) + C shim (m2gtkshim.c)
  tests/      gm2 tests + run_tests.sh (+ a private GSettings schema)
  examples/   runnable GTK4 / libadwaita programs
  tools/      gir2def.py, gir_verify.py, run_gir_check.sh
  gen/        generator output (git-ignored)
  build/      generated binaries (git-ignored)

License

No license has been chosen yet; there is no LICENSE file in this repository.