feat(app): signal WINCH for .resize system event
This allows the application to automatically re-draw and resize if the application receives the signal by the terminal emulator.
This commit is contained in:
35
src/app.zig
35
src/app.zig
@@ -28,11 +28,17 @@ pub fn App(comptime E: type) type {
|
||||
thread: ?Thread = null,
|
||||
quit_event: Thread.ResetEvent,
|
||||
termios: ?posix.termios = null,
|
||||
winch_registered: bool = false,
|
||||
|
||||
pub const SignalHandler = struct {
|
||||
context: *anyopaque,
|
||||
callback: *const fn (context: *anyopaque) void,
|
||||
};
|
||||
// global variable for the registered handler for WINCH
|
||||
var handler_ctx: *anyopaque = undefined;
|
||||
/// registered WINCH handler to report resize events
|
||||
fn handleWinch(_: c_int) callconv(.C) void {
|
||||
const this: *@This() = @ptrCast(@alignCast(handler_ctx));
|
||||
// NOTE this does not have to be done if in-band resize events are supported
|
||||
// -> the signal might not work correctly when hosting the application over ssh!
|
||||
this.postEvent(.resize);
|
||||
}
|
||||
|
||||
pub const init: @This() = .{
|
||||
.queue = .{},
|
||||
@@ -45,6 +51,17 @@ pub fn App(comptime E: type) type {
|
||||
// post init event (as the very first element to be in the queue - event loop)
|
||||
this.postEvent(.init);
|
||||
|
||||
if (!this.winch_registered) {
|
||||
handler_ctx = this;
|
||||
var act = posix.Sigaction{
|
||||
.handler = .{ .handler = handleWinch },
|
||||
.mask = posix.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
posix.sigaction(posix.SIG.WINCH, &act, null);
|
||||
this.winch_registered = true;
|
||||
}
|
||||
|
||||
this.quit_event.reset();
|
||||
this.thread = try Thread.spawn(.{}, @This().run, .{this});
|
||||
|
||||
@@ -159,7 +176,7 @@ pub fn App(comptime E: type) type {
|
||||
'Q' => input.F2,
|
||||
'R' => input.F3,
|
||||
'S' => input.F4,
|
||||
else => unreachable, // switch case prevents in this case form ever happening
|
||||
else => unreachable,
|
||||
},
|
||||
};
|
||||
this.postEvent(.{ .key = key });
|
||||
@@ -205,7 +222,7 @@ pub fn App(comptime E: type) type {
|
||||
'I' => this.postEvent(.{ .focus = true }),
|
||||
'O' => this.postEvent(.{ .focus = false }),
|
||||
'M', 'm' => {
|
||||
std.debug.assert(sequence.len >= 4);
|
||||
assert(sequence.len >= 4);
|
||||
if (sequence[2] != '<') break;
|
||||
|
||||
const delim1 = mem.indexOfScalarPos(u8, sequence, 3, ';') orelse break;
|
||||
@@ -252,7 +269,7 @@ pub fn App(comptime E: type) type {
|
||||
// Device Status Report
|
||||
// CSI Ps n
|
||||
// CSI ? Ps n
|
||||
std.debug.assert(sequence.len >= 3);
|
||||
assert(sequence.len >= 3);
|
||||
},
|
||||
't' => {
|
||||
// XTWINOPS
|
||||
@@ -267,6 +284,7 @@ pub fn App(comptime E: type) type {
|
||||
|
||||
_ = width_char;
|
||||
_ = height_char;
|
||||
this.postEvent(.resize);
|
||||
// this.postEvent(.{ .size = .{
|
||||
// .x = fmt.parseUnsigned(u16, width_char, 10) catch break,
|
||||
// .y = fmt.parseUnsigned(u16, height_char, 10) catch break,
|
||||
@@ -298,7 +316,7 @@ pub fn App(comptime E: type) type {
|
||||
0x0a, 0x0d => .{ .cp = input.Enter },
|
||||
0x01...0x07, 0x0b...0x0c, 0x0e...0x1a => .{ .cp = b + 0x60, .mod = .{ .ctrl = true } },
|
||||
0x1b => escape: {
|
||||
std.debug.assert(read_bytes == 1);
|
||||
assert(read_bytes == 1);
|
||||
break :escape .{ .cp = input.Escape };
|
||||
},
|
||||
0x7f => .{ .cp = input.Backspace },
|
||||
@@ -333,6 +351,7 @@ const mem = std.mem;
|
||||
const fmt = std.fmt;
|
||||
const posix = std.posix;
|
||||
const Thread = std.Thread;
|
||||
const assert = std.debug.assert;
|
||||
const code_point = @import("code_point");
|
||||
const event = @import("event.zig");
|
||||
const input = @import("input.zig");
|
||||
|
||||
Reference in New Issue
Block a user