What's new?
A few things have progressed since the preview1 release of ocgtk.
Signals
Full support for signals is now in progress. Signals allow GTK components to notify your code when something happens, such as a button click or a window being closed.
ocgtk already had basic GObject signal and closures1 support, but it was only available for signals that took no parameters.
With the latest updates in the signals branch, ocgtk now supports signals that take parameters and return values in any of the types already supported by ocgtk.
Expanded GValue support
Part of supporting signals correctly was expanding the set of types that could be marshalled and unmarshalled in the GValue type.
GValue is a box type that can carry primitive types (integers, strings, etc), vector types (like lists or arrays) as well as full GObject instances. They're particularly useful to language bindings because they help avoid a lot of the "glue" code needed to be generated (allowing marshalling between C and the host language to be handled generically at runtime).
Expanded integer and floating point type support
Although not strictly required for signals support or full GValue support, a number of interfaces reference specific integer types like gint8 or guint16. OCaml doesn't have a direct mapping for these, so new types were defined that store integers internally as an OCaml 63bit integer.
module UInt32 : sig
(** Unsigned 32-bit integer ([guint32]).
Backed by [private int] — no heap allocation. Range: [0, 4294967295].
Requires a 64-bit OCaml runtime. ocgtk does not support 32-bit targets. *)
type t = private int
val of_int : int -> t
(** [of_int n] converts [n] to a [UInt32.t].
@raise Invalid_argument if [n < 0 || n > 4294967295]. *)
val to_int : t -> int
(** [to_int v] returns the underlying integer value. *)
end
These wrappers use abstract types with strict converters to ensure that the integer value passed in fits within the bounds of the type.
Generator split from ocgtk
gir_gen is the internal utility that converts GObject Introspection (GIR) XML files into OCaml and C code that binds against a GObject library. It was originally just an executable within the ocgtk dune project, but it has been moved to its own top-level dune project within the ocgtk repository.
The last release was quite painful under the old code layout as it meant that all of gir_gen's dependencies needed to be installed in order for opam to build and test ocgtk, even though they were ultimately unused by the ocgtk library. Splitting them up now means that ocgtk can be released with only one dependency (ocaml-integers).
A major benefit of this change is that we can also relax the minimum OCaml version requirement for ocgtk to at least the 5.x series, as gir_gen has a hard dependency on a recent ppxlib version that requires ocaml >= 5.3.0.
It's also made it possible to get ocgtk building on Windows using the mingw port (unfortunately there will be no release for Windows yet).2
overrides mechanism
Another problem that cropped up in some of the signals work and in the last release was known inconsistencies in what is declared in the GObject Introspection (GIR) files for various libraries like GTK or GDK, and what is actually supported by each library. Complicating that is missing version annotations for some constructs, and certain classes and functions that are only supported on certain operating systems (that aren't documented in the GIR).
Similar to the Rust implementation of its GTK bindings, ocgtk now has an overrides mechanism that allows you to specify overrides for a particular construct (class, record, interface, enumeration or bitfield) or part of a construct (method, field):3
- minimum version of a library required
- supported (or unsupported) platforms
- ignore the construct altogether
- (soon) whether get or set accessors are supported for record fields
ocgtk is distributed with pre-generated bindings, but those are usually for a specific version of the GIR for a library. In order to allow it to compile for older versions of those libraries, the C code uses preprocessor definitions to create stubs when there is a definiton for a method that is only available on newer versions than what is installed on your computer (or if your OS doesn't support the method).
For example, GIO has a whole set of classes like UnixInputStream and UnixMountPoint that are only available on Linux or MacOS. The C code for those classes will simply throw an exception when compiled on Windows.
The OCaml code for those classes will still exist in full - I chose not to use a preprocessor to simplify its compilation and any libraries that depend on it (you can do version detection at runtime for libraries generated with ocgtk).
What's coming up
In addition to signals support, I'm also:
- working on improving the architecture documentation. This is currently sparse, and has been hampered by a lack of real architectural consideration earlier on, but there is a clear "3-layered" architecture now in the code and code generation that is worth documenting (not to mention the numerous edge cases we try and handle due to idiosyncracies in GIR)
- building out support for record field accessors - this is rather complicated due to GIR's ability to support a wide range of exotic C constructs in language bindings, like NULL-terminated and fixed size arrays, GLib types like SList and List, etc.)
- planning fully cross-referenced API documentation generation. This is a tricky problem as GIR's built in format for generating links between types and methods needs to be translated into the specific format required by odoc - we will need to build internal symbol tables and use those when translating types (and make them supported across namespaces).
- filling out support for more GLib types that are used in GTK and GIO (e.g. GArray, GHashTable, etc.) - each one requires special handling, and we're getting to the long tail of seldom used but completely different types that need to be mapped to an OCaml equivalent
ahrefs has been kind enough to provide a grant to help fund the development of ocgtk toward a version 1.0 release. ocgtk fills a gap in the OCaml ecosystem for user interface library bindings that has been somewhat lacking until recently, so my hope is to provide support for GTK which is at least on par with other popular languages like Python or Rust. I'm grateful for their support and investment in OCaml, especially for a small language ecosystem like OCaml.
Closures are the GObject way of enabling C and bindings to other languages to receive events from C-based GObject library code, like GTK or GDK. They are preferred over pure callbacks as they carry enough runtime type information (through GValue types) to allow language bindings to create glue without needing specific code generated for each signal type. ↩
Windows is a complex beast. I can't get ocgtk compiling with the cygwin port as it lacks precompiled GTK binaries (and it's a nightmare to try and build them myself). The mingw port is more promising as it has precompiled GTK binaries, but I'm also unable to release it due to outstanding bugs in ocamlfind (which prevent many OCaml libraries from being built). There are patches. ↩
It also needs to support transitive dependency overrides: some libraries only introduce a field or method in a newer version, that when referred to in a C binding for a library that depends on it, will fail to compile if the newer version of the transitive dependency is not present on the OS. For example, the GTK
TextTag.text-transformproperty bindings will only be generated if the Pango dependency is version 1.5.0 or higher, becausetext-transformrefers to types in Pango that only appear in this newer version. ↩
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.