A Gtk4 binding for GNU Modula.

Eric Streit 2605deaeb3 feat(demos): demo batch 3 - choosers and canvas 2 dagen geleden
demos 2605deaeb3 feat(demos): demo batch 3 - choosers and canvas 2 dagen geleden
examples 6b58592c30 feat: full Gdk/Gsk coverage; bind libspelling and libpanel 2 dagen geleden
lib 0a7848e256 feat: expressions, constraints, shortcuts, Graphene/GtkSnapshot 2 dagen geleden
src 2605deaeb3 feat(demos): demo batch 3 - choosers and canvas 2 dagen geleden
tests 2605deaeb3 feat(demos): demo batch 3 - choosers and canvas 2 dagen geleden
tools a8038bedcb feat(demos): full gtk-demo catalogue, name-based registry and browser 2 dagen geleden
.gitignore 61d3d29ae9 feat: m2 gtk-demo browser (demos/) with five ported demos 2 dagen geleden
Makefile 2605deaeb3 feat(demos): demo batch 3 - choosers and canvas 2 dagen geleden
README.md 77f7ed7f98 refactor: hand-curate the Gdk and Gsk bindings 2 dagen geleden
c-union.md 8620beada1 adding some explanantions and gir relative docs 2 dagen geleden
introspection.md 8620beada1 adding some explanantions and gir relative docs 2 dagen geleden
m2gtk4-gir-generator.zip 8620beada1 adding some explanantions and gir relative docs 2 dagen geleden

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

  • 414 binding modules across GLib, GObject, Gio, GTK4, Gdk, Gsk, libadwaita, GtkSourceView 5, libspelling and libpanel — windows, widgets, inputs, containers, model-backed views, actions/menus/accelerators, dialogs, CSS, drawing (Cairo/Pango), rich text, gestures, drag-and-drop, media, OpenGL, printing, expressions/constraints/shortcuts, settings, the complete GNOME (libadwaita) widget set — every Adw* class — the GtkSourceView 5 code-editor widgets, full Gdk/Gsk coverage, and the libspelling / libpanel platform libraries. All of these are hand-written modules; the generator output (kept in gen/, git-ignored) is the broad-coverage companion.
  • Pure-Modula-2 helpers: GtkUtils (string/number conversion, Connect, SetAccel, ownership and GError helpers) and GtkClosures (per-connection signal contexts with cleanup).
  • 45 tests, 30 runnable examples, and an m2 gtk-demo browser (demos/) modelled on GTK's own gtk4-demo.
  • 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 10256 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.
  • GtkSourceView 5 development files (pkg-config --cflags --libs gtksourceview-5, tested 5.16.0) — optional; enables the GtkSource* modules, test_sourceview and the sourceview example.
  • libspelling and libpanel development files (libspelling-1, tested 0.4.8; libpanel-1, tested 1.10.0) — optional; enable the Spelling*/Panel* modules, test_platform and the spelling example.
export PATH=$HOME/bin/Modula2/Gm2/bin:$PATH

Build and test

make                 # build all tests, examples and the demo browser 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
customlayout custom GtkLayoutManager via the C shim
gl OpenGL clearing inside a GtkGLArea
richtext Pango markup, marks and entry completion in a text view
snapshot custom GdkPaintable drawn with GtkSnapshot + Graphene
media GdkTexture in a GtkPicture, plus GtkVideo
clipboard copy/paste text through GdkClipboard
adw libadwaita preferences app (toolbar view, rows, toast, about)
adw_widgets view-switcher title, view stack, banner, squeezer, wrap box, avatar, spinner
adw_views tabbed window: AdwTabView/AdwTabBar/AdwTabButton/AdwTabOverview
sourceview GtkSourceView 5 code editor (highlighting, line numbers, style scheme)
spelling GtkSourceView 5 + libspelling spell-checking

m2 gtk-demo (demos/)

A small Modula-2 port of GTK's gtk4-demo, mirroring its structure: a browser window with a searchable sidebar of demos, where activating a row opens the demo in its own window.

File Role
demos/GtkDemo.def / .mod the demo registry (title, keywords, build function)
demos/DemoImpl.def / .mod the demo implementations
demos/m2gtkdemo.mod the browser application (GtkApplication + sidebar + search)

