aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-14 02:50:00 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-14 02:50:00 +0300
commited4253d6031c9a3b12ddd6dfc7d7bf39480ae761 (patch)
tree315cea3ee7b5ad3a7c3f9fb31272064a0eed5dd2 /src/client
parent287bf51640f8e1bed227cb27d93377d962851196 (diff)
feat: add tmux-style mouse capture config
Diffstat (limited to 'src/client')
-rw-r--r--src/client/mod.rs32
1 files changed, 28 insertions, 4 deletions
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 9679e95c..075eeb55 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -46,6 +46,8 @@ static RECEIVED_KITTY_GRAPHICS_IDS: OnceLock<Mutex<HashSet<u32>>> = OnceLock::ne
struct ClientState {
/// Stateful semantic-frame encoder used when the server sends FrameData.
blit_encoder: blit::BlitEncoder,
+ /// Whether host mouse capture is currently active.
+ mouse_capture_active: bool,
/// The terminal size we reported to the server in our last Hello/Resize.
reported_size: (u16, u16),
/// Client-local sound playback config, refreshed on server request.
@@ -141,14 +143,18 @@ impl From<protocol::FramingError> for ClientError {
// Terminal setup / restore
// ---------------------------------------------------------------------------
-/// Sets up the terminal for client mode (raw mode, mouse, keyboard enhancements).
+/// Sets up the terminal for client mode (raw mode, optional mouse, keyboard enhancements).
///
/// Returns a guard that restores the terminal when dropped.
-fn setup_terminal() -> io::Result<TerminalGuard> {
+fn setup_terminal(mouse_capture: bool) -> io::Result<TerminalGuard> {
ratatui::init();
+ if mouse_capture {
+ execute!(io::stdout(), EnableMouseCapture)?;
+ } else {
+ execute!(io::stdout(), DisableMouseCapture)?;
+ }
execute!(
io::stdout(),
- EnableMouseCapture,
EnableBracketedPaste,
EnableFocusChange,
PushKeyboardEnhancementFlags(crate::input::ime_compatible_keyboard_enhancement_flags())
@@ -176,6 +182,14 @@ fn write_terminal_restore_postlude(writer: &mut impl io::Write) -> io::Result<()
writer.flush()
}
+fn set_mouse_capture(enabled: bool) -> io::Result<()> {
+ if enabled {
+ execute!(io::stdout(), EnableMouseCapture)
+ } else {
+ execute!(io::stdout(), DisableMouseCapture)
+ }
+}
+
fn restore_terminal_state(in_tmux: bool) {
let _ = clear_received_kitty_graphics(&mut io::stdout());
@@ -337,7 +351,7 @@ pub fn run_client() -> io::Result<()> {
// Now set up the terminal (raw mode, mouse, keyboard enhancements).
// This must happen AFTER the handshake succeeds, so we don't leave
// the terminal in raw mode if the server rejects us.
- let _guard = setup_terminal().map_err(|err| {
+ let _guard = setup_terminal(false).map_err(|err| {
eprintln!("herdr: failed to set up terminal: {err}");
err
})?;
@@ -372,6 +386,7 @@ pub fn run_client() -> io::Result<()> {
should_quit,
sound_config,
kitty_graphics_enabled,
+ false,
negotiated_encoding,
)
.await
@@ -416,10 +431,12 @@ async fn run_client_loop(
should_quit: Arc<AtomicBool>,
sound_config: crate::config::SoundConfig,
kitty_graphics_enabled: bool,
+ mouse_capture_active: bool,
negotiated_encoding: RenderEncoding,
) -> Result<(), ClientError> {
let mut state = ClientState {
blit_encoder: blit::BlitEncoder::new(),
+ mouse_capture_active,
reported_size: (cols, rows),
sound_config,
kitty_graphics_enabled,
@@ -530,6 +547,13 @@ async fn run_client_loop(
ServerMessage::ReloadSoundConfig => {
reload_local_sound_config(&mut state.sound_config);
}
+ ServerMessage::MouseCapture { enabled } => {
+ let desired = enabled;
+ if desired != state.mouse_capture_active {
+ set_mouse_capture(desired).map_err(ClientError::ConnectionFailed)?;
+ state.mouse_capture_active = desired;
+ }
+ }
ServerMessage::Welcome { .. } => {
debug!("received unexpected Welcome in main loop");
}