From ac12f9e3981de689786a82455135c8d4c87dd2b0 Mon Sep 17 00:00:00 2001 From: vasilito Date: Fri, 19 Jun 2026 17:49:56 +0300 Subject: [PATCH] tlc: fix terminal resize detection with poll-based event loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MC uses SIGWINCH→self-pipe→select(stdin+pipe) for resize detection. In safe Rust (#![deny(unsafe_code)]), the equivalent is polling stdin with a 200ms timeout via rustix::event::poll. This wakes the main loop every 200ms even without key input, allowing terminal size check and full redraw on resize. Same result as MC's signal-driven approach without requiring unsafe signal handlers. Previously the event loop blocked indefinitely on events.next() — terminal resize was only detected on the next keypress. --- local/recipes/tui/tlc/source/src/app.rs | 28 +++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/local/recipes/tui/tlc/source/src/app.rs b/local/recipes/tui/tlc/source/src/app.rs index 988d3e8e0e..d44c0cc705 100644 --- a/local/recipes/tui/tlc/source/src/app.rs +++ b/local/recipes/tui/tlc/source/src/app.rs @@ -10,9 +10,11 @@ use std::path::PathBuf; use std::io::Write; +use std::os::fd::AsFd; use std::time::Duration; use anyhow::Result; +use rustix::event::{poll, PollFd, PollFlags, Timespec}; use termion::event::{Event as TermEvent, Key as TermKey}; use termion::input::TermReadEventsAndRaw; @@ -55,7 +57,16 @@ impl Application { let mut last_size = tui.size(); - // Main event loop — blocking stdin, raw mode is active. + // Stdin fd for poll — obtained without locking so the subshell + // can still read from stdin when we drop into CTRL+O. + let stdin = std::io::stdin(); + let stdin_fd = stdin.as_fd(); + let poll_timeout = + Timespec::try_from(Duration::from_millis(200)).unwrap_or_default(); + + // Main event loop — MC-style resize detection via poll timeout. + // poll() wakes every 200ms even without key input, letting us + // detect terminal resize without requiring a keypress. loop { let cur_size = tui.size(); if cur_size != last_size { @@ -72,7 +83,20 @@ impl Application { render(&mut tui, &mut fm)?; } - let stdin = std::io::stdin(); + // Poll stdin with 200ms timeout. On timeout (no key), loop + // back and re-check terminal size. On data, read the key. + let mut poll_fds = [PollFd::new(&stdin, PollFlags::IN)]; + match poll(&mut poll_fds, Some(&poll_timeout)) { + Ok(0) => continue, + Ok(_) => { + if !poll_fds[0].revents().contains(PollFlags::IN) { + continue; + } + } + Err(_) => continue, + } + + // Data available on stdin — read the key event. let mut events = stdin.lock().events_and_raw(); let (event, raw) = match events.next() { Some(Ok(pair)) => pair,