|
|
@@ -6,8 +6,9 @@ address arithmetic, set ordering, complex types, pragmas, UTF-8 string
|
|
|
libraries, collections, the TopSpeed B-tree Toolkit, communications,
|
|
|
pyserial, ModBase, network-library design, terminal escape libraries,
|
|
|
C FFI and GTK4/SQLite/SDL bindings, an audit of the existing gm2
|
|
|
-bindings, PIM versus ISO coroutines, external assembly, and the
|
|
|
-dynamic-set design. It is not a commitment to implement everything.
|
|
|
+bindings, the GTK4 OOP facade, PIM versus ISO coroutines, external
|
|
|
+assembly, and the dynamic-set design. It is not a commitment to
|
|
|
+implement everything.
|
|
|
|
|
|
**State at the time of writing:** suite 310/310, one-shot fixpoint OK
|
|
|
(3,845,439 bytes), per-object fixpoint OK (11 objects), corpus
|
|
|
@@ -2375,7 +2376,282 @@ a small GTK4 subset using the existing closure/shim architecture.
|
|
|
|
|
|
---
|
|
|
|
|
|
-## 16. Suggested order of future work
|
|
|
+## 16. GTK4 OOP facade: limits and recommended layering
|
|
|
+
|
|
|
+V3 classes can make GTK4 application code look much more like
|
|
|
+Modula-2, but they should be an **optional high-level facade**, not the
|
|
|
+implementation mechanism for the binding itself. Directly mapping V3
|
|
|
+classes onto GObject classes is not viable: V3's Clarion-form classes
|
|
|
+and GObject's C object model have incompatible layouts and lifecycles.
|
|
|
+
|
|
|
+### 16.1 Three layers
|
|
|
+
|
|
|
+The binding should distinguish three layers:
|
|
|
+
|
|
|
+```text
|
|
|
+1. raw GTK4 C binding -- ADDRESS handles and free procedures
|
|
|
+2. typed/opaque wrapper -- Modula-2 types and module procedures
|
|
|
+3. optional OOP facade -- CLASS Window, CLASS Button, methods
|
|
|
+```
|
|
|
+
|
|
|
+The existing `m2-GTK4` is layer 1. That is the right canonical
|
|
|
+low-level layer: generated or reviewed `FOR "C"` modules with opaque
|
|
|
+`ADDRESS` handles.
|
|
|
+
|
|
|
+V3 classes belong at layer 3, for application code. A typed opaque
|
|
|
+layer can sit between them, especially where class fields would expose
|
|
|
+implementation details.
|
|
|
+
|
|
|
+### 16.2 Why V3 classes are not GObject classes
|
|
|
+
|
|
|
+V3 classes:
|
|
|
+
|
|
|
+- are records with fields;
|
|
|
+- have a hidden `THIS` receiver in methods;
|
|
|
+- have a hidden V3 vtable pointer for virtual methods;
|
|
|
+- use V3 method-call and subtype rules;
|
|
|
+- have value semantics when used as values;
|
|
|
+- have no destructors or finalizers;
|
|
|
+- support single inheritance only.
|
|
|
+
|
|
|
+GObject has:
|
|
|
+
|
|
|
+- its own `GObject` header, type system and class/instance structures;
|
|
|
+- reference counting and floating references;
|
|
|
+- signals and properties;
|
|
|
+- interfaces;
|
|
|
+- `GObjectClass`/`GtkWidgetClass` vfunc tables;
|
|
|
+- C-side construction and finalization.
|
|
|
+
|
|
|
+A V3 class instance cannot be passed to C as a `GtkWidget *`. The V3
|
|
|
+wrapper and the GTK object must remain separate:
|
|
|
+
|
|
|
+```modula2
|
|
|
+CLASS Window;
|
|
|
+ handle : ADDRESS; (* the real GtkWindow *, not the V3 object *)
|
|
|
+ ...
|
|
|
+END Window;
|
|
|
+```
|
|
|
+
|
|
|
+C never sees the V3 object. It receives only the handle stored inside
|
|
|
+it. Direct GObject subclassing still requires C shims such as the
|
|
|
+existing `m2gtkshim.c`.
|
|
|
+
|
|
|
+### 16.3 Ownership and reference counting
|
|
|
+
|
|
|
+A V3 class copy copies its fields. If a wrapper contains a handle and
|
|
|
+an owner flag, copying the wrapper can create two apparent owners of
|
|
|
+the same GObject and lead to a double `Unref`.
|
|
|
+
|
|
|
+Use pointer wrappers and explicit lifetime operations:
|
|
|
+
|
|
|
+```modula2
|
|
|
+TYPE
|
|
|
+ CLASS Object;
|
|
|
+ handle : ADDRESS;
|
|
|
+ owned : BOOLEAN;
|
|
|
+ PROCEDURE Ref;
|
|
|
+ PROCEDURE Unref;
|
|
|
+ END Object;
|
|
|
+
|
|
|
+ CLASS Widget (Object);
|
|
|
+ PROCEDURE SetVisible (on : BOOLEAN);
|
|
|
+ END Widget;
|
|
|
+
|
|
|
+ CLASS Window (Widget);
|
|
|
+ PROCEDURE SetTitle (s : ARRAY OF CHAR);
|
|
|
+ PROCEDURE Present;
|
|
|
+ END Window;
|
|
|
+
|
|
|
+ WindowPtr = POINTER TO Window;
|
|
|
+```
|
|
|
+
|
|
|
+Constructors should return pointer wrappers, for example
|
|
|
+`NewWindow (app) : WindowPtr`.
|
|
|
+
|
|
|
+Rules:
|
|
|
+
|
|
|
+- distinguish owned from borrowed handles;
|
|
|
+- sink floating references where necessary;
|
|
|
+- `DISPOSE` frees only the V3 wrapper, never the GObject;
|
|
|
+- call `Unref`/`Close` explicitly before disposing the wrapper;
|
|
|
+- do not rely on V3 `BEGIN ... END` as a per-object constructor: a
|
|
|
+ class initialisation body runs once at startup, not for each object;
|
|
|
+- keep wrapper objects on the main GTK thread.
|
|
|
+
|
|
|
+V3 has no destructors, so ownership must be documented and tested, not
|
|
|
+inferred from scope exit.
|
|
|
+
|
|
|
+### 16.4 Encapsulation
|
|
|
+
|
|
|
+V3 classes have no private fields. A class declared in a definition
|
|
|
+module exposes its fields to clients, including the GTK handle and owner
|
|
|
+flag. This weakens the abstraction.
|
|
|
+
|
|
|
+Options:
|
|
|
+
|
|
|
+1. accept the exposed handle and document it as an internal field;
|
|
|
+2. use an opaque handle type plus module procedures for the most
|
|
|
+ sensitive objects;
|
|
|
+3. add private class fields or opaque classes to V3 later.
|
|
|
+
|
|
|
+A true private-field facility is compiler work and should not block the
|
|
|
+first GTK4 facade. For a small facade, exposing `handle` as an
|
|
|
+implementation detail may be acceptable; for a public library API, an
|
|
|
+opaque wrapper is cleaner.
|
|
|
+
|
|
|
+### 16.5 Inheritance and GTK interfaces
|
|
|
+
|
|
|
+V3 single inheritance can model GTK's primary class chain:
|
|
|
+
|
|
|
+```text
|
|
|
+GObject -> GtkWidget -> GtkWindow -> GtkApplicationWindow
|
|
|
+GObject -> GtkWidget -> GtkButton
|
|
|
+GObject -> GtkWidget -> GtkBox
|
|
|
+```
|
|
|
+
|
|
|
+It cannot model GTK's interfaces. GTK and Gio use interfaces such as
|
|
|
+`GtkAccessible`, `GtkBuildable`, `GtkNative`, `GtkRoot`,
|
|
|
+`GtkShortcutManager`, `GListModel` and `GtkTreeModel`.
|
|
|
+
|
|
|
+Do not encode interfaces as extra V3 parents: V3 rejects multiple
|
|
|
+parents. Provide interface capabilities as separate modules or
|
|
|
+free-function groups:
|
|
|
+
|
|
|
+```text
|
|
|
+Accessible.SetLabel
|
|
|
+Buildable.AddFromString
|
|
|
+Native.GetSurface
|
|
|
+```
|
|
|
+
|
|
|
+Only the main widget chain should use class inheritance. Services and
|
|
|
+non-widget objects such as `GtkApplication`, `GtkBuilder`, models and
|
|
|
+file dialogs can use their own wrapper roots or opaque handles.
|
|
|
+
|
|
|
+### 16.6 Constructors and properties
|
|
|
+
|
|
|
+GTK construction is C-style:
|
|
|
+
|
|
|
+```c
|
|
|
+gtk_application_window_new (app)
|
|
|
+g_object_new (type, "property", value, ...)
|
|
|
+```
|
|
|
+
|
|
|
+V3 has no named-property arguments or general varargs. Constructors in
|
|
|
+the facade should be ordinary module procedures:
|
|
|
+
|
|
|
+```modula2
|
|
|
+PROCEDURE NewWindow (app : Application) : WindowPtr;
|
|
|
+PROCEDURE NewButton (label : ARRAY OF CHAR) : ButtonPtr;
|
|
|
+```
|
|
|
+
|
|
|
+Property-heavy construction should use C wrappers or
|
|
|
+`g_object_new_with_properties`, not an invented V3 constructor syntax.
|
|
|
+Keep `gtk_*_new` and `g_object_new` wrappers in the raw binding; the
|
|
|
+OOP facade should call them rather than duplicate them.
|
|
|
+
|
|
|
+### 16.7 Signals and callbacks
|
|
|
+
|
|
|
+Do not pass a V3 instance method directly to `g_signal_connect_data`.
|
|
|
+A V3 method has a hidden receiver and is not a C function pointer with
|
|
|
+GObject's callback ABI.
|
|
|
+
|
|
|
+Use the existing `GtkClosures` pattern:
|
|
|
+
|
|
|
+- one top-level trampoline per callback signature;
|
|
|
+- a wrapper/context pointer as `gpointer` user data;
|
|
|
+- an explicit destroy handler;
|
|
|
+- optional V3 virtual dispatch inside the trampoline.
|
|
|
+
|
|
|
+For example, a clicked handler can receive the wrapper pointer as user
|
|
|
+data, cast it back to `ButtonPtr`, and invoke a V3 method or virtual
|
|
|
+method. The trampoline must still be a top-level procedure with the C
|
|
|
+callback signature.
|
|
|
+
|
|
|
+The same rule applies to `GtkApplication` activate/startup handlers,
|
|
|
+`GtkDrawingArea` draw functions and model callbacks.
|
|
|
+
|
|
|
+### 16.8 GObject subclassing
|
|
|
+
|
|
|
+Custom widgets, custom `GdkPaintable` implementations, custom
|
|
|
+`GtkLayoutManager`s and custom `GListModel`s still require C:
|
|
|
+
|
|
|
+- register the GObject type;
|
|
|
+- provide `class_init`/`instance_init`;
|
|
|
+- override vfuncs;
|
|
|
+- handle finalization and properties.
|
|
|
+
|
|
|
+V3 classes cannot do this. Keep `m2gtkshim.c` for subclassing. The
|
|
|
+OOP facade can wrap the resulting C object afterward, but it cannot
|
|
|
+replace the shim.
|
|
|
+
|
|
|
+### 16.9 Recommended package layout
|
|
|
+
|
|
|
+```text
|
|
|
+bindings/gtk4/raw/ FOR "C" modules, ADDRESS handles
|
|
|
+bindings/gtk4/wrap/ optional typed wrappers / opaque types
|
|
|
+bindings/gtk4/oop/ optional CLASS facade
|
|
|
+bindings/gtk4/shim/ m2gtkshim.c, GtkClosures
|
|
|
+```
|
|
|
+
|
|
|
+The raw binding remains canonical. The OOP facade should cover only a
|
|
|
+small, useful application subset:
|
|
|
+
|
|
|
+```text
|
|
|
+Object, Widget, Window, ApplicationWindow,
|
|
|
+Button, Label, Box, Entry, ListBox, DrawingArea,
|
|
|
+Application, Builder
|
|
|
+```
|
|
|
+
|
|
|
+Do not create a parallel class hierarchy for all 432 GTK/GDK/Gsk/Gio/
|
|
|
+Adw modules. Keep uncommon APIs at the raw or typed-wrapper layer.
|
|
|
+The GIR generator can help produce raw modules; the OOP facade should
|
|
|
+be hand-curated because ownership and API shape need judgement.
|
|
|
+
|
|
|
+### 16.10 Is it more "Modula-2"?
|
|
|
+
|
|
|
+Not necessarily. Classic Modula-2 style is primarily:
|
|
|
+
|
|
|
+- modules as namespaces;
|
|
|
+- opaque types;
|
|
|
+- explicit procedures;
|
|
|
+- strong typing;
|
|
|
+- explicit ownership.
|
|
|
+
|
|
|
+V3 `CLASS` is a Clarion-form extension, not ISO Modula-2 OO. An OOP
|
|
|
+facade can be nicer for application code, but it is not automatically
|
|
|
+more standard. The most idiomatic design is probably:
|
|
|
+
|
|
|
+```text
|
|
|
+opaque handle types + module procedures
|
|
|
+```
|
|
|
+
|
|
|
+with classes as an optional convenience layer for users who prefer:
|
|
|
+
|
|
|
+```modula2
|
|
|
+win^.SetTitle("Demo");
|
|
|
+btn^.OnClicked(OnQuit);
|
|
|
+```
|
|
|
+
|
|
|
+### 16.11 Bottom line
|
|
|
+
|
|
|
+- Use V3 OOP for a high-level GTK4 facade, not for the raw binding.
|
|
|
+- Keep `ADDRESS`-based `FOR "C"` modules as the canonical layer.
|
|
|
+- Use `POINTER TO CLASS` wrappers holding a C handle and ownership flag.
|
|
|
+- Add explicit `Ref`/`Unref`/`Close`; do not rely on `DISPOSE`.
|
|
|
+- Model only GTK's primary single-inheritance chain with classes.
|
|
|
+- Keep GTK interfaces as separate module capabilities.
|
|
|
+- Use `GtkClosures`/top-level trampolines for signals; never pass V3
|
|
|
+ methods directly to C.
|
|
|
+- Keep `m2gtkshim.c` for GObject subclassing.
|
|
|
+- Wrap a small common subset first; do not mirror all 432 modules.
|
|
|
+
|
|
|
+This gives a more Modula-2 application API without pretending that V3
|
|
|
+classes are GObject classes.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 17. Suggested order of future work
|
|
|
|
|
|
1. `TBITSIZE` (small, standard-facing).
|
|
|
2. `ADDADR`/`SUBADR`/`DIFADR` (small, ISO SYSTEM).
|
|
|
@@ -2395,14 +2671,18 @@ a small GTK4 subset using the existing closure/shim architecture.
|
|
|
pkg-config; SQLite, then SDL3, then GTK4.
|
|
|
10. Port the existing gm2 bindings: SQLite first, SDL3 second, GTK4
|
|
|
subset last; keep them as separate `bindings/` packages.
|
|
|
-11. Communications: `Serial`/`Transport` first; protocols and terminal
|
|
|
+11. Add an optional GTK4 OOP facade over the raw binding: small
|
|
|
+ `Object`/`Widget` hierarchy, pointer wrappers, explicit
|
|
|
+ `Ref`/`Unref`/`Close`, `GtkClosures` trampolines; no direct GObject
|
|
|
+ subclassing.
|
|
|
+12. Communications: `Serial`/`Transport` first; protocols and terminal
|
|
|
emulation optional.
|
|
|
-12. Network: `Net`/`Transport`, TCP/UDP, `Poll`, then optional TLS/HTTP.
|
|
|
-13. Database: SQLite binding or typed B-tree table layer; dBase III
|
|
|
+13. Network: `Net`/`Transport`, TCP/UDP, `Poll`, then optional TLS/HTTP.
|
|
|
+14. Database: SQLite binding or typed B-tree table layer; dBase III
|
|
|
only if required.
|
|
|
-14. ISO `COROUTINES` subset, then interrupt/protection layer.
|
|
|
-15. Dynamic `SET CONST` and deep composite copies.
|
|
|
-16. Parser error recovery and diagnostics.
|
|
|
+15. ISO `COROUTINES` subset, then interrupt/protection layer.
|
|
|
+16. Dynamic `SET CONST` and deep composite copies.
|
|
|
+17. Parser error recovery and diagnostics.
|
|
|
|
|
|
External assembly needs no compiler work; document it as the supported
|
|
|
low-level escape hatch.
|