# PR #1683: fix: crash on resize - URL: https://github.com/openai/codex/pull/1683 - Author: nornagon-openai - Created: 2025-07-25 20:47:32 UTC - Updated: 2025-07-25 21:23:45 UTC - Changes: +44/-32, Files changed: 1, Commits: 2 ## Description Without this, resizing the terminal prints "Error: The cursor position could not be read within a normal duration" and quits the app. ## Full Diff ```diff diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 4e2133a6be..e7097e6af0 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -88,39 +88,51 @@ impl App<'_> { { let app_event_tx = app_event_tx.clone(); std::thread::spawn(move || { - while let Ok(event) = crossterm::event::read() { - match event { - crossterm::event::Event::Key(key_event) => { - app_event_tx.send(AppEvent::KeyEvent(key_event)); - } - crossterm::event::Event::Resize(_, _) => { - app_event_tx.send(AppEvent::RequestRedraw); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollUp, - .. - }) => { - scroll_event_helper.scroll_up(); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollDown, - .. - }) => { - scroll_event_helper.scroll_down(); - } - crossterm::event::Event::Paste(pasted) => { - // Many terminals convert newlines to \r when - // pasting, e.g. [iTerm2][]. But [tui-textarea - // expects \n][tui-textarea]. This seems like a bug - // in tui-textarea IMO, but work around it for now. - // [tui-textarea]: https://github.com/rhysd/tui-textarea/blob/4d18622eeac13b309e0ff6a55a46ac6706da68cf/src/textarea.rs#L782-L783 - // [iTerm2]: https://github.com/gnachman/iTerm2/blob/5d0c0d9f68523cbd0494dad5422998964a2ecd8d/sources/iTermPasteHelper.m#L206-L216 - let pasted = pasted.replace("\r", "\n"); - app_event_tx.send(AppEvent::Paste(pasted)); - } - _ => { - // Ignore any other events. + loop { + // This timeout is necessary to avoid holding the event lock + // that crossterm::event::read() acquires. In particular, + // reading the cursor position (crossterm::cursor::position()) + // needs to acquire the event lock, and so will fail if it + // can't acquire it within 2 sec. Resizing the terminal + // crashes the app if the cursor position can't be read. + if let Ok(true) = crossterm::event::poll(Duration::from_millis(100)) { + if let Ok(event) = crossterm::event::read() { + match event { + crossterm::event::Event::Key(key_event) => { + app_event_tx.send(AppEvent::KeyEvent(key_event)); + } + crossterm::event::Event::Resize(_, _) => { + app_event_tx.send(AppEvent::RequestRedraw); + } + crossterm::event::Event::Mouse(MouseEvent { + kind: MouseEventKind::ScrollUp, + .. + }) => { + scroll_event_helper.scroll_up(); + } + crossterm::event::Event::Mouse(MouseEvent { + kind: MouseEventKind::ScrollDown, + .. + }) => { + scroll_event_helper.scroll_down(); + } + crossterm::event::Event::Paste(pasted) => { + // Many terminals convert newlines to \r when + // pasting, e.g. [iTerm2][]. But [tui-textarea + // expects \n][tui-textarea]. This seems like a bug + // in tui-textarea IMO, but work around it for now. + // [tui-textarea]: https://github.com/rhysd/tui-textarea/blob/4d18622eeac13b309e0ff6a55a46ac6706da68cf/src/textarea.rs#L782-L783 + // [iTerm2]: https://github.com/gnachman/iTerm2/blob/5d0c0d9f68523cbd0494dad5422998964a2ecd8d/sources/iTermPasteHelper.m#L206-L216 + let pasted = pasted.replace("\r", "\n"); + app_event_tx.send(AppEvent::Paste(pasted)); + } + _ => { + // Ignore any other events. + } + } } + } else { + // Timeout expired, no `Event` is available } } }); ``` ## Review Comments ### codex-rs/tui/src/app.rs - Created: 2025-07-25 21:12:39 UTC | Link: https://github.com/openai/codex/pull/1683#discussion_r2232016460 ```diff @@ -88,39 +88,51 @@ impl App<'_> { { let app_event_tx = app_event_tx.clone(); std::thread::spawn(move || { - while let Ok(event) = crossterm::event::read() { - match event { - crossterm::event::Event::Key(key_event) => { - app_event_tx.send(AppEvent::KeyEvent(key_event)); - } - crossterm::event::Event::Resize(_, _) => { - app_event_tx.send(AppEvent::RequestRedraw); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollUp, - .. - }) => { - scroll_event_helper.scroll_up(); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollDown, - .. - }) => { - scroll_event_helper.scroll_down(); - } - crossterm::event::Event::Paste(pasted) => { - // Many terminals convert newlines to \r when - // pasting, e.g. [iTerm2][]. But [tui-textarea - // expects \n][tui-textarea]. This seems like a bug - // in tui-textarea IMO, but work around it for now. - // [tui-textarea]: https://github.com/rhysd/tui-textarea/blob/4d18622eeac13b309e0ff6a55a46ac6706da68cf/src/textarea.rs#L782-L783 - // [iTerm2]: https://github.com/gnachman/iTerm2/blob/5d0c0d9f68523cbd0494dad5422998964a2ecd8d/sources/iTermPasteHelper.m#L206-L216 - let pasted = pasted.replace("\r", "\n"); - app_event_tx.send(AppEvent::Paste(pasted)); - } - _ => { - // Ignore any other events. + loop { + // This timeout is necessary to avoid holding the event lock + // that crossterm::event::read() acquires. In particular, + // reading the cursor position (crossterm::cursor::position()) + // needs to acquire the event lock, and so will fail if it + // can't acquire it within 2 sec. Resizing the terminal + // crashes the app if the cursor position can't be read. + if let Ok(true) = crossterm::event::poll(Duration::from_millis(100)) { ``` > This is...surprising. Maybe link to https://github.com/ratatui/ratatui/blob/9836f0760d4a053d9d1eba78171be89cb22dc850/examples/apps/inline/src/main.rs#L98-L118 to cite an example from Ratatui itself? - Created: 2025-07-25 21:12:49 UTC | Link: https://github.com/openai/codex/pull/1683#discussion_r2232017016 ```diff @@ -88,39 +88,51 @@ impl App<'_> { { let app_event_tx = app_event_tx.clone(); std::thread::spawn(move || { - while let Ok(event) = crossterm::event::read() { - match event { - crossterm::event::Event::Key(key_event) => { - app_event_tx.send(AppEvent::KeyEvent(key_event)); - } - crossterm::event::Event::Resize(_, _) => { - app_event_tx.send(AppEvent::RequestRedraw); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollUp, - .. - }) => { - scroll_event_helper.scroll_up(); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollDown, - .. - }) => { - scroll_event_helper.scroll_down(); - } - crossterm::event::Event::Paste(pasted) => { - // Many terminals convert newlines to \r when - // pasting, e.g. [iTerm2][]. But [tui-textarea - // expects \n][tui-textarea]. This seems like a bug - // in tui-textarea IMO, but work around it for now. - // [tui-textarea]: https://github.com/rhysd/tui-textarea/blob/4d18622eeac13b309e0ff6a55a46ac6706da68cf/src/textarea.rs#L782-L783 - // [iTerm2]: https://github.com/gnachman/iTerm2/blob/5d0c0d9f68523cbd0494dad5422998964a2ecd8d/sources/iTermPasteHelper.m#L206-L216 - let pasted = pasted.replace("\r", "\n"); - app_event_tx.send(AppEvent::Paste(pasted)); - } - _ => { - // Ignore any other events. + loop { + // This timeout is necessary to avoid holding the event lock + // that crossterm::event::read() acquires. In particular, + // reading the cursor position (crossterm::cursor::position()) + // needs to acquire the event lock, and so will fail if it + // can't acquire it within 2 sec. Resizing the terminal ``` > 2 sec or 100ms? - Created: 2025-07-25 21:14:37 UTC | Link: https://github.com/openai/codex/pull/1683#discussion_r2232019640 ```diff @@ -88,39 +88,51 @@ impl App<'_> { { let app_event_tx = app_event_tx.clone(); std::thread::spawn(move || { - while let Ok(event) = crossterm::event::read() { - match event { - crossterm::event::Event::Key(key_event) => { - app_event_tx.send(AppEvent::KeyEvent(key_event)); - } - crossterm::event::Event::Resize(_, _) => { - app_event_tx.send(AppEvent::RequestRedraw); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollUp, - .. - }) => { - scroll_event_helper.scroll_up(); - } - crossterm::event::Event::Mouse(MouseEvent { - kind: MouseEventKind::ScrollDown, - .. - }) => { - scroll_event_helper.scroll_down(); - } - crossterm::event::Event::Paste(pasted) => { - // Many terminals convert newlines to \r when - // pasting, e.g. [iTerm2][]. But [tui-textarea - // expects \n][tui-textarea]. This seems like a bug - // in tui-textarea IMO, but work around it for now. - // [tui-textarea]: https://github.com/rhysd/tui-textarea/blob/4d18622eeac13b309e0ff6a55a46ac6706da68cf/src/textarea.rs#L782-L783 - // [iTerm2]: https://github.com/gnachman/iTerm2/blob/5d0c0d9f68523cbd0494dad5422998964a2ecd8d/sources/iTermPasteHelper.m#L206-L216 - let pasted = pasted.replace("\r", "\n"); - app_event_tx.send(AppEvent::Paste(pasted)); - } - _ => { - // Ignore any other events. + loop { + // This timeout is necessary to avoid holding the event lock + // that crossterm::event::read() acquires. In particular, + // reading the cursor position (crossterm::cursor::position()) + // needs to acquire the event lock, and so will fail if it + // can't acquire it within 2 sec. Resizing the terminal + // crashes the app if the cursor position can't be read. + if let Ok(true) = crossterm::event::poll(Duration::from_millis(100)) { ``` > Also, it looks like that example is doing something slightly different: should we be modeling it more closely?