//! This is the render state that is given to a renderer. const std = @import("std"); const Allocator = std.mem.Allocator; const Inspector = @import("../inspector/main.zig").Inspector; const terminal = @import("../terminal/main.zig"); const renderer = @import("../renderer.zig"); /// The mutex that must be held while reading any of the data in the /// members of this state. Note that the state itself is NOT protected /// by the mutex and is NOT thread-safe, only the members values of the /// state (i.e. the terminal, devmode, etc. values). mutex: *std.Thread.Mutex, /// The terminal data. terminal: *terminal.Terminal, /// The terminal inspector, if any. This will be null while the inspector /// is not active and will be set when it is active. inspector: ?*Inspector = null, /// Dead key state. This will render the current dead key preedit text /// over the cursor. This currently only ever renders a single codepoint. /// Preedit can in theory be multiple codepoints long but that is left as /// a future exercise. preedit: ?Preedit = null, /// Mouse state. This only contains state relevant to what renderers /// need about the mouse. mouse: Mouse = .{}, pub const Mouse = struct { /// The point on the viewport where the mouse currently is. We use /// viewport points to avoid the complexity of mapping the mouse to /// the renderer state. point: ?terminal.point.Viewport = null, }; /// The pre-edit state. See Surface.preeditCallback for more information. pub const Preedit = struct { /// The codepoints to render as preedit text. codepoints: []const Codepoint = &.{}, /// A single codepoint to render as preedit text. pub const Codepoint = struct { codepoint: u21, wide: bool = false, }; /// Deinit this preedit that was cre pub fn deinit(self: *const Preedit, alloc: Allocator) void { alloc.free(self.codepoints); } /// Allocate a copy of this preedit in the given allocator.. pub fn clone(self: *const Preedit, alloc: Allocator) !Preedit { return .{ .codepoints = try alloc.dupe(Codepoint, self.codepoints), }; } /// The width in cells of all codepoints in the preedit. pub fn width(self: *const Preedit) usize { var result: usize = 0; for (self.codepoints) |cp| { result += if (cp.wide) 2 else 1; } return result; } /// Range returns the start and end x position of the preedit text /// along with any codepoint offset necessary to fit the preedit /// into the available space. pub fn range(self: *const Preedit, start: usize, max: usize) struct { start: usize, end: usize, cp_offset: usize, } { // If our width is greater than the number of cells we have // then we need to adjust our codepoint start to a point where // our width would be less than the number of cells we have. const w, const cp_offset = width: { // max is inclusive, so we need to add 1 to it. const max_width = max - start + 1; // Rebuild our width in reverse order. This is because we want // to offset by the end cells, not the start cells (if we have to). var w: usize = 0; for (0..self.codepoints.len) |i| { const reverse_i = self.codepoints.len - i - 1; const cp = self.codepoints[reverse_i]; w += if (cp.wide) 2 else 1; if (w > max_width) { break :width .{ w, reverse_i }; } } // Width fit in the max width so no offset necessary. break :width .{ w, 0 }; }; // If our preedit goes off the end of the screen, we adjust it so // that it shifts left. const end = start + w; const start_offset = if (end > max) end - max else 0; return .{ .start = start -| start_offset, .end = end -| start_offset, .cp_offset = cp_offset, }; } };