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,
|
thread: ?Thread = null,
|
||||||
quit_event: Thread.ResetEvent,
|
quit_event: Thread.ResetEvent,
|
||||||
termios: ?posix.termios = null,
|
termios: ?posix.termios = null,
|
||||||
|
winch_registered: bool = false,
|
||||||
|
|
||||||
pub const SignalHandler = struct {
|
// global variable for the registered handler for WINCH
|
||||||
context: *anyopaque,
|
var handler_ctx: *anyopaque = undefined;
|
||||||
callback: *const fn (context: *anyopaque) void,
|
/// 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() = .{
|
pub const init: @This() = .{
|
||||||
.queue = .{},
|
.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)
|
// post init event (as the very first element to be in the queue - event loop)
|
||||||
this.postEvent(.init);
|
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.quit_event.reset();
|
||||||
this.thread = try Thread.spawn(.{}, @This().run, .{this});
|
this.thread = try Thread.spawn(.{}, @This().run, .{this});
|
||||||
|
|
||||||
@@ -159,7 +176,7 @@ pub fn App(comptime E: type) type {
|
|||||||
'Q' => input.F2,
|
'Q' => input.F2,
|
||||||
'R' => input.F3,
|
'R' => input.F3,
|
||||||
'S' => input.F4,
|
'S' => input.F4,
|
||||||
else => unreachable, // switch case prevents in this case form ever happening
|
else => unreachable,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
this.postEvent(.{ .key = key });
|
this.postEvent(.{ .key = key });
|
||||||
@@ -205,7 +222,7 @@ pub fn App(comptime E: type) type {
|
|||||||
'I' => this.postEvent(.{ .focus = true }),
|
'I' => this.postEvent(.{ .focus = true }),
|
||||||
'O' => this.postEvent(.{ .focus = false }),
|
'O' => this.postEvent(.{ .focus = false }),
|
||||||
'M', 'm' => {
|
'M', 'm' => {
|
||||||
std.debug.assert(sequence.len >= 4);
|
assert(sequence.len >= 4);
|
||||||
if (sequence[2] != '<') break;
|
if (sequence[2] != '<') break;
|
||||||
|
|
||||||
const delim1 = mem.indexOfScalarPos(u8, sequence, 3, ';') orelse break;
|
const delim1 = mem.indexOfScalarPos(u8, sequence, 3, ';') orelse break;
|
||||||
@@ -252,7 +269,7 @@ pub fn App(comptime E: type) type {
|
|||||||
// Device Status Report
|
// Device Status Report
|
||||||
// CSI Ps n
|
// CSI Ps n
|
||||||
// CSI ? Ps n
|
// CSI ? Ps n
|
||||||
std.debug.assert(sequence.len >= 3);
|
assert(sequence.len >= 3);
|
||||||
},
|
},
|
||||||
't' => {
|
't' => {
|
||||||
// XTWINOPS
|
// XTWINOPS
|
||||||
@@ -267,6 +284,7 @@ pub fn App(comptime E: type) type {
|
|||||||
|
|
||||||
_ = width_char;
|
_ = width_char;
|
||||||
_ = height_char;
|
_ = height_char;
|
||||||
|
this.postEvent(.resize);
|
||||||
// this.postEvent(.{ .size = .{
|
// this.postEvent(.{ .size = .{
|
||||||
// .x = fmt.parseUnsigned(u16, width_char, 10) catch break,
|
// .x = fmt.parseUnsigned(u16, width_char, 10) catch break,
|
||||||
// .y = fmt.parseUnsigned(u16, height_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 },
|
0x0a, 0x0d => .{ .cp = input.Enter },
|
||||||
0x01...0x07, 0x0b...0x0c, 0x0e...0x1a => .{ .cp = b + 0x60, .mod = .{ .ctrl = true } },
|
0x01...0x07, 0x0b...0x0c, 0x0e...0x1a => .{ .cp = b + 0x60, .mod = .{ .ctrl = true } },
|
||||||
0x1b => escape: {
|
0x1b => escape: {
|
||||||
std.debug.assert(read_bytes == 1);
|
assert(read_bytes == 1);
|
||||||
break :escape .{ .cp = input.Escape };
|
break :escape .{ .cp = input.Escape };
|
||||||
},
|
},
|
||||||
0x7f => .{ .cp = input.Backspace },
|
0x7f => .{ .cp = input.Backspace },
|
||||||
@@ -333,6 +351,7 @@ const mem = std.mem;
|
|||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
const posix = std.posix;
|
const posix = std.posix;
|
||||||
const Thread = std.Thread;
|
const Thread = std.Thread;
|
||||||
|
const assert = std.debug.assert;
|
||||||
const code_point = @import("code_point");
|
const code_point = @import("code_point");
|
||||||
const event = @import("event.zig");
|
const event = @import("event.zig");
|
||||||
const input = @import("input.zig");
|
const input = @import("input.zig");
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ pub const SystemEvent = union(enum) {
|
|||||||
init,
|
init,
|
||||||
/// Quit event to signify the end of the event loop (rendering should stop afterwards)
|
/// Quit event to signify the end of the event loop (rendering should stop afterwards)
|
||||||
quit,
|
quit,
|
||||||
|
/// Resize event to signify that the application should re-draw to resize
|
||||||
|
resize,
|
||||||
/// Error event to notify other containers about a recoverable error
|
/// Error event to notify other containers about a recoverable error
|
||||||
err: struct {
|
err: struct {
|
||||||
/// actual error
|
/// actual error
|
||||||
|
|||||||
Reference in New Issue
Block a user