summaryrefslogtreecommitdiff
path: root/src/terminal/c/osc.zig
blob: 8b6a8409c05e3b92f192efe9f6ded6f11b206f20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
const std = @import("std");
const assert = std.debug.assert;
const builtin = @import("builtin");
const lib_alloc = @import("../../lib/allocator.zig");
const CAllocator = lib_alloc.Allocator;
const osc = @import("../osc.zig");
const Result = @import("result.zig").Result;

/// C: GhosttyOscParser
pub const Parser = ?*osc.Parser;

/// C: GhosttyOscCommand
pub const Command = ?*osc.Command;

pub fn new(
    alloc_: ?*const CAllocator,
    result: *Parser,
) callconv(.c) Result {
    const alloc = lib_alloc.default(alloc_);
    const ptr = alloc.create(osc.Parser) catch
        return .out_of_memory;
    ptr.* = .initAlloc(alloc);
    result.* = ptr;
    return .success;
}

pub fn free(parser_: Parser) callconv(.c) void {
    // C-built parsers always have an associated allocator.
    const parser = parser_ orelse return;
    const alloc = parser.alloc.?;
    parser.deinit();
    alloc.destroy(parser);
}

pub fn reset(parser_: Parser) callconv(.c) void {
    parser_.?.reset();
}

pub fn next(parser_: Parser, byte: u8) callconv(.c) void {
    parser_.?.next(byte);
}

pub fn end(parser_: Parser, terminator: u8) callconv(.c) Command {
    return parser_.?.end(terminator);
}

pub fn commandType(command_: Command) callconv(.c) osc.Command.Key {
    const command = command_ orelse return .invalid;
    return command.*;
}

/// C: GhosttyOscCommandData
pub const CommandData = enum(c_int) {
    invalid = 0,
    change_window_title_str = 1,

    /// Output type expected for querying the data of the given kind.
    pub fn OutType(comptime self: CommandData) type {
        return switch (self) {
            .invalid => void,
            .change_window_title_str => [*:0]const u8,
        };
    }
};

pub fn commandData(
    command_: Command,
    data: CommandData,
    out: ?*anyopaque,
) callconv(.c) bool {
    return switch (data) {
        inline else => |comptime_data| commandDataTyped(
            command_,
            comptime_data,
            @ptrCast(@alignCast(out)),
        ),
    };
}

fn commandDataTyped(
    command_: Command,
    comptime data: CommandData,
    out: *data.OutType(),
) bool {
    const command = command_.?;
    switch (data) {
        .invalid => return false,
        .change_window_title_str => switch (command.*) {
            .change_window_title => |v| out.* = v.ptr,
            else => return false,
        },
    }

    return true;
}

test "alloc" {
    const testing = std.testing;
    var p: Parser = undefined;
    try testing.expectEqual(Result.success, new(
        &lib_alloc.test_allocator,
        &p,
    ));
    free(p);
}

test "command type null" {
    const testing = std.testing;
    try testing.expectEqual(.invalid, commandType(null));
}

test "change window title" {
    const testing = std.testing;
    var p: Parser = undefined;
    try testing.expectEqual(Result.success, new(
        &lib_alloc.test_allocator,
        &p,
    ));
    defer free(p);

    // Parse it
    next(p, '0');
    next(p, ';');
    next(p, 'a');
    const cmd = end(p, 0);
    try testing.expectEqual(.change_window_title, commandType(cmd));

    // Extract the title
    var title: [*:0]const u8 = undefined;
    try testing.expect(commandData(cmd, .change_window_title_str, @ptrCast(&title)));
    try testing.expectEqualStrings("a", std.mem.span(title));
}