diff options
author | Dettorer <Dettorer@users.noreply.github.com> | 2020-11-24 00:11:03 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-11-23 23:11:03 +0000 |
commit | 2fd2db4afa232ebd15dbfff88160224badeaa669 (patch) | |
tree | ef0cdf3311df017da5fff4d29ce898d690980a3e /alacritty | |
parent | 07cfe8bbba0851ff4989f6aaf082d72130cd0f5b (diff) | |
download | r-alacritty-2fd2db4afa232ebd15dbfff88160224badeaa669.tar.gz r-alacritty-2fd2db4afa232ebd15dbfff88160224badeaa669.tar.bz2 r-alacritty-2fd2db4afa232ebd15dbfff88160224badeaa669.zip |
Add blinking cursor support
This adds support for blinking the terminal cursor. This can be
controlled either using the configuration file, or using escape
sequences.
The supported control sequences for changing the blinking state are
`CSI Ps SP q` and private mode 12.
Diffstat (limited to 'alacritty')
-rw-r--r-- | alacritty/src/cursor.rs | 14 | ||||
-rw-r--r-- | alacritty/src/display.rs | 7 | ||||
-rw-r--r-- | alacritty/src/event.rs | 71 | ||||
-rw-r--r-- | alacritty/src/input.rs | 32 | ||||
-rw-r--r-- | alacritty/src/renderer/mod.rs | 2 | ||||
-rw-r--r-- | alacritty/src/scheduler.rs | 1 |
6 files changed, 100 insertions, 27 deletions
diff --git a/alacritty/src/cursor.rs b/alacritty/src/cursor.rs index 8c782185..edf76bf3 100644 --- a/alacritty/src/cursor.rs +++ b/alacritty/src/cursor.rs @@ -2,10 +2,10 @@ use crossfont::{BitmapBuffer, Metrics, RasterizedGlyph}; -use alacritty_terminal::ansi::CursorStyle; +use alacritty_terminal::ansi::CursorShape; pub fn get_cursor_glyph( - cursor: CursorStyle, + cursor: CursorShape, metrics: Metrics, offset_x: i8, offset_y: i8, @@ -26,11 +26,11 @@ pub fn get_cursor_glyph( } match cursor { - CursorStyle::HollowBlock => get_box_cursor_glyph(height, width, line_width), - CursorStyle::Underline => get_underline_cursor_glyph(width, line_width), - CursorStyle::Beam => get_beam_cursor_glyph(height, line_width), - CursorStyle::Block => get_block_cursor_glyph(height, width), - CursorStyle::Hidden => RasterizedGlyph::default(), + CursorShape::HollowBlock => get_box_cursor_glyph(height, width, line_width), + CursorShape::Underline => get_underline_cursor_glyph(width, line_width), + CursorShape::Beam => get_beam_cursor_glyph(height, line_width), + CursorShape::Block => get_block_cursor_glyph(height, width), + CursorShape::Hidden => RasterizedGlyph::default(), } } diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs index af21001e..451874c8 100644 --- a/alacritty/src/display.rs +++ b/alacritty/src/display.rs @@ -160,6 +160,9 @@ pub struct Display { #[cfg(not(any(target_os = "macos", windows)))] pub is_x11: bool, + /// UI cursor visibility for blinking. + pub cursor_hidden: bool, + renderer: QuadRenderer, glyph_cache: GlyphCache, meter: Meter, @@ -300,6 +303,7 @@ impl Display { is_x11, #[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))] wayland_event_queue, + cursor_hidden: false, }) } @@ -442,8 +446,9 @@ impl Display { let viewport_match = search_state .focused_match() .and_then(|focused_match| terminal.grid().clamp_buffer_range_to_visible(focused_match)); + let cursor_hidden = self.cursor_hidden || search_state.regex().is_some(); - let grid_cells = terminal.renderable_cells(config, !search_active).collect::<Vec<_>>(); + let grid_cells = terminal.renderable_cells(config, !cursor_hidden).collect::<Vec<_>>(); let visual_bell_intensity = terminal.visual_bell.intensity(); let background_color = terminal.background_color(); let cursor_point = terminal.grid().cursor.point; diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index c1f81300..8ecf6f00 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -67,6 +67,7 @@ pub enum Event { Scroll(Scroll), ConfigReload(PathBuf), Message(Message), + BlinkCursor, SearchNext, } @@ -150,6 +151,7 @@ pub struct ActionContext<'a, N, T> { pub urls: &'a Urls, pub scheduler: &'a mut Scheduler, pub search_state: &'a mut SearchState, + cursor_hidden: &'a mut bool, cli_options: &'a CLIOptions, font_size: &'a mut Size, } @@ -495,6 +497,28 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon } } + /// Handle keyboard typing start. + /// + /// This will temporarily disable some features like terminal cursor blinking or the mouse + /// cursor. + /// + /// All features are re-enabled again automatically. + #[inline] + fn on_typing_start(&mut self) { + // Disable cursor blinking. + let blink_interval = self.config.cursor.blink_interval(); + if let Some(timer) = self.scheduler.get_mut(TimerId::BlinkCursor) { + timer.deadline = Instant::now() + Duration::from_millis(blink_interval); + *self.cursor_hidden = false; + self.terminal.dirty = true; + } + + // Hide mouse cursor. + if self.config.ui_config.mouse.hide_when_typing { + self.window.set_mouse_visible(false); + } + } + #[inline] fn search_direction(&self) -> Direction { self.search_state.direction @@ -667,6 +691,33 @@ impl<'a, N: Notify + 'a, T: EventListener> ActionContext<'a, N, T> { origin.line = (origin.line as isize + self.search_state.display_offset_delta) as usize; origin } + + /// Update the cursor blinking state. + fn update_cursor_blinking(&mut self) { + // Get config cursor style. + let mut cursor_style = self.config.cursor.style; + if self.terminal.mode().contains(TermMode::VI) { + cursor_style = self.config.cursor.vi_mode_style.unwrap_or(cursor_style); + }; + + // Check terminal cursor style. + let terminal_blinking = self.terminal.cursor_style().blinking; + let blinking = cursor_style.blinking_override().unwrap_or(terminal_blinking); + + // Update cursor blinking state. + self.scheduler.unschedule(TimerId::BlinkCursor); + if blinking && self.terminal.is_focused { + self.scheduler.schedule( + GlutinEvent::UserEvent(Event::BlinkCursor), + Duration::from_millis(self.config.cursor.blink_interval()), + true, + TimerId::BlinkCursor, + ) + } else { + *self.cursor_hidden = false; + self.terminal.dirty = true; + } + } } #[derive(Debug, Eq, PartialEq)] @@ -804,6 +855,12 @@ impl<N: Notify + OnResize> Processor<N> { { let mut scheduler = Scheduler::new(); + // Start the initial cursor blinking timer. + if self.config.cursor.style().blinking { + let event: Event = TerminalEvent::CursorBlinkingChange(true).into(); + self.event_queue.push(event.into()); + } + event_loop.run_return(|event, event_loop, control_flow| { if self.config.ui_config.debug.print_events { info!("glutin event: {:?}", event); @@ -873,6 +930,7 @@ impl<N: Notify + OnResize> Processor<N> { scheduler: &mut scheduler, search_state: &mut self.search_state, cli_options: &self.cli_options, + cursor_hidden: &mut self.display.cursor_hidden, event_loop, }; let mut processor = input::Processor::new(context, &self.display.highlighted_url); @@ -953,6 +1011,10 @@ impl<N: Notify + OnResize> Processor<N> { Event::SearchNext => processor.ctx.goto_match(None), Event::ConfigReload(path) => Self::reload_config(&path, processor), Event::Scroll(scroll) => processor.ctx.scroll(scroll), + Event::BlinkCursor => { + *processor.ctx.cursor_hidden ^= true; + processor.ctx.terminal.dirty = true; + }, Event::TerminalEvent(event) => match event { TerminalEvent::Title(title) => { let ui_config = &processor.ctx.config.ui_config; @@ -983,6 +1045,9 @@ impl<N: Notify + OnResize> Processor<N> { }, TerminalEvent::MouseCursorDirty => processor.reset_mouse_cursor(), TerminalEvent::Exit => (), + TerminalEvent::CursorBlinkingChange(_) => { + processor.ctx.update_cursor_blinking(); + }, }, }, GlutinEvent::RedrawRequested(_) => processor.ctx.terminal.dirty = true, @@ -1033,6 +1098,7 @@ impl<N: Notify + OnResize> Processor<N> { processor.ctx.window.set_mouse_visible(true); } + processor.ctx.update_cursor_blinking(); processor.on_focus_change(is_focused); } }, @@ -1111,7 +1177,7 @@ impl<N: Notify + OnResize> Processor<N> { processor.ctx.terminal.update_config(&config); - // Reload cursor if we've changed its thickness. + // Reload cursor if its thickness has changed. if (processor.ctx.config.cursor.thickness() - config.cursor.thickness()).abs() > std::f64::EPSILON { @@ -1154,6 +1220,9 @@ impl<N: Notify + OnResize> Processor<N> { *processor.ctx.config = config; + // Update cursor blinking. + processor.ctx.update_cursor_blinking(); + processor.ctx.terminal.dirty = true; } diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs index 348db610..ce89625b 100644 --- a/alacritty/src/input.rs +++ b/alacritty/src/input.rs @@ -103,6 +103,7 @@ pub trait ActionContext<T: EventListener> { fn advance_search_origin(&mut self, direction: Direction); fn search_direction(&self) -> Direction; fn search_active(&self) -> bool; + fn on_typing_start(&mut self); } trait Execute<T: EventListener> { @@ -138,9 +139,7 @@ impl<T: EventListener> Execute<T> for Action { fn execute<A: ActionContext<T>>(&self, ctx: &mut A) { match *self { Action::Esc(ref s) => { - if ctx.config().ui_config.mouse.hide_when_typing { - ctx.window_mut().set_mouse_visible(false); - } + ctx.on_typing_start(); ctx.clear_selection(); ctx.scroll(Scroll::Bottom); @@ -167,10 +166,7 @@ impl<T: EventListener> Execute<T> for Action { Action::ClearSelection => ctx.clear_selection(), Action::ToggleViMode => ctx.terminal_mut().toggle_vi_mode(), Action::ViMotion(motion) => { - if ctx.config().ui_config.mouse.hide_when_typing { - ctx.window_mut().set_mouse_visible(false); - } - + ctx.on_typing_start(); ctx.terminal_mut().vi_motion(motion) }, Action::ViAction(ViAction::ToggleNormalSelection) => { @@ -870,6 +866,13 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> { self.ctx.window_mut().set_mouse_cursor(mouse_state.into()); } + /// Reset mouse cursor based on modifier and terminal state. + #[inline] + pub fn reset_mouse_cursor(&mut self) { + let mouse_state = self.mouse_state(); + self.ctx.window_mut().set_mouse_cursor(mouse_state.into()); + } + /// Process a received character. pub fn received_char(&mut self, c: char) { let suppress_chars = *self.ctx.suppress_chars(); @@ -890,9 +893,7 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> { return; } - if self.ctx.config().ui_config.mouse.hide_when_typing { - self.ctx.window_mut().set_mouse_visible(false); - } + self.ctx.on_typing_start(); self.ctx.scroll(Scroll::Bottom); self.ctx.clear_selection(); @@ -917,13 +918,6 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> { *self.ctx.received_count() += 1; } - /// Reset mouse cursor based on modifier and terminal state. - #[inline] - pub fn reset_mouse_cursor(&mut self) { - let mouse_state = self.mouse_state(); - self.ctx.window_mut().set_mouse_cursor(mouse_state.into()); - } - /// Attempt to find a binding and execute its action. /// /// The provided mode, mods, and key must match what is allowed by a binding @@ -1270,6 +1264,10 @@ mod tests { fn scheduler_mut(&mut self) -> &mut Scheduler { unimplemented!(); } + + fn on_typing_start(&mut self) { + unimplemented!(); + } } macro_rules! test_clickstate { diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index f63f92fd..af1f3c3e 100644 --- a/alacritty/src/renderer/mod.rs +++ b/alacritty/src/renderer/mod.rs @@ -1010,7 +1010,7 @@ impl<'a> RenderApi<'a> { let metrics = glyph_cache.metrics; let glyph = glyph_cache.cursor_cache.entry(cursor_key).or_insert_with(|| { self.load_glyph(&cursor::get_cursor_glyph( - cursor_key.style, + cursor_key.shape, metrics, self.config.font.offset.x, self.config.font.offset.y, diff --git a/alacritty/src/scheduler.rs b/alacritty/src/scheduler.rs index db6180ca..5e454141 100644 --- a/alacritty/src/scheduler.rs +++ b/alacritty/src/scheduler.rs @@ -14,6 +14,7 @@ type Event = GlutinEvent<'static, AlacrittyEvent>; pub enum TimerId { SelectionScrolling, DelayedSearch, + BlinkCursor, } /// Event scheduled to be emitted at a specific time. |