diff --git a/src/cli/action.zig b/src/cli/action.zig index 8113f991a..397c9eb12 100644 --- a/src/cli/action.zig +++ b/src/cli/action.zig @@ -11,6 +11,7 @@ const list_colors = @import("list_colors.zig"); const list_actions = @import("list_actions.zig"); const show_config = @import("show_config.zig"); const validate_config = @import("validate_config.zig"); +const notify = @import("notify.zig"); /// Special commands that can be invoked via CLI flags. These are all /// invoked by using `+` as a CLI flag. The only exception is @@ -43,6 +44,9 @@ pub const Action = enum { // Validate passed config file @"validate-config", + // Send a desktop notification + notify, + pub const Error = error{ /// Multiple actions were detected. You can specify at most one /// action on the CLI otherwise the behavior desired is ambiguous. @@ -134,6 +138,7 @@ pub const Action = enum { .@"list-actions" => try list_actions.run(alloc), .@"show-config" => try show_config.run(alloc), .@"validate-config" => try validate_config.run(alloc), + .notify => try notify.run(alloc), }; } diff --git a/src/cli/notify.zig b/src/cli/notify.zig new file mode 100644 index 000000000..0f73342a3 --- /dev/null +++ b/src/cli/notify.zig @@ -0,0 +1,61 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const Action = @import("action.zig").Action; +const args = @import("args.zig"); + +const log = std.log.scoped(.notify); + +pub const Config = struct { + /// This is set by the CLI parser for deinit. + _arena: ?ArenaAllocator = null, + + /// The title of the notifiction. + title: [:0]const u8 = "Ghostty", + + /// The body of the notification. + body: ?[:0]const u8 = null, + + pub fn deinit(self: *Config) void { + if (self._arena) |arena| arena.deinit(); + self.* = undefined; + } + + /// Enables "-h" and "--help" to work. + pub fn help(self: Config) !void { + _ = self; + return Action.help_error; + } +}; + +/// The `notify` command is used to send a desktop notification to the user +/// using OSC 777. +/// +/// The `--title` argument is used to set the title of the notification. If +/// unspecified, the default of "Ghostty" will be used. +/// +/// The `--body` argument is used to set the body of the notification. This +/// argument is required. +pub fn run(alloc: Allocator) !u8 { + var iter = try std.process.argsWithAllocator(alloc); + defer iter.deinit(); + return try runArgs(alloc, &iter); +} + +fn runArgs(alloc_gpa: Allocator, argsIter: anytype) !u8 { + var config: Config = .{}; + defer config.deinit(); + try args.parse(Config, alloc_gpa, &config, argsIter); + + const body = config.body orelse { + return Action.help_error; + }; + + var buffer: [2048]u8 = undefined; + const message = try std.fmt.bufPrint(&buffer, "\x1b]777;notify;{s};{s}\x1b\\", .{ config.title, body }); + + const stdout = std.io.getStdOut(); + try stdout.writeAll(message); + + return 0; +}