mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-22 11:46:11 +03:00
cli/gtk: add -e to +new-window
This adds the `-e` flag to the `+new-window` CLI action. This allows a command to be passed from the CLI to the running instance of Ghostty. Nothing is done with that command besides logging its presence.
This commit is contained in:
@ -1731,10 +1731,44 @@ fn gtkActionShowGTKInspector(
|
|||||||
|
|
||||||
fn gtkActionNewWindow(
|
fn gtkActionNewWindow(
|
||||||
_: *gio.SimpleAction,
|
_: *gio.SimpleAction,
|
||||||
_: ?*glib.Variant,
|
parameter_: ?*glib.Variant,
|
||||||
self: *App,
|
self: *App,
|
||||||
) callconv(.c) void {
|
) callconv(.c) void {
|
||||||
log.info("received new window action", .{});
|
log.debug("received new window action", .{});
|
||||||
|
|
||||||
|
parameter: {
|
||||||
|
// were we given a parameter?
|
||||||
|
const parameter = parameter_ orelse break :parameter;
|
||||||
|
|
||||||
|
const as = glib.VariantType.new("as");
|
||||||
|
defer as.free();
|
||||||
|
|
||||||
|
// ensure that the supplied parameter is an array of strings
|
||||||
|
if (glib.Variant.isOfType(parameter, as) == 0) {
|
||||||
|
log.warn("parameter is of type {s}", .{parameter.getTypeString()});
|
||||||
|
break :parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s = glib.VariantType.new("s");
|
||||||
|
defer s.free();
|
||||||
|
|
||||||
|
var it: glib.VariantIter = undefined;
|
||||||
|
_ = it.init(parameter);
|
||||||
|
|
||||||
|
while (it.nextValue()) |value| {
|
||||||
|
defer value.unref();
|
||||||
|
|
||||||
|
// just to be sure
|
||||||
|
if (value.isOfType(s) == 0) continue;
|
||||||
|
|
||||||
|
var len: usize = undefined;
|
||||||
|
const buf = value.getString(&len);
|
||||||
|
const str = buf[0..len];
|
||||||
|
|
||||||
|
log.debug("new-window command argument: {s}", .{str});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ = self.core_app.mailbox.push(.{
|
_ = self.core_app.mailbox.push(.{
|
||||||
.new_window = .{},
|
.new_window = .{},
|
||||||
}, .{ .forever = {} });
|
}, .{ .forever = {} });
|
||||||
@ -1751,7 +1785,10 @@ fn initActions(self: *App) void {
|
|||||||
// For action names:
|
// For action names:
|
||||||
// https://docs.gtk.org/gio/type_func.Action.name_is_valid.html
|
// https://docs.gtk.org/gio/type_func.Action.name_is_valid.html
|
||||||
const t = glib.ext.VariantType.newFor(u64);
|
const t = glib.ext.VariantType.newFor(u64);
|
||||||
defer glib.VariantType.free(t);
|
defer t.free();
|
||||||
|
|
||||||
|
const as = glib.VariantType.new("as");
|
||||||
|
defer as.free();
|
||||||
|
|
||||||
const actions = .{
|
const actions = .{
|
||||||
.{ "quit", gtkActionQuit, null },
|
.{ "quit", gtkActionQuit, null },
|
||||||
@ -1760,6 +1797,7 @@ fn initActions(self: *App) void {
|
|||||||
.{ "present-surface", gtkActionPresentSurface, t },
|
.{ "present-surface", gtkActionPresentSurface, t },
|
||||||
.{ "show-gtk-inspector", gtkActionShowGTKInspector, null },
|
.{ "show-gtk-inspector", gtkActionShowGTKInspector, null },
|
||||||
.{ "new-window", gtkActionNewWindow, null },
|
.{ "new-window", gtkActionNewWindow, null },
|
||||||
|
.{ "new-window-command", gtkActionNewWindow, as },
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (actions) |entry| {
|
inline for (actions) |entry| {
|
||||||
|
@ -24,10 +24,32 @@ pub const Options = struct {
|
|||||||
/// precedence over `--debug`.
|
/// precedence over `--debug`.
|
||||||
class: ?[:0]const u8 = null,
|
class: ?[:0]const u8 = null,
|
||||||
|
|
||||||
// Enable arg parsing diagnostics so that we don't get an error if
|
/// Set to `true` if `-e` was found on the command line.
|
||||||
// there is a "normal" config setting on the cli.
|
_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,
|
||||||
|
|
||||||
|
/// Enable arg parsing diagnostics so that we don't get an error if
|
||||||
|
/// there is a "normal" config setting on the cli.
|
||||||
_diagnostics: diagnostics.DiagnosticList = .{},
|
_diagnostics: diagnostics.DiagnosticList = .{},
|
||||||
|
|
||||||
|
/// Manual parse hook, used to deal with `-e`
|
||||||
|
pub fn parseManuallyHook(self: *Options, alloc: Allocator, arg: []const u8, iter: anytype) Allocator.Error!bool {
|
||||||
|
// If it's not `-e` continue with the standard argument parsning.
|
||||||
|
if (!std.mem.eql(u8, arg, "-e")) return true;
|
||||||
|
|
||||||
|
self._command = true;
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Options) void {
|
pub fn deinit(self: *Options) void {
|
||||||
if (self._arena) |arena| arena.deinit();
|
if (self._arena) |arena| arena.deinit();
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
@ -43,6 +65,12 @@ pub const Options = struct {
|
|||||||
/// The `new-window` will use native platform IPC to open up a new window in a
|
/// The `new-window` will use native platform IPC to open up a new window in a
|
||||||
/// running instance of Ghostty.
|
/// running instance of Ghostty.
|
||||||
///
|
///
|
||||||
|
/// 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
|
||||||
|
/// in the new window rather than the default. If `-e` is not specified, Ghostty
|
||||||
|
/// will use the default command (either specified with `command` in your config
|
||||||
|
/// or your default shell as configured on your system).
|
||||||
|
///
|
||||||
/// On GTK, D-Bus activation must be properly configured. Ghostty does not need
|
/// On GTK, D-Bus activation must be properly configured. Ghostty does not need
|
||||||
/// to be running for this to open a new window, making it suitable for binding
|
/// to be running for this to open a new window, making it suitable for binding
|
||||||
/// to keys in your window manager (if other methods for configuring global
|
/// to keys in your window manager (if other methods for configuring global
|
||||||
@ -79,6 +107,9 @@ pub const Options = struct {
|
|||||||
/// * `--class=<class>`: If set, open up a new window in a custom instance of Ghostty. The
|
/// * `--class=<class>`: If set, open up a new window in a custom instance of Ghostty. The
|
||||||
/// class must be a valid GTK application ID.
|
/// 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.
|
||||||
|
///
|
||||||
/// Available since: 1.2.0
|
/// Available since: 1.2.0
|
||||||
pub fn run(alloc: Allocator) !u8 {
|
pub fn run(alloc: Allocator) !u8 {
|
||||||
var iter = try args.argsIterator(alloc);
|
var iter = try args.argsIterator(alloc);
|
||||||
@ -120,6 +151,11 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||||||
if (exit) return 1;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
var count: usize = 0;
|
var count: usize = 0;
|
||||||
if (opts.release) count += 1;
|
if (opts.release) count += 1;
|
||||||
if (opts.debug) count += 1;
|
if (opts.debug) count += 1;
|
||||||
@ -140,6 +176,6 @@ fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, the platform is unsupported.
|
// If we get here, the platform is unsupported.
|
||||||
try stderr.print("+new-window is unsupported on this platform\n", .{});
|
try stderr.print("+new-window is unsupported on this platform.\n", .{});
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,18 @@ const Options = @import("../new_window.zig").Options;
|
|||||||
|
|
||||||
// Use a D-Bus method call to open a new window on GTK.
|
// Use a D-Bus method call to open a new window on GTK.
|
||||||
// See: https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
|
// See: https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI
|
||||||
|
//
|
||||||
|
// `ghostty +new-window --release` is equivalent to the following command:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// 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:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// gdbus call --session --dest con.mitchellh.ghostty --object-path /com/mitchellh/ghostty --method org.gtk.Actions.Activate new-window-command '[<@as ["echo" "hello"]>]' []
|
||||||
|
// ```
|
||||||
pub fn new_window(alloc: Allocator, stderr: std.fs.File.Writer, opts: Options) (Allocator.Error || std.posix.WriteError)!u8 {
|
pub fn new_window(alloc: Allocator, stderr: std.fs.File.Writer, opts: Options) (Allocator.Error || std.posix.WriteError)!u8 {
|
||||||
// Get the appropriate bus name and object path for contacting the
|
// Get the appropriate bus name and object path for contacting the
|
||||||
// Ghostty instance we're interested in.
|
// Ghostty instance we're interested in.
|
||||||
@ -84,18 +96,42 @@ pub fn new_window(alloc: Allocator, stderr: std.fs.File.Writer, opts: Options) (
|
|||||||
errdefer builder.unref();
|
errdefer builder.unref();
|
||||||
|
|
||||||
// action
|
// action
|
||||||
builder.add("s", "new-window");
|
if (opts._arguments.items.len == 0) {
|
||||||
|
builder.add("s", "new-window");
|
||||||
|
} else {
|
||||||
|
builder.add("s", "new-window-command");
|
||||||
|
}
|
||||||
|
|
||||||
// parameters
|
// parameters
|
||||||
{
|
{
|
||||||
const parameters = glib.VariantType.new("av");
|
const av = glib.VariantType.new("av");
|
||||||
defer glib.free(parameters);
|
defer av.free();
|
||||||
|
|
||||||
builder.open(parameters);
|
var parameters: glib.VariantBuilder = undefined;
|
||||||
defer builder.close();
|
parameters.init(av);
|
||||||
|
|
||||||
// we have no parameters
|
if (opts._arguments.items.len > 0) {
|
||||||
|
// If `-e` was specified on the command line, he first parameter
|
||||||
|
// is an array of strings that contain the arguments that came
|
||||||
|
// afer `-e`, which will be interpreted as a command to run.
|
||||||
|
{
|
||||||
|
const as = glib.VariantType.new("as");
|
||||||
|
defer as.free();
|
||||||
|
|
||||||
|
var command: glib.VariantBuilder = undefined;
|
||||||
|
command.init(as);
|
||||||
|
|
||||||
|
for (opts._arguments.items) |argument| {
|
||||||
|
command.add("s", argument.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters.add("v", command.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addValue(parameters.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const platform_data = glib.VariantType.new("a{sv}");
|
const platform_data = glib.VariantType.new("a{sv}");
|
||||||
defer glib.free(platform_data);
|
defer glib.free(platform_data);
|
||||||
|
Reference in New Issue
Block a user