mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-17 01:06:08 +03:00
terminal/new: charsets
This commit is contained in:
@ -2785,6 +2785,7 @@ test "Terminal: print writes to bottom if scrolled" {
|
||||
}
|
||||
}
|
||||
|
||||
// X
|
||||
test "Terminal: print charset" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
@ -2809,6 +2810,7 @@ test "Terminal: print charset" {
|
||||
}
|
||||
}
|
||||
|
||||
// X
|
||||
test "Terminal: print charset outside of ASCII" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
@ -2829,6 +2831,7 @@ test "Terminal: print charset outside of ASCII" {
|
||||
}
|
||||
}
|
||||
|
||||
// X
|
||||
test "Terminal: print invoke charset" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
@ -2849,6 +2852,7 @@ test "Terminal: print invoke charset" {
|
||||
}
|
||||
}
|
||||
|
||||
// X
|
||||
test "Terminal: print invoke charset single" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
|
@ -4,6 +4,7 @@ const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const ansi = @import("../ansi.zig");
|
||||
const charsets = @import("../charsets.zig");
|
||||
const kitty = @import("../kitty.zig");
|
||||
const sgr = @import("../sgr.zig");
|
||||
const unicode = @import("../../unicode/main.zig");
|
||||
@ -29,6 +30,9 @@ cursor: Cursor,
|
||||
/// The saved cursor
|
||||
saved_cursor: ?SavedCursor = null,
|
||||
|
||||
/// The charset state
|
||||
charset: CharsetState = .{},
|
||||
|
||||
/// The current or most recent protected mode. Once a protection mode is
|
||||
/// set, this will never become "off" again until the screen is reset.
|
||||
/// The current state of whether protection attributes should be set is
|
||||
@ -79,8 +83,24 @@ pub const SavedCursor = struct {
|
||||
protected: bool,
|
||||
pending_wrap: bool,
|
||||
origin: bool,
|
||||
// TODO
|
||||
//charset: CharsetState,
|
||||
charset: CharsetState,
|
||||
};
|
||||
|
||||
/// State required for all charset operations.
|
||||
pub const CharsetState = struct {
|
||||
/// The list of graphical charsets by slot
|
||||
charsets: CharsetArray = CharsetArray.initFill(charsets.Charset.utf8),
|
||||
|
||||
/// GL is the slot to use when using a 7-bit printable char (up to 127)
|
||||
/// GR used for 8-bit printable chars.
|
||||
gl: charsets.Slots = .G0,
|
||||
gr: charsets.Slots = .G2,
|
||||
|
||||
/// Single shift where a slot is used for exactly one char.
|
||||
single_shift: ?charsets.Slots = null,
|
||||
|
||||
/// An array to map a charset slot to a lookup table.
|
||||
const CharsetArray = std.EnumArray(charsets.Slots, charsets.Charset);
|
||||
};
|
||||
|
||||
/// Initialize a new screen.
|
||||
|
@ -483,8 +483,27 @@ fn printCell(
|
||||
) void {
|
||||
// TODO: spacers should use a bgcolor only cell
|
||||
|
||||
// TODO: charsets
|
||||
const c: u21 = unmapped_c;
|
||||
const c: u21 = c: {
|
||||
// TODO: non-utf8 handling, gr
|
||||
|
||||
// If we're single shifting, then we use the key exactly once.
|
||||
const key = if (self.screen.charset.single_shift) |key_once| blk: {
|
||||
self.screen.charset.single_shift = null;
|
||||
break :blk key_once;
|
||||
} else self.screen.charset.gl;
|
||||
const set = self.screen.charset.charsets.get(key);
|
||||
|
||||
// UTF-8 or ASCII is used as-is
|
||||
if (set == .utf8 or set == .ascii) break :c unmapped_c;
|
||||
|
||||
// If we're outside of ASCII range this is an invalid value in
|
||||
// this table so we just return space.
|
||||
if (unmapped_c > std.math.maxInt(u8)) break :c ' ';
|
||||
|
||||
// Get our lookup table and map it
|
||||
const table = set.table();
|
||||
break :c @intCast(table[@intCast(unmapped_c)]);
|
||||
};
|
||||
|
||||
const cell = self.screen.cursor.page_cell;
|
||||
|
||||
@ -598,6 +617,31 @@ fn printWrap(self: *Terminal) !void {
|
||||
self.screen.cursor.page_row.wrap_continuation = true;
|
||||
}
|
||||
|
||||
/// Set the charset into the given slot.
|
||||
pub fn configureCharset(self: *Terminal, slot: charsets.Slots, set: charsets.Charset) void {
|
||||
self.screen.charset.charsets.set(slot, set);
|
||||
}
|
||||
|
||||
/// Invoke the charset in slot into the active slot. If single is true,
|
||||
/// then this will only be invoked for a single character.
|
||||
pub fn invokeCharset(
|
||||
self: *Terminal,
|
||||
active: charsets.ActiveSlot,
|
||||
slot: charsets.Slots,
|
||||
single: bool,
|
||||
) void {
|
||||
if (single) {
|
||||
assert(active == .GL);
|
||||
self.screen.charset.single_shift = slot;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (active) {
|
||||
.GL => self.screen.charset.gl = slot,
|
||||
.GR => self.screen.charset.gr = slot,
|
||||
}
|
||||
}
|
||||
|
||||
/// Carriage return moves the cursor to the first column.
|
||||
pub fn carriageReturn(self: *Terminal) void {
|
||||
// Always reset pending wrap state
|
||||
@ -787,8 +831,7 @@ pub fn saveCursor(self: *Terminal) void {
|
||||
.protected = self.screen.cursor.protected,
|
||||
.pending_wrap = self.screen.cursor.pending_wrap,
|
||||
.origin = self.modes.get(.origin),
|
||||
//TODO
|
||||
//.charset = self.screen.charset,
|
||||
.charset = self.screen.charset,
|
||||
};
|
||||
}
|
||||
|
||||
@ -804,8 +847,7 @@ pub fn restoreCursor(self: *Terminal) !void {
|
||||
.protected = false,
|
||||
.pending_wrap = false,
|
||||
.origin = false,
|
||||
// TODO
|
||||
//.charset = .{},
|
||||
.charset = .{},
|
||||
};
|
||||
|
||||
// Set the style first because it can fail
|
||||
@ -814,7 +856,7 @@ pub fn restoreCursor(self: *Terminal) !void {
|
||||
errdefer self.screen.cursor.style = old_style;
|
||||
try self.screen.manualStyleUpdate();
|
||||
|
||||
//self.screen.charset = saved.charset;
|
||||
self.screen.charset = saved.charset;
|
||||
self.modes.set(.origin, saved.origin);
|
||||
self.screen.cursor.pending_wrap = saved.pending_wrap;
|
||||
self.screen.cursor.protected = saved.protected;
|
||||
@ -2429,6 +2471,88 @@ test "Terminal: print writes to bottom if scrolled" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: print charset" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
// G1 should have no effect
|
||||
t.configureCharset(.G1, .dec_special);
|
||||
t.configureCharset(.G2, .dec_special);
|
||||
t.configureCharset(.G3, .dec_special);
|
||||
|
||||
// Basic grid writing
|
||||
try t.print('`');
|
||||
t.configureCharset(.G0, .utf8);
|
||||
try t.print('`');
|
||||
t.configureCharset(.G0, .ascii);
|
||||
try t.print('`');
|
||||
t.configureCharset(.G0, .dec_special);
|
||||
try t.print('`');
|
||||
{
|
||||
const str = try t.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
try testing.expectEqualStrings("```◆", str);
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: print charset outside of ASCII" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
// G1 should have no effect
|
||||
t.configureCharset(.G1, .dec_special);
|
||||
t.configureCharset(.G2, .dec_special);
|
||||
t.configureCharset(.G3, .dec_special);
|
||||
|
||||
// Basic grid writing
|
||||
t.configureCharset(.G0, .dec_special);
|
||||
try t.print('`');
|
||||
try t.print(0x1F600);
|
||||
{
|
||||
const str = try t.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
try testing.expectEqualStrings("◆ ", str);
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: print invoke charset" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
t.configureCharset(.G1, .dec_special);
|
||||
|
||||
// Basic grid writing
|
||||
try t.print('`');
|
||||
t.invokeCharset(.GL, .G1, false);
|
||||
try t.print('`');
|
||||
try t.print('`');
|
||||
t.invokeCharset(.GL, .G0, false);
|
||||
try t.print('`');
|
||||
{
|
||||
const str = try t.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
try testing.expectEqualStrings("`◆◆`", str);
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: print invoke charset single" {
|
||||
var t = try init(testing.allocator, 80, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
|
||||
t.configureCharset(.G1, .dec_special);
|
||||
|
||||
// Basic grid writing
|
||||
try t.print('`');
|
||||
t.invokeCharset(.GL, .G1, true);
|
||||
try t.print('`');
|
||||
try t.print('`');
|
||||
{
|
||||
const str = try t.plainString(testing.allocator);
|
||||
defer testing.allocator.free(str);
|
||||
try testing.expectEqualStrings("`◆`", str);
|
||||
}
|
||||
}
|
||||
|
||||
test "Terminal: soft wrap" {
|
||||
var t = try init(testing.allocator, 3, 80);
|
||||
defer t.deinit(testing.allocator);
|
||||
@ -5946,15 +6070,15 @@ test "Terminal: saveCursor" {
|
||||
defer t.deinit(alloc);
|
||||
|
||||
try t.setAttribute(.{ .bold = {} });
|
||||
//t.screen.charset.gr = .G3;
|
||||
t.screen.charset.gr = .G3;
|
||||
t.modes.set(.origin, true);
|
||||
t.saveCursor();
|
||||
//t.screen.charset.gr = .G0;
|
||||
t.screen.charset.gr = .G0;
|
||||
try t.setAttribute(.{ .unset = {} });
|
||||
t.modes.set(.origin, false);
|
||||
try t.restoreCursor();
|
||||
try testing.expect(t.screen.cursor.style.flags.bold);
|
||||
//try testing.expect(t.screen.charset.gr == .G3);
|
||||
try testing.expect(t.screen.charset.gr == .G3);
|
||||
try testing.expect(t.modes.get(.origin));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user