From b6c16c463579747c90fd3a98d11794980f8f191b Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Mon, 23 Feb 2026 20:58:35 +0100 Subject: [PATCH 1/3] Hide cursor after spawning external programs --- src/interpreter/flamegraph.rs | 3 +++ src/utils.rs | 35 +++++++++++++++++++++++++++++++++++ src/view/providers/client.rs | 33 ++++++++------------------------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/interpreter/flamegraph.rs b/src/interpreter/flamegraph.rs index 17dc532..302fa7e 100644 --- a/src/interpreter/flamegraph.rs +++ b/src/interpreter/flamegraph.rs @@ -66,6 +66,9 @@ pub fn show(block: Columns) -> AppResult<()> { } terminal.clear()?; + // ratatui's Terminal::drop may shows the cursor, re-hide it for cursive + drop(terminal); + crossterm::execute!(io::stderr(), crossterm::cursor::Hide)?; Ok(()) } diff --git a/src/utils.rs b/src/utils.rs index ba41c77..6a86b3a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -18,6 +18,37 @@ use std::process::{Command, Stdio}; use syntect::{highlighting::ThemeSet, parsing::SyntaxSet}; use tempfile::Builder; +/// RAII guard that leaves cursive's terminal state (raw mode, alternate screen, +/// mouse capture, hidden cursor) and restores it on drop. +pub struct TerminalRawModeGuard; + +impl TerminalRawModeGuard { + pub fn leave() -> Self { + crossterm::terminal::disable_raw_mode().unwrap(); + crossterm::execute!( + std::io::stdout(), + crossterm::event::DisableMouseCapture, + crossterm::cursor::Show, + crossterm::terminal::LeaveAlternateScreen, + ) + .unwrap(); + Self + } +} + +impl Drop for TerminalRawModeGuard { + fn drop(&mut self) { + crossterm::terminal::enable_raw_mode().unwrap(); + crossterm::execute!( + std::io::stdout(), + crossterm::terminal::EnterAlternateScreen, + crossterm::event::EnableMouseCapture, + crossterm::cursor::Hide, + ) + .unwrap(); + } +} + pub fn fuzzy_actions(siv: &mut Cursive, actions: Vec, on_select: F) where F: Fn(&mut Cursive, String) + 'static + Send + Sync, @@ -206,11 +237,15 @@ pub fn edit_query(query: &str, settings: &HashMap) -> Result { cb_sink .send(Box::new(move |siv| { @@ -107,17 +100,7 @@ impl ViewProvider for ClientViewProvider { } } - enable_raw_mode().unwrap(); - execute!( - io::stdout(), - crossterm::event::EnableMouseCapture, - crossterm::terminal::Clear(crossterm::terminal::ClearType::All) - ) - .unwrap(); - - // Force a full redraw of the screen siv.complete_clear(); - - log::info!("Client terminated. Raw mode and mouse capture enabled."); + log::info!("Client terminated."); } } From c9eba04a01b316fce1205021020229b34436a7a0 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Mon, 23 Feb 2026 21:13:02 +0100 Subject: [PATCH 2/3] Use crossterm from cursive --- Cargo.toml | 1 + src/utils.rs | 27 +++++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9ce5d5..3e0473d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ console-subscriber = { version = "*", default-features = false, optional = true # Flamegraphs flamelens = { git = "https://github.com/ys-l/flamelens", branch = "main", default-features = false } ratatui = { version = "0.29.0", features = ["unstable-rendered-line-info"] } +# Should **only** with the flamelens, since cursive re-export it, while flamelens does not crossterm = { version = "0.28.1", features = ["use-dev-tty"] } # Sharing aes-gcm = { version = "0.10", default-features = false, features = ["aes", "alloc"] } diff --git a/src/utils.rs b/src/utils.rs index 6a86b3a..f6b50c5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -20,16 +20,23 @@ use tempfile::Builder; /// RAII guard that leaves cursive's terminal state (raw mode, alternate screen, /// mouse capture, hidden cursor) and restores it on drop. +/// +/// Uses cursive's re-exported crossterm to ensure we operate on the same global +/// raw mode state that the cursive backend uses. pub struct TerminalRawModeGuard; +use cursive::backends::crossterm::crossterm as ct; + impl TerminalRawModeGuard { pub fn leave() -> Self { - crossterm::terminal::disable_raw_mode().unwrap(); - crossterm::execute!( + ct::terminal::disable_raw_mode().unwrap(); + ct::execute!( std::io::stdout(), - crossterm::event::DisableMouseCapture, - crossterm::cursor::Show, - crossterm::terminal::LeaveAlternateScreen, + ct::event::DisableMouseCapture, + ct::style::ResetColor, + ct::style::SetAttribute(ct::style::Attribute::Reset), + ct::cursor::Show, + ct::terminal::LeaveAlternateScreen, ) .unwrap(); Self @@ -38,12 +45,12 @@ impl TerminalRawModeGuard { impl Drop for TerminalRawModeGuard { fn drop(&mut self) { - crossterm::terminal::enable_raw_mode().unwrap(); - crossterm::execute!( + ct::terminal::enable_raw_mode().unwrap(); + ct::execute!( std::io::stdout(), - crossterm::terminal::EnterAlternateScreen, - crossterm::event::EnableMouseCapture, - crossterm::cursor::Hide, + ct::terminal::EnterAlternateScreen, + ct::event::EnableMouseCapture, + ct::cursor::Hide, ) .unwrap(); } From 56756b1103996674dab1ddc324baf6a758ceed1b Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Mon, 23 Feb 2026 21:19:52 +0100 Subject: [PATCH 3/3] Add an info message when spawning client (to aviod mixing outputs of client and chdig) --- src/view/providers/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/view/providers/client.rs b/src/view/providers/client.rs index 16c8f89..a8964f5 100644 --- a/src/view/providers/client.rs +++ b/src/view/providers/client.rs @@ -72,6 +72,7 @@ impl ViewProvider for ClientViewProvider { let result = { let _guard = TerminalRawModeGuard::leave(); + eprintln!("\n--- chdig: launching clickhouse client ---\n"); cmd.status() };