The demos are simplified ports of the C originals in gtk/demos/gtk-demo: Links (links.c, markup hyperlinks + activate-link), List Box (listbox.c), Flow Box (flowbox.c, colour swatches drawn with gdk_rgba_parse + gdk_cairo_set_source_rgba), Expander (expander.c) and CSS Basics (css_basics.c, live CSS editing through a GtkCssProvider). Each demo follows the gtk-demo convention — a build procedure that creates its window once, then toggles its visibility — so the registry in GtkDemo is just a table of PROCEDURE (ADDRESS) : ADDRESS.

make demos
./build/demos/m2gtkdemo

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
GtkExpression, GtkStringSorter, GtkNumericSorter, GtkStringFilter, GtkBoolFilter expression-based sorters/filters
GtkConstraintLayout, GtkConstraint, GtkConstraintGuide constraint layout
GtkShortcut, GtkShortcutController keyboard shortcuts
Graphene, GtkSnapshot geometry records and snapshot rendering
GtkMultiSelection, GtkBitset multiple selection and bitsets
GtkTreeListModel, GtkTreeListRow, GtkTreeExpander tree models and expanders
GtkDirectoryList, GtkBookmarkList directory / bookmark models
GtkDropTarget, GtkDragSource, GdkContentProvider, GdkClipboard drag-and-drop and clipboard
GdkTexture, GdkPixbuf, GtkVideo, GtkMediaFile, GtkMediaStream images and media
GtkGLArea, GdkGLContext, GL OpenGL rendering (-lGL)
GtkPrintOperation, GtkPrintSettings, GtkPageSetup, GtkPrintContext printing
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
GtkTextTag, GtkTextTagTable, GtkTextMark, GtkEntryBuffer rich-text tags, marks and entry buffer
GtkEntryCompletion entry completion (deprecated; GtkTreeModel-based)
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
GtkAssistant, GtkIconView, GtkEmojiChooser wizard, icon grid (deprecated), emoji picker
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, AdwWindow, AdwWindowTitle Adwaita application, window, title
AdwHeaderBar, AdwToolbarView header bar and toolbar view
AdwViewStack, AdwViewStackPage, AdwViewStackPages animated page stack
AdwViewSwitcher, AdwViewSwitcherBar, AdwViewSwitcherTitle, AdwInlineViewSwitcher view switchers
AdwNavigationView, AdwNavigationPage push/pop navigation
AdwNavigationSplitView, AdwOverlaySplitView adaptive split views
AdwFlap, AdwLeaflet, AdwLeafletPage fold/overlay sidebars (deprecated)
AdwBottomSheet bottom sheet with a bottom bar
AdwTabView, AdwTabPage, AdwTabBar, AdwTabButton, AdwTabOverview tabs
AdwCarousel, AdwCarouselIndicatorDots, AdwCarouselIndicatorLines carousel pagers
AdwToggle, AdwToggleGroup segmented toggles
AdwDialog, AdwAlertDialog, AdwAboutDialog dialogs
AdwPreferencesDialog, AdwPreferencesWindow preferences dialogs/windows
AdwPreferencesPage, AdwPreferencesGroup, AdwPreferencesRow preferences layout
AdwActionRow, AdwSwitchRow, AdwEntryRow, AdwComboRow, AdwExpanderRow settings rows
AdwButtonRow, AdwSpinRow, AdwPasswordEntryRow button, spin and password rows
AdwClamp, AdwClampScrollable, AdwClampLayout clamping containers/layouts
AdwSqueezer, AdwSqueezerPage show-the-largest-that-fits
AdwWrapBox, AdwWrapLayout wrapping lines
AdwBreakpoint, AdwBreakpointCondition, AdwBreakpointBin adaptive breakpoints
AdwMultiLayoutView, AdwLayout, AdwLayoutSlot multi-layout containers
AdwAnimation, AdwAnimationTarget, AdwTimedAnimation, AdwSpringAnimation, AdwSpringParams, AdwCallbackAnimationTarget, AdwPropertyAnimationTarget animations
AdwSwipeTracker, AdwStyleManager swipe tracking and Adwaita styling
AdwEnumListModel, AdwEnumListItem enum-backed list models
AdwBin, AdwSpinner, AdwSpinnerPaintable, AdwAvatar misc widgets
AdwButtonContent, AdwSplitButton rich button content
AdwStatusPage, AdwBanner status/banner
AdwToast, AdwToastOverlay transient notifications
AdwAboutWindow, AdwMessageDialog about and message dialogs
AdwEnums shared Adw enums (view switcher, animation/easing, flap/leaflet, dialog, tab shortcuts, …)

