///|
#warnings("-7")
#cfg(not(platform="windows"))
priv struct SigwinchWatcher {
read : @async/pipe.PipeRead
write : @async/pipe.PipeWrite
}
///|
#cfg(not(platform="windows"))
let global_sigwinch_watcher : Ref[SigwinchWatcher?] = Ref(None)
///|
#cfg(not(platform="windows"))
fn sigwinch_watcher() -> SigwinchWatcher raise {
if global_sigwinch_watcher.val is Some(watcher) {
return watcher
}
let (read, write) = @async/pipe.pipe()
if install_sigwinch_handler(write.fd()) < 0 {
read.close()
write.close()
@os_error.check_errno("install SIGWINCH handler")
}
let watcher = SigwinchWatcher::{ read, write }
global_sigwinch_watcher.val = Some(watcher)
watcher
}
///|
#cfg(not(platform="windows"))
async fn SigwinchWatcher::wait(self : SigwinchWatcher) -> Unit {
let buf = FixedArray::make(64, b'\x00')
ignore(self.read.read(buf, max_len=buf.length()))
// Drain with raw nonblocking read semantics. moonbitlang/async Reader APIs,
// including read and read_some, wait for readiness after EAGAIN instead of
// exposing "empty for now"; resize coalescing must stop at that boundary.
drain_sigwinch_pipe(self.read.fd())
}
///|
#cfg(not(platform="windows"))
async fn Tty::read_resize_event(self : Self) -> Event {
let watcher = sigwinch_watcher()
for ;; {
watcher.wait()
try self.window_size() catch {
_ => ()
} noraise {
size => return Resize(size)
}
}
}
///|
#cfg(not(platform="windows"))
async fn Tty::read_event_from_sources(
self : Self,
esc_timeout_ms : Int,
) -> Event {
@async.with_task_group() <| group => {
group.spawn_bg(no_wait=true) <| () => {
group.return_immediately(
read_decoded_input_event(self.reader, esc_timeout_ms),
)
}
self.read_resize_event()
}
}
///|
#cfg(not(platform="windows"))
extern "C" fn install_sigwinch_handler(fd : @async/types.Fd) -> Int = "moonbit_tty_install_sigwinch_handler"
///|
#cfg(not(platform="windows"))
extern "C" fn drain_sigwinch_pipe(fd : @async/types.Fd) -> Unit = "moonbit_tty_drain_sigwinch_pipe"