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.
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.GtkUtils (string/number conversion,
Connect, SetAccel, ownership and GError helpers) and
GtkClosures (per-connection signal contexts with cleanup).demos/) modelled on GTK's own gtk4-demo.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.gm2 (tested: 16.0.1 experimental)pkg-config --cflags --libs gtk4, tested 4.18.6)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.pkg-config --cflags --libs gtksourceview-5, tested 5.16.0) —
optional; enables the GtkSource* modules, test_sourceview and
the sourceview example.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
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
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)
| 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 |
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 | 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 |
| 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 |
| 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 |
| 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, …) |
| 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 |
| 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.
| 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 |
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 |
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:
GtkUtils.Unref (g_object_unref);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.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.GSourceFunc = PROCEDURE (ADDRESS) : INTEGER;.: [ T ]; otherwise ignoring it is an
error.REAL is C double; SHORTREAL is float; avoid LONGREAL.ADR(proc) where the parameter is ADDRESS/GCallback.ARRAY OF CHAR maps to char *; VAR parameters map to T *; opaque
handles travel as ADDRESS.*) and (* (write void ptr, not void *).GtkTextIter,
80 bytes) can be modelled as ARRAY [0..N] OF ADDRESS.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.
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)
No license has been chosen yet; there is no LICENSE file in this
repository.