loop: fully clean up tty when stopping event loop
Previously, stopping the read thread would not fully clean up the tty. This could result in issues such as #9 where the terminal state was borked upon return to shell or if temporarily exiting the TUI. Fixes: #9
This commit is contained in:
parent
2f83b0d6ca
commit
50242b984b
4 changed files with 83 additions and 66 deletions
|
@ -82,6 +82,12 @@ pub fn main() !void {
|
||||||
vx.queueRefresh();
|
vx.queueRefresh();
|
||||||
} else if (key.matches('n', .{ .ctrl = true })) {
|
} else if (key.matches('n', .{ .ctrl = true })) {
|
||||||
try vx.notify("vaxis", "hello from vaxis");
|
try vx.notify("vaxis", "hello from vaxis");
|
||||||
|
loop.stop();
|
||||||
|
var child = std.process.Child.init(&.{"nvim"}, alloc);
|
||||||
|
_ = try child.spawnAndWait();
|
||||||
|
try loop.run();
|
||||||
|
try vx.enterAltScreen();
|
||||||
|
vx.queueRefresh();
|
||||||
} else if (key.matches(vaxis.Key.enter, .{})) {
|
} else if (key.matches(vaxis.Key.enter, .{})) {
|
||||||
text_input.clearAndFree();
|
text_input.clearAndFree();
|
||||||
} else {
|
} else {
|
||||||
|
|
14
src/Loop.zig
14
src/Loop.zig
|
@ -30,10 +30,16 @@ pub fn Loop(comptime T: type) type {
|
||||||
|
|
||||||
/// stops reading from the tty and returns it to it's initial state
|
/// stops reading from the tty and returns it to it's initial state
|
||||||
pub fn stop(self: *Self) void {
|
pub fn stop(self: *Self) void {
|
||||||
if (self.vaxis.tty) |*tty| tty.stop();
|
if (self.vaxis.tty) |*tty| {
|
||||||
if (self.thread) |thread| {
|
// stop the read loop, then join the thread
|
||||||
thread.join();
|
tty.stop();
|
||||||
self.thread = null;
|
if (self.thread) |thread| {
|
||||||
|
thread.join();
|
||||||
|
self.thread = null;
|
||||||
|
}
|
||||||
|
// once thread is closed we can deinit the tty
|
||||||
|
tty.deinit();
|
||||||
|
self.vaxis.tty = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
src/Tty.zig
24
src/Tty.zig
|
@ -25,6 +25,15 @@ should_quit: bool = false,
|
||||||
|
|
||||||
buffered_writer: BufferedWriter,
|
buffered_writer: BufferedWriter,
|
||||||
|
|
||||||
|
state: struct {
|
||||||
|
/// if we are in the alt screen
|
||||||
|
alt_screen: bool = false,
|
||||||
|
/// if we have entered kitty keyboard
|
||||||
|
kitty_keyboard: bool = false,
|
||||||
|
bracketed_paste: bool = false,
|
||||||
|
mouse: bool = false,
|
||||||
|
} = .{},
|
||||||
|
|
||||||
/// initializes a Tty instance by opening /dev/tty and "making it raw"
|
/// initializes a Tty instance by opening /dev/tty and "making it raw"
|
||||||
pub fn init() !Tty {
|
pub fn init() !Tty {
|
||||||
// Open our tty
|
// Open our tty
|
||||||
|
@ -42,6 +51,21 @@ pub fn init() !Tty {
|
||||||
|
|
||||||
/// release resources associated with the Tty return it to its original state
|
/// release resources associated with the Tty return it to its original state
|
||||||
pub fn deinit(self: *Tty) void {
|
pub fn deinit(self: *Tty) void {
|
||||||
|
if (self.state.kitty_keyboard) {
|
||||||
|
_ = self.write(ctlseqs.csi_u_pop) catch {};
|
||||||
|
}
|
||||||
|
if (self.state.mouse) {
|
||||||
|
_ = self.write(ctlseqs.mouse_reset) catch {};
|
||||||
|
}
|
||||||
|
if (self.state.bracketed_paste) {
|
||||||
|
_ = self.write(ctlseqs.bp_reset) catch {};
|
||||||
|
}
|
||||||
|
if (self.state.alt_screen) {
|
||||||
|
_ = self.write(ctlseqs.rmcup) catch {};
|
||||||
|
}
|
||||||
|
// always show the cursor on exit
|
||||||
|
_ = self.write(ctlseqs.show_cursor) catch {};
|
||||||
|
self.flush() catch {};
|
||||||
posix.tcsetattr(self.fd, .FLUSH, self.termios) catch |err| {
|
posix.tcsetattr(self.fd, .FLUSH, self.termios) catch |err| {
|
||||||
log.err("couldn't restore terminal: {}", .{err});
|
log.err("couldn't restore terminal: {}", .{err});
|
||||||
};
|
};
|
||||||
|
|
105
src/Vaxis.zig
105
src/Vaxis.zig
|
@ -42,15 +42,6 @@ screen: Screen,
|
||||||
/// the next render
|
/// the next render
|
||||||
screen_last: InternalScreen = undefined,
|
screen_last: InternalScreen = undefined,
|
||||||
|
|
||||||
state: struct {
|
|
||||||
/// if we are in the alt screen
|
|
||||||
alt_screen: bool = false,
|
|
||||||
/// if we have entered kitty keyboard
|
|
||||||
kitty_keyboard: bool = false,
|
|
||||||
bracketed_paste: bool = false,
|
|
||||||
mouse: bool = false,
|
|
||||||
} = .{},
|
|
||||||
|
|
||||||
caps: Capabilities = .{},
|
caps: Capabilities = .{},
|
||||||
|
|
||||||
/// if we should redraw the entire screen on the next render
|
/// if we should redraw the entire screen on the next render
|
||||||
|
@ -86,23 +77,7 @@ pub fn init(alloc: std.mem.Allocator, _: Options) !Vaxis {
|
||||||
/// 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, alloc: ?std.mem.Allocator) void {
|
pub fn deinit(self: *Vaxis, alloc: ?std.mem.Allocator) void {
|
||||||
if (self.tty) |_| {
|
if (self.tty) |*tty| {
|
||||||
var tty = &self.tty.?;
|
|
||||||
if (self.state.kitty_keyboard) {
|
|
||||||
_ = tty.write(ctlseqs.csi_u_pop) catch {};
|
|
||||||
}
|
|
||||||
if (self.state.mouse) {
|
|
||||||
_ = tty.write(ctlseqs.mouse_reset) catch {};
|
|
||||||
}
|
|
||||||
if (self.state.bracketed_paste) {
|
|
||||||
_ = tty.write(ctlseqs.bp_reset) catch {};
|
|
||||||
}
|
|
||||||
if (self.state.alt_screen) {
|
|
||||||
_ = tty.write(ctlseqs.rmcup) catch {};
|
|
||||||
}
|
|
||||||
// always show the cursor on exit
|
|
||||||
_ = tty.write(ctlseqs.show_cursor) catch {};
|
|
||||||
tty.flush() catch {};
|
|
||||||
tty.deinit();
|
tty.deinit();
|
||||||
}
|
}
|
||||||
if (alloc) |a| {
|
if (alloc) |a| {
|
||||||
|
@ -147,20 +122,22 @@ 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) !void {
|
pub fn enterAltScreen(self: *Vaxis) !void {
|
||||||
if (self.state.alt_screen) return;
|
if (self.tty) |*tty| {
|
||||||
var tty = self.tty orelse return;
|
if (tty.state.alt_screen) return;
|
||||||
_ = try tty.write(ctlseqs.smcup);
|
_ = try tty.write(ctlseqs.smcup);
|
||||||
try tty.flush();
|
try tty.flush();
|
||||||
self.state.alt_screen = true;
|
tty.state.alt_screen = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// exit the alternate screen
|
/// exit the alternate screen
|
||||||
pub fn exitAltScreen(self: *Vaxis) !void {
|
pub fn exitAltScreen(self: *Vaxis) !void {
|
||||||
if (!self.state.alt_screen) return;
|
if (self.tty) |*tty| {
|
||||||
var tty = self.tty orelse return;
|
if (!tty.state.alt_screen) return;
|
||||||
_ = try tty.write(ctlseqs.rmcup);
|
_ = try tty.write(ctlseqs.rmcup);
|
||||||
try tty.flush();
|
try tty.flush();
|
||||||
self.state.alt_screen = false;
|
tty.state.alt_screen = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// write queries to the terminal to determine capabilities. Individual
|
/// write queries to the terminal to determine capabilities. Individual
|
||||||
|
@ -487,16 +464,18 @@ pub fn render(self: *Vaxis) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enableKittyKeyboard(self: *Vaxis, flags: Key.KittyFlags) !void {
|
fn enableKittyKeyboard(self: *Vaxis, flags: Key.KittyFlags) !void {
|
||||||
self.state.kitty_keyboard = true;
|
if (self.tty) |*tty| {
|
||||||
const flag_int: u5 = @bitCast(flags);
|
const flag_int: u5 = @bitCast(flags);
|
||||||
try std.fmt.format(
|
try std.fmt.format(
|
||||||
self.tty.?.buffered_writer.writer(),
|
tty.buffered_writer.writer(),
|
||||||
ctlseqs.csi_u_push,
|
ctlseqs.csi_u_push,
|
||||||
.{
|
.{
|
||||||
flag_int,
|
flag_int,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
try self.tty.?.flush();
|
try tty.flush();
|
||||||
|
tty.state.kitty_keyboard = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// send a system notification
|
/// send a system notification
|
||||||
|
@ -533,14 +512,15 @@ pub fn setTitle(self: *Vaxis, title: []const u8) !void {
|
||||||
// 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, enable: bool) !void {
|
pub fn setBracketedPaste(self: *Vaxis, enable: bool) !void {
|
||||||
if (self.tty == null) return;
|
if (self.tty) |*tty| {
|
||||||
self.state.bracketed_paste = enable;
|
const seq = if (enable)
|
||||||
const seq = if (enable)
|
ctlseqs.bp_set
|
||||||
ctlseqs.bp_set
|
else
|
||||||
else
|
ctlseqs.bp_reset;
|
||||||
ctlseqs.bp_reset;
|
_ = try tty.write(seq);
|
||||||
_ = try self.tty.?.write(seq);
|
try tty.flush();
|
||||||
try self.tty.?.flush();
|
tty.state.bracketed_paste = enable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// set the mouse shape
|
/// set the mouse shape
|
||||||
|
@ -550,14 +530,15 @@ pub fn setMouseShape(self: *Vaxis, shape: Shape) void {
|
||||||
|
|
||||||
/// turn mouse reporting on or off
|
/// turn mouse reporting on or off
|
||||||
pub fn setMouseMode(self: *Vaxis, enable: bool) !void {
|
pub fn setMouseMode(self: *Vaxis, enable: bool) !void {
|
||||||
var tty = self.tty orelse return;
|
if (self.tty) |*tty| {
|
||||||
self.state.mouse = enable;
|
tty.state.mouse = enable;
|
||||||
if (enable) {
|
if (enable) {
|
||||||
_ = try tty.write(ctlseqs.mouse_set);
|
_ = try tty.write(ctlseqs.mouse_set);
|
||||||
try tty.flush();
|
try tty.flush();
|
||||||
} else {
|
} else {
|
||||||
_ = try tty.write(ctlseqs.mouse_reset);
|
_ = try tty.write(ctlseqs.mouse_reset);
|
||||||
try tty.flush();
|
try tty.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue