Something went wrong. Try again.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909const std = @import("std");const testing = std.testing;const Allocator = std.mem.Allocator;const lib = @import("../lib.zig");const CAllocator = lib.alloc.Allocator;const colorpkg = @import("../color.zig");const cursorpkg = @import("../cursor.zig");const page = @import("../page.zig");const size = @import("../size.zig");const Style = @import("../style.zig").Style;const terminal_c = @import("terminal.zig");const ZigTerminal = @import("../Terminal.zig");const renderpkg = @import("../render.zig");const Result = @import("result.zig").Result;const row = @import("row.zig");const style_c = @import("style.zig");
const log = std.log.scoped(.render_state_c);
const RenderStateWrapper = struct { alloc: std.mem.Allocator, state: renderpkg.RenderState = .empty,};
const RowIteratorWrapper = struct { alloc: std.mem.Allocator,
/// The current index (also y value) into the row list. y: ?size.CellCountInt,
/// These are the raw pointers into the render state data. raws: []const page.Row, cells: []const std.MultiArrayList(renderpkg.RenderState.Cell), selection: []const ?[2]size.CellCountInt, dirty: []bool,
/// The color palette from the render state, needed to resolve /// palette-indexed background colors on cells. palette: *const colorpkg.Palette,};
const RowCellsWrapper = struct { alloc: std.mem.Allocator, x: ?size.CellCountInt, raws: []const page.Cell, graphemes: []const []const u21, styles: []const Style, selection: ?[2]size.CellCountInt,
/// The color palette, needed to resolve palette-indexed background colors. palette: *const colorpkg.Palette,};
/// C: GhosttyRenderStatepub const RenderState = ?*RenderStateWrapper;
/// C: GhosttyRenderStateRowIteratorpub const RowIterator = ?*RowIteratorWrapper;
/// C: GhosttyRenderStateRowCellspub const RowCells = ?*RowCellsWrapper;
/// C: GhosttyRenderStateDirtypub const Dirty = renderpkg.RenderState.Dirty;
/// C: GhosttyRenderStateRowSelectionpub const RowSelection = extern struct { size: usize = @sizeOf(RowSelection), start_x: u16 = 0, end_x: u16 = 0,};
/// C: GhosttyRenderStateCursorVisualStylepub const CursorVisualStyle = enum(c_int) { bar = 0, block = 1, underline = 2, block_hollow = 3,
pub fn fromCursorStyle(s: cursorpkg.Style) CursorVisualStyle { return switch (s) { .bar => .bar, .block => .block, .underline => .underline, .block_hollow => .block_hollow, }; }};
/// C: GhosttyRenderStateDatapub const Data = enum(c_int) { invalid = 0, cols = 1, rows = 2, dirty = 3, row_iterator = 4, color_background = 5, color_foreground = 6, color_cursor = 7, color_cursor_has_value = 8, color_palette = 9, cursor_visual_style = 10, cursor_visible = 11, cursor_blinking = 12, cursor_password_input = 13, cursor_viewport_has_value = 14, cursor_viewport_x = 15, cursor_viewport_y = 16, cursor_viewport_wide_tail = 17,
/// Output type expected for querying the data of the given kind. pub fn OutType(comptime self: Data) type { return switch (self) { .invalid => void, .cols, .rows => size.CellCountInt, .dirty => Dirty, .row_iterator => RowIterator, .color_background, .color_foreground, .color_cursor => colorpkg.RGB.C, .color_cursor_has_value => bool, .color_palette => colorpkg.PaletteC, .cursor_visual_style => CursorVisualStyle, .cursor_visible, .cursor_blinking, .cursor_password_input => bool, .cursor_viewport_has_value, .cursor_viewport_wide_tail => bool, .cursor_viewport_x, .cursor_viewport_y => size.CellCountInt, }; }};
/// C: GhosttyRenderStateOptionpub const SetOption = enum(c_int) { dirty = 0,
/// Input type expected for setting the option. pub fn InType(comptime self: SetOption) type { return switch (self) { .dirty => Dirty, }; }};
/// C: GhosttyRenderStateColorspub const Colors = extern struct { size: usize = @sizeOf(Colors), background: colorpkg.RGB.C, foreground: colorpkg.RGB.C, cursor: colorpkg.RGB.C, cursor_has_value: bool, palette: colorpkg.PaletteC,};
pub fn new( alloc_: ?*const CAllocator, result: *RenderState,) callconv(lib.calling_conv) Result { result.* = new_(alloc_) catch |err| { result.* = null; return switch (err) { error.OutOfMemory => .out_of_memory, }; };
return .success;}
fn new_(alloc_: ?*const CAllocator) error{OutOfMemory}!*RenderStateWrapper { const alloc = lib.alloc.default(alloc_); const ptr = alloc.create(RenderStateWrapper) catch return error.OutOfMemory; ptr.* = .{ .alloc = alloc }; return ptr;}
pub fn free(state_: RenderState) callconv(lib.calling_conv) void { const state = state_ orelse return; const alloc = state.alloc; state.state.deinit(alloc); alloc.destroy(state);}
pub fn update( state_: RenderState, terminal_: terminal_c.Terminal,) callconv(lib.calling_conv) Result { const state = state_ orelse return .invalid_value; const t: *ZigTerminal = (terminal_ orelse return .invalid_value).terminal;
state.state.update(state.alloc, t) catch return .out_of_memory; return .success;}
pub fn get( state_: RenderState, data: Data, out: ?*anyopaque,) callconv(lib.calling_conv) Result { if (comptime std.debug.runtime_safety) { _ = std.meta.intToEnum(Data, @intFromEnum(data)) catch { log.warn("render_state_get invalid data value={d}", .{@intFromEnum(data)}); return .invalid_value; }; }
return switch (data) { .invalid => .invalid_value, inline else => |comptime_data| getTyped( state_, comptime_data, @ptrCast(@alignCast(out)), ), };}
pub fn get_multi( state_: RenderState, count: usize, keys: ?[*]const Data, values: ?[*]?*anyopaque, out_written: ?*usize,) callconv(lib.calling_conv) Result { const k = keys orelse return .invalid_value; const v = values orelse return .invalid_value;
for (0..count) |i| { const result = get(state_, k[i], v[i]); if (result != .success) { if (out_written) |w| w.* = i; return result; } } if (out_written) |w| w.* = count; return .success;}
fn getTyped( state_: RenderState, comptime data: Data, out: *data.OutType(),) Result { const state = state_ orelse return .invalid_value; switch (data) { .invalid => return .invalid_value, .cols => out.* = state.state.cols, .rows => out.* = state.state.rows, .dirty => out.* = state.state.dirty, .row_iterator => { const it = out.* orelse return .invalid_value; const row_data = state.state.row_data.slice(); it.* = .{ .alloc = it.alloc, .y = null, .raws = row_data.items(.raw), .cells = row_data.items(.cells), .selection = row_data.items(.selection), .dirty = row_data.items(.dirty), .palette = &state.state.colors.palette, }; }, .color_background => out.* = state.state.colors.background.cval(), .color_foreground => out.* = state.state.colors.foreground.cval(), .color_cursor => { const cursor = state.state.colors.cursor orelse return .invalid_value; out.* = cursor.cval(); }, .color_cursor_has_value => out.* = state.state.colors.cursor != null, .color_palette => out.* = colorpkg.paletteCval(&state.state.colors.palette), .cursor_visual_style => out.* = CursorVisualStyle.fromCursorStyle(state.state.cursor.visual_style), .cursor_visible => out.* = state.state.cursor.visible, .cursor_blinking => out.* = state.state.cursor.blinking, .cursor_password_input => out.* = state.state.cursor.password_input, .cursor_viewport_has_value => out.* = state.state.cursor.viewport != null, .cursor_viewport_x => { const vp = state.state.cursor.viewport orelse return .invalid_value; out.* = vp.x; }, .cursor_viewport_y => { const vp = state.state.cursor.viewport orelse return .invalid_value; out.* = vp.y; }, .cursor_viewport_wide_tail => { const vp = state.state.cursor.viewport orelse return .invalid_value; out.* = vp.wide_tail; }, }
return .success;}
pub fn set( state_: RenderState, option: SetOption, value: ?*const anyopaque,) callconv(lib.calling_conv) Result { if (comptime std.debug.runtime_safety) { _ = std.meta.intToEnum(SetOption, @intFromEnum(option)) catch { log.warn("render_state_set invalid option value={d}", .{@intFromEnum(option)}); return .invalid_value; }; }
return switch (option) { inline else => |comptime_option| setTyped( state_, comptime_option, @ptrCast(@alignCast(value orelse return .invalid_value)), ), };}
fn setTyped( state_: RenderState, comptime option: SetOption, value: *const option.InType(),) Result { const state = state_ orelse return .invalid_value; switch (option) { .dirty => state.state.dirty = value.*, }
return .success;}
pub fn colors_get( state_: RenderState, out_colors_: ?*Colors,) callconv(lib.calling_conv) Result { const state = state_ orelse return .invalid_value; const out_colors = out_colors_ orelse return .invalid_value; const out_size = out_colors.size; if (out_size < @sizeOf(usize)) return .invalid_value;
const colors = state.state.colors; if (lib.structSizedFieldFits( Colors, out_size, "background", )) { out_colors.background = colors.background.cval(); }
if (lib.structSizedFieldFits( Colors, out_size, "foreground", )) { out_colors.foreground = colors.foreground.cval(); }
if (colors.cursor) |cursor| { if (lib.structSizedFieldFits( Colors, out_size, "cursor", )) { out_colors.cursor = cursor.cval(); } }
if (lib.structSizedFieldFits( Colors, out_size, "cursor_has_value", )) { out_colors.cursor_has_value = colors.cursor != null; }
{ const palette_offset = @offsetOf(Colors, "palette"); if (out_size > palette_offset) { const available = out_size - palette_offset; const max_entries = @min(colors.palette.len, available / @sizeOf(colorpkg.RGB.C)); for (0..max_entries) |i| { out_colors.palette[i] = colors.palette[i].cval(); } } }
return .success;}
pub fn row_iterator_new( alloc_: ?*const CAllocator, result: *RowIterator,) callconv(lib.calling_conv) Result { const alloc = lib.alloc.default(alloc_); const ptr = alloc.create(RowIteratorWrapper) catch { result.* = null; return .out_of_memory; }; ptr.* = .{ .alloc = alloc, .y = undefined, .raws = undefined, .cells = undefined, .selection = undefined, .dirty = undefined, .palette = undefined, }; result.* = ptr; return .success;}
pub fn row_iterator_free(iterator_: RowIterator) callconv(lib.calling_conv) void { const iterator = iterator_ orelse return; const alloc = iterator.alloc; alloc.destroy(iterator);}
pub fn row_iterator_next(iterator_: RowIterator) callconv(lib.calling_conv) bool { const it = iterator_ orelse return false; const next_y: size.CellCountInt = if (it.y) |y| y + 1 else 0; if (next_y >= it.raws.len) return false; it.y = next_y; return true;}
pub fn row_cells_new( alloc_: ?*const CAllocator, result: *RowCells,) callconv(lib.calling_conv) Result { const alloc = lib.alloc.default(alloc_); const ptr = alloc.create(RowCellsWrapper) catch { result.* = null; return .out_of_memory; }; ptr.* = .{ .alloc = alloc, .x = undefined, .raws = undefined, .graphemes = undefined, .styles = undefined, .selection = undefined, .palette = undefined, }; result.* = ptr; return .success;}
pub fn row_cells_next(cells_: RowCells) callconv(lib.calling_conv) bool { const cells = cells_ orelse return false; const next_x: size.CellCountInt = if (cells.x) |x| x + 1 else 0; if (next_x >= cells.raws.len) return false; cells.x = next_x; return true;}
pub fn row_cells_select(cells_: RowCells, x: size.CellCountInt) callconv(lib.calling_conv) Result { const cells = cells_ orelse return .invalid_value; if (x >= cells.raws.len) return .invalid_value; cells.x = x; return .success;}
pub fn row_cells_free(cells_: RowCells) callconv(lib.calling_conv) void { const cells = cells_ orelse return; const alloc = cells.alloc; alloc.destroy(cells);}
/// C: GhosttyRenderStateRowCellsDatapub const RowCellsData = enum(c_int) { invalid = 0, raw = 1, style = 2, graphemes_len = 3, graphemes_buf = 4, bg_color = 5, fg_color = 6, selected = 7, has_styling = 8, graphemes_utf8 = 9,
/// Output type expected for querying the data of the given kind. pub fn OutType(comptime self: RowCellsData) type { return switch (self) { .invalid => void, .raw => page.Cell.C, .style => style_c.Style, .graphemes_len => u32, .graphemes_buf => u32, .bg_color, .fg_color => colorpkg.RGB.C, .selected, .has_styling => bool, .graphemes_utf8 => lib.Buffer, }; }};
pub fn row_cells_get( cells_: RowCells, data: RowCellsData, out: ?*anyopaque,) callconv(lib.calling_conv) Result { if (comptime std.debug.runtime_safety) { _ = std.meta.intToEnum(RowCellsData, @intFromEnum(data)) catch { log.warn("render_state_row_cells_get invalid data value={d}", .{@intFromEnum(data)}); return .invalid_value; }; } if (out == null) return .invalid_value;
return switch (data) { .invalid => .invalid_value, inline else => |comptime_data| rowCellsGetTyped( cells_, comptime_data, @ptrCast(@alignCast(out)), ), };}
pub fn row_cells_get_multi( cells_: RowCells, count: usize, keys: ?[*]const RowCellsData, values: ?[*]?*anyopaque, out_written: ?*usize,) callconv(lib.calling_conv) Result { const k = keys orelse return .invalid_value; const v = values orelse return .invalid_value;
for (0..count) |i| { const result = row_cells_get(cells_, k[i], v[i]); if (result != .success) { if (out_written) |w| w.* = i; return result; } } if (out_written) |w| w.* = count; return .success;}
fn rowCellsGetTyped( cells_: RowCells, comptime data: RowCellsData, out: *data.OutType(),) Result { const cells = cells_ orelse return .invalid_value; const x = cells.x orelse return .invalid_value; const cell = cells.raws[x]; switch (data) { .invalid => return .invalid_value, .raw => out.* = cell.cval(), .style => out.* = if (cell.hasStyling()) style_c.Style.fromStyle(cells.styles[x]) else style_c.Style.fromStyle(.{}), .graphemes_len => { if (!cell.hasText()) { out.* = 0; return .success; } const extra = if (cell.hasGrapheme()) cells.graphemes[x] else &[_]u21{}; out.* = @intCast(1 + extra.len); }, .graphemes_buf => { if (!cell.hasText()) return .success; const extra = if (cell.hasGrapheme()) cells.graphemes[x] else &[_]u21{}; const buf: [*]u32 = @ptrCast(out); buf[0] = cell.codepoint(); for (extra, 1..) |cp, i| { buf[i] = cp; } }, .bg_color => { const s: Style = if (cell.hasStyling()) cells.styles[x] else .{}; const bg = s.bg(&cell, cells.palette) orelse return .invalid_value; out.* = bg.cval(); }, .fg_color => { const s: Style = if (cell.hasStyling()) cells.styles[x] else .{}; if (s.fg_color == .none) return .invalid_value; const fg = s.fg(.{ .default = .{}, .palette = cells.palette }); out.* = fg.cval(); }, .selected => out.* = if (cells.selection) |sel| x >= sel[0] and x <= sel[1] else false, .has_styling => out.* = cell.hasStyling(), .graphemes_utf8 => return rowCellsGetGraphemesUtf8(cell, if (cell.hasGrapheme()) cells.graphemes[x] else &.{}, out), }
return .success;}
fn rowCellsGetGraphemesUtf8( cell: page.Cell, extra: []const u21, out: *lib.Buffer,) Result { out.len = 0;
if (!cell.hasText()) return .success;
var needed = std.unicode.utf8CodepointSequenceLength(cell.codepoint()) catch return .invalid_value; for (extra) |cp| { needed += std.unicode.utf8CodepointSequenceLength(cp) catch return .invalid_value; } out.len = needed;
if (out.ptr == null or out.cap < needed) return .out_of_space;
const buf = out.ptr.?[0..out.cap]; var i: usize = 0; i += std.unicode.utf8Encode(cell.codepoint(), buf[i..]) catch return .invalid_value; for (extra) |cp| { i += std.unicode.utf8Encode(cp, buf[i..]) catch return .invalid_value; }
out.len = i; return .success;}
/// C: GhosttyRenderStateRowDatapub const RowData = enum(c_int) { invalid = 0, dirty = 1, raw = 2, cells = 3, selection = 4,
/// Output type expected for querying the data of the given kind. pub fn OutType(comptime self: RowData) type { return switch (self) { .invalid => void, .dirty => bool, .raw => row.CRow, .cells => RowCells, .selection => RowSelection, }; }};
/// C: GhosttyRenderStateRowOptionpub const RowOption = enum(c_int) { dirty = 0,
/// Input type expected for setting the option. pub fn InType(comptime self: RowOption) type { return switch (self) { .dirty => bool, }; }};
pub fn row_get( iterator_: RowIterator, data: RowData, out: ?*anyopaque,) callconv(lib.calling_conv) Result { if (comptime std.debug.runtime_safety) { _ = std.meta.intToEnum(RowData, @intFromEnum(data)) catch { log.warn("render_state_row_get invalid data value={d}", .{@intFromEnum(data)}); return .invalid_value; }; }
return switch (data) { .invalid => .invalid_value, inline else => |comptime_data| rowGetTyped( iterator_, comptime_data, @ptrCast(@alignCast(out)), ), };}
pub fn row_get_multi( iterator_: RowIterator, count: usize, keys: ?[*]const RowData, values: ?[*]?*anyopaque, out_written: ?*usize,) callconv(lib.calling_conv) Result { const k = keys orelse return .invalid_value; const v = values orelse return .invalid_value;
for (0..count) |i| { const result = row_get(iterator_, k[i], v[i]); if (result != .success) { if (out_written) |w| w.* = i; return result; } } if (out_written) |w| w.* = count; return .success;}
fn rowGetTyped( iterator_: RowIterator, comptime data: RowData, out: *data.OutType(),) Result { const it = iterator_ orelse return .invalid_value; const y = it.y orelse return .invalid_value; switch (data) { .invalid => return .invalid_value, .dirty => out.* = it.dirty[y], .raw => out.* = it.raws[y].cval(), .cells => { const cells = out.* orelse return .invalid_value; const cell_data = it.cells[y].slice(); cells.* = .{ .alloc = cells.alloc, .x = null, .raws = cell_data.items(.raw), .graphemes = cell_data.items(.grapheme), .styles = cell_data.items(.style), .selection = it.selection[y], .palette = it.palette, }; }, .selection => { const out_size = out.size; if (out_size < @sizeOf(RowSelection)) return .invalid_value;
const sel = it.selection[y] orelse return .no_value; out.start_x = sel[0]; out.end_x = sel[1]; }, }
return .success;}
pub fn row_set( iterator_: RowIterator, option: RowOption, value: ?*const anyopaque,) callconv(lib.calling_conv) Result { if (comptime std.debug.runtime_safety) { _ = std.meta.intToEnum(RowOption, @intFromEnum(option)) catch { log.warn("render_state_row_set invalid option value={d}", .{@intFromEnum(option)}); return .invalid_value; }; }
return switch (option) { inline else => |comptime_option| rowSetTyped( iterator_, comptime_option, @ptrCast(@alignCast(value orelse return .invalid_value)), ), };}
fn rowSetTyped( iterator_: RowIterator, comptime option: RowOption, value: *const option.InType(),) Result { const it = iterator_ orelse return .invalid_value; const y = it.y orelse return .invalid_value; switch (option) { .dirty => it.dirty[y] = value.*, }
return .success;}
test "render: new/free" { var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); try testing.expect(state != null); free(state);}
test "render: free null" { free(null);}
test "render: update invalid value" { var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.invalid_value, update(null, null)); try testing.expectEqual(Result.invalid_value, update(state, null));}
test "render: get invalid value" { var cols: size.CellCountInt = 0; try testing.expectEqual(Result.invalid_value, get(null, .cols, @ptrCast(&cols)));}
test "render: get invalid data" { var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.invalid_value, get(state, .invalid, null));}
test "render: colors get invalid value" { var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
var colors: Colors = std.mem.zeroes(Colors); colors.size = @sizeOf(Colors);
try testing.expectEqual(Result.invalid_value, colors_get(null, &colors)); try testing.expectEqual(Result.invalid_value, colors_get(state, null));
colors.size = @sizeOf(usize) - 1; try testing.expectEqual(Result.invalid_value, colors_get(state, &colors));}
test "render: get/set dirty invalid value" { var dirty: Dirty = .false; try testing.expectEqual(Result.invalid_value, get(null, .dirty, @ptrCast(&dirty))); const dirty_full: Dirty = .full; try testing.expectEqual(Result.invalid_value, set(null, .dirty, @ptrCast(&dirty_full)));}
test "render: get/set dirty" { var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
var dirty: Dirty = undefined; try testing.expectEqual(Result.success, get(state, .dirty, @ptrCast(&dirty))); try testing.expectEqual(Dirty.false, dirty);
const dirty_partial: Dirty = .partial; try testing.expectEqual(Result.success, set(state, .dirty, @ptrCast(&dirty_partial))); try testing.expectEqual(Result.success, get(state, .dirty, @ptrCast(&dirty))); try testing.expectEqual(Dirty.partial, dirty);
const dirty_full: Dirty = .full; try testing.expectEqual(Result.success, set(state, .dirty, @ptrCast(&dirty_full))); try testing.expectEqual(Result.success, get(state, .dirty, @ptrCast(&dirty))); try testing.expectEqual(Dirty.full, dirty);}
test "render: set null value" { var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.invalid_value, set(state, .dirty, null));}
test "render: row iterator get invalid value" { var iterator: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &iterator, )); defer row_iterator_free(iterator);
try testing.expectEqual(Result.invalid_value, get(null, .row_iterator, @ptrCast(&iterator)));}
test "render: row iterator new/free" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var iterator: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &iterator, )); defer row_iterator_free(iterator);
try testing.expect(iterator != null);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator)));
const iterator_ptr = iterator.?; const row_data = state.?.state.row_data.slice();
try testing.expectEqual(@as(?size.CellCountInt, null), iterator_ptr.y); try testing.expectEqual(row_data.items(.raw).len, iterator_ptr.raws.len); try testing.expectEqual(row_data.items(.cells).len, iterator_ptr.cells.len); try testing.expectEqual(row_data.items(.selection).len, iterator_ptr.selection.len); try testing.expectEqual(row_data.items(.dirty).len, iterator_ptr.dirty.len);}
test "render: row iterator free null" { row_iterator_free(null);}
test "render: row iterator next null" { try testing.expect(!row_iterator_next(null));}
test "render: row get null" { var dirty: bool = undefined; try testing.expectEqual(Result.invalid_value, row_get(null, .dirty, @ptrCast(&dirty)));}
test "render: row get invalid data" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var iterator: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &iterator, )); defer row_iterator_free(iterator);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator))); try testing.expect(row_iterator_next(iterator)); try testing.expectEqual(Result.invalid_value, row_get(iterator, .invalid, null));}
test "render: row set null" { const dirty = false; try testing.expectEqual(Result.invalid_value, row_set(null, .dirty, @ptrCast(&dirty)));}
test "render: row set before iteration" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var iterator: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &iterator, )); defer row_iterator_free(iterator);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator))); const dirty = false; try testing.expectEqual(Result.invalid_value, row_set(iterator, .dirty, @ptrCast(&dirty)));}
test "render: row get before iteration" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var iterator: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &iterator, )); defer row_iterator_free(iterator);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator))); var dirty: bool = undefined; try testing.expectEqual(Result.invalid_value, row_get(iterator, .dirty, @ptrCast(&dirty)));}
test "render: row get/set dirty" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
// Dirty the first row so the iterator has at least one dirty row to observe. terminal_c.vt_write(terminal, "hello", 5); try testing.expectEqual(Result.success, update(state, terminal));
// Create an iterator and verify it is dirty. var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it)); var dirty: bool = undefined; try testing.expectEqual(Result.success, row_get(it, .dirty, @ptrCast(&dirty))); try testing.expect(dirty);
// Clear dirty on this row. const dirty_false = false; try testing.expectEqual(Result.success, row_set(it, .dirty, @ptrCast(&dirty_false)));
// It should not be dirty anymore. var it2: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it2, )); defer row_iterator_free(it2);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it2))); try testing.expect(row_iterator_next(it2)); try testing.expectEqual(Result.success, row_get(it2, .dirty, @ptrCast(&dirty))); try testing.expect(!dirty);}
test "render: row get selection" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 10, .rows = 3, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
const t = terminal.?.terminal; const screen = t.screens.active; try screen.select(.init( screen.pages.pin(.{ .active = .{ .x = 2, .y = 1 } }).?, screen.pages.pin(.{ .active = .{ .x = 4, .y = 1 } }).?, false, ));
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it)));
var sel: RowSelection = .{}; try testing.expect(row_iterator_next(it)); try testing.expectEqual(Result.no_value, row_get(it, .selection, @ptrCast(&sel)));
try testing.expect(row_iterator_next(it)); sel = .{}; try testing.expectEqual(Result.success, row_get(it, .selection, @ptrCast(&sel))); try testing.expectEqual(@as(u16, 2), sel.start_x); try testing.expectEqual(@as(u16, 4), sel.end_x);
try testing.expect(row_iterator_next(it)); sel = .{}; try testing.expectEqual(Result.no_value, row_get(it, .selection, @ptrCast(&sel)));}
test "render: row cells get selected" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 10, .rows = 3, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
const t = terminal.?.terminal; const screen = t.screens.active; try screen.select(.init( screen.pages.pin(.{ .active = .{ .x = 2, .y = 1 } }).?, screen.pages.pin(.{ .active = .{ .x = 4, .y = 1 } }).?, false, ));
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it)));
try testing.expect(row_iterator_next(it)); try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells)));
var selected: bool = true; try testing.expectEqual(Result.success, row_cells_select(cells, 0)); try testing.expectEqual(Result.success, row_cells_get(cells, .selected, @ptrCast(&selected))); try testing.expect(!selected);
try testing.expect(row_iterator_next(it)); try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells)));
try testing.expectEqual(Result.success, row_cells_select(cells, 1)); try testing.expectEqual(Result.success, row_cells_get(cells, .selected, @ptrCast(&selected))); try testing.expect(!selected);
try testing.expectEqual(Result.success, row_cells_select(cells, 2)); try testing.expectEqual(Result.success, row_cells_get(cells, .selected, @ptrCast(&selected))); try testing.expect(selected);
try testing.expectEqual(Result.success, row_cells_select(cells, 4)); try testing.expectEqual(Result.success, row_cells_get(cells, .selected, @ptrCast(&selected))); try testing.expect(selected);
try testing.expectEqual(Result.success, row_cells_select(cells, 5)); try testing.expectEqual(Result.success, row_cells_get(cells, .selected, @ptrCast(&selected))); try testing.expect(!selected);
try testing.expectEqual(Result.success, row_cells_select(cells, 3)); selected = false; var written: usize = 0; const keys = [_]RowCellsData{.selected}; var values = [_]?*anyopaque{@ptrCast(&selected)}; try testing.expectEqual(Result.success, row_cells_get_multi(cells, keys.len, &keys, &values, &written)); try testing.expectEqual(keys.len, written); try testing.expect(selected);}
test "render: row cells get has_styling" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 10, .rows = 3, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
const input = "A\x1b[31mB"; terminal_c.vt_write(terminal, input, input.len);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it)); try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells)));
var has_styling = true; try testing.expectEqual(Result.success, row_cells_select(cells, 0)); try testing.expectEqual(Result.success, row_cells_get(cells, .has_styling, @ptrCast(&has_styling))); try testing.expect(!has_styling);
try testing.expectEqual(Result.success, row_cells_select(cells, 1)); try testing.expectEqual(Result.success, row_cells_get(cells, .has_styling, @ptrCast(&has_styling))); try testing.expect(has_styling);
has_styling = false; var written: usize = 0; const keys = [_]RowCellsData{.has_styling}; var values = [_]?*anyopaque{@ptrCast(&has_styling)}; try testing.expectEqual(Result.success, row_cells_get_multi(cells, keys.len, &keys, &values, &written)); try testing.expectEqual(keys.len, written); try testing.expect(has_styling);}
test "render: row cells get graphemes utf8" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 10, .rows = 3, .max_scrollback = 10_000 }, )); defer terminal_c.free(terminal);
const input = "e\u{301}"; terminal_c.vt_write(terminal, input, input.len);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it)); try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells)));
try testing.expectEqual(Result.success, row_cells_select(cells, 0));
var text: lib.Buffer = .{}; try testing.expectEqual(Result.out_of_space, row_cells_get(cells, .graphemes_utf8, @ptrCast(&text))); try testing.expectEqual(@as(usize, input.len), text.len);
var small = [_]u8{ 'x', 'x' }; text = .{ .ptr = &small, .cap = small.len }; try testing.expectEqual(Result.out_of_space, row_cells_get(cells, .graphemes_utf8, @ptrCast(&text))); try testing.expectEqual(@as(usize, input.len), text.len); try testing.expectEqualSlices(u8, &.{ 'x', 'x' }, &small);
var buf: [8]u8 = undefined; text = .{ .ptr = &buf, .cap = buf.len }; try testing.expectEqual(Result.success, row_cells_get(cells, .graphemes_utf8, @ptrCast(&text))); try testing.expectEqual(input.len, text.len); try testing.expectEqualStrings(input, buf[0..text.len]);
try testing.expectEqual(Result.success, row_cells_select(cells, 1)); text = .{ .ptr = &buf, .cap = buf.len }; try testing.expectEqual(Result.success, row_cells_get(cells, .graphemes_utf8, @ptrCast(&text))); try testing.expectEqual(@as(usize, 0), text.len);}
test "render: row iterator next" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var iterator: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &iterator, )); defer row_iterator_free(iterator);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator)));
const rows = state.?.state.rows; if (rows == 0) { try testing.expect(!row_iterator_next(iterator)); return; }
try testing.expect(row_iterator_next(iterator)); try testing.expectEqual(@as(?size.CellCountInt, 0), iterator.?.y);
var i: size.CellCountInt = 1; while (i < rows) : (i += 1) { try testing.expect(row_iterator_next(iterator)); try testing.expectEqual(@as(?size.CellCountInt, i), iterator.?.y); }
try testing.expect(!row_iterator_next(iterator)); try testing.expectEqual(@as(?size.CellCountInt, rows - 1), iterator.?.y);}
test "render: update" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
terminal_c.vt_write(terminal, "hello", 5); try testing.expectEqual(Result.success, update(state, terminal));
var cols: size.CellCountInt = 0; var rows_val: size.CellCountInt = 0; try testing.expectEqual(Result.success, get(state, .cols, @ptrCast(&cols))); try testing.expectEqual(Result.success, get(state, .rows, @ptrCast(&rows_val))); try testing.expectEqual(@as(size.CellCountInt, 80), cols); try testing.expectEqual(@as(size.CellCountInt, 24), rows_val);}
test "render: colors get" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var colors: Colors = std.mem.zeroes(Colors); colors.size = @sizeOf(Colors); try testing.expectEqual(Result.success, colors_get(state, &colors));
const state_colors = &state.?.state.colors; try testing.expectEqual(state_colors.background.cval(), colors.background); try testing.expectEqual(state_colors.foreground.cval(), colors.foreground);
if (state_colors.cursor) |cursor| { try testing.expect(colors.cursor_has_value); try testing.expectEqual(cursor.cval(), colors.cursor); } else { try testing.expect(!colors.cursor_has_value); }
for (state_colors.palette, colors.palette) |expected, actual| { try testing.expectEqual(expected.cval(), actual); }}
test "render: row cells bg_color no background" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
// Write plain text (no background color set). terminal_c.vt_write(terminal, "hello", 5);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it));
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells))); try testing.expect(row_cells_next(cells));
// No background set, should return invalid_value. var bg: colorpkg.RGB.C = undefined; try testing.expectEqual(Result.invalid_value, row_cells_get(cells, .bg_color, @ptrCast(&bg)));}
test "render: row cells bg_color from style" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
// Set an RGB background via SGR 48;2;R;G;B and write text. terminal_c.vt_write(terminal, "\x1b[48;2;10;20;30mA", 18);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it));
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells))); try testing.expect(row_cells_next(cells));
var bg: colorpkg.RGB.C = undefined; try testing.expectEqual(Result.success, row_cells_get(cells, .bg_color, @ptrCast(&bg))); try testing.expectEqual(@as(u8, 10), bg.r); try testing.expectEqual(@as(u8, 20), bg.g); try testing.expectEqual(@as(u8, 30), bg.b);}
test "render: row cells bg_color from content tag" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
// Set an RGB background and then erase the line. The erased cells // should carry the background color via the content tag (bg_color_rgb) // rather than through the style. terminal_c.vt_write(terminal, "\x1b[48;2;10;20;30m\x1b[2K", 21);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it));
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells))); try testing.expect(row_cells_next(cells));
var bg: colorpkg.RGB.C = undefined; try testing.expectEqual(Result.success, row_cells_get(cells, .bg_color, @ptrCast(&bg))); try testing.expectEqual(@as(u8, 10), bg.r); try testing.expectEqual(@as(u8, 20), bg.g); try testing.expectEqual(@as(u8, 30), bg.b);}
test "render: row cells fg_color no foreground" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
// Write plain text (no foreground color set). terminal_c.vt_write(terminal, "hello", 5);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it));
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells))); try testing.expect(row_cells_next(cells));
// No foreground set, should return invalid_value. var fg: colorpkg.RGB.C = undefined; try testing.expectEqual(Result.invalid_value, row_cells_get(cells, .fg_color, @ptrCast(&fg)));}
test "render: row cells fg_color from style" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
// Set an RGB foreground via SGR 38;2;R;G;B and write text. terminal_c.vt_write(terminal, "\x1b[38;2;10;20;30mA", 18);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it));
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells))); try testing.expect(row_cells_next(cells));
var fg: colorpkg.RGB.C = undefined; try testing.expectEqual(Result.success, row_cells_get(cells, .fg_color, @ptrCast(&fg))); try testing.expectEqual(@as(u8, 10), fg.r); try testing.expectEqual(@as(u8, 20), fg.g); try testing.expectEqual(@as(u8, 30), fg.b);}
test "render: colors get supports truncated sized struct" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000, }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var colors: Colors = std.mem.zeroes(Colors); const sentinel: colorpkg.RGB.C = .{ .r = 0xAA, .g = 0xBB, .b = 0xCC }; for (&colors.palette) |*entry| entry.* = sentinel;
colors.size = @offsetOf(Colors, "palette") + @sizeOf(colorpkg.RGB.C) * 2; try testing.expectEqual(Result.success, colors_get(state, &colors));
const state_colors = &state.?.state.colors; try testing.expectEqual(state_colors.palette[0].cval(), colors.palette[0]); try testing.expectEqual(state_colors.palette[1].cval(), colors.palette[1]); try testing.expectEqual(sentinel, colors.palette[2]);}
test "render: get_multi success" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000 }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var cols: u16 = 0; var rows: u16 = 0; var written: usize = 0;
const keys = [_]Data{ .cols, .rows }; var values = [_]?*anyopaque{ @ptrCast(&cols), @ptrCast(&rows) }; try testing.expectEqual(Result.success, get_multi(state, keys.len, &keys, &values, &written)); try testing.expectEqual(keys.len, written); try testing.expectEqual(80, cols); try testing.expectEqual(24, rows);}
test "render: get_multi null returns invalid_value" { var cols: u16 = 0; var values = [_]?*anyopaque{@ptrCast(&cols)}; try testing.expectEqual(Result.invalid_value, get_multi(null, 1, null, &values, null));}
test "render: row_get_multi success" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000 }, )); defer terminal_c.free(terminal);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it));
var dirty: bool = true; var written: usize = 0;
const keys = [_]RowData{.dirty}; var values = [_]?*anyopaque{@ptrCast(&dirty)}; try testing.expectEqual(Result.success, row_get_multi(it, keys.len, &keys, &values, &written)); try testing.expectEqual(keys.len, written);}
test "render: row_get_multi null returns invalid_value" { var dirty: bool = false; var values = [_]?*anyopaque{@ptrCast(&dirty)}; try testing.expectEqual(Result.invalid_value, row_get_multi(null, 1, null, &values, null));}
test "render: row_cells_get_multi success" { var terminal: terminal_c.Terminal = null; try testing.expectEqual(Result.success, terminal_c.new( &lib.alloc.test_allocator, &terminal, .{ .cols = 80, .rows = 24, .max_scrollback = 10_000 }, )); defer terminal_c.free(terminal);
terminal_c.vt_write(terminal, "A", 1);
var state: RenderState = null; try testing.expectEqual(Result.success, new( &lib.alloc.test_allocator, &state, )); defer free(state);
try testing.expectEqual(Result.success, update(state, terminal));
var it: RowIterator = null; try testing.expectEqual(Result.success, row_iterator_new( &lib.alloc.test_allocator, &it, )); defer row_iterator_free(it);
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it))); try testing.expect(row_iterator_next(it));
var cells: RowCells = null; try testing.expectEqual(Result.success, row_cells_new( &lib.alloc.test_allocator, &cells, )); defer row_cells_free(cells);
try testing.expectEqual(Result.success, row_get(it, .cells, @ptrCast(&cells))); try testing.expect(row_cells_next(cells));
var raw: row.CRow = undefined; var written: usize = 0;
const keys = [_]RowCellsData{.raw}; var values = [_]?*anyopaque{@ptrCast(&raw)}; try testing.expectEqual(Result.success, row_cells_get_multi(cells, keys.len, &keys, &values, &written)); try testing.expectEqual(keys.len, written);}
test "render: row_cells_get_multi null returns invalid_value" { var raw: row.CRow = undefined; var values = [_]?*anyopaque{@ptrCast(&raw)}; try testing.expectEqual(Result.invalid_value, row_cells_get_multi(null, 1, null, &values, null));}