aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/apprt/gtk/class.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/apprt/gtk/class.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/apprt/gtk/class.zig')
-rw-r--r--vendor/libghostty-vt/src/apprt/gtk/class.zig367
1 files changed, 367 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/apprt/gtk/class.zig b/vendor/libghostty-vt/src/apprt/gtk/class.zig
new file mode 100644
index 00000000..942666cf
--- /dev/null
+++ b/vendor/libghostty-vt/src/apprt/gtk/class.zig
@@ -0,0 +1,367 @@
+//! This files contains all the GObject classes for the GTK apprt
+//! along with helpers to work with them.
+
+const std = @import("std");
+const glib = @import("glib");
+const gobject = @import("gobject");
+const gtk = @import("gtk");
+
+const ext = @import("ext.zig");
+pub const Application = @import("class/application.zig").Application;
+pub const Window = @import("class/window.zig").Window;
+pub const Config = @import("class/config.zig").Config;
+pub const Surface = @import("class/surface.zig").Surface;
+
+/// Common methods for all GObject classes we create.
+pub fn Common(
+ comptime Self: type,
+ comptime Private: ?type,
+) type {
+ return struct {
+ /// Upcast our type to a parent type or interface. This will fail at
+ /// compile time if the cast isn't 100% safe. For unsafe casts,
+ /// use `gobject.ext.cast` instead. We don't have a helper for that
+ /// because its uncommon and unsafe behavior should be noisier.
+ pub fn as(self: *Self, comptime T: type) *T {
+ return gobject.ext.as(T, self);
+ }
+
+ /// Increase the reference count of the object.
+ pub fn ref(self: *Self) *Self {
+ return @ptrCast(@alignCast(gobject.Object.ref(self.as(gobject.Object))));
+ }
+
+ /// If the reference count is 1 and the object is floating, clear the
+ /// floating attribute. Otherwise, increase the reference count by 1.
+ pub fn refSink(self: *Self) *Self {
+ return @ptrCast(@alignCast(gobject.Object.refSink(self.as(gobject.Object))));
+ }
+
+ /// Decrease the reference count of the object.
+ pub fn unref(self: *Self) void {
+ gobject.Object.unref(self.as(gobject.Object));
+ }
+
+ /// Access the private data of the object. This should be forwarded
+ /// via a non-pub const usually.
+ pub const private = if (Private) |P| (struct {
+ fn private(self: *Self) *P {
+ return gobject.ext.impl_helpers.getPrivate(
+ self,
+ P,
+ P.offset,
+ );
+ }
+ }).private else {};
+
+ /// Get the class for the object.
+ ///
+ /// This _seems_ ugly and unsafe but this is how GObject
+ /// works under the hood. From the [GObject Type System
+ /// Concepts](https://docs.gtk.org/gobject/concepts.html) documentation:
+ ///
+ /// Every object must define two structures: its class structure
+ /// and its instance structure. All class structures must contain
+ /// as first member a GTypeClass structure. All instance structures
+ /// must contain as first member a GTypeInstance structure.
+ /// …
+ /// These constraints allow the type system to make sure that
+ /// every object instance (identified by a pointer to the object’s
+ /// instance structure) contains in its first bytes a pointer to the
+ /// object’s class structure.
+ /// …
+ /// The C standard mandates that the first field of a C structure is
+ /// stored starting in the first byte of the buffer used to hold the
+ /// structure’s fields in memory. This means that the first field of
+ /// an instance of an object B is A’s first field which in turn is
+ /// GTypeInstance‘s first field which in turn is g_class, a pointer
+ /// to B’s class structure.
+ ///
+ /// This means that to access the class structure for an object you cast it
+ /// to `*gobject.TypeInstance` and then access the `f_g_class` field.
+ ///
+ /// https://gitlab.gnome.org/GNOME/glib/-/blob/2c08654b62d52a31c4e4d13d7d85e12b989e72be/gobject/gtype.h#L555-571
+ /// https://gitlab.gnome.org/GNOME/glib/-/blob/2c08654b62d52a31c4e4d13d7d85e12b989e72be/gobject/gtype.h#L2673
+ ///
+ pub fn getClass(self: *Self) ?*Self.Class {
+ const type_instance: *gobject.TypeInstance = @ptrCast(self);
+ return @ptrCast(type_instance.f_g_class orelse return null);
+ }
+
+ /// Define a virtual method. The `Self.Class` type must have a field
+ /// named `name` which is a function pointer in the following form:
+ ///
+ /// ?*const fn (*Self) callconv(.c) void
+ ///
+ /// The virtual method may take additional parameters and specify
+ /// a non-void return type. The parameters and return type must be
+ /// valid for the C calling convention.
+ pub fn defineVirtualMethod(
+ comptime name: [:0]const u8,
+ ) type {
+ return struct {
+ pub fn call(
+ class: anytype,
+ object: *ClassInstance(@TypeOf(class)),
+ params: anytype,
+ ) (fn_info.return_type orelse void) {
+ const func = @field(
+ gobject.ext.as(Self.Class, class),
+ name,
+ ).?;
+ @call(.auto, func, .{
+ gobject.ext.as(Self, object),
+ } ++ params);
+ }
+
+ pub fn implement(
+ class: anytype,
+ implementation: *const ImplementFunc(@TypeOf(class)),
+ ) void {
+ @field(gobject.ext.as(
+ Self.Class,
+ class,
+ ), name) = @ptrCast(implementation);
+ }
+
+ /// The type info of the virtual method.
+ const fn_info = fn_info: {
+ // This is broken down like this so its slightly more
+ // readable. We expect a field named "name" on the Class
+ // with the rough type of `?*const fn` and we need the
+ // function info.
+ const Field = @FieldType(Self.Class, name);
+ const opt = @typeInfo(Field).optional;
+ const ptr = @typeInfo(opt.child).pointer;
+ break :fn_info @typeInfo(ptr.child).@"fn";
+ };
+
+ /// The instance type for a class.
+ fn ClassInstance(comptime T: type) type {
+ return @typeInfo(T).pointer.child.Instance;
+ }
+
+ /// The function type for implementations. This is the same type
+ /// as the virtual method but the self parameter points to the
+ /// target instead of the original class.
+ fn ImplementFunc(comptime T: type) type {
+ var params: [fn_info.params.len]std.builtin.Type.Fn.Param = undefined;
+ @memcpy(&params, fn_info.params);
+ params[0].type = *ClassInstance(T);
+ return @Type(.{ .@"fn" = .{
+ .calling_convention = fn_info.calling_convention,
+ .is_generic = fn_info.is_generic,
+ .is_var_args = fn_info.is_var_args,
+ .return_type = fn_info.return_type,
+ .params = &params,
+ } });
+ }
+ };
+ }
+
+ /// A helper that creates a property that reads and writes a
+ /// private field with only shallow copies. This is good for primitives
+ /// such as bools, numbers, etc.
+ pub fn privateShallowFieldAccessor(
+ comptime name: []const u8,
+ ) gobject.ext.Accessor(
+ Self,
+ @FieldType(Private.?, name),
+ ) {
+ return gobject.ext.privateFieldAccessor(
+ Self,
+ Private.?,
+ &Private.?.offset,
+ name,
+ );
+ }
+
+ /// A helper that can be used to create a property that reads and
+ /// writes a private boxed gobject field type.
+ ///
+ /// Reading the property will result in allocating a pointer and
+ /// setting it will free the previous pointer.
+ ///
+ /// The object class (Self) must still free the private field
+ /// in finalize!
+ pub fn privateBoxedFieldAccessor(
+ comptime name: []const u8,
+ ) gobject.ext.Accessor(
+ Self,
+ @FieldType(Private.?, name),
+ ) {
+ return .{
+ .getter = &struct {
+ fn get(self: *Self, value: *gobject.Value) void {
+ gobject.ext.Value.set(
+ value,
+ @field(private(self), name),
+ );
+ }
+ }.get,
+ .setter = &struct {
+ fn set(self: *Self, value: *const gobject.Value) void {
+ const priv = private(self);
+ if (@field(priv, name)) |v| {
+ ext.boxedFree(
+ @typeInfo(@TypeOf(v)).pointer.child,
+ v,
+ );
+ }
+
+ const T = @TypeOf(@field(priv, name));
+ @field(
+ priv,
+ name,
+ ) = gobject.ext.Value.dup(value, T);
+ }
+ }.set,
+ };
+ }
+
+ /// A helper that can be used to create a property that reads and
+ /// writes a private field gobject field type (reference counted).
+ ///
+ /// Reading the property will result in taking a reference to the
+ /// value and writing the property will unref the previous value.
+ ///
+ /// The object class (Self) must still free the private field
+ /// in finalize!
+ pub fn privateObjFieldAccessor(
+ comptime name: []const u8,
+ ) gobject.ext.Accessor(
+ Self,
+ @FieldType(Private.?, name),
+ ) {
+ return .{
+ .getter = &struct {
+ fn get(self: *Self, value: *gobject.Value) void {
+ gobject.ext.Value.set(
+ value,
+ @field(private(self), name),
+ );
+ }
+ }.get,
+ .setter = &struct {
+ fn set(self: *Self, value: *const gobject.Value) void {
+ const priv = private(self);
+ if (@field(priv, name)) |v| v.unref();
+
+ const T = @TypeOf(@field(priv, name));
+ @field(
+ priv,
+ name,
+ ) = gobject.ext.Value.dup(value, T);
+ }
+ }.set,
+ };
+ }
+
+ /// A helper that can be used to create a property that reads and
+ /// writes a private `?[:0]const u8` field type.
+ ///
+ /// Reading the property will result in a copy of the string
+ /// and callers are responsible for freeing it.
+ ///
+ /// Writing the property will free the previous value and copy
+ /// the new value into the private field.
+ ///
+ /// The object class (Self) must still free the private field
+ /// in finalize!
+ pub fn privateStringFieldAccessor(
+ comptime name: []const u8,
+ ) gobject.ext.Accessor(
+ Self,
+ @FieldType(Private.?, name),
+ ) {
+ const S = struct {
+ fn getter(self: *Self) ?[:0]const u8 {
+ return @field(private(self), name);
+ }
+
+ fn setter(self: *Self, value: ?[:0]const u8) void {
+ const priv = private(self);
+ if (@field(priv, name)) |v| {
+ glib.free(@ptrCast(@constCast(v)));
+ }
+
+ // We don't need to copy this because it was already
+ // copied by the typedAccessor.
+ @field(priv, name) = value;
+ }
+ };
+
+ return gobject.ext.typedAccessor(
+ Self,
+ ?[:0]const u8,
+ .{
+ .getter = S.getter,
+ .getter_transfer = .none,
+ .setter = S.setter,
+ .setter_transfer = .full,
+ },
+ );
+ }
+
+ /// Common class functions.
+ pub const Class = struct {
+ pub fn as(class: *Self.Class, comptime T: type) *T {
+ return gobject.ext.as(T, class);
+ }
+
+ /// Bind a template child to a private entry in the class.
+ pub const bindTemplateChildPrivate = if (Private) |P| (struct {
+ pub fn bindTemplateChildPrivate(
+ class: *Self.Class,
+ comptime name: [:0]const u8,
+ comptime options: gtk.ext.BindTemplateChildOptions,
+ ) void {
+ gtk.ext.impl_helpers.bindTemplateChildPrivate(
+ class,
+ name,
+ P,
+ P.offset,
+ options,
+ );
+ }
+ }).bindTemplateChildPrivate else {};
+
+ /// Bind a function pointer to a template callback symbol.
+ pub fn bindTemplateCallback(
+ class: *Self.Class,
+ comptime name: [:0]const u8,
+ comptime func: anytype,
+ ) void {
+ {
+ const ptr_ti = @typeInfo(@TypeOf(func));
+ if (ptr_ti != .pointer) {
+ @compileError("bound function must be a pointer type");
+ }
+ if (ptr_ti.pointer.size != .one) {
+ @compileError("bound function must be a pointer to a function");
+ }
+
+ const func_ti = @typeInfo(ptr_ti.pointer.child);
+ if (func_ti != .@"fn") {
+ @compileError("bound function must be a function pointer");
+ }
+ if (func_ti.@"fn".return_type == bool) {
+ // glib booleans are ints and returning a Zig bool type
+ // I think uses a byte and causes ABI issues.
+ @compileError("bound function must return c_int instead of bool");
+ }
+ }
+
+ gtk.Widget.Class.bindTemplateCallbackFull(
+ class.as(gtk.Widget.Class),
+ name,
+ @ptrCast(func),
+ );
+ }
+ };
+ };
+}
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}