diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/renderer/State.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/renderer/State.zig')
| -rw-r--r-- | vendor/libghostty-vt/src/renderer/State.zig | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/renderer/State.zig b/vendor/libghostty-vt/src/renderer/State.zig new file mode 100644 index 00000000..e882ddce --- /dev/null +++ b/vendor/libghostty-vt/src/renderer/State.zig @@ -0,0 +1,123 @@ +//! 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 terminalpkg = @import("../terminal/main.zig"); +const inputpkg = @import("../input.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: *terminalpkg.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: ?terminalpkg.point.Coordinate = null, + + /// The mods that are currently active for the last mouse event. + /// This could really just be mods in general and we probably will + /// move it out of mouse state at some point. + mods: inputpkg.Mods = .{}, +}; + +/// 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: terminalpkg.size.CellCountInt, + max: terminalpkg.size.CellCountInt, + ) struct { + start: terminalpkg.size.CellCountInt, + end: terminalpkg.size.CellCountInt, + 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: terminalpkg.size.CellCountInt = 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, + }; + } +}; |
