mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 08:46:08 +03:00
zig build test with renamed terminal package
This commit is contained in:
@ -156,7 +156,8 @@ const Mouse = struct {
|
||||
|
||||
/// The point at which the left mouse click happened. This is in screen
|
||||
/// coordinates so that scrolling preserves the location.
|
||||
left_click_point: terminal.point.ScreenPoint = .{},
|
||||
//TODO(paged-terminal)
|
||||
//left_click_point: terminal.point.ScreenPoint = .{},
|
||||
|
||||
/// The starting xpos/ypos of the left click. Note that if scrolling occurs,
|
||||
/// these will point to different "cells", but the xpos/ypos will stay
|
||||
|
@ -14,8 +14,8 @@ const Allocator = std.mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const cli = @import("../cli.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const terminalnew = @import("../terminal2/main.zig");
|
||||
const terminal = @import("../terminal-old/main.zig");
|
||||
const terminalnew = @import("../terminal/main.zig");
|
||||
|
||||
const Args = struct {
|
||||
mode: Mode = .noop,
|
||||
|
@ -291,7 +291,7 @@ palette: Palette = .{},
|
||||
/// a prompt, regardless of this configuration. You can disable that behavior
|
||||
/// by specifying `shell-integration-features = no-cursor` or disabling shell
|
||||
/// integration entirely.
|
||||
@"cursor-style": terminal.Cursor.Style = .block,
|
||||
@"cursor-style": terminal.CursorStyle = .block,
|
||||
|
||||
/// Sets the default blinking state of the cursor. This is just the default
|
||||
/// state; running programs may override the cursor style using `DECSCUSR` (`CSI
|
||||
|
@ -10,7 +10,7 @@ const GroupCache = font.GroupCache;
|
||||
const Library = font.Library;
|
||||
const Style = font.Style;
|
||||
const Presentation = font.Presentation;
|
||||
const terminal = @import("../../terminal/main.zig").new;
|
||||
const terminal = @import("../../terminal/main.zig");
|
||||
|
||||
const log = std.log.scoped(.font_shaper);
|
||||
|
||||
|
@ -3,7 +3,7 @@ const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const font = @import("../main.zig");
|
||||
const shape = @import("../shape.zig");
|
||||
const terminal = @import("../../terminal/main.zig").new;
|
||||
const terminal = @import("../../terminal/main.zig");
|
||||
|
||||
/// A single text run. A text run is only valid for one Shaper instance and
|
||||
/// until the next run is created. A text run never goes across multiple
|
||||
|
@ -35,8 +35,9 @@ mouse: struct {
|
||||
last_xpos: f64 = 0,
|
||||
last_ypos: f64 = 0,
|
||||
|
||||
/// Last hovered screen point
|
||||
last_point: terminal.point.ScreenPoint = .{},
|
||||
// Last hovered screen point
|
||||
// TODO(paged-terminal)
|
||||
// last_point: terminal.point.ScreenPoint = .{},
|
||||
} = .{},
|
||||
|
||||
/// A selected cell.
|
||||
@ -63,7 +64,8 @@ const CellInspect = union(enum) {
|
||||
const Selected = struct {
|
||||
row: usize,
|
||||
col: usize,
|
||||
cell: terminal.Screen.Cell,
|
||||
// TODO(paged-terminal)
|
||||
//cell: terminal.Screen.Cell,
|
||||
};
|
||||
|
||||
pub fn request(self: *CellInspect) void {
|
||||
|
@ -309,7 +309,7 @@ test {
|
||||
_ = @import("segmented_pool.zig");
|
||||
_ = @import("inspector/main.zig");
|
||||
_ = @import("terminal/main.zig");
|
||||
_ = @import("terminal2/main.zig");
|
||||
_ = @import("terminal-old/main.zig");
|
||||
_ = @import("terminfo/main.zig");
|
||||
_ = @import("simd/main.zig");
|
||||
_ = @import("unicode/main.zig");
|
||||
|
@ -12,7 +12,7 @@ pub const CursorStyle = enum {
|
||||
underline,
|
||||
|
||||
/// Create a cursor style from the terminal style request.
|
||||
pub fn fromTerminal(style: terminal.Cursor.Style) ?CursorStyle {
|
||||
pub fn fromTerminal(style: terminal.CursorStyle) ?CursorStyle {
|
||||
return switch (style) {
|
||||
.bar => .bar,
|
||||
.block => .block,
|
||||
@ -57,7 +57,7 @@ pub fn cursorStyle(
|
||||
}
|
||||
|
||||
// Otherwise, we use whatever style the terminal wants.
|
||||
return CursorStyle.fromTerminal(state.terminal.screen.cursor.style);
|
||||
return CursorStyle.fromTerminal(state.terminal.screen.cursor.cursor_style);
|
||||
}
|
||||
|
||||
test "cursor: default uses configured style" {
|
||||
@ -66,7 +66,7 @@ test "cursor: default uses configured style" {
|
||||
var term = try terminal.Terminal.init(alloc, 10, 10);
|
||||
defer term.deinit(alloc);
|
||||
|
||||
term.screen.cursor.style = .bar;
|
||||
term.screen.cursor.cursor_style = .bar;
|
||||
term.modes.set(.cursor_blinking, true);
|
||||
|
||||
var state: State = .{
|
||||
@ -87,7 +87,7 @@ test "cursor: blinking disabled" {
|
||||
var term = try terminal.Terminal.init(alloc, 10, 10);
|
||||
defer term.deinit(alloc);
|
||||
|
||||
term.screen.cursor.style = .bar;
|
||||
term.screen.cursor.cursor_style = .bar;
|
||||
term.modes.set(.cursor_blinking, false);
|
||||
|
||||
var state: State = .{
|
||||
@ -108,7 +108,7 @@ test "cursor: explictly not visible" {
|
||||
var term = try terminal.Terminal.init(alloc, 10, 10);
|
||||
defer term.deinit(alloc);
|
||||
|
||||
term.screen.cursor.style = .bar;
|
||||
term.screen.cursor.cursor_style = .bar;
|
||||
term.modes.set(.cursor_visible, false);
|
||||
term.modes.set(.cursor_blinking, false);
|
||||
|
||||
|
@ -169,139 +169,140 @@ pub const MatchSet = struct {
|
||||
}
|
||||
};
|
||||
|
||||
test "matchset" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
// Initialize our screen
|
||||
var s = try Screen.init(alloc, 5, 5, 0);
|
||||
defer s.deinit();
|
||||
const str = "1ABCD2EFGH\n3IJKL";
|
||||
try s.testWriteString(str);
|
||||
|
||||
// Get a set
|
||||
var set = try Set.fromConfig(alloc, &.{
|
||||
.{
|
||||
.regex = "AB",
|
||||
.action = .{ .open = {} },
|
||||
.highlight = .{ .always = {} },
|
||||
},
|
||||
|
||||
.{
|
||||
.regex = "EF",
|
||||
.action = .{ .open = {} },
|
||||
.highlight = .{ .always = {} },
|
||||
},
|
||||
});
|
||||
defer set.deinit(alloc);
|
||||
|
||||
// Get our matches
|
||||
var match = try set.matchSet(alloc, &s, .{}, .{});
|
||||
defer match.deinit(alloc);
|
||||
try testing.expectEqual(@as(usize, 2), match.matches.len);
|
||||
|
||||
// Test our matches
|
||||
try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
}
|
||||
|
||||
test "matchset hover links" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
// Initialize our screen
|
||||
var s = try Screen.init(alloc, 5, 5, 0);
|
||||
defer s.deinit();
|
||||
const str = "1ABCD2EFGH\n3IJKL";
|
||||
try s.testWriteString(str);
|
||||
|
||||
// Get a set
|
||||
var set = try Set.fromConfig(alloc, &.{
|
||||
.{
|
||||
.regex = "AB",
|
||||
.action = .{ .open = {} },
|
||||
.highlight = .{ .hover = {} },
|
||||
},
|
||||
|
||||
.{
|
||||
.regex = "EF",
|
||||
.action = .{ .open = {} },
|
||||
.highlight = .{ .always = {} },
|
||||
},
|
||||
});
|
||||
defer set.deinit(alloc);
|
||||
|
||||
// Not hovering over the first link
|
||||
{
|
||||
var match = try set.matchSet(alloc, &s, .{}, .{});
|
||||
defer match.deinit(alloc);
|
||||
try testing.expectEqual(@as(usize, 1), match.matches.len);
|
||||
|
||||
// Test our matches
|
||||
try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
}
|
||||
|
||||
// Hovering over the first link
|
||||
{
|
||||
var match = try set.matchSet(alloc, &s, .{ .x = 1, .y = 0 }, .{});
|
||||
defer match.deinit(alloc);
|
||||
try testing.expectEqual(@as(usize, 2), match.matches.len);
|
||||
|
||||
// Test our matches
|
||||
try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
}
|
||||
}
|
||||
|
||||
test "matchset mods no match" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
// Initialize our screen
|
||||
var s = try Screen.init(alloc, 5, 5, 0);
|
||||
defer s.deinit();
|
||||
const str = "1ABCD2EFGH\n3IJKL";
|
||||
try s.testWriteString(str);
|
||||
|
||||
// Get a set
|
||||
var set = try Set.fromConfig(alloc, &.{
|
||||
.{
|
||||
.regex = "AB",
|
||||
.action = .{ .open = {} },
|
||||
.highlight = .{ .always = {} },
|
||||
},
|
||||
|
||||
.{
|
||||
.regex = "EF",
|
||||
.action = .{ .open = {} },
|
||||
.highlight = .{ .always_mods = .{ .ctrl = true } },
|
||||
},
|
||||
});
|
||||
defer set.deinit(alloc);
|
||||
|
||||
// Get our matches
|
||||
var match = try set.matchSet(alloc, &s, .{}, .{});
|
||||
defer match.deinit(alloc);
|
||||
try testing.expectEqual(@as(usize, 1), match.matches.len);
|
||||
|
||||
// Test our matches
|
||||
try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
}
|
||||
// TODO(paged-terminal)
|
||||
// test "matchset" {
|
||||
// const testing = std.testing;
|
||||
// const alloc = testing.allocator;
|
||||
//
|
||||
// // Initialize our screen
|
||||
// var s = try Screen.init(alloc, 5, 5, 0);
|
||||
// defer s.deinit();
|
||||
// const str = "1ABCD2EFGH\n3IJKL";
|
||||
// try s.testWriteString(str);
|
||||
//
|
||||
// // Get a set
|
||||
// var set = try Set.fromConfig(alloc, &.{
|
||||
// .{
|
||||
// .regex = "AB",
|
||||
// .action = .{ .open = {} },
|
||||
// .highlight = .{ .always = {} },
|
||||
// },
|
||||
//
|
||||
// .{
|
||||
// .regex = "EF",
|
||||
// .action = .{ .open = {} },
|
||||
// .highlight = .{ .always = {} },
|
||||
// },
|
||||
// });
|
||||
// defer set.deinit(alloc);
|
||||
//
|
||||
// // Get our matches
|
||||
// var match = try set.matchSet(alloc, &s, .{}, .{});
|
||||
// defer match.deinit(alloc);
|
||||
// try testing.expectEqual(@as(usize, 2), match.matches.len);
|
||||
//
|
||||
// // Test our matches
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
// }
|
||||
//
|
||||
// test "matchset hover links" {
|
||||
// const testing = std.testing;
|
||||
// const alloc = testing.allocator;
|
||||
//
|
||||
// // Initialize our screen
|
||||
// var s = try Screen.init(alloc, 5, 5, 0);
|
||||
// defer s.deinit();
|
||||
// const str = "1ABCD2EFGH\n3IJKL";
|
||||
// try s.testWriteString(str);
|
||||
//
|
||||
// // Get a set
|
||||
// var set = try Set.fromConfig(alloc, &.{
|
||||
// .{
|
||||
// .regex = "AB",
|
||||
// .action = .{ .open = {} },
|
||||
// .highlight = .{ .hover = {} },
|
||||
// },
|
||||
//
|
||||
// .{
|
||||
// .regex = "EF",
|
||||
// .action = .{ .open = {} },
|
||||
// .highlight = .{ .always = {} },
|
||||
// },
|
||||
// });
|
||||
// defer set.deinit(alloc);
|
||||
//
|
||||
// // Not hovering over the first link
|
||||
// {
|
||||
// var match = try set.matchSet(alloc, &s, .{}, .{});
|
||||
// defer match.deinit(alloc);
|
||||
// try testing.expectEqual(@as(usize, 1), match.matches.len);
|
||||
//
|
||||
// // Test our matches
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
// }
|
||||
//
|
||||
// // Hovering over the first link
|
||||
// {
|
||||
// var match = try set.matchSet(alloc, &s, .{ .x = 1, .y = 0 }, .{});
|
||||
// defer match.deinit(alloc);
|
||||
// try testing.expectEqual(@as(usize, 2), match.matches.len);
|
||||
//
|
||||
// // Test our matches
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// test "matchset mods no match" {
|
||||
// const testing = std.testing;
|
||||
// const alloc = testing.allocator;
|
||||
//
|
||||
// // Initialize our screen
|
||||
// var s = try Screen.init(alloc, 5, 5, 0);
|
||||
// defer s.deinit();
|
||||
// const str = "1ABCD2EFGH\n3IJKL";
|
||||
// try s.testWriteString(str);
|
||||
//
|
||||
// // Get a set
|
||||
// var set = try Set.fromConfig(alloc, &.{
|
||||
// .{
|
||||
// .regex = "AB",
|
||||
// .action = .{ .open = {} },
|
||||
// .highlight = .{ .always = {} },
|
||||
// },
|
||||
//
|
||||
// .{
|
||||
// .regex = "EF",
|
||||
// .action = .{ .open = {} },
|
||||
// .highlight = .{ .always_mods = .{ .ctrl = true } },
|
||||
// },
|
||||
// });
|
||||
// defer set.deinit(alloc);
|
||||
//
|
||||
// // Get our matches
|
||||
// var match = try set.matchSet(alloc, &s, .{}, .{});
|
||||
// defer match.deinit(alloc);
|
||||
// try testing.expectEqual(@as(usize, 1), match.matches.len);
|
||||
//
|
||||
// // Test our matches
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 0, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 1, .y = 0 }));
|
||||
// try testing.expect(match.orderedContains(.{ .x = 2, .y = 0 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 3, .y = 0 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 1 }));
|
||||
// try testing.expect(!match.orderedContains(.{ .x = 1, .y = 2 }));
|
||||
// }
|
||||
|
7920
src/terminal-old/Screen.zig
Normal file
7920
src/terminal-old/Screen.zig
Normal file
File diff suppressed because it is too large
Load Diff
1165
src/terminal-old/Selection.zig
Normal file
1165
src/terminal-old/Selection.zig
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,6 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
|
||||
const command = @import("graphics_command.zig");
|
||||
const point = @import("../point.zig");
|
||||
const PageList = @import("../PageList.zig");
|
||||
const internal_os = @import("../../os/main.zig");
|
||||
const stb = @import("../../stb/main.zig");
|
||||
|
||||
@ -452,8 +451,16 @@ pub const Image = struct {
|
||||
/// be rounded up to the nearest grid cell since we can't place images
|
||||
/// in partial grid cells.
|
||||
pub const Rect = struct {
|
||||
top_left: PageList.Pin,
|
||||
bottom_right: PageList.Pin,
|
||||
top_left: point.ScreenPoint = .{},
|
||||
bottom_right: point.ScreenPoint = .{},
|
||||
|
||||
/// True if the rect contains a given screen point.
|
||||
pub fn contains(self: Rect, p: point.ScreenPoint) bool {
|
||||
return p.y >= self.top_left.y and
|
||||
p.y <= self.bottom_right.y and
|
||||
p.x >= self.top_left.x and
|
||||
p.x <= self.bottom_right.x;
|
||||
}
|
||||
};
|
||||
|
||||
/// Easy base64 encoding function.
|
@ -6,12 +6,12 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const terminal = @import("../main.zig");
|
||||
const point = @import("../point.zig");
|
||||
const command = @import("graphics_command.zig");
|
||||
const PageList = @import("../PageList.zig");
|
||||
const Screen = @import("../Screen.zig");
|
||||
const LoadingImage = @import("graphics_image.zig").LoadingImage;
|
||||
const Image = @import("graphics_image.zig").Image;
|
||||
const Rect = @import("graphics_image.zig").Rect;
|
||||
const Command = command.Command;
|
||||
const ScreenPoint = point.ScreenPoint;
|
||||
|
||||
const log = std.log.scoped(.kitty_gfx);
|
||||
|
||||
@ -53,18 +53,13 @@ pub const ImageStorage = struct {
|
||||
total_bytes: usize = 0,
|
||||
total_limit: usize = 320 * 1000 * 1000, // 320MB
|
||||
|
||||
pub fn deinit(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
s: *terminal.Screen,
|
||||
) void {
|
||||
pub fn deinit(self: *ImageStorage, alloc: Allocator) void {
|
||||
if (self.loading) |loading| loading.destroy(alloc);
|
||||
|
||||
var it = self.images.iterator();
|
||||
while (it.next()) |kv| kv.value_ptr.deinit(alloc);
|
||||
self.images.deinit(alloc);
|
||||
|
||||
self.clearPlacements(s);
|
||||
self.placements.deinit(alloc);
|
||||
}
|
||||
|
||||
@ -175,12 +170,6 @@ pub const ImageStorage = struct {
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
fn clearPlacements(self: *ImageStorage, s: *terminal.Screen) void {
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| entry.value_ptr.deinit(s);
|
||||
self.placements.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
/// Get an image by its ID. If the image doesn't exist, null is returned.
|
||||
pub fn imageById(self: *const ImageStorage, image_id: u32) ?Image {
|
||||
return self.images.get(image_id);
|
||||
@ -208,20 +197,19 @@ pub const ImageStorage = struct {
|
||||
pub fn delete(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
t: *terminal.Terminal,
|
||||
t: *const terminal.Terminal,
|
||||
cmd: command.Delete,
|
||||
) void {
|
||||
switch (cmd) {
|
||||
.all => |delete_images| if (delete_images) {
|
||||
// We just reset our entire state.
|
||||
self.deinit(alloc, &t.screen);
|
||||
self.deinit(alloc);
|
||||
self.* = .{
|
||||
.dirty = true,
|
||||
.total_limit = self.total_limit,
|
||||
};
|
||||
} else {
|
||||
// Delete all our placements
|
||||
self.clearPlacements(&t.screen);
|
||||
self.placements.deinit(alloc);
|
||||
self.placements = .{};
|
||||
self.dirty = true;
|
||||
@ -229,7 +217,6 @@ pub const ImageStorage = struct {
|
||||
|
||||
.id => |v| self.deleteById(
|
||||
alloc,
|
||||
&t.screen,
|
||||
v.image_id,
|
||||
v.placement_id,
|
||||
v.delete,
|
||||
@ -237,59 +224,29 @@ pub const ImageStorage = struct {
|
||||
|
||||
.newest => |v| newest: {
|
||||
const img = self.imageByNumber(v.image_number) orelse break :newest;
|
||||
self.deleteById(
|
||||
alloc,
|
||||
&t.screen,
|
||||
img.id,
|
||||
v.placement_id,
|
||||
v.delete,
|
||||
);
|
||||
self.deleteById(alloc, img.id, v.placement_id, v.delete);
|
||||
},
|
||||
|
||||
.intersect_cursor => |delete_images| {
|
||||
self.deleteIntersecting(
|
||||
alloc,
|
||||
t,
|
||||
.{ .active = .{
|
||||
.x = t.screen.cursor.x,
|
||||
.y = t.screen.cursor.y,
|
||||
} },
|
||||
delete_images,
|
||||
{},
|
||||
null,
|
||||
);
|
||||
const target = (point.Viewport{
|
||||
.x = t.screen.cursor.x,
|
||||
.y = t.screen.cursor.y,
|
||||
}).toScreen(&t.screen);
|
||||
self.deleteIntersecting(alloc, t, target, delete_images, {}, null);
|
||||
},
|
||||
|
||||
.intersect_cell => |v| {
|
||||
self.deleteIntersecting(
|
||||
alloc,
|
||||
t,
|
||||
.{ .active = .{
|
||||
.x = v.x,
|
||||
.y = v.y,
|
||||
} },
|
||||
v.delete,
|
||||
{},
|
||||
null,
|
||||
);
|
||||
const target = (point.Viewport{ .x = v.x, .y = v.y }).toScreen(&t.screen);
|
||||
self.deleteIntersecting(alloc, t, target, v.delete, {}, null);
|
||||
},
|
||||
|
||||
.intersect_cell_z => |v| {
|
||||
self.deleteIntersecting(
|
||||
alloc,
|
||||
t,
|
||||
.{ .active = .{
|
||||
.x = v.x,
|
||||
.y = v.y,
|
||||
} },
|
||||
v.delete,
|
||||
v.z,
|
||||
struct {
|
||||
fn filter(ctx: i32, p: Placement) bool {
|
||||
return p.z == ctx;
|
||||
}
|
||||
}.filter,
|
||||
);
|
||||
const target = (point.Viewport{ .x = v.x, .y = v.y }).toScreen(&t.screen);
|
||||
self.deleteIntersecting(alloc, t, target, v.delete, v.z, struct {
|
||||
fn filter(ctx: i32, p: Placement) bool {
|
||||
return p.z == ctx;
|
||||
}
|
||||
}.filter);
|
||||
},
|
||||
|
||||
.column => |v| {
|
||||
@ -298,7 +255,6 @@ pub const ImageStorage = struct {
|
||||
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||
const rect = entry.value_ptr.rect(img, t);
|
||||
if (rect.top_left.x <= v.x and rect.bottom_right.x >= v.x) {
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (v.delete) self.deleteIfUnused(alloc, img.id);
|
||||
}
|
||||
@ -308,24 +264,15 @@ pub const ImageStorage = struct {
|
||||
self.dirty = true;
|
||||
},
|
||||
|
||||
.row => |v| row: {
|
||||
// v.y is in active coords so we want to convert it to a pin
|
||||
// so we can compare by page offsets.
|
||||
const target_pin = t.screen.pages.pin(.{ .active = .{
|
||||
.y = v.y,
|
||||
} }) orelse break :row;
|
||||
.row => |v| {
|
||||
// Get the screenpoint y
|
||||
const y = (point.Viewport{ .x = 0, .y = v.y }).toScreen(&t.screen).y;
|
||||
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||
const rect = entry.value_ptr.rect(img, t);
|
||||
|
||||
// We need to copy our pin to ensure we are at least at
|
||||
// the top-left x.
|
||||
var target_pin_copy = target_pin;
|
||||
target_pin_copy.x = rect.top_left.x;
|
||||
if (target_pin_copy.isBetween(rect.top_left, rect.bottom_right)) {
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
if (rect.top_left.y <= y and rect.bottom_right.y >= y) {
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (v.delete) self.deleteIfUnused(alloc, img.id);
|
||||
}
|
||||
@ -340,7 +287,6 @@ pub const ImageStorage = struct {
|
||||
while (it.next()) |entry| {
|
||||
if (entry.value_ptr.z == v.z) {
|
||||
const image_id = entry.key_ptr.image_id;
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (v.delete) self.deleteIfUnused(alloc, image_id);
|
||||
}
|
||||
@ -359,7 +305,6 @@ pub const ImageStorage = struct {
|
||||
fn deleteById(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
s: *terminal.Screen,
|
||||
image_id: u32,
|
||||
placement_id: u32,
|
||||
delete_unused: bool,
|
||||
@ -369,18 +314,14 @@ pub const ImageStorage = struct {
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| {
|
||||
if (entry.key_ptr.image_id == image_id) {
|
||||
entry.value_ptr.deinit(s);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (self.placements.getEntry(.{
|
||||
_ = self.placements.remove(.{
|
||||
.image_id = image_id,
|
||||
.placement_id = .{ .tag = .external, .id = placement_id },
|
||||
})) |entry| {
|
||||
entry.value_ptr.deinit(s);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If this is specified, then we also delete the image
|
||||
@ -412,22 +353,18 @@ pub const ImageStorage = struct {
|
||||
fn deleteIntersecting(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
t: *terminal.Terminal,
|
||||
p: point.Point,
|
||||
t: *const terminal.Terminal,
|
||||
p: point.ScreenPoint,
|
||||
delete_unused: bool,
|
||||
filter_ctx: anytype,
|
||||
comptime filter: ?fn (@TypeOf(filter_ctx), Placement) bool,
|
||||
) void {
|
||||
// Convert our target point to a pin for comparison.
|
||||
const target_pin = t.screen.pages.pin(p) orelse return;
|
||||
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||
const rect = entry.value_ptr.rect(img, t);
|
||||
if (target_pin.isBetween(rect.top_left, rect.bottom_right)) {
|
||||
if (rect.contains(p)) {
|
||||
if (filter) |f| if (!f(filter_ctx, entry.value_ptr.*)) continue;
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (delete_unused) self.deleteIfUnused(alloc, img.id);
|
||||
}
|
||||
@ -549,8 +486,8 @@ pub const ImageStorage = struct {
|
||||
};
|
||||
|
||||
pub const Placement = struct {
|
||||
/// The tracked pin for this placement.
|
||||
pin: *PageList.Pin,
|
||||
/// The location of the image on the screen.
|
||||
point: ScreenPoint,
|
||||
|
||||
/// Offset of the x/y from the top-left of the cell.
|
||||
x_offset: u32 = 0,
|
||||
@ -569,13 +506,6 @@ pub const ImageStorage = struct {
|
||||
/// The z-index for this placement.
|
||||
z: i32 = 0,
|
||||
|
||||
pub fn deinit(
|
||||
self: *const Placement,
|
||||
s: *terminal.Screen,
|
||||
) void {
|
||||
s.pages.untrackPin(self.pin);
|
||||
}
|
||||
|
||||
/// Returns a selection of the entire rectangle this placement
|
||||
/// occupies within the screen.
|
||||
pub fn rect(
|
||||
@ -585,13 +515,13 @@ pub const ImageStorage = struct {
|
||||
) Rect {
|
||||
// If we have columns/rows specified we can simplify this whole thing.
|
||||
if (self.columns > 0 and self.rows > 0) {
|
||||
var br = switch (self.pin.downOverflow(self.rows)) {
|
||||
.offset => |v| v,
|
||||
.overflow => |v| v.end,
|
||||
return .{
|
||||
.top_left = self.point,
|
||||
.bottom_right = .{
|
||||
.x = @min(self.point.x + self.columns, t.cols - 1),
|
||||
.y = self.point.y + self.rows,
|
||||
},
|
||||
};
|
||||
br.x = @min(self.pin.x + self.columns, t.cols - 1);
|
||||
|
||||
return .{ .top_left = self.pin.*, .bottom_right = br };
|
||||
}
|
||||
|
||||
// Calculate our cell size.
|
||||
@ -612,31 +542,17 @@ pub const ImageStorage = struct {
|
||||
const width_cells: u32 = @intFromFloat(@ceil(width_f64 / cell_width_f64));
|
||||
const height_cells: u32 = @intFromFloat(@ceil(height_f64 / cell_height_f64));
|
||||
|
||||
// TODO(paged-terminal): clean this logic up above
|
||||
var br = switch (self.pin.downOverflow(height_cells)) {
|
||||
.offset => |v| v,
|
||||
.overflow => |v| v.end,
|
||||
};
|
||||
br.x = @min(self.pin.x + width_cells, t.cols - 1);
|
||||
|
||||
return .{
|
||||
.top_left = self.pin.*,
|
||||
.bottom_right = br,
|
||||
.top_left = self.point,
|
||||
.bottom_right = .{
|
||||
.x = @min(self.point.x + width_cells, t.cols - 1),
|
||||
.y = self.point.y + height_cells,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Our pin for the placement
|
||||
fn trackPin(
|
||||
t: *terminal.Terminal,
|
||||
pt: point.Point.Coordinate,
|
||||
) !*PageList.Pin {
|
||||
return try t.screen.pages.trackPin(t.screen.pages.pin(.{
|
||||
.active = pt,
|
||||
}).?);
|
||||
}
|
||||
|
||||
test "storage: add placement with zero placement id" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -646,11 +562,11 @@ test "storage: add placement with zero placement id" {
|
||||
t.height_px = 100;
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
|
||||
try testing.expectEqual(@as(usize, 2), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
@ -671,22 +587,20 @@ test "storage: delete all placements and images" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .all = true });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 0), s.images.count());
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements and images preserves limit" {
|
||||
@ -694,16 +608,15 @@ test "storage: delete all placements and images preserves limit" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
s.total_limit = 5000;
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .all = true });
|
||||
@ -711,7 +624,6 @@ test "storage: delete all placements and images preserves limit" {
|
||||
try testing.expectEqual(@as(usize, 0), s.images.count());
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 5000), s.total_limit);
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements" {
|
||||
@ -719,22 +631,20 @@ test "storage: delete all placements" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .all = false });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements by image id" {
|
||||
@ -742,22 +652,20 @@ test "storage: delete all placements by image id" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .id = .{ .image_id = 2 } });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements by image id and unused images" {
|
||||
@ -765,22 +673,20 @@ test "storage: delete all placements by image id and unused images" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .id = .{ .delete = true, .image_id = 2 } });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete placement by specific id" {
|
||||
@ -788,16 +694,15 @@ test "storage: delete placement by specific id" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .id = .{
|
||||
@ -808,7 +713,6 @@ test "storage: delete placement by specific id" {
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 2), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||
try testing.expectEqual(tracked + 2, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete intersecting cursor" {
|
||||
@ -818,23 +722,22 @@ test "storage: delete intersecting cursor" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
|
||||
t.screen.cursorAbsolute(12, 12);
|
||||
t.screen.cursor.x = 12;
|
||||
t.screen.cursor.y = 12;
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .intersect_cursor = false });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
||||
@ -850,23 +753,22 @@ test "storage: delete intersecting cursor plus unused" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
|
||||
t.screen.cursorAbsolute(12, 12);
|
||||
t.screen.cursor.x = 12;
|
||||
t.screen.cursor.y = 12;
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .intersect_cursor = true });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
||||
@ -882,23 +784,22 @@ test "storage: delete intersecting cursor hits multiple" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
|
||||
t.screen.cursorAbsolute(26, 26);
|
||||
t.screen.cursor.x = 26;
|
||||
t.screen.cursor.y = 26;
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .intersect_cursor = true });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 1), s.images.count());
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete by column" {
|
||||
@ -908,14 +809,13 @@ test "storage: delete by column" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .column = .{
|
||||
@ -925,7 +825,6 @@ test "storage: delete by column" {
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
||||
@ -941,14 +840,13 @@ test "storage: delete by row" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
defer s.deinit(alloc);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .row = .{
|
||||
@ -958,7 +856,6 @@ test "storage: delete by row" {
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
Before Width: | Height: | Size: 86 B After Width: | Height: | Size: 86 B |
@ -15,27 +15,21 @@ pub const color = @import("color.zig");
|
||||
pub const device_status = @import("device_status.zig");
|
||||
pub const kitty = @import("kitty.zig");
|
||||
pub const modes = @import("modes.zig");
|
||||
pub const page = @import("page.zig");
|
||||
pub const parse_table = @import("parse_table.zig");
|
||||
pub const x11_color = @import("x11_color.zig");
|
||||
|
||||
pub const Charset = charsets.Charset;
|
||||
pub const CharsetSlot = charsets.Slots;
|
||||
pub const CharsetActiveSlot = charsets.ActiveSlot;
|
||||
pub const Cell = page.Cell;
|
||||
pub const CSI = Parser.Action.CSI;
|
||||
pub const DCS = Parser.Action.DCS;
|
||||
pub const MouseShape = @import("mouse_shape.zig").MouseShape;
|
||||
pub const Page = page.Page;
|
||||
pub const PageList = @import("PageList.zig");
|
||||
pub const Parser = @import("Parser.zig");
|
||||
pub const Pin = PageList.Pin;
|
||||
pub const Screen = @import("Screen.zig");
|
||||
pub const Selection = @import("Selection.zig");
|
||||
pub const Screen = @import("Screen.zig");
|
||||
pub const Terminal = @import("Terminal.zig");
|
||||
pub const Stream = stream.Stream;
|
||||
pub const Cursor = Screen.Cursor;
|
||||
pub const CursorStyle = Screen.CursorStyle;
|
||||
pub const CursorStyleReq = ansi.CursorStyle;
|
||||
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
|
||||
pub const Mode = modes.Mode;
|
||||
@ -48,12 +42,17 @@ pub const EraseLine = csi.EraseLine;
|
||||
pub const TabClear = csi.TabClear;
|
||||
pub const Attribute = sgr.Attribute;
|
||||
|
||||
// TODO(paged-terminal)
|
||||
pub const StringMap = @import("StringMap.zig");
|
||||
|
||||
/// If we're targeting wasm then we export some wasm APIs.
|
||||
pub usingnamespace if (builtin.target.isWasm()) struct {
|
||||
pub usingnamespace @import("wasm.zig");
|
||||
} else struct {};
|
||||
|
||||
// TODO(paged-terminal) remove before merge
|
||||
pub const new = @import("../terminal/main.zig");
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
||||
// todo: make top-level imports
|
||||
_ = @import("bitmap_allocator.zig");
|
||||
_ = @import("hash_map.zig");
|
||||
_ = @import("size.zig");
|
||||
_ = @import("style.zig");
|
||||
}
|
254
src/terminal-old/point.zig
Normal file
254
src/terminal-old/point.zig
Normal file
@ -0,0 +1,254 @@
|
||||
const std = @import("std");
|
||||
const terminal = @import("main.zig");
|
||||
const Screen = terminal.Screen;
|
||||
|
||||
// This file contains various types to represent x/y coordinates. We
|
||||
// use different types so that we can lean on type-safety to get the
|
||||
// exact expected type of point.
|
||||
|
||||
/// Active is a point within the active part of the screen.
|
||||
pub const Active = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
|
||||
pub fn toScreen(self: Active, screen: *const Screen) ScreenPoint {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = screen.history + self.y,
|
||||
};
|
||||
}
|
||||
|
||||
test "toScreen with scrollback" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 3, 5, 3);
|
||||
defer s.deinit();
|
||||
const str = "1\n2\n3\n4\n5\n6\n7\n8";
|
||||
try s.testWriteString(str);
|
||||
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 1,
|
||||
.y = 5,
|
||||
}, (Active{ .x = 1, .y = 2 }).toScreen(&s));
|
||||
}
|
||||
};
|
||||
|
||||
/// Viewport is a point within the viewport of the screen.
|
||||
pub const Viewport = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
|
||||
pub fn toScreen(self: Viewport, screen: *const Screen) ScreenPoint {
|
||||
// x is unchanged, y we have to add the visible offset to
|
||||
// get the full offset from the top.
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = screen.viewport + self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn eql(self: Viewport, other: Viewport) bool {
|
||||
return self.x == other.x and self.y == other.y;
|
||||
}
|
||||
|
||||
test "toScreen with no scrollback" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 3, 5, 0);
|
||||
defer s.deinit();
|
||||
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
}, (Viewport{ .x = 1, .y = 1 }).toScreen(&s));
|
||||
}
|
||||
|
||||
test "toScreen with scrollback" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 3, 5, 3);
|
||||
defer s.deinit();
|
||||
|
||||
// At the bottom
|
||||
try s.scroll(.{ .screen = 6 });
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 0,
|
||||
.y = 3,
|
||||
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
|
||||
|
||||
// Move the viewport a bit up
|
||||
try s.scroll(.{ .screen = -1 });
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 0,
|
||||
.y = 2,
|
||||
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
|
||||
|
||||
// Move the viewport to top
|
||||
try s.scroll(.{ .top = {} });
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
|
||||
}
|
||||
};
|
||||
|
||||
/// A screen point. This is offset from the top of the scrollback
|
||||
/// buffer. If the screen is scrolled or resized, this will have to
|
||||
/// be recomputed.
|
||||
pub const ScreenPoint = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
|
||||
/// Returns if this point is before another point.
|
||||
pub fn before(self: ScreenPoint, other: ScreenPoint) bool {
|
||||
return self.y < other.y or
|
||||
(self.y == other.y and self.x < other.x);
|
||||
}
|
||||
|
||||
/// Returns if two points are equal.
|
||||
pub fn eql(self: ScreenPoint, other: ScreenPoint) bool {
|
||||
return self.x == other.x and self.y == other.y;
|
||||
}
|
||||
|
||||
/// Returns true if this screen point is currently in the active viewport.
|
||||
pub fn inViewport(self: ScreenPoint, screen: *const Screen) bool {
|
||||
return self.y >= screen.viewport and
|
||||
self.y < screen.viewport + screen.rows;
|
||||
}
|
||||
|
||||
/// Converts this to a viewport point. If the point is above the
|
||||
/// viewport this will move the point to (0, 0) and if it is below
|
||||
/// the viewport it'll move it to (cols - 1, rows - 1).
|
||||
pub fn toViewport(self: ScreenPoint, screen: *const Screen) Viewport {
|
||||
// TODO: test
|
||||
|
||||
// Before viewport
|
||||
if (self.y < screen.viewport) return .{ .x = 0, .y = 0 };
|
||||
|
||||
// After viewport
|
||||
if (self.y > screen.viewport + screen.rows) return .{
|
||||
.x = screen.cols - 1,
|
||||
.y = screen.rows - 1,
|
||||
};
|
||||
|
||||
return .{ .x = self.x, .y = self.y - screen.viewport };
|
||||
}
|
||||
|
||||
/// Returns a screen point iterator. This will iterate over all of
|
||||
/// of the points in a screen in a given direction one by one.
|
||||
///
|
||||
/// The iterator is only valid as long as the screen is not resized.
|
||||
pub fn iterator(
|
||||
self: ScreenPoint,
|
||||
screen: *const Screen,
|
||||
dir: Direction,
|
||||
) Iterator {
|
||||
return .{ .screen = screen, .current = self, .direction = dir };
|
||||
}
|
||||
|
||||
pub const Iterator = struct {
|
||||
screen: *const Screen,
|
||||
current: ?ScreenPoint,
|
||||
direction: Direction,
|
||||
|
||||
pub fn next(self: *Iterator) ?ScreenPoint {
|
||||
const current = self.current orelse return null;
|
||||
self.current = switch (self.direction) {
|
||||
.left_up => left_up: {
|
||||
if (current.x == 0) {
|
||||
if (current.y == 0) break :left_up null;
|
||||
break :left_up .{
|
||||
.x = self.screen.cols - 1,
|
||||
.y = current.y - 1,
|
||||
};
|
||||
}
|
||||
|
||||
break :left_up .{
|
||||
.x = current.x - 1,
|
||||
.y = current.y,
|
||||
};
|
||||
},
|
||||
|
||||
.right_down => right_down: {
|
||||
if (current.x == self.screen.cols - 1) {
|
||||
const max = self.screen.rows + self.screen.max_scrollback;
|
||||
if (current.y == max - 1) break :right_down null;
|
||||
break :right_down .{
|
||||
.x = 0,
|
||||
.y = current.y + 1,
|
||||
};
|
||||
}
|
||||
|
||||
break :right_down .{
|
||||
.x = current.x + 1,
|
||||
.y = current.y,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
return current;
|
||||
}
|
||||
};
|
||||
|
||||
test "before" {
|
||||
const testing = std.testing;
|
||||
|
||||
const p: ScreenPoint = .{ .x = 5, .y = 2 };
|
||||
try testing.expect(p.before(.{ .x = 6, .y = 2 }));
|
||||
try testing.expect(p.before(.{ .x = 3, .y = 3 }));
|
||||
}
|
||||
|
||||
test "iterator" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 5, 5, 0);
|
||||
defer s.deinit();
|
||||
|
||||
// Back from the first line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 1, .y = 0 };
|
||||
var it = pt.iterator(&s, .left_up);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 1, .y = 0 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 0, .y = 0 }, it.next().?);
|
||||
try testing.expect(it.next() == null);
|
||||
}
|
||||
|
||||
// Back from second line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 1, .y = 1 };
|
||||
var it = pt.iterator(&s, .left_up);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 1, .y = 1 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 0, .y = 1 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 4, .y = 0 }, it.next().?);
|
||||
}
|
||||
|
||||
// Forward last line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 3, .y = 4 };
|
||||
var it = pt.iterator(&s, .right_down);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 3, .y = 4 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 4, .y = 4 }, it.next().?);
|
||||
try testing.expect(it.next() == null);
|
||||
}
|
||||
|
||||
// Forward not last line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 3, .y = 3 };
|
||||
var it = pt.iterator(&s, .right_down);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 3, .y = 3 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 4, .y = 3 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 0, .y = 4 }, it.next().?);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Direction that points can go.
|
||||
pub const Direction = enum { left_up, right_down };
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
10718
src/terminal/Screen.zig
10718
src/terminal/Screen.zig
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ const posix = std.posix;
|
||||
|
||||
const command = @import("graphics_command.zig");
|
||||
const point = @import("../point.zig");
|
||||
const PageList = @import("../PageList.zig");
|
||||
const internal_os = @import("../../os/main.zig");
|
||||
const stb = @import("../../stb/main.zig");
|
||||
|
||||
@ -452,16 +453,8 @@ pub const Image = struct {
|
||||
/// be rounded up to the nearest grid cell since we can't place images
|
||||
/// in partial grid cells.
|
||||
pub const Rect = struct {
|
||||
top_left: point.ScreenPoint = .{},
|
||||
bottom_right: point.ScreenPoint = .{},
|
||||
|
||||
/// True if the rect contains a given screen point.
|
||||
pub fn contains(self: Rect, p: point.ScreenPoint) bool {
|
||||
return p.y >= self.top_left.y and
|
||||
p.y <= self.bottom_right.y and
|
||||
p.x >= self.top_left.x and
|
||||
p.x <= self.bottom_right.x;
|
||||
}
|
||||
top_left: PageList.Pin,
|
||||
bottom_right: PageList.Pin,
|
||||
};
|
||||
|
||||
/// Easy base64 encoding function.
|
||||
|
@ -6,12 +6,12 @@ const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const terminal = @import("../main.zig");
|
||||
const point = @import("../point.zig");
|
||||
const command = @import("graphics_command.zig");
|
||||
const PageList = @import("../PageList.zig");
|
||||
const Screen = @import("../Screen.zig");
|
||||
const LoadingImage = @import("graphics_image.zig").LoadingImage;
|
||||
const Image = @import("graphics_image.zig").Image;
|
||||
const Rect = @import("graphics_image.zig").Rect;
|
||||
const Command = command.Command;
|
||||
const ScreenPoint = point.ScreenPoint;
|
||||
|
||||
const log = std.log.scoped(.kitty_gfx);
|
||||
|
||||
@ -53,13 +53,18 @@ pub const ImageStorage = struct {
|
||||
total_bytes: usize = 0,
|
||||
total_limit: usize = 320 * 1000 * 1000, // 320MB
|
||||
|
||||
pub fn deinit(self: *ImageStorage, alloc: Allocator) void {
|
||||
pub fn deinit(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
s: *terminal.Screen,
|
||||
) void {
|
||||
if (self.loading) |loading| loading.destroy(alloc);
|
||||
|
||||
var it = self.images.iterator();
|
||||
while (it.next()) |kv| kv.value_ptr.deinit(alloc);
|
||||
self.images.deinit(alloc);
|
||||
|
||||
self.clearPlacements(s);
|
||||
self.placements.deinit(alloc);
|
||||
}
|
||||
|
||||
@ -170,6 +175,12 @@ pub const ImageStorage = struct {
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
fn clearPlacements(self: *ImageStorage, s: *terminal.Screen) void {
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| entry.value_ptr.deinit(s);
|
||||
self.placements.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
/// Get an image by its ID. If the image doesn't exist, null is returned.
|
||||
pub fn imageById(self: *const ImageStorage, image_id: u32) ?Image {
|
||||
return self.images.get(image_id);
|
||||
@ -197,19 +208,20 @@ pub const ImageStorage = struct {
|
||||
pub fn delete(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
t: *const terminal.Terminal,
|
||||
t: *terminal.Terminal,
|
||||
cmd: command.Delete,
|
||||
) void {
|
||||
switch (cmd) {
|
||||
.all => |delete_images| if (delete_images) {
|
||||
// We just reset our entire state.
|
||||
self.deinit(alloc);
|
||||
self.deinit(alloc, &t.screen);
|
||||
self.* = .{
|
||||
.dirty = true,
|
||||
.total_limit = self.total_limit,
|
||||
};
|
||||
} else {
|
||||
// Delete all our placements
|
||||
self.clearPlacements(&t.screen);
|
||||
self.placements.deinit(alloc);
|
||||
self.placements = .{};
|
||||
self.dirty = true;
|
||||
@ -217,6 +229,7 @@ pub const ImageStorage = struct {
|
||||
|
||||
.id => |v| self.deleteById(
|
||||
alloc,
|
||||
&t.screen,
|
||||
v.image_id,
|
||||
v.placement_id,
|
||||
v.delete,
|
||||
@ -224,29 +237,59 @@ pub const ImageStorage = struct {
|
||||
|
||||
.newest => |v| newest: {
|
||||
const img = self.imageByNumber(v.image_number) orelse break :newest;
|
||||
self.deleteById(alloc, img.id, v.placement_id, v.delete);
|
||||
self.deleteById(
|
||||
alloc,
|
||||
&t.screen,
|
||||
img.id,
|
||||
v.placement_id,
|
||||
v.delete,
|
||||
);
|
||||
},
|
||||
|
||||
.intersect_cursor => |delete_images| {
|
||||
const target = (point.Viewport{
|
||||
.x = t.screen.cursor.x,
|
||||
.y = t.screen.cursor.y,
|
||||
}).toScreen(&t.screen);
|
||||
self.deleteIntersecting(alloc, t, target, delete_images, {}, null);
|
||||
self.deleteIntersecting(
|
||||
alloc,
|
||||
t,
|
||||
.{ .active = .{
|
||||
.x = t.screen.cursor.x,
|
||||
.y = t.screen.cursor.y,
|
||||
} },
|
||||
delete_images,
|
||||
{},
|
||||
null,
|
||||
);
|
||||
},
|
||||
|
||||
.intersect_cell => |v| {
|
||||
const target = (point.Viewport{ .x = v.x, .y = v.y }).toScreen(&t.screen);
|
||||
self.deleteIntersecting(alloc, t, target, v.delete, {}, null);
|
||||
self.deleteIntersecting(
|
||||
alloc,
|
||||
t,
|
||||
.{ .active = .{
|
||||
.x = v.x,
|
||||
.y = v.y,
|
||||
} },
|
||||
v.delete,
|
||||
{},
|
||||
null,
|
||||
);
|
||||
},
|
||||
|
||||
.intersect_cell_z => |v| {
|
||||
const target = (point.Viewport{ .x = v.x, .y = v.y }).toScreen(&t.screen);
|
||||
self.deleteIntersecting(alloc, t, target, v.delete, v.z, struct {
|
||||
fn filter(ctx: i32, p: Placement) bool {
|
||||
return p.z == ctx;
|
||||
}
|
||||
}.filter);
|
||||
self.deleteIntersecting(
|
||||
alloc,
|
||||
t,
|
||||
.{ .active = .{
|
||||
.x = v.x,
|
||||
.y = v.y,
|
||||
} },
|
||||
v.delete,
|
||||
v.z,
|
||||
struct {
|
||||
fn filter(ctx: i32, p: Placement) bool {
|
||||
return p.z == ctx;
|
||||
}
|
||||
}.filter,
|
||||
);
|
||||
},
|
||||
|
||||
.column => |v| {
|
||||
@ -255,6 +298,7 @@ pub const ImageStorage = struct {
|
||||
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||
const rect = entry.value_ptr.rect(img, t);
|
||||
if (rect.top_left.x <= v.x and rect.bottom_right.x >= v.x) {
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (v.delete) self.deleteIfUnused(alloc, img.id);
|
||||
}
|
||||
@ -264,15 +308,24 @@ pub const ImageStorage = struct {
|
||||
self.dirty = true;
|
||||
},
|
||||
|
||||
.row => |v| {
|
||||
// Get the screenpoint y
|
||||
const y = (point.Viewport{ .x = 0, .y = v.y }).toScreen(&t.screen).y;
|
||||
.row => |v| row: {
|
||||
// v.y is in active coords so we want to convert it to a pin
|
||||
// so we can compare by page offsets.
|
||||
const target_pin = t.screen.pages.pin(.{ .active = .{
|
||||
.y = v.y,
|
||||
} }) orelse break :row;
|
||||
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||
const rect = entry.value_ptr.rect(img, t);
|
||||
if (rect.top_left.y <= y and rect.bottom_right.y >= y) {
|
||||
|
||||
// We need to copy our pin to ensure we are at least at
|
||||
// the top-left x.
|
||||
var target_pin_copy = target_pin;
|
||||
target_pin_copy.x = rect.top_left.x;
|
||||
if (target_pin_copy.isBetween(rect.top_left, rect.bottom_right)) {
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (v.delete) self.deleteIfUnused(alloc, img.id);
|
||||
}
|
||||
@ -287,6 +340,7 @@ pub const ImageStorage = struct {
|
||||
while (it.next()) |entry| {
|
||||
if (entry.value_ptr.z == v.z) {
|
||||
const image_id = entry.key_ptr.image_id;
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (v.delete) self.deleteIfUnused(alloc, image_id);
|
||||
}
|
||||
@ -305,6 +359,7 @@ pub const ImageStorage = struct {
|
||||
fn deleteById(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
s: *terminal.Screen,
|
||||
image_id: u32,
|
||||
placement_id: u32,
|
||||
delete_unused: bool,
|
||||
@ -314,14 +369,18 @@ pub const ImageStorage = struct {
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| {
|
||||
if (entry.key_ptr.image_id == image_id) {
|
||||
entry.value_ptr.deinit(s);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_ = self.placements.remove(.{
|
||||
if (self.placements.getEntry(.{
|
||||
.image_id = image_id,
|
||||
.placement_id = .{ .tag = .external, .id = placement_id },
|
||||
});
|
||||
})) |entry| {
|
||||
entry.value_ptr.deinit(s);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// If this is specified, then we also delete the image
|
||||
@ -353,18 +412,22 @@ pub const ImageStorage = struct {
|
||||
fn deleteIntersecting(
|
||||
self: *ImageStorage,
|
||||
alloc: Allocator,
|
||||
t: *const terminal.Terminal,
|
||||
p: point.ScreenPoint,
|
||||
t: *terminal.Terminal,
|
||||
p: point.Point,
|
||||
delete_unused: bool,
|
||||
filter_ctx: anytype,
|
||||
comptime filter: ?fn (@TypeOf(filter_ctx), Placement) bool,
|
||||
) void {
|
||||
// Convert our target point to a pin for comparison.
|
||||
const target_pin = t.screen.pages.pin(p) orelse return;
|
||||
|
||||
var it = self.placements.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const img = self.imageById(entry.key_ptr.image_id) orelse continue;
|
||||
const rect = entry.value_ptr.rect(img, t);
|
||||
if (rect.contains(p)) {
|
||||
if (target_pin.isBetween(rect.top_left, rect.bottom_right)) {
|
||||
if (filter) |f| if (!f(filter_ctx, entry.value_ptr.*)) continue;
|
||||
entry.value_ptr.deinit(&t.screen);
|
||||
self.placements.removeByPtr(entry.key_ptr);
|
||||
if (delete_unused) self.deleteIfUnused(alloc, img.id);
|
||||
}
|
||||
@ -486,8 +549,8 @@ pub const ImageStorage = struct {
|
||||
};
|
||||
|
||||
pub const Placement = struct {
|
||||
/// The location of the image on the screen.
|
||||
point: ScreenPoint,
|
||||
/// The tracked pin for this placement.
|
||||
pin: *PageList.Pin,
|
||||
|
||||
/// Offset of the x/y from the top-left of the cell.
|
||||
x_offset: u32 = 0,
|
||||
@ -506,6 +569,13 @@ pub const ImageStorage = struct {
|
||||
/// The z-index for this placement.
|
||||
z: i32 = 0,
|
||||
|
||||
pub fn deinit(
|
||||
self: *const Placement,
|
||||
s: *terminal.Screen,
|
||||
) void {
|
||||
s.pages.untrackPin(self.pin);
|
||||
}
|
||||
|
||||
/// Returns a selection of the entire rectangle this placement
|
||||
/// occupies within the screen.
|
||||
pub fn rect(
|
||||
@ -515,13 +585,13 @@ pub const ImageStorage = struct {
|
||||
) Rect {
|
||||
// If we have columns/rows specified we can simplify this whole thing.
|
||||
if (self.columns > 0 and self.rows > 0) {
|
||||
return .{
|
||||
.top_left = self.point,
|
||||
.bottom_right = .{
|
||||
.x = @min(self.point.x + self.columns, t.cols - 1),
|
||||
.y = self.point.y + self.rows,
|
||||
},
|
||||
var br = switch (self.pin.downOverflow(self.rows)) {
|
||||
.offset => |v| v,
|
||||
.overflow => |v| v.end,
|
||||
};
|
||||
br.x = @min(self.pin.x + self.columns, t.cols - 1);
|
||||
|
||||
return .{ .top_left = self.pin.*, .bottom_right = br };
|
||||
}
|
||||
|
||||
// Calculate our cell size.
|
||||
@ -542,17 +612,31 @@ pub const ImageStorage = struct {
|
||||
const width_cells: u32 = @intFromFloat(@ceil(width_f64 / cell_width_f64));
|
||||
const height_cells: u32 = @intFromFloat(@ceil(height_f64 / cell_height_f64));
|
||||
|
||||
// TODO(paged-terminal): clean this logic up above
|
||||
var br = switch (self.pin.downOverflow(height_cells)) {
|
||||
.offset => |v| v,
|
||||
.overflow => |v| v.end,
|
||||
};
|
||||
br.x = @min(self.pin.x + width_cells, t.cols - 1);
|
||||
|
||||
return .{
|
||||
.top_left = self.point,
|
||||
.bottom_right = .{
|
||||
.x = @min(self.point.x + width_cells, t.cols - 1),
|
||||
.y = self.point.y + height_cells,
|
||||
},
|
||||
.top_left = self.pin.*,
|
||||
.bottom_right = br,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Our pin for the placement
|
||||
fn trackPin(
|
||||
t: *terminal.Terminal,
|
||||
pt: point.Point.Coordinate,
|
||||
) !*PageList.Pin {
|
||||
return try t.screen.pages.trackPin(t.screen.pages.pin(.{
|
||||
.active = pt,
|
||||
}).?);
|
||||
}
|
||||
|
||||
test "storage: add placement with zero placement id" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
@ -562,11 +646,11 @@ test "storage: add placement with zero placement id" {
|
||||
t.height_px = 100;
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
try s.addPlacement(alloc, 1, 0, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
|
||||
try testing.expectEqual(@as(usize, 2), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
@ -587,20 +671,22 @@ test "storage: delete all placements and images" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .all = true });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 0), s.images.count());
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements and images preserves limit" {
|
||||
@ -608,15 +694,16 @@ test "storage: delete all placements and images preserves limit" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
s.total_limit = 5000;
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .all = true });
|
||||
@ -624,6 +711,7 @@ test "storage: delete all placements and images preserves limit" {
|
||||
try testing.expectEqual(@as(usize, 0), s.images.count());
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 5000), s.total_limit);
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements" {
|
||||
@ -631,20 +719,22 @@ test "storage: delete all placements" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .all = false });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements by image id" {
|
||||
@ -652,20 +742,22 @@ test "storage: delete all placements by image id" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .id = .{ .image_id = 2 } });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete all placements by image id and unused images" {
|
||||
@ -673,20 +765,22 @@ test "storage: delete all placements by image id and unused images" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .id = .{ .delete = true, .image_id = 2 } });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete placement by specific id" {
|
||||
@ -694,15 +788,16 @@ test "storage: delete placement by specific id" {
|
||||
const alloc = testing.allocator;
|
||||
var t = try terminal.Terminal.init(alloc, 3, 3);
|
||||
defer t.deinit(alloc);
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1 });
|
||||
try s.addImage(alloc, .{ .id = 2 });
|
||||
try s.addImage(alloc, .{ .id = 3 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .point = .{ .x = 1, .y = 1 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
try s.addPlacement(alloc, 2, 1, .{ .pin = try trackPin(&t, .{ .x = 1, .y = 1 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .id = .{
|
||||
@ -713,6 +808,7 @@ test "storage: delete placement by specific id" {
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 2), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 3), s.images.count());
|
||||
try testing.expectEqual(tracked + 2, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete intersecting cursor" {
|
||||
@ -722,22 +818,23 @@ test "storage: delete intersecting cursor" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
|
||||
t.screen.cursor.x = 12;
|
||||
t.screen.cursor.y = 12;
|
||||
t.screen.cursorAbsolute(12, 12);
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .intersect_cursor = false });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
||||
@ -753,22 +850,23 @@ test "storage: delete intersecting cursor plus unused" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
|
||||
t.screen.cursor.x = 12;
|
||||
t.screen.cursor.y = 12;
|
||||
t.screen.cursorAbsolute(12, 12);
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .intersect_cursor = true });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
||||
@ -784,22 +882,23 @@ test "storage: delete intersecting cursor hits multiple" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
|
||||
t.screen.cursor.x = 26;
|
||||
t.screen.cursor.y = 26;
|
||||
t.screen.cursorAbsolute(26, 26);
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .intersect_cursor = true });
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 0), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 1), s.images.count());
|
||||
try testing.expectEqual(tracked, t.screen.pages.countTrackedPins());
|
||||
}
|
||||
|
||||
test "storage: delete by column" {
|
||||
@ -809,13 +908,14 @@ test "storage: delete by column" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .column = .{
|
||||
@ -825,6 +925,7 @@ test "storage: delete by column" {
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
||||
@ -840,13 +941,14 @@ test "storage: delete by row" {
|
||||
defer t.deinit(alloc);
|
||||
t.width_px = 100;
|
||||
t.height_px = 100;
|
||||
const tracked = t.screen.pages.countTrackedPins();
|
||||
|
||||
var s: ImageStorage = .{};
|
||||
defer s.deinit(alloc);
|
||||
defer s.deinit(alloc, &t.screen);
|
||||
try s.addImage(alloc, .{ .id = 1, .width = 50, .height = 50 });
|
||||
try s.addImage(alloc, .{ .id = 2, .width = 25, .height = 25 });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .point = .{ .x = 0, .y = 0 } });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .point = .{ .x = 25, .y = 25 } });
|
||||
try s.addPlacement(alloc, 1, 1, .{ .pin = try trackPin(&t, .{ .x = 0, .y = 0 }) });
|
||||
try s.addPlacement(alloc, 1, 2, .{ .pin = try trackPin(&t, .{ .x = 25, .y = 25 }) });
|
||||
|
||||
s.dirty = false;
|
||||
s.delete(alloc, &t, .{ .row = .{
|
||||
@ -856,6 +958,7 @@ test "storage: delete by row" {
|
||||
try testing.expect(s.dirty);
|
||||
try testing.expectEqual(@as(usize, 1), s.placements.count());
|
||||
try testing.expectEqual(@as(usize, 2), s.images.count());
|
||||
try testing.expectEqual(tracked + 1, t.screen.pages.countTrackedPins());
|
||||
|
||||
// verify the placement is what we expect
|
||||
try testing.expect(s.placements.get(.{
|
||||
|
@ -15,21 +15,27 @@ pub const color = @import("color.zig");
|
||||
pub const device_status = @import("device_status.zig");
|
||||
pub const kitty = @import("kitty.zig");
|
||||
pub const modes = @import("modes.zig");
|
||||
pub const page = @import("page.zig");
|
||||
pub const parse_table = @import("parse_table.zig");
|
||||
pub const x11_color = @import("x11_color.zig");
|
||||
|
||||
pub const Charset = charsets.Charset;
|
||||
pub const CharsetSlot = charsets.Slots;
|
||||
pub const CharsetActiveSlot = charsets.ActiveSlot;
|
||||
pub const Cell = page.Cell;
|
||||
pub const CSI = Parser.Action.CSI;
|
||||
pub const DCS = Parser.Action.DCS;
|
||||
pub const MouseShape = @import("mouse_shape.zig").MouseShape;
|
||||
pub const Page = page.Page;
|
||||
pub const PageList = @import("PageList.zig");
|
||||
pub const Parser = @import("Parser.zig");
|
||||
pub const Selection = @import("Selection.zig");
|
||||
pub const Pin = PageList.Pin;
|
||||
pub const Screen = @import("Screen.zig");
|
||||
pub const Selection = @import("Selection.zig");
|
||||
pub const Terminal = @import("Terminal.zig");
|
||||
pub const Stream = stream.Stream;
|
||||
pub const Cursor = Screen.Cursor;
|
||||
pub const CursorStyle = Screen.CursorStyle;
|
||||
pub const CursorStyleReq = ansi.CursorStyle;
|
||||
pub const DeviceAttributeReq = ansi.DeviceAttributeReq;
|
||||
pub const Mode = modes.Mode;
|
||||
@ -42,17 +48,12 @@ pub const EraseLine = csi.EraseLine;
|
||||
pub const TabClear = csi.TabClear;
|
||||
pub const Attribute = sgr.Attribute;
|
||||
|
||||
// TODO(paged-terminal)
|
||||
pub const StringMap = @import("StringMap.zig");
|
||||
|
||||
/// If we're targeting wasm then we export some wasm APIs.
|
||||
pub usingnamespace if (builtin.target.isWasm()) struct {
|
||||
pub usingnamespace @import("wasm.zig");
|
||||
} else struct {};
|
||||
|
||||
// TODO(paged-terminal) remove before merge
|
||||
pub const new = @import("../terminal2/main.zig");
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
||||
// todo: make top-level imports
|
||||
_ = @import("bitmap_allocator.zig");
|
||||
_ = @import("hash_map.zig");
|
||||
_ = @import("size.zig");
|
||||
_ = @import("style.zig");
|
||||
}
|
||||
|
@ -1,254 +1,86 @@
|
||||
const std = @import("std");
|
||||
const terminal = @import("main.zig");
|
||||
const Screen = terminal.Screen;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
// This file contains various types to represent x/y coordinates. We
|
||||
// use different types so that we can lean on type-safety to get the
|
||||
// exact expected type of point.
|
||||
/// The possible reference locations for a point. When someone says "(42, 80)" in the context of a terminal, that could mean multiple
|
||||
/// things: it is in the current visible viewport? the current active
|
||||
/// area of the screen where the cursor is? the entire scrollback history?
|
||||
/// etc. This tag is used to differentiate those cases.
|
||||
pub const Tag = enum {
|
||||
/// Top-left is part of the active area where a running program can
|
||||
/// jump the cursor and make changes. The active area is the "editable"
|
||||
/// part of the screen.
|
||||
///
|
||||
/// The bottom-right of the active tag differs from all other tags
|
||||
/// because it includes the full height (rows) of the screen, including
|
||||
/// rows that may not be written yet. This is required because the active
|
||||
/// area is fully "addressable" by the running program (see below) whereas
|
||||
/// the other tags are used primarliy for reading/modifying past-written
|
||||
/// data so they can't address unwritten rows.
|
||||
///
|
||||
/// Note for those less familiar with terminal functionality: there
|
||||
/// are escape sequences to move the cursor to any position on
|
||||
/// the screen, but it is limited to the size of the viewport and
|
||||
/// the bottommost part of the screen. Terminal programs can't --
|
||||
/// with sequences at the time of writing this comment -- modify
|
||||
/// anything in the scrollback, visible viewport (if it differs
|
||||
/// from the active area), etc.
|
||||
active,
|
||||
|
||||
/// Active is a point within the active part of the screen.
|
||||
pub const Active = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
/// Top-left is the visible viewport. This means that if the user
|
||||
/// has scrolled in any direction, top-left changes. The bottom-right
|
||||
/// is the last written row from the top-left.
|
||||
viewport,
|
||||
|
||||
pub fn toScreen(self: Active, screen: *const Screen) ScreenPoint {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = screen.history + self.y,
|
||||
/// Top-left is the furthest back in the scrollback history
|
||||
/// supported by the screen and the bottom-right is the bottom-right
|
||||
/// of the last written row. Note this last point is important: the
|
||||
/// bottom right is NOT necessarilly the same as "active" because
|
||||
/// "active" always allows referencing the full rows tall of the
|
||||
/// screen whereas "screen" only contains written rows.
|
||||
screen,
|
||||
|
||||
/// The top-left is the same as "screen" but the bottom-right is
|
||||
/// the line just before the top of "active". This contains only
|
||||
/// the scrollback history.
|
||||
history,
|
||||
};
|
||||
|
||||
/// An x/y point in the terminal for some definition of location (tag).
|
||||
pub const Point = union(Tag) {
|
||||
active: Coordinate,
|
||||
viewport: Coordinate,
|
||||
screen: Coordinate,
|
||||
history: Coordinate,
|
||||
|
||||
pub const Coordinate = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
};
|
||||
|
||||
pub fn coord(self: Point) Coordinate {
|
||||
return switch (self) {
|
||||
.active,
|
||||
.viewport,
|
||||
.screen,
|
||||
.history,
|
||||
=> |v| v,
|
||||
};
|
||||
}
|
||||
|
||||
test "toScreen with scrollback" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 3, 5, 3);
|
||||
defer s.deinit();
|
||||
const str = "1\n2\n3\n4\n5\n6\n7\n8";
|
||||
try s.testWriteString(str);
|
||||
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 1,
|
||||
.y = 5,
|
||||
}, (Active{ .x = 1, .y = 2 }).toScreen(&s));
|
||||
}
|
||||
};
|
||||
|
||||
/// Viewport is a point within the viewport of the screen.
|
||||
/// A point in the terminal that is always in the viewport area.
|
||||
pub const Viewport = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
|
||||
pub fn toScreen(self: Viewport, screen: *const Screen) ScreenPoint {
|
||||
// x is unchanged, y we have to add the visible offset to
|
||||
// get the full offset from the top.
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = screen.viewport + self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn eql(self: Viewport, other: Viewport) bool {
|
||||
return self.x == other.x and self.y == other.y;
|
||||
}
|
||||
|
||||
test "toScreen with no scrollback" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 3, 5, 0);
|
||||
defer s.deinit();
|
||||
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 1,
|
||||
.y = 1,
|
||||
}, (Viewport{ .x = 1, .y = 1 }).toScreen(&s));
|
||||
}
|
||||
|
||||
test "toScreen with scrollback" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 3, 5, 3);
|
||||
defer s.deinit();
|
||||
|
||||
// At the bottom
|
||||
try s.scroll(.{ .screen = 6 });
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 0,
|
||||
.y = 3,
|
||||
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
|
||||
|
||||
// Move the viewport a bit up
|
||||
try s.scroll(.{ .screen = -1 });
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 0,
|
||||
.y = 2,
|
||||
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
|
||||
|
||||
// Move the viewport to top
|
||||
try s.scroll(.{ .top = {} });
|
||||
try testing.expectEqual(ScreenPoint{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
}, (Viewport{ .x = 0, .y = 0 }).toScreen(&s));
|
||||
}
|
||||
};
|
||||
|
||||
/// A screen point. This is offset from the top of the scrollback
|
||||
/// buffer. If the screen is scrolled or resized, this will have to
|
||||
/// be recomputed.
|
||||
pub const ScreenPoint = struct {
|
||||
/// A point in the terminal that is in relation to the entire screen.
|
||||
pub const Screen = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
|
||||
/// Returns if this point is before another point.
|
||||
pub fn before(self: ScreenPoint, other: ScreenPoint) bool {
|
||||
return self.y < other.y or
|
||||
(self.y == other.y and self.x < other.x);
|
||||
}
|
||||
|
||||
/// Returns if two points are equal.
|
||||
pub fn eql(self: ScreenPoint, other: ScreenPoint) bool {
|
||||
return self.x == other.x and self.y == other.y;
|
||||
}
|
||||
|
||||
/// Returns true if this screen point is currently in the active viewport.
|
||||
pub fn inViewport(self: ScreenPoint, screen: *const Screen) bool {
|
||||
return self.y >= screen.viewport and
|
||||
self.y < screen.viewport + screen.rows;
|
||||
}
|
||||
|
||||
/// Converts this to a viewport point. If the point is above the
|
||||
/// viewport this will move the point to (0, 0) and if it is below
|
||||
/// the viewport it'll move it to (cols - 1, rows - 1).
|
||||
pub fn toViewport(self: ScreenPoint, screen: *const Screen) Viewport {
|
||||
// TODO: test
|
||||
|
||||
// Before viewport
|
||||
if (self.y < screen.viewport) return .{ .x = 0, .y = 0 };
|
||||
|
||||
// After viewport
|
||||
if (self.y > screen.viewport + screen.rows) return .{
|
||||
.x = screen.cols - 1,
|
||||
.y = screen.rows - 1,
|
||||
};
|
||||
|
||||
return .{ .x = self.x, .y = self.y - screen.viewport };
|
||||
}
|
||||
|
||||
/// Returns a screen point iterator. This will iterate over all of
|
||||
/// of the points in a screen in a given direction one by one.
|
||||
///
|
||||
/// The iterator is only valid as long as the screen is not resized.
|
||||
pub fn iterator(
|
||||
self: ScreenPoint,
|
||||
screen: *const Screen,
|
||||
dir: Direction,
|
||||
) Iterator {
|
||||
return .{ .screen = screen, .current = self, .direction = dir };
|
||||
}
|
||||
|
||||
pub const Iterator = struct {
|
||||
screen: *const Screen,
|
||||
current: ?ScreenPoint,
|
||||
direction: Direction,
|
||||
|
||||
pub fn next(self: *Iterator) ?ScreenPoint {
|
||||
const current = self.current orelse return null;
|
||||
self.current = switch (self.direction) {
|
||||
.left_up => left_up: {
|
||||
if (current.x == 0) {
|
||||
if (current.y == 0) break :left_up null;
|
||||
break :left_up .{
|
||||
.x = self.screen.cols - 1,
|
||||
.y = current.y - 1,
|
||||
};
|
||||
}
|
||||
|
||||
break :left_up .{
|
||||
.x = current.x - 1,
|
||||
.y = current.y,
|
||||
};
|
||||
},
|
||||
|
||||
.right_down => right_down: {
|
||||
if (current.x == self.screen.cols - 1) {
|
||||
const max = self.screen.rows + self.screen.max_scrollback;
|
||||
if (current.y == max - 1) break :right_down null;
|
||||
break :right_down .{
|
||||
.x = 0,
|
||||
.y = current.y + 1,
|
||||
};
|
||||
}
|
||||
|
||||
break :right_down .{
|
||||
.x = current.x + 1,
|
||||
.y = current.y,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
return current;
|
||||
}
|
||||
};
|
||||
|
||||
test "before" {
|
||||
const testing = std.testing;
|
||||
|
||||
const p: ScreenPoint = .{ .x = 5, .y = 2 };
|
||||
try testing.expect(p.before(.{ .x = 6, .y = 2 }));
|
||||
try testing.expect(p.before(.{ .x = 3, .y = 3 }));
|
||||
}
|
||||
|
||||
test "iterator" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try Screen.init(alloc, 5, 5, 0);
|
||||
defer s.deinit();
|
||||
|
||||
// Back from the first line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 1, .y = 0 };
|
||||
var it = pt.iterator(&s, .left_up);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 1, .y = 0 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 0, .y = 0 }, it.next().?);
|
||||
try testing.expect(it.next() == null);
|
||||
}
|
||||
|
||||
// Back from second line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 1, .y = 1 };
|
||||
var it = pt.iterator(&s, .left_up);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 1, .y = 1 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 0, .y = 1 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 4, .y = 0 }, it.next().?);
|
||||
}
|
||||
|
||||
// Forward last line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 3, .y = 4 };
|
||||
var it = pt.iterator(&s, .right_down);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 3, .y = 4 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 4, .y = 4 }, it.next().?);
|
||||
try testing.expect(it.next() == null);
|
||||
}
|
||||
|
||||
// Forward not last line
|
||||
{
|
||||
var pt: ScreenPoint = .{ .x = 3, .y = 3 };
|
||||
var it = pt.iterator(&s, .right_down);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 3, .y = 3 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 4, .y = 3 }, it.next().?);
|
||||
try testing.expectEqual(ScreenPoint{ .x = 0, .y = 4 }, it.next().?);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Direction that points can go.
|
||||
pub const Direction = enum { left_up, right_down };
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
/// The possible reference locations for a point. When someone says "(42, 80)" in the context of a terminal, that could mean multiple
|
||||
/// things: it is in the current visible viewport? the current active
|
||||
/// area of the screen where the cursor is? the entire scrollback history?
|
||||
/// etc. This tag is used to differentiate those cases.
|
||||
pub const Tag = enum {
|
||||
/// Top-left is part of the active area where a running program can
|
||||
/// jump the cursor and make changes. The active area is the "editable"
|
||||
/// part of the screen.
|
||||
///
|
||||
/// The bottom-right of the active tag differs from all other tags
|
||||
/// because it includes the full height (rows) of the screen, including
|
||||
/// rows that may not be written yet. This is required because the active
|
||||
/// area is fully "addressable" by the running program (see below) whereas
|
||||
/// the other tags are used primarliy for reading/modifying past-written
|
||||
/// data so they can't address unwritten rows.
|
||||
///
|
||||
/// Note for those less familiar with terminal functionality: there
|
||||
/// are escape sequences to move the cursor to any position on
|
||||
/// the screen, but it is limited to the size of the viewport and
|
||||
/// the bottommost part of the screen. Terminal programs can't --
|
||||
/// with sequences at the time of writing this comment -- modify
|
||||
/// anything in the scrollback, visible viewport (if it differs
|
||||
/// from the active area), etc.
|
||||
active,
|
||||
|
||||
/// Top-left is the visible viewport. This means that if the user
|
||||
/// has scrolled in any direction, top-left changes. The bottom-right
|
||||
/// is the last written row from the top-left.
|
||||
viewport,
|
||||
|
||||
/// Top-left is the furthest back in the scrollback history
|
||||
/// supported by the screen and the bottom-right is the bottom-right
|
||||
/// of the last written row. Note this last point is important: the
|
||||
/// bottom right is NOT necessarilly the same as "active" because
|
||||
/// "active" always allows referencing the full rows tall of the
|
||||
/// screen whereas "screen" only contains written rows.
|
||||
screen,
|
||||
|
||||
/// The top-left is the same as "screen" but the bottom-right is
|
||||
/// the line just before the top of "active". This contains only
|
||||
/// the scrollback history.
|
||||
history,
|
||||
};
|
||||
|
||||
/// An x/y point in the terminal for some definition of location (tag).
|
||||
pub const Point = union(Tag) {
|
||||
active: Coordinate,
|
||||
viewport: Coordinate,
|
||||
screen: Coordinate,
|
||||
history: Coordinate,
|
||||
|
||||
pub const Coordinate = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
};
|
||||
|
||||
pub fn coord(self: Point) Coordinate {
|
||||
return switch (self) {
|
||||
.active,
|
||||
.viewport,
|
||||
.screen,
|
||||
.history,
|
||||
=> |v| v,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// A point in the terminal that is always in the viewport area.
|
||||
pub const Viewport = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
|
||||
pub fn eql(self: Viewport, other: Viewport) bool {
|
||||
return self.x == other.x and self.y == other.y;
|
||||
}
|
||||
};
|
||||
|
||||
/// A point in the terminal that is in relation to the entire screen.
|
||||
pub const Screen = struct {
|
||||
x: usize = 0,
|
||||
y: usize = 0,
|
||||
};
|
@ -78,7 +78,7 @@ pub const DerivedConfig = struct {
|
||||
|
||||
palette: terminal.color.Palette,
|
||||
image_storage_limit: usize,
|
||||
cursor_style: terminal.Cursor.Style,
|
||||
cursor_style: terminal.CursorStyle,
|
||||
cursor_blink: ?bool,
|
||||
cursor_color: ?configpkg.Config.Color,
|
||||
foreground: configpkg.Config.Color,
|
||||
@ -155,7 +155,7 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec {
|
||||
);
|
||||
|
||||
// Set our default cursor style
|
||||
term.screen.cursor.style = opts.config.cursor_style;
|
||||
term.screen.cursor.cursor_style = opts.config.cursor_style;
|
||||
|
||||
var subprocess = try Subprocess.init(alloc, opts);
|
||||
errdefer subprocess.deinit();
|
||||
@ -1666,7 +1666,7 @@ const StreamHandler = struct {
|
||||
/// The default cursor state. This is used with CSI q. This is
|
||||
/// set to true when we're currently in the default cursor state.
|
||||
default_cursor: bool = true,
|
||||
default_cursor_style: terminal.Cursor.Style,
|
||||
default_cursor_style: terminal.CursorStyle,
|
||||
default_cursor_blink: ?bool,
|
||||
default_cursor_color: ?terminal.color.RGB,
|
||||
|
||||
@ -1843,7 +1843,7 @@ const StreamHandler = struct {
|
||||
|
||||
.decscusr => {
|
||||
const blink = self.terminal.modes.get(.cursor_blinking);
|
||||
const style: u8 = switch (self.terminal.screen.cursor.style) {
|
||||
const style: u8 = switch (self.terminal.screen.cursor.cursor_style) {
|
||||
.block => if (blink) 1 else 2,
|
||||
.underline => if (blink) 3 else 4,
|
||||
.bar => if (blink) 5 else 6,
|
||||
@ -2358,7 +2358,7 @@ const StreamHandler = struct {
|
||||
switch (style) {
|
||||
.default => {
|
||||
self.default_cursor = true;
|
||||
self.terminal.screen.cursor.style = self.default_cursor_style;
|
||||
self.terminal.screen.cursor.cursor_style = self.default_cursor_style;
|
||||
self.terminal.modes.set(
|
||||
.cursor_blinking,
|
||||
self.default_cursor_blink orelse true,
|
||||
@ -2366,32 +2366,32 @@ const StreamHandler = struct {
|
||||
},
|
||||
|
||||
.blinking_block => {
|
||||
self.terminal.screen.cursor.style = .block;
|
||||
self.terminal.screen.cursor.cursor_style = .block;
|
||||
self.terminal.modes.set(.cursor_blinking, true);
|
||||
},
|
||||
|
||||
.steady_block => {
|
||||
self.terminal.screen.cursor.style = .block;
|
||||
self.terminal.screen.cursor.cursor_style = .block;
|
||||
self.terminal.modes.set(.cursor_blinking, false);
|
||||
},
|
||||
|
||||
.blinking_underline => {
|
||||
self.terminal.screen.cursor.style = .underline;
|
||||
self.terminal.screen.cursor.cursor_style = .underline;
|
||||
self.terminal.modes.set(.cursor_blinking, true);
|
||||
},
|
||||
|
||||
.steady_underline => {
|
||||
self.terminal.screen.cursor.style = .underline;
|
||||
self.terminal.screen.cursor.cursor_style = .underline;
|
||||
self.terminal.modes.set(.cursor_blinking, false);
|
||||
},
|
||||
|
||||
.blinking_bar => {
|
||||
self.terminal.screen.cursor.style = .bar;
|
||||
self.terminal.screen.cursor.cursor_style = .bar;
|
||||
self.terminal.modes.set(.cursor_blinking, true);
|
||||
},
|
||||
|
||||
.steady_bar => {
|
||||
self.terminal.screen.cursor.style = .bar;
|
||||
self.terminal.screen.cursor.cursor_style = .bar;
|
||||
self.terminal.modes.set(.cursor_blinking, false);
|
||||
},
|
||||
|
||||
|
Reference in New Issue
Block a user