GtkSourceView 5

Module Contents
GtkSourceBuffer, GtkSourceView source buffer and view (highlighting, line numbers, marks, gutter)
GtkSourceLanguage, GtkSourceLanguageManager syntax-language definitions and lookup
GtkSourceStyleScheme, GtkSourceStyleSchemeManager, GtkSourceStyle colour schemes
GtkSourceMark, GtkSourceMarkAttributes, GtkSourceTag source marks and tags
GtkSourceGutter, GtkSourceGutterLines, GtkSourceGutterRenderer, GtkSourceGutterRendererText, GtkSourceGutterRendererPixbuf gutters and renderers
GtkSourceCompletion, GtkSourceCompletionContext, GtkSourceCompletionCell, GtkSourceCompletionProposal, GtkSourceCompletionProvider, GtkSourceCompletionWords, GtkSourceCompletionSnippets code completion
GtkSourceSnippet, GtkSourceSnippetChunk, GtkSourceSnippetContext, GtkSourceSnippetManager snippets
GtkSourceSearchContext, GtkSourceSearchSettings search and replace
GtkSourceRegion, GtkSourceRegionIter subregions of a buffer
GtkSourceFile, GtkSourceFileLoader, GtkSourceFileSaver, GtkSourceEncoding file I/O and encodings
GtkSourceMap, GtkSourceSpaceDrawer, GtkSourceStyleSchemeChooser, GtkSourceStyleSchemeChooserButton, GtkSourceStyleSchemeChooserWidget, GtkSourceStyleSchemePreview minimap, whitespace and scheme choosers
GtkSourceHover, GtkSourceHoverContext, GtkSourceHoverDisplay, GtkSourceHoverProvider hover providers
GtkSourceIndenter, GtkSourcePrintCompositor, GtkSourceVimIMContext indentation, printing and the Vim input method
GtkSourceEnums, GtkSourceFunctions shared enums and namespace functions

Gdk / Gsk (full coverage)

Modules Contents
Gdk* (~58) displays, monitors, seats, devices, events, surfaces, toplevels/popups, cursors, drag-and-drop, clips, content (de)serialization, textures and draw contexts, Cairo/GL/Vulkan contexts, frame clocks, RGBA/Rectangle value types, GdkEnums, GdkFunctions
Gsk* (~48) render nodes (container, cairo, color, text, texture, transform, shadow, …), renderers, transforms, rounded/clip/path/shadow helpers, GL shaders, GskEnums, GskFunctions

These namespace modules are hand-curated (the parallel to the rest of src/). They are complete: every Gdk*/Gsk* class/interface/record plus the namespace functions and enums.

libspelling / libpanel

Modules Contents
SpellingChecker, SpellingDictionary, SpellingLanguage, SpellingProvider, SpellingTextBufferAdapter libspelling spell-checking
PanelApplication, PanelWorkbench, PanelWorkspace, PanelDocumentWorkspace libpanel workbench/workspace
PanelFrame(+Header, HeaderBar, TabBar, Switcher), PanelGrid, PanelGridColumn, PanelPaned, PanelStatusbar, PanelOmniBar, PanelDock, PanelToggleButton panel layout and docks
PanelWidget, PanelSession, PanelSessionItem, PanelSaveDelegate, PanelSaveDialog, PanelChangesDialog documents and sessions
PanelSettings, PanelLayeredSettings, PanelGSettingsActionGroup, PanelActionMuxer, PanelMenuManager, PanelThemeSelector, PanelInhibitor settings, actions and menus
PanelEnums, PanelFunctions, SpellingFunctions enums and namespace functions

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, GdkPaintables (Cairo- and snapshot-based), a generic GListModel, and a custom GtkLayoutManager, all driven by Modula-2 callbacks

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/GtkSource/ Gdk/Gsk/Spelling/Panel, 828 generated modules parse under gm2 -fiso and all 10256 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
  demos/      the m2 gtk-demo browser (registry + demos + application)
  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.