refactor(vaxis): require an io.AnyWriter for writes
All vaxis functions that write to the terminal now require an io.AnyWriter as a parameter
This commit is contained in:
parent
59abd7d7d4
commit
5f41978054
13 changed files with 294 additions and 517 deletions
97
README.md
97
README.md
|
@ -15,38 +15,46 @@ Contributions are welcome.
|
||||||
|
|
||||||
Vaxis uses zig `0.12.0`.
|
Vaxis uses zig `0.12.0`.
|
||||||
|
|
||||||
## Feature comparison
|
## Features
|
||||||
|
|
||||||
| Feature | Vaxis | libvaxis | notcurses |
|
| Feature | libvaxis |
|
||||||
| ------------------------------ | :---: | :------: | :-------: |
|
| ------------------------------ | :------: |
|
||||||
| RGB | ✅ | ✅ | ✅ |
|
| RGB | ✅ |
|
||||||
| Hyperlinks | ✅ | ✅ | ❌ |
|
| Hyperlinks | ✅ |
|
||||||
| Bracketed Paste | ✅ | ✅ | ❌ |
|
| Bracketed Paste | ✅ |
|
||||||
| Kitty Keyboard | ✅ | ✅ | ✅ |
|
| Kitty Keyboard | ✅ |
|
||||||
| Styled Underlines | ✅ | ✅ | ✅ |
|
| Styled Underlines | ✅ |
|
||||||
| Mouse Shapes (OSC 22) | ✅ | ✅ | ❌ |
|
| Mouse Shapes (OSC 22) | ✅ |
|
||||||
| System Clipboard (OSC 52) | ✅ | ✅ | ❌ |
|
| System Clipboard (OSC 52) | ✅ |
|
||||||
| System Notifications (OSC 9) | ✅ | ✅ | ❌ |
|
| System Notifications (OSC 9) | ✅ |
|
||||||
| System Notifications (OSC 777) | ✅ | ✅ | ❌ |
|
| System Notifications (OSC 777) | ✅ |
|
||||||
| Synchronized Output (DEC 2026) | ✅ | ✅ | ✅ |
|
| Synchronized Output (DEC 2026) | ✅ |
|
||||||
| Unicode Core (DEC 2027) | ✅ | ✅ | ❌ |
|
| Unicode Core (DEC 2027) | ✅ |
|
||||||
| Color Mode Updates (DEC 2031) | ✅ | ✅ | ❌ |
|
| Color Mode Updates (DEC 2031) | ✅ |
|
||||||
| Images (full/space) | ✅ | planned | ✅ |
|
| Images (kitty) | ✅ |
|
||||||
| Images (half block) | ✅ | planned | ✅ |
|
|
||||||
| Images (quadrant) | ✅ | planned | ✅ |
|
|
||||||
| Images (sextant) | ❌ | ❌ | ✅ |
|
|
||||||
| Images (sixel) | ✅ | ❌ | ✅ |
|
|
||||||
| Images (kitty) | ✅ | ✅ | ✅ |
|
|
||||||
| Images (iterm2) | ❌ | ❌ | ✅ |
|
|
||||||
| Video | ❌ | ❌ | ✅ |
|
|
||||||
| Dank | 🆗 | 🆗 | ✅ |
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
[Documentation](https://rockorager.github.io/libvaxis/#vaxis.Vaxis)
|
[Documentation](https://rockorager.github.io/libvaxis/#vaxis.Vaxis)
|
||||||
|
|
||||||
The below example can be run using `zig build run 2>log`. stderr must be
|
Vaxis requires three basic primitives to operate:
|
||||||
redirected in order to not print to the same screen.
|
|
||||||
|
1. A TTY instance
|
||||||
|
2. An instance of Vaxis
|
||||||
|
3. An event loop
|
||||||
|
|
||||||
|
The library provides a general purpose posix TTY implementation, as well as a
|
||||||
|
multi-threaded event loop implementation. Users of the library are encouraged to
|
||||||
|
use the event loop of their choice. The event loop is responsible for reading
|
||||||
|
the TTY, passing the read bytes to the vaxis parser, and handling events.
|
||||||
|
|
||||||
|
A core feature of Vaxis is it's ability to detect features via terminal queries
|
||||||
|
instead of relying on a terminfo database. This requires that the event loop
|
||||||
|
also handle these query responses and update the Vaxis.caps struct accordingly.
|
||||||
|
See the `Loop` implementation to see how this is done if writing your own event
|
||||||
|
loop.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
```zig
|
```zig
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
@ -76,21 +84,35 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
// Initialize a tty
|
||||||
|
var tty = try vaxis.Tty.init();
|
||||||
|
defer tty.deinit();
|
||||||
|
|
||||||
// Initialize Vaxis
|
// Initialize Vaxis
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
// deinit takes an optional allocator. If your program is exiting, you can
|
// deinit takes an optional allocator. If your program is exiting, you can
|
||||||
// choose to pass a null allocator to save some exit time.
|
// choose to pass a null allocator to save some exit time.
|
||||||
defer vx.deinit(alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{};
|
// The event loop requires an intrusive init. We create an instance with
|
||||||
|
// stable points to Vaxis and our TTY, then init the instance. Doing so
|
||||||
|
// installs a signal handler for SIGWINCH on posix TTYs
|
||||||
|
//
|
||||||
|
// This event loop is thread safe. It reads the tty in a separate thread
|
||||||
|
var loop: vaxis.Loop(Event) = .{
|
||||||
|
.tty = &tty,
|
||||||
|
.vaxis = &vaxis,
|
||||||
|
};
|
||||||
|
try loop.init();
|
||||||
|
|
||||||
// Start the read loop. This puts the terminal in raw mode and begins
|
// Start the read loop. This puts the terminal in raw mode and begins
|
||||||
// reading user input
|
// reading user input
|
||||||
try loop.run();
|
try loop.start();
|
||||||
defer loop.stop();
|
defer loop.stop();
|
||||||
|
|
||||||
// Optionally enter the alternate screen
|
// Optionally enter the alternate screen
|
||||||
try vx.enterAltScreen();
|
try vx.enterAltScreen(tty.anyWriter());
|
||||||
|
|
||||||
// We'll adjust the color index every keypress for the border
|
// We'll adjust the color index every keypress for the border
|
||||||
var color_idx: u8 = 0;
|
var color_idx: u8 = 0;
|
||||||
|
@ -100,12 +122,10 @@ pub fn main() !void {
|
||||||
var text_input = TextInput.init(alloc);
|
var text_input = TextInput.init(alloc);
|
||||||
defer text_input.deinit();
|
defer text_input.deinit();
|
||||||
|
|
||||||
// Sends queries to terminal to detect certain features. This should
|
// Sends queries to terminal to detect certain features. This should always
|
||||||
// _always_ be called, but is left to the application to decide when
|
// be called after entering the alt screen, if you are using the alt screen
|
||||||
try vx.queryTerminal();
|
try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
|
|
||||||
// The main event loop. Vaxis provides a thread safe, blocking, buffered
|
|
||||||
// queue which can serve as the primary event queue for an application
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// nextEvent blocks until an event is in the queue
|
// nextEvent blocks until an event is in the queue
|
||||||
const event = loop.nextEvent();
|
const event = loop.nextEvent();
|
||||||
|
@ -141,7 +161,7 @@ pub fn main() !void {
|
||||||
// more than one byte will incur an allocation on the first render
|
// more than one byte will incur an allocation on the first render
|
||||||
// after it is drawn. Thereafter, it will not allocate unless the
|
// after it is drawn. Thereafter, it will not allocate unless the
|
||||||
// screen is resized
|
// screen is resized
|
||||||
.winsize => |ws| try vx.resize(alloc, ws),
|
.winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,8 +195,9 @@ pub fn main() !void {
|
||||||
// Draw the text_input in the child window
|
// Draw the text_input in the child window
|
||||||
text_input.draw(child);
|
text_input.draw(child);
|
||||||
|
|
||||||
// Render the screen
|
// Render the screen. Using a buffered writer will offer much better
|
||||||
try vx.render();
|
// performance, but is not required
|
||||||
|
try vx.render(tty.anyWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -14,15 +14,19 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
var tty = try vaxis.Tty.init();
|
||||||
|
defer tty.deinit();
|
||||||
|
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
defer vx.deinit(alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx };
|
||||||
|
try loop.init();
|
||||||
|
|
||||||
try loop.run();
|
try loop.start();
|
||||||
defer loop.stop();
|
defer loop.stop();
|
||||||
|
|
||||||
try vx.queryTerminal();
|
try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
|
|
||||||
var text_input = TextInput.init(alloc, &vx.unicode);
|
var text_input = TextInput.init(alloc, &vx.unicode);
|
||||||
defer text_input.deinit();
|
defer text_input.deinit();
|
||||||
|
@ -70,7 +74,7 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.winsize => |ws| {
|
.winsize => |ws| {
|
||||||
try vx.resize(alloc, ws);
|
try vx.resize(alloc, tty.anyWriter(), ws);
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
@ -91,7 +95,7 @@ pub fn main() !void {
|
||||||
_ = try win.print(&seg, .{ .row_offset = j + 1 });
|
_ = try win.print(&seg, .{ .row_offset = j + 1 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try vx.render();
|
try vx.render(tty.anyWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,24 +18,27 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
var tty = try vaxis.Tty.init();
|
||||||
|
defer tty.deinit();
|
||||||
|
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
defer vx.deinit(alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx };
|
||||||
|
try loop.init();
|
||||||
|
|
||||||
try loop.run();
|
try loop.start();
|
||||||
defer loop.stop();
|
defer loop.stop();
|
||||||
|
|
||||||
try vx.enterAltScreen();
|
try vx.enterAltScreen(tty.anyWriter());
|
||||||
|
try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
try vx.queryTerminal();
|
|
||||||
|
|
||||||
const imgs = [_]vaxis.Image{
|
const imgs = [_]vaxis.Image{
|
||||||
try vx.loadImage(alloc, .{ .path = "examples/zig.png" }),
|
try vx.loadImage(alloc, tty.anyWriter(), .{ .path = "examples/zig.png" }),
|
||||||
try vx.loadImage(alloc, .{ .path = "examples/vaxis.png" }),
|
try vx.loadImage(alloc, tty.anyWriter(), .{ .path = "examples/vaxis.png" }),
|
||||||
};
|
};
|
||||||
defer vx.freeImage(imgs[0].id);
|
defer vx.freeImage(tty.anyWriter(), imgs[0].id);
|
||||||
defer vx.freeImage(imgs[1].id);
|
defer vx.freeImage(tty.anyWriter(), imgs[1].id);
|
||||||
|
|
||||||
var n: usize = 0;
|
var n: usize = 0;
|
||||||
|
|
||||||
|
@ -54,7 +57,7 @@ pub fn main() !void {
|
||||||
else if (key.matches('k', .{}))
|
else if (key.matches('k', .{}))
|
||||||
clip_y -|= 1;
|
clip_y -|= 1;
|
||||||
},
|
},
|
||||||
.winsize => |ws| try vx.resize(alloc, ws),
|
.winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
|
||||||
}
|
}
|
||||||
|
|
||||||
n = (n + 1) % imgs.len;
|
n = (n + 1) % imgs.len;
|
||||||
|
@ -68,6 +71,6 @@ pub fn main() !void {
|
||||||
.y = clip_y,
|
.y = clip_y,
|
||||||
} });
|
} });
|
||||||
|
|
||||||
try vx.render();
|
try vx.render(tty.anyWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,19 +14,20 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
// Initialize Vaxis
|
var tty = try vaxis.Tty.init();
|
||||||
|
defer tty.deinit();
|
||||||
|
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
defer vx.deinit(alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx };
|
||||||
|
try loop.init();
|
||||||
|
|
||||||
// Start the read loop. This puts the terminal in raw mode and begins
|
try loop.start();
|
||||||
// reading user input
|
|
||||||
try loop.run();
|
|
||||||
defer loop.stop();
|
defer loop.stop();
|
||||||
|
|
||||||
// Optionally enter the alternate screen
|
// Optionally enter the alternate screen
|
||||||
try vx.enterAltScreen();
|
try vx.enterAltScreen(tty.anyWriter());
|
||||||
|
|
||||||
// We'll adjust the color index every keypress
|
// We'll adjust the color index every keypress
|
||||||
var color_idx: u8 = 0;
|
var color_idx: u8 = 0;
|
||||||
|
@ -51,7 +52,7 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.winsize => |ws| {
|
.winsize => |ws| {
|
||||||
try vx.resize(alloc, ws);
|
try vx.resize(alloc, tty.anyWriter(), ws);
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
@ -85,7 +86,7 @@ pub fn main() !void {
|
||||||
child.writeCell(i, 0, cell);
|
child.writeCell(i, 0, cell);
|
||||||
}
|
}
|
||||||
// Render the screen
|
// Render the screen
|
||||||
try vx.render();
|
try vx.render(tty.anyWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,18 +23,22 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
var tty = try vaxis.Tty.init();
|
||||||
|
defer tty.deinit();
|
||||||
|
|
||||||
// Initialize Vaxis
|
// Initialize Vaxis
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
defer vx.deinit(alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx };
|
||||||
|
try loop.init();
|
||||||
|
|
||||||
try loop.run();
|
try loop.start();
|
||||||
defer loop.stop();
|
defer loop.stop();
|
||||||
|
|
||||||
// Optionally enter the alternate screen
|
// Optionally enter the alternate screen
|
||||||
try vx.enterAltScreen();
|
// try vx.enterAltScreen(tty.anyWriter());
|
||||||
try vx.queryTerminal();
|
try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
|
|
||||||
var nvim = try vaxis.widgets.nvim.Nvim(Event).init(alloc, &loop);
|
var nvim = try vaxis.widgets.nvim.Nvim(Event).init(alloc, &loop);
|
||||||
try nvim.spawn();
|
try nvim.spawn();
|
||||||
|
@ -53,7 +57,7 @@ pub fn main() !void {
|
||||||
try nvim.update(.{ .key_press = key });
|
try nvim.update(.{ .key_press = key });
|
||||||
},
|
},
|
||||||
.winsize => |ws| {
|
.winsize => |ws| {
|
||||||
try vx.resize(alloc, ws);
|
try vx.resize(alloc, tty.anyWriter(), ws);
|
||||||
},
|
},
|
||||||
.nvim => |nvim_event| {
|
.nvim => |nvim_event| {
|
||||||
switch (nvim_event) {
|
switch (nvim_event) {
|
||||||
|
@ -74,6 +78,6 @@ pub fn main() !void {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
try nvim.draw(child);
|
try nvim.draw(child);
|
||||||
try vx.render();
|
try vx.render(tty.anyWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const vaxis = @import("vaxis");
|
|
||||||
const Cell = vaxis.Cell;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.main);
|
|
||||||
|
|
||||||
const Event = union(enum) {
|
|
||||||
winsize: vaxis.Winsize,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
const alloc = gpa.allocator();
|
|
||||||
var vx = try vaxis.init(alloc, .{});
|
|
||||||
defer vx.deinit(alloc);
|
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
|
||||||
|
|
||||||
try loop.run();
|
|
||||||
defer loop.stop();
|
|
||||||
try vx.enterAltScreen();
|
|
||||||
try vx.queryTerminal();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const event = loop.nextEvent();
|
|
||||||
switch (event) {
|
|
||||||
.winsize => |ws| {
|
|
||||||
try vx.resize(alloc, ws);
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const timer_start = std.time.microTimestamp();
|
|
||||||
var iter: usize = 0;
|
|
||||||
while (iter < 10_000) : (iter += 1) {
|
|
||||||
const win = vx.window();
|
|
||||||
const child = win.initChild(0, 0, .{ .limit = 20 }, .{ .limit = 20 });
|
|
||||||
win.clear();
|
|
||||||
var row: usize = 0;
|
|
||||||
while (row < child.height) : (row += 1) {
|
|
||||||
var col: usize = 0;
|
|
||||||
while (col < child.width) : (col += 1) {
|
|
||||||
child.writeCell(col, row, .{
|
|
||||||
.char = .{
|
|
||||||
.grapheme = " ",
|
|
||||||
.width = 1,
|
|
||||||
},
|
|
||||||
.style = .{
|
|
||||||
.bg = .{ .index = @truncate(col + iter) },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try vx.render();
|
|
||||||
}
|
|
||||||
try vx.exitAltScreen();
|
|
||||||
const took = std.time.microTimestamp() - timer_start;
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("\r\ntook {d}ms to render 10,000 times\r\n", .{@divTrunc(took, std.time.us_per_ms)});
|
|
||||||
}
|
|
|
@ -31,20 +31,17 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
// Initialize Vaxis with our event type
|
var tty = try vaxis.Tty.init();
|
||||||
var vx = try vaxis.init(Event, .{});
|
defer tty.deinit();
|
||||||
// deinit takes an optional allocator. If your program is exiting, you can
|
|
||||||
// choose to pass a null allocator to save some exit time.
|
|
||||||
defer vx.deinit(alloc);
|
|
||||||
|
|
||||||
// Start the read loop. This puts the terminal in raw mode and begins
|
var vx = try vaxis.init(alloc, .{});
|
||||||
// reading user input
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
try vx.startReadThread();
|
|
||||||
defer vx.stopReadThread();
|
|
||||||
|
|
||||||
// Optionally enter the alternate screen
|
var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx };
|
||||||
try vx.enterAltScreen();
|
try loop.init();
|
||||||
defer vx.exitAltScreen() catch {};
|
|
||||||
|
try loop.start();
|
||||||
|
defer loop.stop();
|
||||||
|
|
||||||
// We'll adjust the color index every keypress for the border
|
// We'll adjust the color index every keypress for the border
|
||||||
var color_idx: u8 = 0;
|
var color_idx: u8 = 0;
|
||||||
|
@ -56,7 +53,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Sends queries to terminal to detect certain features. This should
|
// Sends queries to terminal to detect certain features. This should
|
||||||
// _always_ be called, but is left to the application to decide when
|
// _always_ be called, but is left to the application to decide when
|
||||||
try vx.queryTerminal();
|
try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
|
|
||||||
try vx.setMouseMode(true);
|
try vx.setMouseMode(true);
|
||||||
|
|
||||||
|
@ -78,8 +75,6 @@ pub fn main() !void {
|
||||||
break;
|
break;
|
||||||
} else if (key.matches('l', .{ .ctrl = true })) {
|
} else if (key.matches('l', .{ .ctrl = true })) {
|
||||||
vx.queueRefresh();
|
vx.queueRefresh();
|
||||||
} else if (key.matches('n', .{ .ctrl = true })) {
|
|
||||||
try vx.notify("vaxis", "hello from vaxis");
|
|
||||||
} else if (key.matches('z', .{ .ctrl = true })) {
|
} else if (key.matches('z', .{ .ctrl = true })) {
|
||||||
try openDirVim(alloc, &vx, "examples");
|
try openDirVim(alloc, &vx, "examples");
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,7 +97,7 @@ pub fn main() !void {
|
||||||
// more than one byte will incur an allocation on the first render
|
// more than one byte will incur an allocation on the first render
|
||||||
// after it is drawn. Thereafter, it will not allocate unless the
|
// after it is drawn. Thereafter, it will not allocate unless the
|
||||||
// screen is resized
|
// screen is resized
|
||||||
.winsize => |ws| try vx.resize(alloc, ws),
|
.winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +123,7 @@ pub fn main() !void {
|
||||||
text_input.draw(border.all(child, style));
|
text_input.draw(border.all(child, style));
|
||||||
|
|
||||||
// Render the screen
|
// Render the screen
|
||||||
try vx.render();
|
try vx.render(tty.anyWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const vaxis = @import("vaxis");
|
|
||||||
const Cell = vaxis.Cell;
|
|
||||||
const TextInput = vaxis.widgets.TextInput;
|
|
||||||
const border = vaxis.widgets.border;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.main);
|
|
||||||
|
|
||||||
const Event = union(enum) {
|
|
||||||
key_press: vaxis.Key,
|
|
||||||
winsize: vaxis.Winsize,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
defer {
|
|
||||||
const deinit_status = gpa.deinit();
|
|
||||||
//fail test; can't try in defer as defer is executed after we return
|
|
||||||
if (deinit_status == .leak) {
|
|
||||||
log.err("memory leak", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const alloc = gpa.allocator();
|
|
||||||
|
|
||||||
var line: ?[]const u8 = null;
|
|
||||||
defer {
|
|
||||||
// do this in defer so that vaxis cleans up terminal state before we
|
|
||||||
// print to stdout
|
|
||||||
if (line) |_| {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
stdout.print("\n{s}\n", .{line.?}) catch {};
|
|
||||||
alloc.free(line.?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var vx = try vaxis.init(alloc, .{});
|
|
||||||
defer vx.deinit(alloc);
|
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
|
||||||
try loop.run();
|
|
||||||
defer loop.stop();
|
|
||||||
|
|
||||||
var text_input = TextInput.init(alloc, &vx.unicode);
|
|
||||||
defer text_input.deinit();
|
|
||||||
|
|
||||||
try vx.queryTerminal();
|
|
||||||
|
|
||||||
const prompt: vaxis.Segment = .{ .text = "$ " };
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const event = loop.nextEvent();
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |key| {
|
|
||||||
if (key.matches('c', .{ .ctrl = true })) {
|
|
||||||
break;
|
|
||||||
} else if (key.matches(vaxis.Key.enter, .{})) {
|
|
||||||
line = try text_input.toOwnedSlice();
|
|
||||||
text_input.clearAndFree();
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
try text_input.update(.{ .key_press = key });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.winsize => |ws| try vx.resize(alloc, ws),
|
|
||||||
}
|
|
||||||
|
|
||||||
const win = vx.window();
|
|
||||||
|
|
||||||
win.clear();
|
|
||||||
_ = try win.printSegment(prompt, .{});
|
|
||||||
|
|
||||||
const input_win = win.child(.{ .x_off = 2 });
|
|
||||||
text_input.draw(input_win);
|
|
||||||
try vx.render();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const vaxis = @import("vaxis");
|
|
||||||
const Cell = vaxis.Cell;
|
|
||||||
const TextInput = vaxis.widgets.TextInput;
|
|
||||||
|
|
||||||
const log = std.log.scoped(.main);
|
|
||||||
pub fn main() !void {
|
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
||||||
defer {
|
|
||||||
const deinit_status = gpa.deinit();
|
|
||||||
if (deinit_status == .leak) {
|
|
||||||
log.err("memory leak", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const alloc = gpa.allocator();
|
|
||||||
|
|
||||||
var vx = try vaxis.init(alloc, .{});
|
|
||||||
defer vx.deinit(alloc);
|
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
|
||||||
|
|
||||||
try loop.run();
|
|
||||||
defer loop.stop();
|
|
||||||
|
|
||||||
try vx.queryTerminal();
|
|
||||||
|
|
||||||
var text_input = TextInput.init(alloc, &vx.unicode);
|
|
||||||
defer text_input.deinit();
|
|
||||||
|
|
||||||
var selected_option: ?usize = null;
|
|
||||||
|
|
||||||
const options = [_][]const u8{
|
|
||||||
"yes",
|
|
||||||
"no",
|
|
||||||
};
|
|
||||||
|
|
||||||
// The main event loop. Vaxis provides a thread safe, blocking, buffered
|
|
||||||
// queue which can serve as the primary event queue for an application
|
|
||||||
while (true) {
|
|
||||||
// nextEvent blocks until an event is in the queue
|
|
||||||
const event = loop.nextEvent();
|
|
||||||
// exhaustive switching ftw. Vaxis will send events if your Event
|
|
||||||
// enum has the fields for those events (ie "key_press", "winsize")
|
|
||||||
switch (event) {
|
|
||||||
.key_press => |key| {
|
|
||||||
if (key.codepoint == 'c' and key.mods.ctrl) {
|
|
||||||
break;
|
|
||||||
} else if (key.matches(vaxis.Key.tab, .{})) {
|
|
||||||
if (selected_option == null) {
|
|
||||||
selected_option = 0;
|
|
||||||
} else {
|
|
||||||
selected_option.? = @min(options.len - 1, selected_option.? + 1);
|
|
||||||
}
|
|
||||||
} else if (key.matches(vaxis.Key.tab, .{ .shift = true })) {
|
|
||||||
if (selected_option == null) {
|
|
||||||
selected_option = 0;
|
|
||||||
} else {
|
|
||||||
selected_option.? = selected_option.? -| 1;
|
|
||||||
}
|
|
||||||
} else if (key.matches(vaxis.Key.enter, .{})) {
|
|
||||||
if (selected_option) |i| {
|
|
||||||
log.err("enter", .{});
|
|
||||||
try text_input.insertSliceAtCursor(options[i]);
|
|
||||||
selected_option = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (selected_option == null)
|
|
||||||
try text_input.update(.{ .key_press = key });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.winsize => |ws| {
|
|
||||||
try vx.resize(alloc, ws);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
const win = vx.window();
|
|
||||||
win.clear();
|
|
||||||
|
|
||||||
const right_win = win.child(.{
|
|
||||||
.x_off = win.width - 4,
|
|
||||||
});
|
|
||||||
const left_win = win.child(.{
|
|
||||||
.width = .{ .limit = 8 },
|
|
||||||
});
|
|
||||||
var right_prompt = [_]vaxis.Segment{
|
|
||||||
.{ .text = "👩🚀🏳️🌈" },
|
|
||||||
};
|
|
||||||
var left_prompt = [_]vaxis.Segment{
|
|
||||||
.{ .text = "👩🚀🏳️🌈~ ", .style = .{ .fg = .{ .index = 4 } } },
|
|
||||||
.{ .text = "", .style = .{ .fg = .{ .index = 5 } } },
|
|
||||||
};
|
|
||||||
_ = try right_win.print(&right_prompt, .{});
|
|
||||||
_ = try left_win.print(&left_prompt, .{});
|
|
||||||
|
|
||||||
const input_win = win.child(.{
|
|
||||||
.x_off = 8,
|
|
||||||
.width = .{ .limit = win.width - 12 },
|
|
||||||
});
|
|
||||||
|
|
||||||
text_input.draw(input_win);
|
|
||||||
|
|
||||||
if (selected_option) |i| {
|
|
||||||
win.hideCursor();
|
|
||||||
for (options, 0..) |opt, j| {
|
|
||||||
log.err("i = {d}, j = {d}, opt = {s}", .{ i, j, opt });
|
|
||||||
var seg = [_]vaxis.Segment{.{
|
|
||||||
.text = opt,
|
|
||||||
.style = if (j == i) .{ .reverse = true } else .{},
|
|
||||||
}};
|
|
||||||
_ = try win.print(&seg, .{ .row_offset = j + 1 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try vx.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our Event. This can contain internal events as well as Vaxis events.
|
|
||||||
// Internal events can be posted into the same queue as vaxis events to allow
|
|
||||||
// for a single event loop with exhaustive switching. Booya
|
|
||||||
const Event = union(enum) {
|
|
||||||
key_press: vaxis.Key,
|
|
||||||
winsize: vaxis.Winsize,
|
|
||||||
focus_in,
|
|
||||||
foo: u8,
|
|
||||||
};
|
|
|
@ -24,18 +24,20 @@ pub fn main() !void {
|
||||||
const user_list = std.ArrayList(User).fromOwnedSlice(alloc, users_buf);
|
const user_list = std.ArrayList(User).fromOwnedSlice(alloc, users_buf);
|
||||||
defer user_list.deinit();
|
defer user_list.deinit();
|
||||||
|
|
||||||
|
var tty = try vaxis.Tty.init();
|
||||||
|
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
defer vx.deinit(alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
var loop: vaxis.Loop(union(enum) {
|
var loop: vaxis.Loop(union(enum) {
|
||||||
key_press: vaxis.Key,
|
key_press: vaxis.Key,
|
||||||
winsize: vaxis.Winsize,
|
winsize: vaxis.Winsize,
|
||||||
}) = .{ .vaxis = &vx };
|
}) = .{ .tty = &tty, .vaxis = &vx };
|
||||||
|
|
||||||
try loop.run();
|
try loop.start();
|
||||||
defer loop.stop();
|
defer loop.stop();
|
||||||
try vx.enterAltScreen();
|
try vx.enterAltScreen(tty.anyWriter());
|
||||||
try vx.queryTerminal();
|
try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
|
|
||||||
const logo =
|
const logo =
|
||||||
\\░█░█░█▀█░█░█░▀█▀░█▀▀░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░
|
\\░█░█░█▀█░█░█░▀█▀░█▀▀░░░▀█▀░█▀█░█▀▄░█░░░█▀▀░
|
||||||
|
@ -145,7 +147,7 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
moving = false;
|
moving = false;
|
||||||
},
|
},
|
||||||
.winsize => |ws| try vx.resize(alloc, ws),
|
.winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
|
||||||
//else => {},
|
//else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +205,7 @@ pub fn main() !void {
|
||||||
cmd_input.draw(bottom_bar);
|
cmd_input.draw(bottom_bar);
|
||||||
|
|
||||||
// Render the screen
|
// Render the screen
|
||||||
try vx.render();
|
try vx.render(tty.anyWriter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
// Initialize Vaxis
|
// Initialize Vaxis
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
defer vx.deinit(tty.anyWriter(), alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{
|
var loop: vaxis.Loop(Event) = .{
|
||||||
.vaxis = &vx,
|
.vaxis = &vx,
|
||||||
|
@ -120,7 +120,7 @@ pub fn main() !void {
|
||||||
// more than one byte will incur an allocation on the first render
|
// more than one byte will incur an allocation on the first render
|
||||||
// after it is drawn. Thereafter, it will not allocate unless the
|
// after it is drawn. Thereafter, it will not allocate unless the
|
||||||
// screen is resized
|
// screen is resized
|
||||||
.winsize => |ws| try vx.resize(alloc, ws, tty.anyWriter()),
|
.winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,18 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
const alloc = gpa.allocator();
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
var tty = try vaxis.Tty.init();
|
||||||
var vx = try vaxis.init(alloc, .{});
|
var vx = try vaxis.init(alloc, .{});
|
||||||
defer vx.deinit(alloc);
|
defer vx.deinit(alloc, tty.anyWriter());
|
||||||
|
|
||||||
var loop: vaxis.Loop(Event) = .{ .vaxis = &vx };
|
var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx };
|
||||||
|
try loop.init();
|
||||||
|
|
||||||
try loop.run();
|
try loop.start();
|
||||||
defer loop.stop();
|
defer loop.stop();
|
||||||
|
|
||||||
try vx.enterAltScreen();
|
try vx.enterAltScreen(tty.anyWriter());
|
||||||
try vx.queryTerminal();
|
try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s);
|
||||||
|
|
||||||
const lower_limit = 30;
|
const lower_limit = 30;
|
||||||
var color_idx: u8 = lower_limit;
|
var color_idx: u8 = lower_limit;
|
||||||
|
@ -42,7 +44,7 @@ pub fn main() !void {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.key_press => |key| if (key.matches('c', .{ .ctrl = true })) return,
|
.key_press => |key| if (key.matches('c', .{ .ctrl = true })) return,
|
||||||
.winsize => |ws| {
|
.winsize => |ws| {
|
||||||
try vx.resize(alloc, ws);
|
try vx.resize(alloc, tty.anyWriter(), ws);
|
||||||
break;
|
break;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -52,7 +54,7 @@ pub fn main() !void {
|
||||||
while (loop.tryEvent()) |event| {
|
while (loop.tryEvent()) |event| {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.key_press => |key| if (key.matches('c', .{ .ctrl = true })) return,
|
.key_press => |key| if (key.matches('c', .{ .ctrl = true })) return,
|
||||||
.winsize => |ws| try vx.resize(alloc, ws),
|
.winsize => |ws| try vx.resize(alloc, tty.anyWriter(), ws),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +69,7 @@ pub fn main() !void {
|
||||||
};
|
};
|
||||||
const center = vaxis.widgets.alignment.center(win, 28, 4);
|
const center = vaxis.widgets.alignment.center(win, 28, 4);
|
||||||
_ = try center.printSegment(segment, .{ .wrap = .grapheme });
|
_ = try center.printSegment(segment, .{ .wrap = .grapheme });
|
||||||
try vx.render();
|
try vx.render(tty.anyWriter());
|
||||||
std.time.sleep(8 * std.time.ns_per_ms);
|
std.time.sleep(8 * std.time.ns_per_ms);
|
||||||
switch (dir) {
|
switch (dir) {
|
||||||
.up => {
|
.up => {
|
||||||
|
|
305
src/Vaxis.zig
305
src/Vaxis.zig
|
@ -9,7 +9,6 @@ const InternalScreen = @import("InternalScreen.zig");
|
||||||
const Key = @import("Key.zig");
|
const Key = @import("Key.zig");
|
||||||
const Mouse = @import("Mouse.zig");
|
const Mouse = @import("Mouse.zig");
|
||||||
const Screen = @import("Screen.zig");
|
const Screen = @import("Screen.zig");
|
||||||
const tty = @import("tty.zig");
|
|
||||||
const Unicode = @import("Unicode.zig");
|
const Unicode = @import("Unicode.zig");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@ const Hyperlink = Cell.Hyperlink;
|
||||||
const KittyFlags = Key.KittyFlags;
|
const KittyFlags = Key.KittyFlags;
|
||||||
const Shape = Mouse.Shape;
|
const Shape = Mouse.Shape;
|
||||||
const Style = Cell.Style;
|
const Style = Cell.Style;
|
||||||
const Winsize = tty.Winsize;
|
const Winsize = @import("tty.zig").Winsize;
|
||||||
|
|
||||||
const ctlseqs = @import("ctlseqs.zig");
|
const ctlseqs = @import("ctlseqs.zig");
|
||||||
const gwidth = @import("gwidth.zig");
|
const gwidth = @import("gwidth.zig");
|
||||||
|
@ -57,6 +56,7 @@ opts: Options = .{},
|
||||||
/// if we should redraw the entire screen on the next render
|
/// if we should redraw the entire screen on the next render
|
||||||
refresh: bool = false,
|
refresh: bool = false,
|
||||||
|
|
||||||
|
// FIXME: remove before committing
|
||||||
/// blocks the main thread until a DA1 query has been received, or the
|
/// blocks the main thread until a DA1 query has been received, or the
|
||||||
/// futex times out
|
/// futex times out
|
||||||
query_futex: atomic.Value(u32) = atomic.Value(u32).init(0),
|
query_futex: atomic.Value(u32) = atomic.Value(u32).init(0),
|
||||||
|
@ -106,40 +106,49 @@ pub fn init(alloc: std.mem.Allocator, opts: Options) !Vaxis {
|
||||||
/// passed, this will free resources associated with Vaxis. This is left as an
|
/// passed, this will free resources associated with Vaxis. This is left as an
|
||||||
/// optional so applications can choose to not free resources when the
|
/// optional so applications can choose to not free resources when the
|
||||||
/// application will be exiting anyways
|
/// application will be exiting anyways
|
||||||
pub fn deinit(self: *Vaxis, writer: AnyWriter, alloc: ?std.mem.Allocator) void {
|
pub fn deinit(self: *Vaxis, alloc: ?std.mem.Allocator, tty: AnyWriter) void {
|
||||||
self.resetState(writer) catch {};
|
self.resetState(tty) catch {};
|
||||||
|
|
||||||
// always show the cursor on exit
|
// always show the cursor on exit
|
||||||
writer.writeAll(ctlseqs.show_cursor) catch {};
|
tty.writeAll(ctlseqs.show_cursor) catch {};
|
||||||
if (alloc) |a| {
|
if (alloc) |a| {
|
||||||
self.screen.deinit(a);
|
self.screen.deinit(a);
|
||||||
self.screen_last.deinit(a);
|
self.screen_last.deinit(a);
|
||||||
}
|
}
|
||||||
if (self.renders > 0) {
|
if (self.renders > 0) {
|
||||||
const tpr = @divTrunc(self.render_dur, self.renders);
|
const tpr = @divTrunc(self.render_dur, self.renders);
|
||||||
log.debug("total renders = {d}", .{self.renders});
|
log.debug("total renders = {d}\r", .{self.renders});
|
||||||
log.debug("microseconds per render = {d}", .{tpr});
|
log.debug("microseconds per render = {d}\r", .{tpr});
|
||||||
}
|
}
|
||||||
self.unicode.deinit();
|
self.unicode.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// resets enabled features
|
/// resets enabled features, sends cursor to home and clears below cursor
|
||||||
pub fn resetState(self: *Vaxis, writer: AnyWriter) !void {
|
pub fn resetState(self: *Vaxis, tty: AnyWriter) !void {
|
||||||
if (self.state.kitty_keyboard) {
|
if (self.state.kitty_keyboard) {
|
||||||
try writer.writeAll(ctlseqs.csi_u_pop);
|
try tty.writeAll(ctlseqs.csi_u_pop);
|
||||||
self.state.kitty_keyboard = false;
|
self.state.kitty_keyboard = false;
|
||||||
}
|
}
|
||||||
if (self.state.mouse) {
|
if (self.state.mouse) {
|
||||||
try self.setMouseMode(writer, false);
|
try self.setMouseMode(tty, false);
|
||||||
}
|
}
|
||||||
if (self.state.bracketed_paste) {
|
if (self.state.bracketed_paste) {
|
||||||
try self.setBracketedPaste(writer, false);
|
try self.setBracketedPaste(tty, false);
|
||||||
}
|
}
|
||||||
if (self.state.alt_screen) {
|
if (self.state.alt_screen) {
|
||||||
try self.exitAltScreen(writer);
|
try tty.writeAll(ctlseqs.home);
|
||||||
|
try tty.writeAll(ctlseqs.erase_below_cursor);
|
||||||
|
try self.exitAltScreen(tty);
|
||||||
|
} else {
|
||||||
|
try tty.writeByte('\r');
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < self.state.cursor.row) : (i += 1) {
|
||||||
|
try tty.writeAll(ctlseqs.ri);
|
||||||
|
}
|
||||||
|
try tty.writeAll(ctlseqs.erase_below_cursor);
|
||||||
}
|
}
|
||||||
if (self.state.color_scheme_updates) {
|
if (self.state.color_scheme_updates) {
|
||||||
try writer.writeAll(ctlseqs.color_scheme_reset);
|
try tty.writeAll(ctlseqs.color_scheme_reset);
|
||||||
self.state.color_scheme_updates = false;
|
self.state.color_scheme_updates = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,7 +157,12 @@ pub fn resetState(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
/// required to display the screen (ie width x height). Any previous screen is
|
/// required to display the screen (ie width x height). Any previous screen is
|
||||||
/// freed when resizing. The cursor will be sent to it's home position and a
|
/// freed when resizing. The cursor will be sent to it's home position and a
|
||||||
/// hardware clear-below-cursor will be sent
|
/// hardware clear-below-cursor will be sent
|
||||||
pub fn resize(self: *Vaxis, alloc: std.mem.Allocator, winsize: Winsize, writer: AnyWriter) !void {
|
pub fn resize(
|
||||||
|
self: *Vaxis,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
tty: AnyWriter,
|
||||||
|
winsize: Winsize,
|
||||||
|
) !void {
|
||||||
log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows });
|
log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows });
|
||||||
self.screen.deinit(alloc);
|
self.screen.deinit(alloc);
|
||||||
self.screen = try Screen.init(alloc, winsize, &self.unicode);
|
self.screen = try Screen.init(alloc, winsize, &self.unicode);
|
||||||
|
@ -159,15 +173,15 @@ pub fn resize(self: *Vaxis, alloc: std.mem.Allocator, winsize: Winsize, writer:
|
||||||
self.screen_last.deinit(alloc);
|
self.screen_last.deinit(alloc);
|
||||||
self.screen_last = try InternalScreen.init(alloc, winsize.cols, winsize.rows);
|
self.screen_last = try InternalScreen.init(alloc, winsize.cols, winsize.rows);
|
||||||
if (self.state.alt_screen)
|
if (self.state.alt_screen)
|
||||||
try writer.writeAll(ctlseqs.home)
|
try tty.writeAll(ctlseqs.home)
|
||||||
else {
|
else {
|
||||||
try writer.writeByte('\r');
|
try tty.writeByte('\r');
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < self.state.cursor.row) : (i += 1) {
|
while (i < self.state.cursor.row) : (i += 1) {
|
||||||
try writer.writeAll(ctlseqs.ri);
|
try tty.writeAll(ctlseqs.ri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try writer.writeAll(ctlseqs.erase_below_cursor);
|
try tty.writeAll(ctlseqs.erase_below_cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns a Window comprising of the entire terminal screen
|
/// returns a Window comprising of the entire terminal screen
|
||||||
|
@ -183,33 +197,34 @@ pub fn window(self: *Vaxis) Window {
|
||||||
|
|
||||||
/// enter the alternate screen. The alternate screen will automatically
|
/// enter the alternate screen. The alternate screen will automatically
|
||||||
/// be exited if calling deinit while in the alt screen
|
/// be exited if calling deinit while in the alt screen
|
||||||
pub fn enterAltScreen(self: *Vaxis, writer: AnyWriter) !void {
|
pub fn enterAltScreen(self: *Vaxis, tty: AnyWriter) !void {
|
||||||
try writer.writeAll(ctlseqs.smcup);
|
try tty.writeAll(ctlseqs.smcup);
|
||||||
self.state.alt_screen = true;
|
self.state.alt_screen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// exit the alternate screen
|
/// exit the alternate screen
|
||||||
pub fn exitAltScreen(self: *Vaxis, writer: AnyWriter) !void {
|
pub fn exitAltScreen(self: *Vaxis, tty: AnyWriter) !void {
|
||||||
try writer.writeAll(ctlseqs.rmcup);
|
try tty.writeAll(ctlseqs.rmcup);
|
||||||
self.state.alt_screen = false;
|
self.state.alt_screen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// write queries to the terminal to determine capabilities. Individual
|
/// write queries to the terminal to determine capabilities. Individual
|
||||||
/// capabilities will be delivered to the client and possibly intercepted by
|
/// capabilities will be delivered to the client and possibly intercepted by
|
||||||
/// Vaxis to enable features
|
/// Vaxis to enable features.
|
||||||
pub fn queryTerminal(self: *Vaxis) !void {
|
///
|
||||||
try self.queryTerminalSend();
|
/// This call will block until Vaxis.query_futex is woken up, or the timeout.
|
||||||
|
/// Event loops can wake up this futex when cap_da1 is received
|
||||||
|
pub fn queryTerminal(self: *Vaxis, tty: AnyWriter, timeout_ns: u64) !void {
|
||||||
|
try self.queryTerminalSend(tty);
|
||||||
// 1 second timeout
|
// 1 second timeout
|
||||||
std.Thread.Futex.timedWait(&self.query_futex, 0, 1 * std.time.ns_per_s) catch {};
|
std.Thread.Futex.timedWait(&self.query_futex, 0, timeout_ns) catch {};
|
||||||
|
try self.enableDetectedFeatures(tty);
|
||||||
try self.enableDetectedFeatures();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// write queries to the terminal to determine capabilities. This function
|
/// write queries to the terminal to determine capabilities. This function
|
||||||
/// is only for use with a custom main loop. Call Vaxis.queryTerminal() if
|
/// is only for use with a custom main loop. Call Vaxis.queryTerminal() if
|
||||||
/// you are using Loop.run()
|
/// you are using Loop.run()
|
||||||
pub fn queryTerminalSend(_: Vaxis, writer: AnyWriter) !void {
|
pub fn queryTerminalSend(_: Vaxis, tty: AnyWriter) !void {
|
||||||
|
|
||||||
// TODO: re-enable this
|
// TODO: re-enable this
|
||||||
// const colorterm = std.posix.getenv("COLORTERM") orelse "";
|
// const colorterm = std.posix.getenv("COLORTERM") orelse "";
|
||||||
|
@ -221,30 +236,24 @@ pub fn queryTerminalSend(_: Vaxis, writer: AnyWriter) !void {
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// TODO: XTGETTCAP queries ("RGB", "Smulx")
|
||||||
// TODO: decide if we actually want to query for focus and sync. It
|
// TODO: decide if we actually want to query for focus and sync. It
|
||||||
// doesn't hurt to blindly use them
|
// doesn't hurt to blindly use them
|
||||||
// _ = try tty.write(ctlseqs.decrqm_focus);
|
// _ = try tty.write(ctlseqs.decrqm_focus);
|
||||||
// _ = try tty.write(ctlseqs.decrqm_sync);
|
// _ = try tty.write(ctlseqs.decrqm_sync);
|
||||||
try writer.writeAll(ctlseqs.decrqm_sgr_pixels);
|
try tty.writeAll(ctlseqs.decrqm_sgr_pixels ++
|
||||||
try writer.writeAll(ctlseqs.decrqm_unicode);
|
ctlseqs.decrqm_unicode ++
|
||||||
try writer.writeAll(ctlseqs.decrqm_color_scheme);
|
ctlseqs.decrqm_color_scheme ++
|
||||||
// TODO: XTVERSION has a DCS response. uncomment when we can parse
|
ctlseqs.xtversion ++
|
||||||
// that
|
ctlseqs.csi_u_query ++
|
||||||
// _ = try tty.write(ctlseqs.xtversion);
|
ctlseqs.kitty_graphics_query ++
|
||||||
try writer.writeAll(ctlseqs.csi_u_query);
|
ctlseqs.primary_device_attrs);
|
||||||
try writer.writeAll(ctlseqs.kitty_graphics_query);
|
|
||||||
// TODO: sixel geometry query interferes with F4 keys.
|
|
||||||
// _ = try tty.write(ctlseqs.sixel_geometry_query);
|
|
||||||
|
|
||||||
// TODO: XTGETTCAP queries ("RGB", "Smulx")
|
|
||||||
|
|
||||||
try writer.writeAll(ctlseqs.primary_device_attrs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enable features detected by responses to queryTerminal. This function
|
/// Enable features detected by responses to queryTerminal. This function
|
||||||
/// is only for use with a custom main loop. Call Vaxis.queryTerminal() if
|
/// is only for use with a custom main loop. Call Vaxis.queryTerminal() if
|
||||||
/// you are using Loop.run()
|
/// you are using Loop.run()
|
||||||
pub fn enableDetectedFeatures(self: *Vaxis) !void {
|
pub fn enableDetectedFeatures(self: *Vaxis, tty: AnyWriter) !void {
|
||||||
// Apply any environment variables
|
// Apply any environment variables
|
||||||
if (std.posix.getenv("ASCIINEMA_REC")) |_|
|
if (std.posix.getenv("ASCIINEMA_REC")) |_|
|
||||||
self.sgr = .legacy;
|
self.sgr = .legacy;
|
||||||
|
@ -263,10 +272,10 @@ pub fn enableDetectedFeatures(self: *Vaxis) !void {
|
||||||
|
|
||||||
// enable detected features
|
// enable detected features
|
||||||
if (self.caps.kitty_keyboard) {
|
if (self.caps.kitty_keyboard) {
|
||||||
try self.enableKittyKeyboard(self.opts.kitty_keyboard_flags);
|
try self.enableKittyKeyboard(tty, self.opts.kitty_keyboard_flags);
|
||||||
}
|
}
|
||||||
if (self.caps.unicode == .unicode) {
|
if (self.caps.unicode == .unicode) {
|
||||||
_ = try tty.write(ctlseqs.unicode_set);
|
try tty.writeAll(ctlseqs.unicode_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +285,7 @@ pub fn queueRefresh(self: *Vaxis) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// draws the screen to the terminal
|
/// draws the screen to the terminal
|
||||||
pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
pub fn render(self: *Vaxis, tty: AnyWriter) !void {
|
||||||
self.renders += 1;
|
self.renders += 1;
|
||||||
self.render_timer.reset();
|
self.render_timer.reset();
|
||||||
defer {
|
defer {
|
||||||
|
@ -289,24 +298,21 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
// TODO: optimize sync so we only sync _when we have changes_. This
|
// TODO: optimize sync so we only sync _when we have changes_. This
|
||||||
// requires a smarter buffered writer, we'll probably have to write
|
// requires a smarter buffered writer, we'll probably have to write
|
||||||
// our own
|
// our own
|
||||||
try writer.writeAll(ctlseqs.sync_set);
|
try tty.writeAll(ctlseqs.sync_set);
|
||||||
defer writer.writeAll(ctlseqs.sync_reset) catch {};
|
defer tty.writeAll(ctlseqs.sync_reset) catch {};
|
||||||
|
|
||||||
// Send the cursor to 0,0
|
// Send the cursor to 0,0
|
||||||
// TODO: this needs to move after we optimize writes. We only do
|
// TODO: this needs to move after we optimize writes. We only do
|
||||||
// this if we have an update to make. We also need to hide cursor
|
// this if we have an update to make. We also need to hide cursor
|
||||||
// and then reshow it if needed
|
// and then reshow it if needed
|
||||||
try writer.writeAll(ctlseqs.hide_cursor);
|
try tty.writeAll(ctlseqs.hide_cursor);
|
||||||
if (self.state.alt_screen)
|
if (self.state.alt_screen)
|
||||||
try writer.writeAll(ctlseqs.home)
|
try tty.writeAll(ctlseqs.home)
|
||||||
else {
|
else {
|
||||||
try writer.writeAll("\r");
|
try tty.writeByte('\r');
|
||||||
var i: usize = 0;
|
try tty.writeBytesNTimes(ctlseqs.ri, self.state.cursor.row);
|
||||||
while (i < self.state.cursor.row) : (i += 1) {
|
|
||||||
try writer.writeAll(ctlseqs.ri);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try writer.writeAll(ctlseqs.sgr_reset);
|
try tty.writeAll(ctlseqs.sgr_reset);
|
||||||
|
|
||||||
// initialize some variables
|
// initialize some variables
|
||||||
var reposition: bool = false;
|
var reposition: bool = false;
|
||||||
|
@ -321,7 +327,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
|
|
||||||
// Clear all images
|
// Clear all images
|
||||||
if (self.caps.kitty_graphics)
|
if (self.caps.kitty_graphics)
|
||||||
try writer.writeAll(ctlseqs.kitty_graphics_clear);
|
try tty.writeAll(ctlseqs.kitty_graphics_clear);
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < self.screen.buf.len) {
|
while (i < self.screen.buf.len) {
|
||||||
|
@ -357,7 +363,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
// Close any osc8 sequence we might be in before
|
// Close any osc8 sequence we might be in before
|
||||||
// repositioning
|
// repositioning
|
||||||
if (link.uri.len > 0) {
|
if (link.uri.len > 0) {
|
||||||
try writer.writeAll(ctlseqs.osc8_clear);
|
try tty.writeAll(ctlseqs.osc8_clear);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -373,52 +379,53 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
if (reposition) {
|
if (reposition) {
|
||||||
reposition = false;
|
reposition = false;
|
||||||
if (self.state.alt_screen)
|
if (self.state.alt_screen)
|
||||||
try writer.print(ctlseqs.cup, .{ row + 1, col + 1 })
|
try tty.print(ctlseqs.cup, .{ row + 1, col + 1 })
|
||||||
else {
|
else {
|
||||||
if (cursor_pos.row == row) {
|
if (cursor_pos.row == row) {
|
||||||
const n = col - cursor_pos.col;
|
const n = col - cursor_pos.col;
|
||||||
try writer.print(ctlseqs.cuf, .{n});
|
if (n > 0)
|
||||||
|
try tty.print(ctlseqs.cuf, .{n});
|
||||||
} else {
|
} else {
|
||||||
try writer.writeByte('\r');
|
try tty.writeByte('\r');
|
||||||
const n = row - cursor_pos.row;
|
const n = row - cursor_pos.row;
|
||||||
try writer.writeByteNTimes('\n', n);
|
try tty.writeByteNTimes('\n', n);
|
||||||
|
if (col > 0)
|
||||||
|
try tty.print(ctlseqs.cuf, .{col});
|
||||||
}
|
}
|
||||||
if (col > 0)
|
|
||||||
try writer.print(ctlseqs.cuf, .{col});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell.image) |img| {
|
if (cell.image) |img| {
|
||||||
try writer.print(
|
try tty.print(
|
||||||
ctlseqs.kitty_graphics_preamble,
|
ctlseqs.kitty_graphics_preamble,
|
||||||
.{img.img_id},
|
.{img.img_id},
|
||||||
);
|
);
|
||||||
if (img.options.pixel_offset) |offset| {
|
if (img.options.pixel_offset) |offset| {
|
||||||
try writer.print(
|
try tty.print(
|
||||||
",X={d},Y={d}",
|
",X={d},Y={d}",
|
||||||
.{ offset.x, offset.y },
|
.{ offset.x, offset.y },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (img.options.clip_region) |clip| {
|
if (img.options.clip_region) |clip| {
|
||||||
if (clip.x) |x|
|
if (clip.x) |x|
|
||||||
try writer.print(",x={d}", .{x});
|
try tty.print(",x={d}", .{x});
|
||||||
if (clip.y) |y|
|
if (clip.y) |y|
|
||||||
try writer.print(",y={d}", .{y});
|
try tty.print(",y={d}", .{y});
|
||||||
if (clip.width) |width|
|
if (clip.width) |width|
|
||||||
try writer.print(",w={d}", .{width});
|
try tty.print(",w={d}", .{width});
|
||||||
if (clip.height) |height|
|
if (clip.height) |height|
|
||||||
try writer.print(",h={d}", .{height});
|
try tty.print(",h={d}", .{height});
|
||||||
}
|
}
|
||||||
if (img.options.size) |size| {
|
if (img.options.size) |size| {
|
||||||
if (size.rows) |rows|
|
if (size.rows) |rows|
|
||||||
try writer.print(",r={d}", .{rows});
|
try tty.print(",r={d}", .{rows});
|
||||||
if (size.cols) |cols|
|
if (size.cols) |cols|
|
||||||
try writer.print(",c={d}", .{cols});
|
try tty.print(",c={d}", .{cols});
|
||||||
}
|
}
|
||||||
if (img.options.z_index) |z| {
|
if (img.options.z_index) |z| {
|
||||||
try writer.print(",z={d}", .{z});
|
try tty.print(",z={d}", .{z});
|
||||||
}
|
}
|
||||||
try writer.writeAll(ctlseqs.kitty_graphics_closing);
|
try tty.writeAll(ctlseqs.kitty_graphics_closing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// something is different, so let's loop through everything and
|
// something is different, so let's loop through everything and
|
||||||
|
@ -427,23 +434,23 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
// foreground
|
// foreground
|
||||||
if (!Cell.Color.eql(cursor.fg, cell.style.fg)) {
|
if (!Cell.Color.eql(cursor.fg, cell.style.fg)) {
|
||||||
switch (cell.style.fg) {
|
switch (cell.style.fg) {
|
||||||
.default => try writer.writeAll(ctlseqs.fg_reset),
|
.default => try tty.writeAll(ctlseqs.fg_reset),
|
||||||
.index => |idx| {
|
.index => |idx| {
|
||||||
switch (idx) {
|
switch (idx) {
|
||||||
0...7 => try writer.print(ctlseqs.fg_base, .{idx}),
|
0...7 => try tty.print(ctlseqs.fg_base, .{idx}),
|
||||||
8...15 => try writer.print(ctlseqs.fg_bright, .{idx - 8}),
|
8...15 => try tty.print(ctlseqs.fg_bright, .{idx - 8}),
|
||||||
else => {
|
else => {
|
||||||
switch (self.sgr) {
|
switch (self.sgr) {
|
||||||
.standard => try writer.print(ctlseqs.fg_indexed, .{idx}),
|
.standard => try tty.print(ctlseqs.fg_indexed, .{idx}),
|
||||||
.legacy => try writer.print(ctlseqs.fg_indexed_legacy, .{idx}),
|
.legacy => try tty.print(ctlseqs.fg_indexed_legacy, .{idx}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.rgb => |rgb| {
|
.rgb => |rgb| {
|
||||||
switch (self.sgr) {
|
switch (self.sgr) {
|
||||||
.standard => try writer.print(ctlseqs.fg_rgb, .{ rgb[0], rgb[1], rgb[2] }),
|
.standard => try tty.print(ctlseqs.fg_rgb, .{ rgb[0], rgb[1], rgb[2] }),
|
||||||
.legacy => try writer.print(ctlseqs.fg_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }),
|
.legacy => try tty.print(ctlseqs.fg_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -451,23 +458,23 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
// background
|
// background
|
||||||
if (!Cell.Color.eql(cursor.bg, cell.style.bg)) {
|
if (!Cell.Color.eql(cursor.bg, cell.style.bg)) {
|
||||||
switch (cell.style.bg) {
|
switch (cell.style.bg) {
|
||||||
.default => try writer.writeAll(ctlseqs.bg_reset),
|
.default => try tty.writeAll(ctlseqs.bg_reset),
|
||||||
.index => |idx| {
|
.index => |idx| {
|
||||||
switch (idx) {
|
switch (idx) {
|
||||||
0...7 => try writer.print(ctlseqs.bg_base, .{idx}),
|
0...7 => try tty.print(ctlseqs.bg_base, .{idx}),
|
||||||
8...15 => try writer.print(ctlseqs.bg_bright, .{idx - 8}),
|
8...15 => try tty.print(ctlseqs.bg_bright, .{idx - 8}),
|
||||||
else => {
|
else => {
|
||||||
switch (self.sgr) {
|
switch (self.sgr) {
|
||||||
.standard => try writer.print(ctlseqs.bg_indexed, .{idx}),
|
.standard => try tty.print(ctlseqs.bg_indexed, .{idx}),
|
||||||
.legacy => try writer.print(ctlseqs.bg_indexed_legacy, .{idx}),
|
.legacy => try tty.print(ctlseqs.bg_indexed_legacy, .{idx}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.rgb => |rgb| {
|
.rgb => |rgb| {
|
||||||
switch (self.sgr) {
|
switch (self.sgr) {
|
||||||
.standard => try writer.print(ctlseqs.bg_rgb, .{ rgb[0], rgb[1], rgb[2] }),
|
.standard => try tty.print(ctlseqs.bg_rgb, .{ rgb[0], rgb[1], rgb[2] }),
|
||||||
.legacy => try writer.print(ctlseqs.bg_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }),
|
.legacy => try tty.print(ctlseqs.bg_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -475,17 +482,17 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
// underline color
|
// underline color
|
||||||
if (!Cell.Color.eql(cursor.ul, cell.style.ul)) {
|
if (!Cell.Color.eql(cursor.ul, cell.style.ul)) {
|
||||||
switch (cell.style.bg) {
|
switch (cell.style.bg) {
|
||||||
.default => try writer.writeAll(ctlseqs.ul_reset),
|
.default => try tty.writeAll(ctlseqs.ul_reset),
|
||||||
.index => |idx| {
|
.index => |idx| {
|
||||||
switch (self.sgr) {
|
switch (self.sgr) {
|
||||||
.standard => try writer.print(ctlseqs.ul_indexed, .{idx}),
|
.standard => try tty.print(ctlseqs.ul_indexed, .{idx}),
|
||||||
.legacy => try writer.print(ctlseqs.ul_indexed_legacy, .{idx}),
|
.legacy => try tty.print(ctlseqs.ul_indexed_legacy, .{idx}),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.rgb => |rgb| {
|
.rgb => |rgb| {
|
||||||
switch (self.sgr) {
|
switch (self.sgr) {
|
||||||
.standard => try writer.print(ctlseqs.ul_rgb, .{ rgb[0], rgb[1], rgb[2] }),
|
.standard => try tty.print(ctlseqs.ul_rgb, .{ rgb[0], rgb[1], rgb[2] }),
|
||||||
.legacy => try writer.print(ctlseqs.ul_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }),
|
.legacy => try tty.print(ctlseqs.ul_rgb_legacy, .{ rgb[0], rgb[1], rgb[2] }),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -500,7 +507,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
.dotted => ctlseqs.ul_dotted,
|
.dotted => ctlseqs.ul_dotted,
|
||||||
.dashed => ctlseqs.ul_dashed,
|
.dashed => ctlseqs.ul_dashed,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
}
|
}
|
||||||
// bold
|
// bold
|
||||||
if (cursor.bold != cell.style.bold) {
|
if (cursor.bold != cell.style.bold) {
|
||||||
|
@ -508,9 +515,9 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
true => ctlseqs.bold_set,
|
true => ctlseqs.bold_set,
|
||||||
false => ctlseqs.bold_dim_reset,
|
false => ctlseqs.bold_dim_reset,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
if (cell.style.dim) {
|
if (cell.style.dim) {
|
||||||
try writer.writeAll(ctlseqs.dim_set);
|
try tty.writeAll(ctlseqs.dim_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// dim
|
// dim
|
||||||
|
@ -519,9 +526,9 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
true => ctlseqs.dim_set,
|
true => ctlseqs.dim_set,
|
||||||
false => ctlseqs.bold_dim_reset,
|
false => ctlseqs.bold_dim_reset,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
if (cell.style.bold) {
|
if (cell.style.bold) {
|
||||||
try writer.writeAll(ctlseqs.bold_set);
|
try tty.writeAll(ctlseqs.bold_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// dim
|
// dim
|
||||||
|
@ -530,7 +537,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
true => ctlseqs.italic_set,
|
true => ctlseqs.italic_set,
|
||||||
false => ctlseqs.italic_reset,
|
false => ctlseqs.italic_reset,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
}
|
}
|
||||||
// dim
|
// dim
|
||||||
if (cursor.blink != cell.style.blink) {
|
if (cursor.blink != cell.style.blink) {
|
||||||
|
@ -538,7 +545,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
true => ctlseqs.blink_set,
|
true => ctlseqs.blink_set,
|
||||||
false => ctlseqs.blink_reset,
|
false => ctlseqs.blink_reset,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
}
|
}
|
||||||
// reverse
|
// reverse
|
||||||
if (cursor.reverse != cell.style.reverse) {
|
if (cursor.reverse != cell.style.reverse) {
|
||||||
|
@ -546,7 +553,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
true => ctlseqs.reverse_set,
|
true => ctlseqs.reverse_set,
|
||||||
false => ctlseqs.reverse_reset,
|
false => ctlseqs.reverse_reset,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
}
|
}
|
||||||
// invisible
|
// invisible
|
||||||
if (cursor.invisible != cell.style.invisible) {
|
if (cursor.invisible != cell.style.invisible) {
|
||||||
|
@ -554,7 +561,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
true => ctlseqs.invisible_set,
|
true => ctlseqs.invisible_set,
|
||||||
false => ctlseqs.invisible_reset,
|
false => ctlseqs.invisible_reset,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
}
|
}
|
||||||
// strikethrough
|
// strikethrough
|
||||||
if (cursor.strikethrough != cell.style.strikethrough) {
|
if (cursor.strikethrough != cell.style.strikethrough) {
|
||||||
|
@ -562,7 +569,7 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
true => ctlseqs.strikethrough_set,
|
true => ctlseqs.strikethrough_set,
|
||||||
false => ctlseqs.strikethrough_reset,
|
false => ctlseqs.strikethrough_reset,
|
||||||
};
|
};
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
// url
|
// url
|
||||||
|
@ -573,15 +580,15 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
// a url
|
// a url
|
||||||
ps = "";
|
ps = "";
|
||||||
}
|
}
|
||||||
try writer.print(ctlseqs.osc8, .{ ps, cell.link.uri });
|
try tty.print(ctlseqs.osc8, .{ ps, cell.link.uri });
|
||||||
}
|
}
|
||||||
try writer.writeAll(cell.char.grapheme);
|
try tty.writeAll(cell.char.grapheme);
|
||||||
cursor_pos.col = col + w;
|
cursor_pos.col = col + w;
|
||||||
cursor_pos.row = row;
|
cursor_pos.row = row;
|
||||||
}
|
}
|
||||||
if (self.screen.cursor_vis) {
|
if (self.screen.cursor_vis) {
|
||||||
if (self.state.alt_screen) {
|
if (self.state.alt_screen) {
|
||||||
try writer.print(
|
try tty.print(
|
||||||
ctlseqs.cup,
|
ctlseqs.cup,
|
||||||
.{
|
.{
|
||||||
self.screen.cursor_row + 1,
|
self.screen.cursor_row + 1,
|
||||||
|
@ -590,30 +597,30 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// TODO: position cursor relative to current location
|
// TODO: position cursor relative to current location
|
||||||
try writer.writeByte('\r');
|
try tty.writeByte('\r');
|
||||||
if (self.screen.cursor_row >= cursor_pos.row)
|
if (self.screen.cursor_row >= cursor_pos.row)
|
||||||
try writer.writeByteNTimes('\n', self.screen.cursor_row - cursor_pos.row)
|
try tty.writeByteNTimes('\n', self.screen.cursor_row - cursor_pos.row)
|
||||||
else
|
else
|
||||||
try writer.writeBytesNTimes(ctlseqs.ri, cursor_pos.row - self.screen.cursor_row);
|
try tty.writeBytesNTimes(ctlseqs.ri, cursor_pos.row - self.screen.cursor_row);
|
||||||
if (self.screen.cursor_col > 0)
|
if (self.screen.cursor_col > 0)
|
||||||
try writer.print(ctlseqs.cuf, .{self.screen.cursor_col});
|
try tty.print(ctlseqs.cuf, .{self.screen.cursor_col});
|
||||||
}
|
}
|
||||||
self.state.cursor.row = self.screen.cursor_row;
|
self.state.cursor.row = self.screen.cursor_row;
|
||||||
self.state.cursor.col = self.screen.cursor_col;
|
self.state.cursor.col = self.screen.cursor_col;
|
||||||
try writer.writeAll(ctlseqs.show_cursor);
|
try tty.writeAll(ctlseqs.show_cursor);
|
||||||
} else {
|
} else {
|
||||||
self.state.cursor.row = cursor_pos.row;
|
self.state.cursor.row = cursor_pos.row;
|
||||||
self.state.cursor.col = cursor_pos.col;
|
self.state.cursor.col = cursor_pos.col;
|
||||||
}
|
}
|
||||||
if (self.screen.mouse_shape != self.screen_last.mouse_shape) {
|
if (self.screen.mouse_shape != self.screen_last.mouse_shape) {
|
||||||
try writer.print(
|
try tty.print(
|
||||||
ctlseqs.osc22_mouse_shape,
|
ctlseqs.osc22_mouse_shape,
|
||||||
.{@tagName(self.screen.mouse_shape)},
|
.{@tagName(self.screen.mouse_shape)},
|
||||||
);
|
);
|
||||||
self.screen_last.mouse_shape = self.screen.mouse_shape;
|
self.screen_last.mouse_shape = self.screen.mouse_shape;
|
||||||
}
|
}
|
||||||
if (self.screen.cursor_shape != self.screen_last.cursor_shape) {
|
if (self.screen.cursor_shape != self.screen_last.cursor_shape) {
|
||||||
try writer.print(
|
try tty.print(
|
||||||
ctlseqs.cursor_shape,
|
ctlseqs.cursor_shape,
|
||||||
.{@intFromEnum(self.screen.cursor_shape)},
|
.{@intFromEnum(self.screen.cursor_shape)},
|
||||||
);
|
);
|
||||||
|
@ -621,34 +628,34 @@ pub fn render(self: *Vaxis, writer: AnyWriter) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enableKittyKeyboard(self: *Vaxis, writer: AnyWriter, flags: Key.KittyFlags) !void {
|
fn enableKittyKeyboard(self: *Vaxis, tty: AnyWriter, flags: Key.KittyFlags) !void {
|
||||||
const flag_int: u5 = @bitCast(flags);
|
const flag_int: u5 = @bitCast(flags);
|
||||||
try writer.print(ctlseqs.csi_u_push, .{flag_int});
|
try tty.print(ctlseqs.csi_u_push, .{flag_int});
|
||||||
self.state.kitty_keyboard = true;
|
self.state.kitty_keyboard = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// send a system notification
|
/// send a system notification
|
||||||
pub fn notify(_: *Vaxis, writer: AnyWriter, title: ?[]const u8, body: []const u8) !void {
|
pub fn notify(_: *Vaxis, tty: AnyWriter, title: ?[]const u8, body: []const u8) !void {
|
||||||
if (title) |t|
|
if (title) |t|
|
||||||
try writer.print(ctlseqs.osc777_notify, .{ t, body })
|
try tty.print(ctlseqs.osc777_notify, .{ t, body })
|
||||||
else
|
else
|
||||||
try writer.print(ctlseqs.osc9_notify, .{body});
|
try tty.print(ctlseqs.osc9_notify, .{body});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sets the window title
|
/// sets the window title
|
||||||
pub fn setTitle(_: *Vaxis, writer: AnyWriter, title: []const u8) !void {
|
pub fn setTitle(_: *Vaxis, tty: AnyWriter, title: []const u8) !void {
|
||||||
try writer.print(ctlseqs.osc2_set_title, .{title});
|
try tty.print(ctlseqs.osc2_set_title, .{title});
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn bracketed paste on or off. An event will be sent at the
|
// turn bracketed paste on or off. An event will be sent at the
|
||||||
// beginning and end of a detected paste. All keystrokes between these
|
// beginning and end of a detected paste. All keystrokes between these
|
||||||
// events were pasted
|
// events were pasted
|
||||||
pub fn setBracketedPaste(self: *Vaxis, writer: AnyWriter, enable: bool) !void {
|
pub fn setBracketedPaste(self: *Vaxis, tty: AnyWriter, enable: bool) !void {
|
||||||
const seq = if (enable)
|
const seq = if (enable)
|
||||||
ctlseqs.bp_set
|
ctlseqs.bp_set
|
||||||
else
|
else
|
||||||
ctlseqs.bp_reset;
|
ctlseqs.bp_reset;
|
||||||
try writer.writeAll(seq);
|
try tty.writeAll(seq);
|
||||||
self.state.bracketed_paste = enable;
|
self.state.bracketed_paste = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,19 +665,19 @@ pub fn setMouseShape(self: *Vaxis, shape: Shape) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the mouse reporting mode
|
/// Change the mouse reporting mode
|
||||||
pub fn setMouseMode(self: *Vaxis, writer: AnyWriter, enable: bool) !void {
|
pub fn setMouseMode(self: *Vaxis, tty: AnyWriter, enable: bool) !void {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
self.state.mouse = true;
|
self.state.mouse = true;
|
||||||
if (self.caps.sgr_pixels) {
|
if (self.caps.sgr_pixels) {
|
||||||
log.debug("enabling mouse mode: pixel coordinates", .{});
|
log.debug("enabling mouse mode: pixel coordinates", .{});
|
||||||
self.state.pixel_mouse = true;
|
self.state.pixel_mouse = true;
|
||||||
try writer.writeAll(ctlseqs.mouse_set_pixels);
|
try tty.writeAll(ctlseqs.mouse_set_pixels);
|
||||||
} else {
|
} else {
|
||||||
log.debug("enabling mouse mode: cell coordinates", .{});
|
log.debug("enabling mouse mode: cell coordinates", .{});
|
||||||
try writer.writeAll(ctlseqs.mouse_set);
|
try tty.writeAll(ctlseqs.mouse_set);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try writer.writeAll(ctlseqs.mouse_reset);
|
try tty.writeAll(ctlseqs.mouse_reset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,7 +716,7 @@ pub fn translateMouse(self: Vaxis, mouse: Mouse) Mouse {
|
||||||
pub fn loadImage(
|
pub fn loadImage(
|
||||||
self: *Vaxis,
|
self: *Vaxis,
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
writer: AnyWriter,
|
tty: AnyWriter,
|
||||||
src: Image.Source,
|
src: Image.Source,
|
||||||
) !Image {
|
) !Image {
|
||||||
if (!self.caps.kitty_graphics) return error.NoGraphicsCapability;
|
if (!self.caps.kitty_graphics) return error.NoGraphicsCapability;
|
||||||
|
@ -730,7 +737,7 @@ pub fn loadImage(
|
||||||
const id = self.next_img_id;
|
const id = self.next_img_id;
|
||||||
|
|
||||||
if (encoded.len < 4096) {
|
if (encoded.len < 4096) {
|
||||||
try writer.print(
|
try tty.print(
|
||||||
"\x1b_Gf=100,i={d};{s}\x1b\\",
|
"\x1b_Gf=100,i={d};{s}\x1b\\",
|
||||||
.{
|
.{
|
||||||
id,
|
id,
|
||||||
|
@ -740,14 +747,14 @@ pub fn loadImage(
|
||||||
} else {
|
} else {
|
||||||
var n: usize = 4096;
|
var n: usize = 4096;
|
||||||
|
|
||||||
try writer.print(
|
try tty.print(
|
||||||
"\x1b_Gf=100,i={d},m=1;{s}\x1b\\",
|
"\x1b_Gf=100,i={d},m=1;{s}\x1b\\",
|
||||||
.{ id, encoded[0..n] },
|
.{ id, encoded[0..n] },
|
||||||
);
|
);
|
||||||
while (n < encoded.len) : (n += 4096) {
|
while (n < encoded.len) : (n += 4096) {
|
||||||
const end: usize = @min(n + 4096, encoded.len);
|
const end: usize = @min(n + 4096, encoded.len);
|
||||||
const m: u2 = if (end == encoded.len) 0 else 1;
|
const m: u2 = if (end == encoded.len) 0 else 1;
|
||||||
try writer.print(
|
try tty.print(
|
||||||
"\x1b_Gm={d};{s}\x1b\\",
|
"\x1b_Gm={d};{s}\x1b\\",
|
||||||
.{
|
.{
|
||||||
m,
|
m,
|
||||||
|
@ -764,28 +771,28 @@ pub fn loadImage(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// deletes an image from the terminal's memory
|
/// deletes an image from the terminal's memory
|
||||||
pub fn freeImage(_: Vaxis, writer: AnyWriter, id: u32) void {
|
pub fn freeImage(_: Vaxis, tty: AnyWriter, id: u32) void {
|
||||||
writer.print("\x1b_Ga=d,d=I,i={d};\x1b\\", .{id}) catch |err| {
|
tty.print("\x1b_Ga=d,d=I,i={d};\x1b\\", .{id}) catch |err| {
|
||||||
log.err("couldn't delete image {d}: {}", .{ id, err });
|
log.err("couldn't delete image {d}: {}", .{ id, err });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copyToSystemClipboard(_: Vaxis, writer: AnyWriter, text: []const u8, encode_allocator: std.mem.Allocator) !void {
|
pub fn copyToSystemClipboard(_: Vaxis, tty: AnyWriter, text: []const u8, encode_allocator: std.mem.Allocator) !void {
|
||||||
const encoder = std.base64.standard.Encoder;
|
const encoder = std.base64.standard.Encoder;
|
||||||
const size = encoder.calcSize(text.len);
|
const size = encoder.calcSize(text.len);
|
||||||
const buf = try encode_allocator.alloc(u8, size);
|
const buf = try encode_allocator.alloc(u8, size);
|
||||||
const b64 = encoder.encode(buf, text);
|
const b64 = encoder.encode(buf, text);
|
||||||
defer encode_allocator.free(buf);
|
defer encode_allocator.free(buf);
|
||||||
try writer.print(
|
try tty.print(
|
||||||
ctlseqs.osc52_clipboard_copy,
|
ctlseqs.osc52_clipboard_copy,
|
||||||
.{b64},
|
.{b64},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn requestSystemClipboard(self: Vaxis, writer: AnyWriter) !void {
|
pub fn requestSystemClipboard(self: Vaxis, tty: AnyWriter) !void {
|
||||||
if (self.opts.system_clipboard_allocator == null) return error.NoClipboardAllocator;
|
if (self.opts.system_clipboard_allocator == null) return error.NoClipboardAllocator;
|
||||||
try writer.print(
|
try tty.print(
|
||||||
ctlseqs.osc52_clipboard_request,
|
ctlseqs.osc52_clipboard_request,
|
||||||
.{},
|
.{},
|
||||||
);
|
);
|
||||||
|
@ -794,12 +801,12 @@ pub fn requestSystemClipboard(self: Vaxis, writer: AnyWriter) !void {
|
||||||
/// Request a color report from the terminal. Note: not all terminals support
|
/// Request a color report from the terminal. Note: not all terminals support
|
||||||
/// reporting colors. It is always safe to try, but you may not receive a
|
/// reporting colors. It is always safe to try, but you may not receive a
|
||||||
/// response.
|
/// response.
|
||||||
pub fn queryColor(_: Vaxis, writer: AnyWriter, kind: Cell.Color.Kind) !void {
|
pub fn queryColor(_: Vaxis, tty: AnyWriter, kind: Cell.Color.Kind) !void {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
.fg => try writer.writeAll(ctlseqs.osc10_query),
|
.fg => try tty.writeAll(ctlseqs.osc10_query),
|
||||||
.bg => try writer.writeAll(ctlseqs.osc11_query),
|
.bg => try tty.writeAll(ctlseqs.osc11_query),
|
||||||
.cursor => try writer.writeAll(ctlseqs.osc12_query),
|
.cursor => try tty.writeAll(ctlseqs.osc12_query),
|
||||||
.index => |idx| try writer.print(ctlseqs.osc4_query, .{idx}),
|
.index => |idx| try tty.print(ctlseqs.osc4_query, .{idx}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,12 +815,12 @@ pub fn queryColor(_: Vaxis, writer: AnyWriter, kind: Cell.Color.Kind) !void {
|
||||||
/// capability. Support can be detected by checking the value of
|
/// capability. Support can be detected by checking the value of
|
||||||
/// vaxis.caps.color_scheme_updates. The initial scheme will be reported when
|
/// vaxis.caps.color_scheme_updates. The initial scheme will be reported when
|
||||||
/// subscribing.
|
/// subscribing.
|
||||||
pub fn subscribeToColorSchemeUpdates(self: Vaxis, writer: AnyWriter) !void {
|
pub fn subscribeToColorSchemeUpdates(self: Vaxis, tty: AnyWriter) !void {
|
||||||
try writer.writeAll(ctlseqs.color_scheme_request);
|
try tty.writeAll(ctlseqs.color_scheme_request);
|
||||||
try writer.writeAll(ctlseqs.color_scheme_set);
|
try tty.writeAll(ctlseqs.color_scheme_set);
|
||||||
self.state.color_scheme_updates = true;
|
self.state.color_scheme_updates = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deviceStatusReport(_: Vaxis, writer: AnyWriter) !void {
|
pub fn deviceStatusReport(_: Vaxis, tty: AnyWriter) !void {
|
||||||
try writer.writeAll(ctlseqs.device_status_report);
|
try tty.writeAll(ctlseqs.device_status_report);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue