mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
Merge pull request #2300 from vancluever/terminal-kitty-large-signed-ints
terminal/kitty: increase value buffer, make 'H' and 'V' i32
This commit is contained in:
@ -122,6 +122,26 @@ test "garbage Kitty command" {
|
|||||||
try testing.expect(h.end() == null);
|
try testing.expect(h.end() == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Kitty command with overflow u32" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var h: Handler = .{};
|
||||||
|
h.start();
|
||||||
|
for ("Ga=p,i=10000000000") |c| h.feed(alloc, c);
|
||||||
|
try testing.expect(h.end() == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Kitty command with overflow i32" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var h: Handler = .{};
|
||||||
|
h.start();
|
||||||
|
for ("Ga=p,i=1,z=-9999999999") |c| h.feed(alloc, c);
|
||||||
|
try testing.expect(h.end() == null);
|
||||||
|
}
|
||||||
|
|
||||||
test "valid Kitty command" {
|
test "valid Kitty command" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
@ -23,10 +23,10 @@ pub const Parser = struct {
|
|||||||
/// This is the list of KV pairs that we're building up.
|
/// This is the list of KV pairs that we're building up.
|
||||||
kv: KV = .{},
|
kv: KV = .{},
|
||||||
|
|
||||||
/// This is used as a buffer to store the key/value of a KV pair.
|
/// This is used as a buffer to store the key/value of a KV pair. The value
|
||||||
/// The value of a KV pair is at most a 32-bit integer which at most
|
/// of a KV pair is at most a 32-bit integer which at most is 10 characters
|
||||||
/// is 10 characters (4294967295).
|
/// (4294967295), plus one character for the sign bit on signed ints.
|
||||||
kv_temp: [10]u8 = undefined,
|
kv_temp: [11]u8 = undefined,
|
||||||
kv_temp_len: u4 = 0,
|
kv_temp_len: u4 = 0,
|
||||||
kv_current: u8 = 0, // Current kv key
|
kv_current: u8 = 0, // Current kv key
|
||||||
|
|
||||||
@ -237,16 +237,14 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only "z" is currently signed. This is a bit of a kloodge; if more
|
// Handle integer fields, parsing signed fields accordingly. We still
|
||||||
// fields become signed we can rethink this but for now we parse
|
// store the fields as u32 as they can be bitcast back later during
|
||||||
// "z" as i32 then bitcast it to u32 then bitcast it back later.
|
// building of the higher-level command tree.
|
||||||
if (self.kv_current == 'z') {
|
const v: u32 = switch (self.kv_current) {
|
||||||
const v = try std.fmt.parseInt(i32, self.kv_temp[0..self.kv_temp_len], 10);
|
'z', 'H', 'V' => @bitCast(try std.fmt.parseInt(i32, self.kv_temp[0..self.kv_temp_len], 10)),
|
||||||
try self.kv.put(alloc, self.kv_current, @bitCast(v));
|
else => try std.fmt.parseInt(u32, self.kv_temp[0..self.kv_temp_len], 10),
|
||||||
} else {
|
};
|
||||||
const v = try std.fmt.parseInt(u32, self.kv_temp[0..self.kv_temp_len], 10);
|
try self.kv.put(alloc, self.kv_current, v);
|
||||||
try self.kv.put(alloc, self.kv_current, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear our temp buffer
|
// Clear our temp buffer
|
||||||
self.kv_temp_len = 0;
|
self.kv_temp_len = 0;
|
||||||
@ -505,8 +503,8 @@ pub const Display = struct {
|
|||||||
virtual_placement: bool = false, // U
|
virtual_placement: bool = false, // U
|
||||||
parent_id: u32 = 0, // P
|
parent_id: u32 = 0, // P
|
||||||
parent_placement_id: u32 = 0, // Q
|
parent_placement_id: u32 = 0, // Q
|
||||||
horizontal_offset: u32 = 0, // H
|
horizontal_offset: i32 = 0, // H
|
||||||
vertical_offset: u32 = 0, // V
|
vertical_offset: i32 = 0, // V
|
||||||
z: i32 = 0, // z
|
z: i32 = 0, // z
|
||||||
|
|
||||||
pub const CursorMovement = enum {
|
pub const CursorMovement = enum {
|
||||||
@ -591,11 +589,13 @@ pub const Display = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (kv.get('H')) |v| {
|
if (kv.get('H')) |v| {
|
||||||
result.horizontal_offset = v;
|
// We can bitcast here because of how we parse it earlier.
|
||||||
|
result.horizontal_offset = @bitCast(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kv.get('V')) |v| {
|
if (kv.get('V')) |v| {
|
||||||
result.vertical_offset = v;
|
// We can bitcast here because of how we parse it earlier.
|
||||||
|
result.vertical_offset = @bitCast(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -1069,6 +1069,95 @@ test "ignore very long values" {
|
|||||||
try testing.expectEqual(@as(u32, 0), v.height);
|
try testing.expectEqual(@as(u32, 0), v.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "ensure very large negative values don't get skipped" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var p = Parser.init(alloc);
|
||||||
|
defer p.deinit();
|
||||||
|
|
||||||
|
const input = "a=p,i=1,z=-2000000000";
|
||||||
|
for (input) |c| try p.feed(c);
|
||||||
|
const command = try p.complete();
|
||||||
|
defer command.deinit(alloc);
|
||||||
|
|
||||||
|
try testing.expect(command.control == .display);
|
||||||
|
const v = command.control.display;
|
||||||
|
try testing.expectEqual(1, v.image_id);
|
||||||
|
try testing.expectEqual(-2000000000, v.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "ensure proper overflow error for u32" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var p = Parser.init(alloc);
|
||||||
|
defer p.deinit();
|
||||||
|
|
||||||
|
const input = "a=p,i=10000000000";
|
||||||
|
for (input) |c| try p.feed(c);
|
||||||
|
try testing.expectError(error.Overflow, p.complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "ensure proper overflow error for i32" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var p = Parser.init(alloc);
|
||||||
|
defer p.deinit();
|
||||||
|
|
||||||
|
const input = "a=p,i=1,z=-9999999999";
|
||||||
|
for (input) |c| try p.feed(c);
|
||||||
|
try testing.expectError(error.Overflow, p.complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "all i32 values" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
{
|
||||||
|
// 'z' (usually z-axis values)
|
||||||
|
var p = Parser.init(alloc);
|
||||||
|
defer p.deinit();
|
||||||
|
const input = "a=p,i=1,z=-1";
|
||||||
|
for (input) |c| try p.feed(c);
|
||||||
|
const command = try p.complete();
|
||||||
|
defer command.deinit(alloc);
|
||||||
|
|
||||||
|
try testing.expect(command.control == .display);
|
||||||
|
const v = command.control.display;
|
||||||
|
try testing.expectEqual(1, v.image_id);
|
||||||
|
try testing.expectEqual(-1, v.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// 'H' (relative placement, horizontal offset)
|
||||||
|
var p = Parser.init(alloc);
|
||||||
|
defer p.deinit();
|
||||||
|
const input = "a=p,i=1,H=-1";
|
||||||
|
for (input) |c| try p.feed(c);
|
||||||
|
const command = try p.complete();
|
||||||
|
defer command.deinit(alloc);
|
||||||
|
|
||||||
|
try testing.expect(command.control == .display);
|
||||||
|
const v = command.control.display;
|
||||||
|
try testing.expectEqual(1, v.image_id);
|
||||||
|
try testing.expectEqual(-1, v.horizontal_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// 'V' (relative placement, vertical offset)
|
||||||
|
var p = Parser.init(alloc);
|
||||||
|
defer p.deinit();
|
||||||
|
const input = "a=p,i=1,V=-1";
|
||||||
|
for (input) |c| try p.feed(c);
|
||||||
|
const command = try p.complete();
|
||||||
|
defer command.deinit(alloc);
|
||||||
|
|
||||||
|
try testing.expect(command.control == .display);
|
||||||
|
const v = command.control.display;
|
||||||
|
try testing.expectEqual(1, v.image_id);
|
||||||
|
try testing.expectEqual(-1, v.vertical_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "response: encode nothing without ID or image number" {
|
test "response: encode nothing without ID or image number" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var buf: [1024]u8 = undefined;
|
var buf: [1024]u8 = undefined;
|
||||||
|
@ -495,3 +495,37 @@ test "kittygfx default format is rgba" {
|
|||||||
const img = storage.imageById(1).?;
|
const img = storage.imageById(1).?;
|
||||||
try testing.expectEqual(command.Transmission.Format.rgba, img.format);
|
try testing.expectEqual(command.Transmission.Format.rgba, img.format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "kittygfx test valid u32 (expect invalid image ID)" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var t = try Terminal.init(alloc, .{ .rows = 5, .cols = 5 });
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
const cmd = try command.Parser.parseString(
|
||||||
|
alloc,
|
||||||
|
"a=p,i=4294967295",
|
||||||
|
);
|
||||||
|
defer cmd.deinit(alloc);
|
||||||
|
const resp = execute(alloc, &t, &cmd).?;
|
||||||
|
try testing.expect(!resp.ok());
|
||||||
|
try testing.expectEqual(resp.message, "ENOENT: image not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "kittygfx test valid i32 (expect invalid image ID)" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var t = try Terminal.init(alloc, .{ .rows = 5, .cols = 5 });
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
const cmd = try command.Parser.parseString(
|
||||||
|
alloc,
|
||||||
|
"a=p,i=1,z=-2147483648",
|
||||||
|
);
|
||||||
|
defer cmd.deinit(alloc);
|
||||||
|
const resp = execute(alloc, &t, &cmd).?;
|
||||||
|
try testing.expect(!resp.ok());
|
||||||
|
try testing.expectEqual(resp.message, "ENOENT: image not found");
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user