-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rather than rely on the nodelay/blocking nature of a TTY read(), this introduces an event loop based on pselect(2). It currently blocks until there is available TTY input, or a SIGWINCH signal is received. There is still some room for improvements, but this provides a solid base for extension like handling input on subprocesses for a preview split. Closes #40 Closes #39
- Loading branch information
1 parent
4713559
commit 8ddc8fd
Showing
7 changed files
with
110 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
//! A simple event loop that tracks signals and tty input based on pselect(2) | ||
//! | ||
//! Because pselect is difficult to call from Zig, a portion of the code is written in C. | ||
//! See loop.c for more details. | ||
|
||
const os = std.os; | ||
const std = @import("std"); | ||
|
||
const Loop = @This(); | ||
|
||
/// The file descriptor of the TTY | ||
ttyfd: os.fd_t, | ||
|
||
/// Because the default for SIGWINCH is to discard the signal, all this | ||
/// handler needs to do is exist for the signal to no longer be ignored. | ||
fn handler(_: c_int) align(1) callconv(.C) void {} | ||
|
||
pub fn init(ttyfd: os.fd_t) !Loop { | ||
// Block handling of SIGWINCH. | ||
// This will be unblocked by the kernel within the pselect() call. | ||
var sigset = os.system.empty_sigset; | ||
os.system.sigaddset(&sigset, os.SIG.WINCH); | ||
_ = os.system.sigprocmask(os.SIG._BLOCK, &sigset, null); | ||
|
||
// Setup SIGWINCH signal handler | ||
var sigaction: os.Sigaction = .{ | ||
.handler = .{ .handler = handler }, | ||
.mask = os.system.empty_sigset, | ||
.flags = os.SA.RESTART, | ||
}; | ||
try os.sigaction(os.SIG.WINCH, &sigaction, null); | ||
|
||
return .{ .ttyfd = ttyfd }; | ||
} | ||
|
||
pub fn deinit(loop: *Loop) void { | ||
_ = loop; | ||
} | ||
|
||
/// This function is defined in loop.c | ||
extern "c" fn wait_internal(ttyfd: c_int) c_int; | ||
|
||
/// Wait for an event | ||
/// | ||
/// An event is either a file descriptor available to read, or a resize. | ||
pub fn wait(loop: *Loop) !Event { | ||
const fd = wait_internal(loop.ttyfd); | ||
|
||
if (fd == loop.ttyfd) { | ||
return .tty; | ||
} else if (fd == -1) { | ||
return .resize; | ||
} else unreachable; | ||
} | ||
|
||
const Event = union(enum) { | ||
tty, | ||
resize, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Using pselect() from Zig is tricky due to macros not working with translate-c | ||
// so we write the interface to pselect() in C and then call this function from Zig | ||
|
||
#include <stdlib.h> | ||
|
||
#include <signal.h> | ||
#include <sys/errno.h> | ||
#include <sys/select.h> | ||
|
||
int wait_internal(int ttyfd) { | ||
|
||
fd_set read_set; | ||
FD_ZERO(&read_set); | ||
FD_SET(ttyfd, &read_set); | ||
|
||
// Create a set that only allows SIGWINCH through | ||
sigset_t sigwinch_set; | ||
sigfillset(&sigwinch_set); | ||
sigdelset(&sigwinch_set, SIGWINCH); | ||
|
||
if (pselect(ttyfd + 1, &read_set, NULL, NULL, NULL, &sigwinch_set) == -1) { | ||
// The only signals that can get through are SIGKILL, SIGSTOP, and SIGWINCH. | ||
// The first two will kill the program no matter what we do, so we assume | ||
// the signal is SIGWINCH. If it is we redraw, otherwise zf will exit. | ||
if (errno == EINTR) { | ||
return -1; | ||
} | ||
} | ||
|
||
if (FD_ISSET(ttyfd, &read_set)) { | ||
return ttyfd; | ||
} | ||
|
||
// Some unreachable error occurred | ||
return -2; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters