aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.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/ipc/DBus.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig')
-rw-r--r--vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig196
1 files changed, 196 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig b/vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig
new file mode 100644
index 00000000..fa4a6723
--- /dev/null
+++ b/vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig
@@ -0,0 +1,196 @@
+//! DBus helper for IPC
+const Self = @This();
+
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+
+const gio = @import("gio");
+const glib = @import("glib");
+
+const apprt = @import("../../../apprt.zig");
+const ApprtApp = @import("../App.zig");
+
+/// The target for this IPC.
+target: apprt.ipc.Target,
+
+/// Connection to the DBus session bus.
+dbus: *gio.DBusConnection,
+
+/// The bus name of the Ghostty instance that we are calling.
+bus_name: [:0]const u8,
+
+/// The object path of the Ghostty instance that we are calling.
+object_path: [:0]const u8,
+
+/// Used to build the DBus payload.
+payload_builder: *glib.VariantBuilder,
+
+/// Used to build the parameters for the IPC.
+parameters_builder: *glib.VariantBuilder,
+
+/// Initialize the helper.
+pub fn init(alloc: Allocator, target: apprt.ipc.Target, action: [:0]const u8) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!Self {
+ var buf: [256]u8 = undefined;
+ var stderr_writer = std.fs.File.stderr().writer(&buf);
+ const stderr = &stderr_writer.interface;
+
+ // Get the appropriate bus name and object path for contacting the
+ // Ghostty instance we're interested in.
+ const bus_name: [:0]const u8, const object_path: [:0]const u8 = switch (target) {
+ .class => |class| result: {
+ // Force the usage of the class specified on the CLI to determine the
+ // bus name and object path.
+ const object_path = try std.fmt.allocPrintSentinel(alloc, "/{s}", .{class}, 0);
+
+ std.mem.replaceScalar(u8, object_path, '.', '/');
+ std.mem.replaceScalar(u8, object_path, '-', '_');
+
+ break :result .{ class, object_path };
+ },
+ .detect => .{ ApprtApp.application_id, ApprtApp.object_path },
+ };
+ errdefer {
+ switch (target) {
+ .class => alloc.free(object_path),
+ .detect => {},
+ }
+ }
+
+ if (gio.Application.idIsValid(bus_name.ptr) == 0) {
+ try stderr.print("D-Bus bus name is not valid: {s}\n", .{bus_name});
+ try stderr.flush();
+ return error.IPCFailed;
+ }
+
+ if (glib.Variant.isObjectPath(object_path.ptr) == 0) {
+ try stderr.print("D-Bus object path is not valid: {s}\n", .{object_path});
+ try stderr.flush();
+ return error.IPCFailed;
+ }
+
+ // Get a connection to the DBus session bus.
+ const dbus = dbus: {
+ var err_: ?*glib.Error = null;
+ defer if (err_) |err| err.free();
+
+ const dbus_ = gio.busGetSync(.session, null, &err_);
+ if (err_) |err| {
+ try stderr.print(
+ "Unable to establish connection to D-Bus session bus: {s}\n",
+ .{err.f_message orelse "(unknown)"},
+ );
+ try stderr.flush();
+ return error.IPCFailed;
+ }
+
+ break :dbus dbus_ orelse {
+ try stderr.print("gio.busGetSync returned null\n", .{});
+ try stderr.flush();
+ return error.IPCFailed;
+ };
+ };
+
+ // Set up the payload builder.
+ const payload_variant_type = glib.VariantType.new("(sava{sv})");
+ defer glib.free(payload_variant_type);
+
+ const payload_builder = glib.VariantBuilder.new(payload_variant_type);
+
+ // Add the action name to the payload.
+ {
+ const s_variant_type = glib.VariantType.new("s");
+ defer s_variant_type.free();
+
+ const bytes = glib.Bytes.new(action.ptr, action.len + 1);
+ defer bytes.unref();
+ const value = glib.Variant.newFromBytes(s_variant_type, bytes, @intFromBool(true));
+
+ payload_builder.addValue(value);
+ }
+
+ // Set up the parameter builder.
+ const parameters_variant_type = glib.VariantType.new("av");
+ defer parameters_variant_type.free();
+
+ const parameters_builder = glib.VariantBuilder.new(parameters_variant_type);
+
+ return .{
+ .target = target,
+ .dbus = dbus,
+ .bus_name = bus_name,
+ .object_path = object_path,
+ .payload_builder = payload_builder,
+ .parameters_builder = parameters_builder,
+ };
+}
+
+/// Add a parameter to the IPC call.
+pub fn addParameter(self: *Self, variant: *glib.Variant) void {
+ self.parameters_builder.add("v", variant);
+}
+
+/// Send the IPC to the remote Ghostty. Once it completes, nothing further
+/// should be done with this object other than call `deinit`.
+pub fn send(self: *Self) (std.Io.Writer.Error || apprt.ipc.Errors)!void {
+ var buf: [256]u8 = undefined;
+ var stderr_writer = std.fs.File.stderr().writer(&buf);
+ const stderr = &stderr_writer.interface;
+
+ // finish building the parameters
+ const parameters = self.parameters_builder.end();
+
+ // Add the parameters to the payload.
+ self.payload_builder.addValue(parameters);
+
+ // Add the platform data to the payload.
+ {
+ const platform_data_variant_type = glib.VariantType.new("a{sv}");
+ defer platform_data_variant_type.free();
+
+ self.payload_builder.open(platform_data_variant_type);
+ defer self.payload_builder.close();
+
+ // We have no platform data.
+ }
+
+ const payload = self.payload_builder.end();
+
+ {
+ var err_: ?*glib.Error = null;
+ defer if (err_) |err| err.free();
+
+ const result_ = self.dbus.callSync(
+ self.bus_name,
+ self.object_path,
+ "org.gtk.Actions",
+ "Activate",
+ payload,
+ null, // We don't care about the return type, we don't do anything with it.
+ .{}, // no flags
+ -1, // default timeout
+ null, // not cancellable
+ &err_,
+ );
+ defer if (result_) |result| result.unref();
+
+ if (err_) |err| {
+ try stderr.print(
+ "D-Bus method call returned an error err={s}\n",
+ .{err.f_message orelse "(unknown)"},
+ );
+ try stderr.flush();
+ return error.IPCFailed;
+ }
+ }
+}
+
+/// Free/unref any data held by this instance.
+pub fn deinit(self: *Self, alloc: Allocator) void {
+ switch (self.target) {
+ .class => alloc.free(self.object_path),
+ .detect => {},
+ }
+ self.parameters_builder.unref();
+ self.payload_builder.unref();
+ self.dbus.unref();
+}