diff --git a/src/apprt/gtk/ipc/new_window.zig b/src/apprt/gtk/ipc/new_window.zig index df657beea..d9bbad426 100644 --- a/src/apprt/gtk/ipc/new_window.zig +++ b/src/apprt/gtk/ipc/new_window.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const gio = @import("gio"); @@ -8,13 +9,13 @@ const apprt = @import("../../../apprt.zig"); // Use a D-Bus method call to open a new window on GTK. // See: https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI // -// `ghostty +new-window --release` is equivalent to the following command: +// `ghostty +new-window` is equivalent to the following command (on a release build): // // ``` // gdbus call --session --dest com.mitchellh.ghostty --object-path /com/mitchellh/ghostty --method org.gtk.Actions.Activate new-window [] [] // ``` // -// `ghostty +new-window --release -e echo hello` would be equivalent to the following command: +// `ghostty +new-window -e echo hello` would be equivalent to the following command (on a release build): // // ``` // gdbus call --session --dest com.mitchellh.ghostty --object-path /com/mitchellh/ghostty --method org.gtk.Actions.Activate new-window-command '[<@as ["echo" "hello"]>]' [] @@ -22,48 +23,23 @@ const apprt = @import("../../../apprt.zig"); pub fn openNewWindow(alloc: Allocator, target: apprt.ipc.Target, value: apprt.ipc.Action.NewWindow) (Allocator.Error || std.posix.WriteError || apprt.ipc.Errors)!bool { const stderr = std.io.getStdErr().writer(); - if (value.arguments.len > 256) { - try stderr.print("The new window IPC supports at most 256 arguments.\n", .{}); - return error.IPCFailed; - } - // 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 = result: { - switch (target) { - .class => |class| { - // Force the usage of the class specified on the CLI to determine the - // bus name and object path. - const object_path = try std.fmt.allocPrintZ(alloc, "/{s}", .{class}); + 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.allocPrintZ(alloc, "/{s}", .{class}); - std.mem.replaceScalar(u8, object_path, '.', '/'); - std.mem.replaceScalar(u8, object_path, '-', '_'); + std.mem.replaceScalar(u8, object_path, '.', '/'); + std.mem.replaceScalar(u8, object_path, '-', '_'); - break :result .{ class, object_path }; - }, - .release => { - // Force the usage of the release bus name and object path. - break :result .{ "com.mitchellh.ghostty", "/com/mitchellh/ghostty" }; - }, - .debug => { - // Force the usage of the debug bus name and object path. - break :result .{ "com.mitchellh.ghostty-debug", "/com/mitchellh/ghostty_debug" }; - }, - .detect => { - // If there is a `GHOSTTY_CLASS` environment variable, use that as the basis - // for the bus name and object path. - if (std.posix.getenv("GHOSTTY_CLASS")) |class| { - const object_path = try std.fmt.allocPrintZ(alloc, "/{s}", .{class}); - - std.mem.replaceScalar(u8, object_path, '.', '/'); - std.mem.replaceScalar(u8, object_path, '-', '_'); - - break :result .{ class, object_path }; - } - // Otherwise fall back to the release bus name and object path. - break :result .{ "com.mitchellh.ghostty", "/com/mitchellh/ghostty" }; - }, - } + break :result .{ class, object_path }; + }, + .detect => switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ "com.mitchellh.ghostty-debug", "/com/mitchellh/ghostty_debug" }, + .ReleaseFast, .ReleaseSmall => .{ "com.mitchellh.ghostty", "/com/mitchellh/ghostty" }, + }, }; if (gio.Application.idIsValid(bus_name.ptr) == 0) { @@ -107,7 +83,7 @@ pub fn openNewWindow(alloc: Allocator, target: apprt.ipc.Target, value: apprt.ip errdefer builder.unref(); // action - if (value.arguments.len == 0) { + if (value.arguments == null) { builder.add("s", "new-window"); } else { builder.add("s", "new-window-command"); @@ -121,7 +97,7 @@ pub fn openNewWindow(alloc: Allocator, target: apprt.ipc.Target, value: apprt.ip var parameters: glib.VariantBuilder = undefined; parameters.init(av); - if (value.arguments.len > 0) { + if (value.arguments) |arguments| { // If `-e` was specified on the command line, the first // parameter is an array of strings that contain the arguments // that came after `-e`, which will be interpreted as a command @@ -133,7 +109,7 @@ pub fn openNewWindow(alloc: Allocator, target: apprt.ipc.Target, value: apprt.ip var command: glib.VariantBuilder = undefined; command.init(as); - for (value.arguments) |argument| { + for (arguments) |argument| { command.add("s", argument.ptr); } diff --git a/src/apprt/ipc.zig b/src/apprt/ipc.zig index 359381b08..76f6cb5df 100644 --- a/src/apprt/ipc.zig +++ b/src/apprt/ipc.zig @@ -9,12 +9,6 @@ pub const Errors = error{ }; pub const Target = union(Key) { - /// Open up a new window in a release instance of Ghostty. - release, - - /// Open up a new window in a debug instance of Ghostty. - debug, - /// Open up a new window in a custom instance of Ghostty. class: [:0]const u8, @@ -23,16 +17,12 @@ pub const Target = union(Key) { // Sync with: ghostty_ipc_target_tag_e pub const Key = enum(c_int) { - release, - debug, class, detect, }; // Sync with: ghostty_ipc_target_u pub const CValue = extern union { - release: void, - debug: void, class: [*:0]const u8, detect: void, }; @@ -78,27 +68,32 @@ pub const Action = union(enum) { new_window: NewWindow, pub const NewWindow = struct { - arguments: [][:0]const u8, + arguments: ?[][:0]const u8, pub const C = extern struct { /// null terminated list of arguments - arguments: [*]?[*:0]const u8, + /// it will be null itself if there are no arguments + arguments: ?[*]?[*:0]const u8, pub fn deinit(self: *NewWindow.C, alloc: Allocator) void { - alloc.free(self.arguments); + if (self.arguments) |arguments| alloc.free(arguments); } }; pub fn cval(self: *NewWindow, alloc: Allocator) Allocator.Error!NewWindow.C { var result: NewWindow.C = undefined; - result.arguments = try alloc.alloc([*:0]const u8, self.arguments.len + 1); + if (self.arguments) |arguments| { + result.arguments = try alloc.alloc([*:0]const u8, arguments.len + 1); - for (self.arguments, 0..) |argument, i| - result.arguments[i] = argument.ptr; + for (arguments, 0..) |argument, i| + result.arguments[i] = argument.ptr; - // add null terminator - result.arguments[self.arguments.len] = null; + // add null terminator + result.arguments[arguments.len] = null; + } else { + result.arguments = null; + } return result; } diff --git a/src/cli/args.zig b/src/cli/args.zig index 1af74df69..0ff3dc047 100644 --- a/src/cli/args.zig +++ b/src/cli/args.zig @@ -153,7 +153,7 @@ pub fn parse( // The error set is dependent on comptime T, so we always add // an extra error so we can have the "else" below. - const ErrSet = @TypeOf(err) || error{ Unknown, OutOfMemory }; + const ErrSet = @TypeOf(err) || error{ Unknown, OutOfMemory } || Error; const message: [:0]const u8 = switch (@as(ErrSet, @errorCast(err))) { // OOM is not recoverable since we need to allocate to // track more error messages. diff --git a/src/cli/new_window.zig b/src/cli/new_window.zig index b81dc0880..995c8b2f0 100644 --- a/src/cli/new_window.zig +++ b/src/cli/new_window.zig @@ -10,22 +10,12 @@ pub const Options = struct { /// This is set by the CLI parser for deinit. _arena: ?ArenaAllocator = null, - /// If `true`, open up a new window in a release instance of Ghostty. - release: bool = false, - - /// If `true`, open up a new window in a debug instance of Ghostty. - debug: bool = false, - - /// If set, open up a new window in a custom instance of Ghostty. Takes - /// precedence over `--debug`. + /// If set, open up a new window in a custom instance of Ghostty. class: ?[:0]const u8 = null, - /// Set to `true` if `-e` was found on the command line. - _command: bool = false, - /// If `-e` is found in the arguments, this will contain all of the /// arguments to pass to Ghostty as the command. - _arguments: std.ArrayListUnmanaged([:0]const u8) = .empty, + _arguments: ?[][:0]const u8 = null, /// Enable arg parsing diagnostics so that we don't get an error if /// there is a "normal" config setting on the cli. @@ -36,13 +26,19 @@ pub const Options = struct { // If it's not `-e` continue with the standard argument parsning. if (!std.mem.eql(u8, arg, "-e")) return true; - self._command = true; + var arguments: std.ArrayListUnmanaged([:0]const u8) = .empty; + errdefer { + for (arguments.items) |argument| alloc.free(argument); + arguments.deinit(alloc); + } // Otherwise gather up the rest of the arguments to use as the command. while (iter.next()) |param| { - try self._arguments.append(alloc, try alloc.dupeZ(u8, param)); + try arguments.append(alloc, try alloc.dupeZ(u8, param)); } + self._arguments = try arguments.toOwnedSlice(alloc); + return false; } @@ -61,10 +57,11 @@ pub const Options = struct { /// The `new-window` will use native platform IPC to open up a new window in a /// running instance of Ghostty. /// -/// If none of `--release`, `--debug`, and `--class` flags are not set, the -/// `new-window` command will try and find the class of the running Ghostty -/// instance in the `GHOSTTY_CLASS` environment variable. If this environment -/// variable is not set, a release instance of Ghostty will be opened. +/// If the `--class` flag is not set, the `new-window` command will try and +/// connect to a running instance of Ghostty based on what optimizations the +/// Ghostty CLI was compiled with. Otherwise the `new-window` command will try +/// and contact a running Ghostty instance that was configured with the same +/// `class` as was given on the command line. /// /// If the `-e` flag is included on the command line, any arguments that follow /// will be sent to the running Ghostty instance and used as the command to run @@ -92,14 +89,8 @@ pub const Options = struct { /// /// Flags: /// -/// * `--release`: If `true`, force opening up a new window in a release instance of -/// Ghostty. -/// -/// * `--debug`: If `true`, force opening up a new window in a debug instance of -/// Ghostty. -/// -/// * `--class=`: If set, open up a new window in a custom instance of Ghostty. The -/// class must be a valid GTK application ID. +/// * `--class=`: If set, open up a new window in a custom instance of +/// Ghostty. The class must be a valid GTK application ID. /// /// * `-e`: Any arguments after this will be interpreted as a command to /// execute inside the new window instead of the default command. @@ -145,23 +136,11 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { if (exit) return 1; } - if (opts._command and opts._arguments.items.len == 0) { - try stderr.print("The -e flag was specified on the command line, but no other arguments were found.\n", .{}); - return 1; - } - if (opts._command and opts._arguments.items.len > 256) { - try stderr.print("The -e flag supports at most 256 arguments.\n", .{}); - return 1; - } - - var count: usize = 0; - if (opts.release) count += 1; - if (opts.debug) count += 1; - if (opts.class) |_| count += 1; - - if (count > 1) { - try stderr.print("The --release, --debug, and --class flags are mutually exclusive, only one may be specified at a time.\n", .{}); - return 1; + if (opts._arguments) |arguments| { + if (arguments.len == 0) { + try stderr.print("The -e flag was specified on the command line, but no other arguments were found.\n", .{}); + return 1; + } } var arena = ArenaAllocator.init(alloc_gpa); @@ -170,15 +149,10 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { if (apprt.IPC.sendIPC( alloc, - target: { - if (opts.class) |class| break :target .{ .class = class }; - if (opts.release) break :target .release; - if (opts.debug) break :target .debug; - break :target .detect; - }, + if (opts.class) |class| .{ .class = class } else .detect, .new_window, .{ - .arguments = opts._arguments.items, + .arguments = opts._arguments, }, ) catch |err| switch (err) { error.IPCFailed => {