mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-06-15 16:13:56 +00:00
terminal/glyph: register request
This commit is contained in:
@@ -21,6 +21,9 @@ pub const max_entries = 1024;
|
||||
/// An empty glossary with no registered glyphs.
|
||||
pub const empty: Glossary = .{ .entries = .empty };
|
||||
|
||||
/// Errors that can occur while registering a glossary entry.
|
||||
pub const RegisterError = Allocator.Error || error{OutOfNamespace};
|
||||
|
||||
/// The set of entries in the glossary keyed by the codepoint.
|
||||
///
|
||||
/// The array hash map preserves insertion order and has O(N)
|
||||
@@ -49,7 +52,7 @@ pub fn register(
|
||||
alloc: Allocator,
|
||||
cp: u21,
|
||||
entry: Entry,
|
||||
) (Allocator.Error || error{OutOfNamespace})!void {
|
||||
) RegisterError!void {
|
||||
// Validate codepoint according to spec.
|
||||
if (!isPrivateUse(cp)) return error.OutOfNamespace;
|
||||
|
||||
@@ -143,7 +146,7 @@ pub const Entry = struct {
|
||||
/// decodes the base64 glyph payload, and stores the decoded outline. The
|
||||
/// returned entry owns decoded glyph memory and must be released with
|
||||
/// `deinit`.
|
||||
pub fn init(alloc: Allocator, req: RegisterReq) InitError!Entry {
|
||||
pub fn init(alloc: Allocator, req: RegisterReq) Entry.InitError!Entry {
|
||||
// Validate format
|
||||
const fmt = req.get(.fmt) orelse return error.InvalidOptions;
|
||||
const design: glyf_rasterize.DesignMetrics = .{
|
||||
|
||||
@@ -28,11 +28,147 @@ pub fn execute(
|
||||
glossary: *Glossary,
|
||||
req: *const Request,
|
||||
) ?Response {
|
||||
_ = alloc;
|
||||
_ = glossary;
|
||||
log.debug("executing glyph protocol request: {t}", .{req.*});
|
||||
return switch (req.*) {
|
||||
.support => .{ .support = .{ .fmt = supported_formats } },
|
||||
.query, .register, .clear => @panic("TODO"),
|
||||
.register => |reg| register(alloc, glossary, reg),
|
||||
.query, .clear => @panic("TODO"),
|
||||
};
|
||||
}
|
||||
|
||||
fn register(
|
||||
alloc: Allocator,
|
||||
glossary: *Glossary,
|
||||
reg: Request.Register,
|
||||
) ?Response {
|
||||
const reply = reg.get(.reply) orelse .all;
|
||||
const cp = registerFallible(alloc, glossary, reg) catch |err| return switch (reply) {
|
||||
.none => null,
|
||||
.all, .failures => .{ .register = .{
|
||||
.cp = reg.get(.cp) orelse 0,
|
||||
.status = .err,
|
||||
.reason = switch (err) {
|
||||
error.OutOfMemory => .{ .other = "out_of_memory" },
|
||||
error.OutOfNamespace => .out_of_namespace,
|
||||
error.PayloadTooLarge => .payload_too_large,
|
||||
error.MalformedPayload => .malformed_payload,
|
||||
error.CompositeUnsupported => .composite_unsupported,
|
||||
error.HintingUnsupported => .hinting_unsupported,
|
||||
error.InvalidOptions,
|
||||
error.UnsupportedFormat,
|
||||
=> .malformed_payload,
|
||||
},
|
||||
} },
|
||||
};
|
||||
|
||||
return switch (reply) {
|
||||
.none, .failures => null,
|
||||
.all => .{ .register = .{ .cp = cp } },
|
||||
};
|
||||
}
|
||||
|
||||
fn registerFallible(
|
||||
alloc: Allocator,
|
||||
glossary: *Glossary,
|
||||
reg: Request.Register,
|
||||
) (Glossary.Entry.InitError || Glossary.RegisterError)!u21 {
|
||||
const cp = reg.get(.cp) orelse
|
||||
return error.MalformedPayload;
|
||||
|
||||
var entry = try Glossary.Entry.init(alloc, reg);
|
||||
errdefer entry.deinit(alloc);
|
||||
|
||||
try glossary.register(alloc, cp, entry);
|
||||
return cp;
|
||||
}
|
||||
|
||||
fn testParse(alloc: Allocator, data: []const u8) !Request {
|
||||
var parser = request.CommandParser.init(alloc, 1024 * 1024);
|
||||
defer parser.deinit();
|
||||
for (data) |byte| try parser.feed(byte);
|
||||
return try parser.complete(alloc);
|
||||
}
|
||||
|
||||
test "execute register stores glyph and returns success" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var glossary: Glossary = .empty;
|
||||
defer glossary.deinit(alloc);
|
||||
|
||||
var req = try testParse(alloc, "r;cp=e0a0;AAAAAAAAAAAAAA==");
|
||||
defer req.deinit(alloc);
|
||||
|
||||
try testing.expectEqual(Response{
|
||||
.register = .{ .cp = 0xE0A0 },
|
||||
}, execute(alloc, &glossary, &req).?);
|
||||
try testing.expect(glossary.contains(0xE0A0));
|
||||
}
|
||||
|
||||
test "execute register reply failures suppresses success" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var glossary: Glossary = .empty;
|
||||
defer glossary.deinit(alloc);
|
||||
|
||||
var req = try testParse(alloc, "r;cp=e0a0;reply=2;AAAAAAAAAAAAAA==");
|
||||
defer req.deinit(alloc);
|
||||
|
||||
try testing.expect(execute(alloc, &glossary, &req) == null);
|
||||
try testing.expect(glossary.contains(0xE0A0));
|
||||
}
|
||||
|
||||
test "execute register reply none suppresses failure" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var glossary: Glossary = .empty;
|
||||
defer glossary.deinit(alloc);
|
||||
|
||||
var req = try testParse(alloc, "r;cp=41;reply=0;%%%not-base64%%%");
|
||||
defer req.deinit(alloc);
|
||||
|
||||
try testing.expect(execute(alloc, &glossary, &req) == null);
|
||||
try testing.expect(!glossary.contains('A'));
|
||||
}
|
||||
|
||||
test "execute register rejects non-PUA" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var glossary: Glossary = .empty;
|
||||
defer glossary.deinit(alloc);
|
||||
|
||||
var req = try testParse(alloc, "r;cp=41;AAAAAAAAAAAAAA==");
|
||||
defer req.deinit(alloc);
|
||||
|
||||
try testing.expectEqual(Response{
|
||||
.register = .{
|
||||
.cp = 'A',
|
||||
.status = .err,
|
||||
.reason = .out_of_namespace,
|
||||
},
|
||||
}, execute(alloc, &glossary, &req).?);
|
||||
try testing.expect(!glossary.contains('A'));
|
||||
}
|
||||
|
||||
test "execute register reports malformed payload" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var glossary: Glossary = .empty;
|
||||
defer glossary.deinit(alloc);
|
||||
|
||||
var req = try testParse(alloc, "r;cp=e0a0;%%%not-base64%%%");
|
||||
defer req.deinit(alloc);
|
||||
|
||||
try testing.expectEqual(Response{
|
||||
.register = .{
|
||||
.cp = 0xE0A0,
|
||||
.status = .err,
|
||||
.reason = .malformed_payload,
|
||||
},
|
||||
}, execute(alloc, &glossary, &req).?);
|
||||
try testing.expect(!glossary.contains(0xE0A0));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user