diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-08 01:36:51 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-08 01:36:51 +0300 |
| commit | fb56e64537de5f93786ed121f703da2a4105badc (patch) | |
| tree | 67f6e100141dbee05c75ec421927d62e54d23ec6 /src/client | |
| parent | 416827872a6e2c820bd58209d267685d4c07cb7d (diff) | |
fix: improve windows pane behavior
Diffstat (limited to 'src/client')
| -rw-r--r-- | src/client/mod.rs | 56 |
1 files changed, 55 insertions, 1 deletions
diff --git a/src/client/mod.rs b/src/client/mod.rs index 8236bb44..1e46f190 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -19,6 +19,8 @@ use std::io::{self, Write as _}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, OnceLock}; use std::time::Duration; +#[cfg(windows)] +use std::time::Instant; use crossterm::event::{ DisableBracketedPaste, DisableFocusChange, DisableMouseCapture, EnableBracketedPaste, @@ -68,12 +70,21 @@ struct ClientState { mouse_scroll_lines: usize, /// Whether outer focus gain should force a full host-terminal redraw. redraw_on_focus_gained: bool, + #[cfg(windows)] + pending_cursor_reveal: Option<PendingCursorReveal>, } #[derive(Debug, Default)] #[cfg(windows)] struct AttachEscapeState; +#[derive(Debug)] +#[cfg(windows)] +struct PendingCursorReveal { + due_at: Instant, + bytes: Vec<u8>, +} + #[derive(Debug, Default)] #[cfg(unix)] struct AttachEscapeState { @@ -201,6 +212,37 @@ fn attach_scroll_action( impl ClientState { fn request_full_redraw(&mut self) { self.blit_encoder = render_ansi::BlitEncoder::new(); + #[cfg(windows)] + { + self.pending_cursor_reveal = None; + } + } + + #[cfg(windows)] + fn update_pending_cursor_reveal(&mut self, reveal: Option<Vec<u8>>) { + const CURSOR_REVEAL_DEBOUNCE: Duration = Duration::from_millis(90); + self.pending_cursor_reveal = reveal.map(|bytes| PendingCursorReveal { + due_at: Instant::now() + CURSOR_REVEAL_DEBOUNCE, + bytes, + }); + } + + #[cfg(windows)] + fn flush_pending_cursor_reveal_if_due(&mut self) { + let Some(pending) = &self.pending_cursor_reveal else { + return; + }; + if pending.due_at > Instant::now() { + return; + } + + let pending = self + .pending_cursor_reveal + .take() + .expect("pending cursor reveal"); + let mut stdout = io::stdout(); + let _ = stdout.write_all(&pending.bytes); + let _ = stdout.flush(); } } @@ -771,6 +813,8 @@ async fn run_client_loop( #[cfg(unix)] mouse_scroll_lines, redraw_on_focus_gained, + #[cfg(windows)] + pending_cursor_reveal: None, }; debug!(?negotiated_encoding, "client render encoding active"); @@ -942,7 +986,14 @@ async fn run_client_loop( } ClientLoopEvent::ServerMessage(msg) => match msg { ServerMessage::Frame(frame_data) => { + #[cfg(windows)] + let encoded = state + .blit_encoder + .encode_with_deferred_cursor_reveal(&frame_data, false); + #[cfg(not(windows))] let encoded = state.blit_encoder.encode(&frame_data, false); + #[cfg(windows)] + let deferred_cursor_reveal = encoded.deferred_cursor_reveal.clone(); let mut stdout = io::stdout(); let graphics = if state.kitty_graphics_enabled { frame_data.graphics.as_slice() @@ -953,6 +1004,8 @@ async fn run_client_loop( write_encoded_frame_with_graphics(&mut stdout, &encoded.bytes, graphics); let _ = stdout.flush(); state.blit_encoder.commit(frame_data, encoded); + #[cfg(windows)] + state.update_pending_cursor_reveal(deferred_cursor_reveal); } ServerMessage::Terminal(frame) => { if state.kitty_graphics_enabled && contains_kitty_graphics_bytes(&frame.bytes) { @@ -1008,7 +1061,8 @@ async fn run_client_loop( ))); } ClientLoopEvent::Timer => { - // Check if we should quit. + #[cfg(windows)] + state.flush_pending_cursor_reveal_if_due(); } } } |
