diff --git a/Cargo.lock b/Cargo.lock index a741a18f..69748fb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ dependencies = [ "serde_derive 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "vte 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "vte 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -993,7 +993,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "vte" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "utf8parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1224,7 +1224,7 @@ dependencies = [ "checksum user32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6717129de5ac253f5642fc78a51d0c7de6f9f53d617fc94e9bae7f6e71cf5504" "checksum utf8parse 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a15ea87f3194a3a454c78d79082b4f5e85f6956ddb6cb86bbfbe4892aa3c0323" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum vte 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "182acba7edb46d25ab5ddfc9f390b8c6cc1dcec7a4efc2798f0a8918f07d8c8a" +"checksum vte 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0221cd88a06e81dea3ef4dcad02a881b19c03c48b6e6a34875cb301ec8de78d9" "checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780" "checksum wayland-client 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ced3094c157b5cc0a08d40530e1a627d9f88b9a436971338d2646439128a559e" "checksum wayland-kbd 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "73bc10e84c1da90777beffecd24742baea17564ffc2a9918af41871c748eb050" diff --git a/Cargo.toml b/Cargo.toml index 4d0af202..dc335150 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ parking_lot = { version = "0.3.1", features = ["nightly"] } serde = "0.8" serde_yaml = "0.5" serde_derive = "0.8" -vte = "0.1.2" +vte = "0.2.1" mio = "0.6" serde_json = "*" copypasta = { path = "./copypasta" } diff --git a/src/ansi.rs b/src/ansi.rs index 4c7ce5b7..b8b063f4 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -13,8 +13,9 @@ // limitations under the License. // //! ANSI Terminal Stream Parsing -use std::ops::Range; use std::io; +use std::ops::Range; +use std::str; use vte; @@ -98,6 +99,9 @@ pub trait TermInfo { /// XXX Should probably not provide default impls for everything, but it makes /// writing specific handler impls for tests far easier. pub trait Handler { + /// OSC to set window title + fn set_title(&mut self, &str) {} + /// A character to be displayed fn input(&mut self, _c: char) {} @@ -455,9 +459,9 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W> } #[inline] - fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, byte: u8) { - err_println!("[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}, byte={:?}", - params, intermediates, ignore, byte as char); + fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) { + err_println!("[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}", + params, intermediates, ignore); } #[inline] @@ -466,23 +470,49 @@ impl<'a, H, W> vte::Perform for Performer<'a, H, W> } #[inline] - fn unhook(&mut self, byte: u8) { - err_println!("[unhandled unhook] byte={:?}", byte); + fn unhook(&mut self) { + err_println!("[unhandled unhook]"); } #[inline] - fn osc_start(&mut self) { - err_println!("[unhandled osc_start]"); - } + fn osc_dispatch(&mut self, params: &[&[u8]]) { + macro_rules! unhandled { + () => {{ + err_print!("[unhandled osc_dispatch]: ["); + for param in params { + err_print!("["); + for byte in *param { + err_print!("{:?}, ", *byte as char); + } + err_print!("],"); + } + err_println!("]"); + }} + } - #[inline] - fn osc_put(&mut self, byte: u8) { - err_println!("[unhandled osc_put] byte={:?}", byte as char); - } + if params.len() != 2 { + unhandled!(); + return; + } - #[inline] - fn osc_end(&mut self, byte: u8) { - err_println!("[unhandled osc_end] byte={:?}", byte); + let kind = params[0]; + let arg = params[1]; + + if kind.is_empty() || arg.is_empty() { + unhandled!(); + return; + } + + match kind[0] { + b'0' | b'2' => { + if let Ok(utf8_title) = str::from_utf8(arg) { + self.handler.set_title(utf8_title); + } + }, + _ => { + unhandled!(); + } + } } #[inline] diff --git a/src/display.rs b/src/display.rs index dccbd7fe..5aa3bac4 100644 --- a/src/display.rs +++ b/src/display.rs @@ -272,6 +272,10 @@ impl Display { // Clear dirty flag terminal.dirty = false; + if let Some(title) = terminal.get_next_title() { + self.window.set_title(&title); + } + { let glyph_cache = &mut self.glyph_cache; // Draw grid diff --git a/src/macros.rs b/src/macros.rs index a6e30104..c96e6d64 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,6 +30,16 @@ macro_rules! err_println { }} } +#[macro_export] +macro_rules! err_print { + ($($arg:tt)*) => {{ + if cfg!(feature = "err-println") { + use std::io::Write; + (write!(&mut ::std::io::stderr(), $($arg)*)).expect("stderr"); + } + }} +} + #[macro_export] macro_rules! debug_println { ($($t:tt)*) => { diff --git a/src/term/mod.rs b/src/term/mod.rs index cc1715a9..581f5039 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -210,6 +210,11 @@ pub struct Term { /// arrays. Without it we wold have to sanitize cursor.col every time we used it. input_needs_wrap: bool, + /// Got a request to set title; it's buffered here until next draw. + /// + /// Would be nice to avoid the allocation... + next_title: Option, + /// Alternate grid alt_grid: Grid, @@ -286,6 +291,11 @@ impl SizeInfo { } impl Term { + #[inline] + pub fn get_next_title(&mut self) -> Option { + self.next_title.take() + } + pub fn new(size: SizeInfo) -> Term { let template = Cell::default(); @@ -304,6 +314,7 @@ impl Term { let scroll_region = Line(0)..grid.num_lines(); Term { + next_title: None, dirty: false, input_needs_wrap: false, grid: grid, @@ -642,6 +653,12 @@ impl ansi::TermInfo for Term { } impl ansi::Handler for Term { + /// Set the window title + #[inline] + fn set_title(&mut self, title: &str) { + self.next_title = Some(title.to_owned()); + } + /// A character to be displayed #[inline] fn input(&mut self, c: char) { diff --git a/src/window.rs b/src/window.rs index 40729e99..a5f6e2c6 100644 --- a/src/window.rs +++ b/src/window.rs @@ -282,6 +282,12 @@ impl Window { pub fn wait_events(&self) -> glutin::WaitEventsIterator { self.glutin_window.wait_events() } + + /// Set the window title + #[inline] + pub fn set_title(&self, title: &str) { + self.glutin_window.set_title(title); + } } impl Proxy {