diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/apprt/gtk/ipc/DBus.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (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.zig | 196 |
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(); +} |
