From 205ebe2f1c71f2ffb047fdd48ac2ff8cbf7b3476 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 15 Oct 2019 22:55:53 +0300 Subject: [PATCH] ui: add window title config option Use xterm window title escape sequences to set window title when launched and restoring the previous one when exiting. If option is blank, no title setting occurs. --- ui/src/conf/terminal.rs | 5 +++ ui/src/state.rs | 59 +++++++++++++------------------- ui/src/terminal.rs | 74 ++++++++++++++++++++++++++++++++++++++++- ui/src/terminal/keys.rs | 53 ----------------------------- 4 files changed, 102 insertions(+), 89 deletions(-) diff --git a/ui/src/conf/terminal.rs b/ui/src/conf/terminal.rs index 86ee649f0..0cbefa5e5 100644 --- a/ui/src/conf/terminal.rs +++ b/ui/src/conf/terminal.rs @@ -19,6 +19,8 @@ * along with meli. If not, see . */ +use super::deserializers::non_empty_string; + /// Settings for terminal display #[derive(Debug, Deserialize, Clone, Serialize)] #[serde(default)] @@ -26,6 +28,8 @@ pub struct TerminalSettings { /// light, dark pub theme: String, pub ascii_drawing: bool, + #[serde(deserialize_with = "non_empty_string")] + pub window_title: Option, } impl Default for TerminalSettings { @@ -33,6 +37,7 @@ impl Default for TerminalSettings { TerminalSettings { theme: "dark".to_string(), ascii_drawing: false, + window_title: Some("meli".to_string()), } } } diff --git a/ui/src/state.rs b/ui/src/state.rs index d505a99bf..e0db9e2ba 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -39,7 +39,7 @@ use std::result; use std::thread; use termion::raw::IntoRawMode; use termion::screen::AlternateScreen; -use termion::{clear, cursor, style}; +use termion::{clear, cursor}; pub type StateStdout = termion::screen::AlternateScreen>; @@ -142,17 +142,7 @@ pub struct State { impl Drop for State { fn drop(&mut self) { // When done, restore the defaults to avoid messing with the terminal. - write!( - self.stdout(), - "{}{}{}{}{}", - clear::All, - style::Reset, - cursor::Goto(1, 1), - cursor::Show, - BracketModeEnd, - ) - .unwrap(); - self.flush(); + self.switch_to_main_screen(); } } @@ -202,15 +192,11 @@ impl State { .collect(); accounts.sort_by(|a, b| a.name().cmp(&b.name())); - let _stdout = std::io::stdout(); - _stdout.lock(); - let stdout = AlternateScreen::from(_stdout.into_raw_mode().unwrap()); - let mut s = State { cols, rows, grid: CellBuffer::new(cols, rows, Cell::with_char(' ')), - stdout: Some(stdout), + stdout: None, child: None, mode: UIMode::Normal, components: Vec::with_capacity(1), @@ -249,16 +235,7 @@ impl State { } } - write!( - s.stdout(), - "{}{}{}{}", - BracketModeStart, - cursor::Hide, - clear::All, - cursor::Goto(1, 1) - ) - .unwrap(); - s.flush(); + s.switch_to_alternate_screen(); debug!("inserting mailbox hashes:"); { /* Account::watch() needs @@ -285,7 +262,7 @@ impl State { account.watch((work_controller, sender, replies)); } } - s.restore_input(); + s.context.restore_input(); s } @@ -338,29 +315,41 @@ impl State { pub fn switch_to_main_screen(&mut self) { write!( self.stdout(), - "{}{}", + "{}{}{}{}", termion::screen::ToMainScreen, - cursor::Show + cursor::Show, + RestoreWindowTitleIconFromStack, + BracketModeEnd, ) .unwrap(); self.flush(); self.stdout = None; self.context.input.kill(); } + pub fn switch_to_alternate_screen(&mut self) { let s = std::io::stdout(); - s.lock(); - self.stdout = Some(AlternateScreen::from(s.into_raw_mode().unwrap())); + + let mut stdout = AlternateScreen::from(s.into_raw_mode().unwrap()); write!( - self.stdout(), - "{}{}{}{}", + &mut stdout, + "{save_title_to_stack}{}{}{}{window_title}{}{}", termion::screen::ToAlternateScreen, cursor::Hide, clear::All, - cursor::Goto(1, 1) + cursor::Goto(1, 1), + BracketModeStart, + save_title_to_stack = SaveWindowTitleIconToStack, + window_title = if let Some(ref title) = self.context.settings.terminal.window_title { + format!("\x1b]2;{}\x07", title) + } else { + String::new() + }, ) .unwrap(); + + self.stdout = Some(stdout); self.flush(); } diff --git a/ui/src/terminal.rs b/ui/src/terminal.rs index e9aadbc07..16a118d1a 100644 --- a/ui/src/terminal.rs +++ b/ui/src/terminal.rs @@ -23,7 +23,6 @@ use self::serde::de::Visitor; use self::serde::{de, Deserialize, Deserializer}; extern crate unicode_segmentation; - #[macro_use] mod position; #[macro_use] @@ -35,3 +34,76 @@ pub use self::cells::*; pub use self::keys::*; pub use self::position::*; pub use self::text_editing::*; + +use std::fmt; +/* + * CSI events we use + */ + +// Some macros taken from termion: +/// Create a CSI-introduced sequence. +macro_rules! csi { + ($( $l:expr ),*) => { concat!("\x1b[", $( $l ),*) }; +} + +/// Derive a CSI sequence struct. +macro_rules! derive_csi_sequence { + ($(#[$outer:meta])* + ($name:ident, $value:expr)) => { + $(#[$outer])* + #[derive(Copy, Clone)] + pub struct $name; + + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, csi!($value)) + } + } + + impl AsRef<[u8]> for $name { + fn as_ref(&self) -> &'static [u8] { + csi!($value).as_bytes() + } + } + + impl AsRef for $name { + fn as_ref(&self) -> &'static str { + csi!($value) + } + } + }; +} + +/* +derive_csi_sequence!( + #[doc = ""] + (DisableMouse, "?1000l") +); +derive_csi_sequence!( + #[doc = ""] + (EnableMouse, "?1000h") +); +*/ + +derive_csi_sequence!( + #[doc = "`CSI Ps ; Ps ; Ps t`, where `Ps = 2 2 ; 0` -> Save xterm icon and window title on stack."] + (SaveWindowTitleIconToStack, "22;0t") +); + +derive_csi_sequence!( + #[doc = "Restore window title and icon from terminal's title stack. `CSI Ps ; Ps ; Ps t`, where `Ps = 2 3 ; 0` -> Restore xterm icon and window title from stack."] + (RestoreWindowTitleIconFromStack, "23;0t") +); + +derive_csi_sequence!( + #[doc = "Empty struct with a Display implementation that returns the byte sequence to start [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"] + (BracketModeStart, "?2004h") +); + +derive_csi_sequence!( + #[doc = "Empty struct with a Display implementation that returns the byte sequence to end [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"] + (BracketModeEnd, "?2003l") +); + +pub const BRACKET_PASTE_START: &[u8] = b"\x1B[200~"; +pub const BRACKET_PASTE_END: &[u8] = b"\x1B[201~"; diff --git a/ui/src/terminal/keys.rs b/ui/src/terminal/keys.rs index 3bf1561e8..ebf4cfe22 100644 --- a/ui/src/terminal/keys.rs +++ b/ui/src/terminal/keys.rs @@ -22,7 +22,6 @@ use super::*; use crossbeam::{channel::Receiver, select}; use serde::{Serialize, Serializer}; -use std::fmt; use std::io; use termion::event::Event as TermionEvent; use termion::event::Key as TermionKey; @@ -191,58 +190,6 @@ pub fn get_events( } } } - -/* - * CSI events we use - */ - -// Some macros taken from termion: -/// Create a CSI-introduced sequence. -macro_rules! csi { - ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) }; -} - -/// Derive a CSI sequence struct. -macro_rules! derive_csi_sequence { - ($(#[$outer:meta])* - ($name:ident, $value:expr)) => { - $(#[$outer])* - #[derive(Copy, Clone)] - pub struct $name; - - impl fmt::Display for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, csi!($value)) - } - } - - impl AsRef<[u8]> for $name { - fn as_ref(&self) -> &'static [u8] { - csi!($value).as_bytes() - } - } - - impl AsRef for $name { - fn as_ref(&self) -> &'static str { - csi!($value) - } - } - }; -} - -derive_csi_sequence!( - #[doc = "Empty struct with a Display implementation that returns the byte sequence to start [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"] - (BracketModeStart, "?2004h") -); - -derive_csi_sequence!( - #[doc = "Empty struct with a Display implementation that returns the byte sequence to end [Bracketed Paste Mode](http://www.xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)"] - (BracketModeEnd, "?2003l") -); - -pub const BRACKET_PASTE_START: &[u8] = b"\x1B[200~"; -pub const BRACKET_PASTE_END: &[u8] = b"\x1B[201~"; - const FIELDS: &[&str] = &[]; impl<'de> Deserialize<'de> for Key {