diff --git a/alacritty.yml b/alacritty.yml index 546d6259..f21db6cc 100644 --- a/alacritty.yml +++ b/alacritty.yml @@ -52,6 +52,10 @@ scrolling: # To disable this completely, set `faux_multiplier` to 0. faux_multiplier: 3 + # Automatically scroll to the bottom when new text is written + # to the terminal. + auto_scroll: false + # Display tabs using this many cells (changes require restart) tabspaces: 8 diff --git a/alacritty_macos.yml b/alacritty_macos.yml index 8f8c4703..9b7b8d1a 100644 --- a/alacritty_macos.yml +++ b/alacritty_macos.yml @@ -50,6 +50,10 @@ scrolling: # To disable this completely, set `faux_multiplier` to 0. faux_multiplier: 3 + # Automatically scroll to the bottom when new text is written + # to the terminal. + auto_scroll: false + # Display tabs using this many cells (changes require restart) tabspaces: 8 diff --git a/src/config.rs b/src/config.rs index f8dad1f2..0eab1bfb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -478,6 +478,8 @@ pub struct Scrolling { #[serde(deserialize_with="deserialize_scrolling_multiplier")] #[serde(default="default_scrolling_multiplier")] pub faux_multiplier: u8, + #[serde(default, deserialize_with="failure_default")] + pub auto_scroll: bool, } fn default_scrolling_history() -> u32 { @@ -495,6 +497,7 @@ impl Default for Scrolling { history: default_scrolling_history(), multiplier: default_scrolling_multiplier(), faux_multiplier: default_scrolling_multiplier(), + auto_scroll: false, } } } diff --git a/src/event_loop.rs b/src/event_loop.rs index 18d48a52..d7d27243 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -255,6 +255,9 @@ impl EventLoop let mut processed = 0; let mut terminal = None; + // Flag to keep track if wakeup has already been sent + let mut send_wakeup = false; + loop { match self.pty.read(&mut buf[..]) { Ok(0) => break, @@ -272,10 +275,14 @@ impl EventLoop // Get reference to terminal. Lock is acquired on initial // iteration and held until there's no bytes left to parse // or we've reached MAX_READ. - if terminal.is_none() { + let terminal = if terminal.is_none() { terminal = Some(self.terminal.lock()); - } - let terminal = terminal.as_mut().unwrap(); + let terminal = terminal.as_mut().unwrap(); + send_wakeup = !terminal.dirty; + terminal + } else { + terminal.as_mut().unwrap() + }; // Run the parser for byte in &buf[..got] { @@ -301,7 +308,7 @@ impl EventLoop // Only request a draw if one hasn't already been requested. if let Some(mut terminal) = terminal { - if !terminal.dirty { + if send_wakeup { self.display.notify(); terminal.dirty = true; } diff --git a/src/input.rs b/src/input.rs index 8cf11811..047c81aa 100644 --- a/src/input.rs +++ b/src/input.rs @@ -192,6 +192,7 @@ impl Action { fn execute(&self, ctx: &mut A) { match *self { Action::Esc(ref s) => { + ctx.scroll(Scroll::Bottom); ctx.write_to_pty(s.clone().into_bytes()) }, Action::Copy => { diff --git a/src/term/mod.rs b/src/term/mod.rs index 3c7ef87c..198f8cea 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -756,6 +756,9 @@ pub struct Term { /// Number of spaces in one tab tabspaces: usize, + + /// Automatically scroll to bottom when new lines are added + auto_scroll: bool, } /// Terminal size info @@ -879,6 +882,7 @@ impl Term { default_cursor_style: config.cursor_style(), dynamic_title: config.dynamic_title(), tabspaces, + auto_scroll: config.scrolling().auto_scroll, } } @@ -905,6 +909,7 @@ impl Term { self.visual_bell.update_config(config); self.default_cursor_style = config.cursor_style(); self.dynamic_title = config.dynamic_title(); + self.auto_scroll = config.scrolling().auto_scroll; } #[inline] @@ -1255,6 +1260,11 @@ impl ansi::Handler for Term { /// A character to be displayed #[inline] fn input(&mut self, c: char) { + // If enabled, scroll to bottom when character is received + if self.auto_scroll { + self.scroll_display(Scroll::Bottom); + } + if self.input_needs_wrap { if !self.mode.contains(mode::TermMode::LINE_WRAP) { return;