From ad0365219f7f264ef0fdf0b3e4401bad7ac40e55 Mon Sep 17 00:00:00 2001 From: John Sullivan Date: Sat, 24 Aug 2019 16:18:51 -0700 Subject: [PATCH] Show text cursor when pressing shift in mouse mode Fixes #2550. --- CHANGELOG.md | 1 + alacritty_terminal/src/ansi.rs | 10 +-- alacritty_terminal/src/event.rs | 21 +++-- alacritty_terminal/src/input.rs | 130 +++++++++++++++++++++-------- alacritty_terminal/src/term/mod.rs | 15 +--- 5 files changed, 112 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4389ce29..c3836221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - URL parser incorrectly handling Markdown URLs and angled brackets - Intermediate bytes of CSI sequences not checked - Wayland clipboard integration +- Use text mouse cursor when mouse mode is temporarily disabled with shift ## 0.3.3 diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index 3189b300..eb8283a6 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -52,11 +52,7 @@ fn parse_rgb_color(color: &[u8]) -> Option { Some((255 * value / max) as u8) }; - Some(Rgb { - r: scale(colors[0])?, - g: scale(colors[1])?, - b: scale(colors[2])?, - }) + Some(Rgb { r: scale(colors[0])?, g: scale(colors[1])?, b: scale(colors[2])? }) } // Parse colors in `#r(rrr)g(ggg)b(bbb)` format @@ -1449,8 +1445,8 @@ pub mod C1 { #[cfg(test)] mod tests { use super::{ - parse_number, xparse_color, Attr, CharsetIndex, Color, Handler, Processor, - StandardCharset, TermInfo, + parse_number, xparse_color, Attr, CharsetIndex, Color, Handler, Processor, StandardCharset, + TermInfo, }; use crate::index::{Column, Line}; use crate::term::color::Rgb; diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index d3daf820..9d2aa78c 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -7,7 +7,7 @@ use std::sync::mpsc; use std::time::Instant; use glutin::dpi::PhysicalSize; -use glutin::{self, ElementState, Event, ModifiersState, MouseButton}; +use glutin::{self, ElementState, Event, MouseButton}; use parking_lot::MutexGuard; use crate::clipboard::ClipboardType; @@ -15,7 +15,7 @@ use crate::config::{self, Config, StartupMode}; use crate::display::OnResize; use crate::grid::Scroll; use crate::index::{Column, Line, Point, Side}; -use crate::input::{self, KeyBinding, MouseBinding}; +use crate::input::{self, KeyBinding, Modifiers, MouseBinding}; use crate::selection::Selection; use crate::sync::FairMutex; use crate::term::{SizeInfo, Term}; @@ -39,7 +39,7 @@ pub struct ActionContext<'a, N> { pub mouse: &'a mut Mouse, pub received_count: &'a mut usize, pub suppress_chars: &'a mut bool, - pub last_modifiers: &'a mut ModifiersState, + pub modifiers: &'a mut Modifiers, pub window_changes: &'a mut WindowChanges, } @@ -141,8 +141,8 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { } #[inline] - fn last_modifiers(&mut self) -> &mut ModifiersState { - &mut self.last_modifiers + fn modifiers(&mut self) -> &mut Modifiers { + &mut self.modifiers } #[inline] @@ -287,7 +287,7 @@ pub struct Processor { hide_mouse: bool, received_count: usize, suppress_chars: bool, - last_modifiers: ModifiersState, + modifiers: Modifiers, pending_events: Vec, window_changes: WindowChanges, save_to_clipboard: bool, @@ -331,7 +331,7 @@ impl Processor { hide_mouse: false, received_count: 0, suppress_chars: false, - last_modifiers: Default::default(), + modifiers: Default::default(), pending_events: Vec::with_capacity(4), window_changes: Default::default(), save_to_clipboard: config.selection.save_to_clipboard, @@ -406,11 +406,9 @@ impl Processor { *window_is_focused = is_focused; if is_focused { - processor.ctx.terminal.dirty = true; processor.ctx.terminal.next_is_urgent = Some(false); + processor.ctx.terminal.dirty = true; } else { - processor.ctx.terminal.reset_url_highlight(); - processor.ctx.terminal.reset_mouse_cursor(); processor.ctx.terminal.dirty = true; *hide_mouse = false; } @@ -426,6 +424,7 @@ impl Processor { processor.ctx.size_info.dpr = new_dpr; processor.ctx.terminal.dirty = true; }, + CursorLeft { .. } => processor.ctx.terminal.reset_url_highlight(), _ => (), } }, @@ -478,7 +477,7 @@ impl Processor { size_info: &mut self.size_info, received_count: &mut self.received_count, suppress_chars: &mut self.suppress_chars, - last_modifiers: &mut self.last_modifiers, + modifiers: &mut self.modifiers, window_changes: &mut self.window_changes, }; diff --git a/alacritty_terminal/src/input.rs b/alacritty_terminal/src/input.rs index 5784d9c2..3144e23f 100644 --- a/alacritty_terminal/src/input.rs +++ b/alacritty_terminal/src/input.rs @@ -25,7 +25,7 @@ use std::time::Instant; use glutin::{ ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta, - TouchPhase, + TouchPhase, VirtualKeyCode, }; use crate::ansi::{ClearMode, Handler}; @@ -73,7 +73,7 @@ pub trait ActionContext { fn mouse_coords(&self) -> Option; fn received_count(&mut self) -> &mut usize; fn suppress_chars(&mut self) -> &mut bool; - fn last_modifiers(&mut self) -> &mut ModifiersState; + fn modifiers(&mut self) -> &mut Modifiers; fn scroll(&mut self, scroll: Scroll); fn hide_window(&mut self); fn terminal(&self) -> &Term; @@ -84,6 +84,47 @@ pub trait ActionContext { fn toggle_simple_fullscreen(&mut self); } +#[derive(Debug, Default, Copy, Clone)] +pub struct Modifiers { + mods: ModifiersState, + lshift: bool, + rshift: bool, +} + +impl Modifiers { + pub fn update(&mut self, input: KeyboardInput) { + match input.virtual_keycode { + Some(VirtualKeyCode::LShift) => self.lshift = input.state == ElementState::Pressed, + Some(VirtualKeyCode::RShift) => self.rshift = input.state == ElementState::Pressed, + _ => (), + } + + self.mods = input.modifiers; + } + + pub fn shift(self) -> bool { + self.lshift || self.rshift + } + + pub fn ctrl(self) -> bool { + self.mods.ctrl + } + + pub fn logo(self) -> bool { + self.mods.logo + } + + pub fn alt(self) -> bool { + self.mods.alt + } +} + +impl From for ModifiersState { + fn from(mods: Modifiers) -> ModifiersState { + ModifiersState { shift: mods.shift(), ..mods.mods } + } +} + /// Describes a state and action to take in that state /// /// This is the shared component of `MouseBinding` and `KeyBinding` @@ -381,41 +422,48 @@ impl From<&'static str> for Action { } } -enum MousePosition { +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum MouseState { Url(Url), MessageBar, MessageBarButton, - Terminal, + Mouse, + Text, } impl<'a, A: ActionContext + 'a> Processor<'a, A> { - fn mouse_position(&mut self, point: Point, modifiers: ModifiersState) -> MousePosition { + fn mouse_state(&mut self, point: Point) -> MouseState { let mouse_mode = TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG | TermMode::MOUSE_REPORT_CLICK; // Check message bar before URL to ignore URLs in the message bar if let Some(message) = self.message_at_point(Some(point)) { if self.message_close_at_point(point, message) { - return MousePosition::MessageBarButton; + return MouseState::MessageBarButton; } else { - return MousePosition::MessageBar; + return MouseState::MessageBar; } } // Check for URL at point with required modifiers held - if self.mouse_config.url.mods().relaxed_eq(modifiers) - && (!self.ctx.terminal().mode().intersects(mouse_mode) || modifiers.shift) + let mods = *self.ctx.modifiers(); + if self.mouse_config.url.mods().relaxed_eq(mods.into()) + && (!self.ctx.terminal().mode().intersects(mouse_mode) || mods.shift()) && self.mouse_config.url.launcher.is_some() { let buffer_point = self.ctx.terminal().visible_to_buffer(point); if let Some(url) = self.ctx.terminal().urls().drain(..).find(|url| url.contains(buffer_point)) { - return MousePosition::Url(url); + return MouseState::Url(url); } } - MousePosition::Terminal + if self.ctx.terminal().mode().intersects(mouse_mode) && !self.ctx.modifiers().shift() { + MouseState::Mouse + } else { + MouseState::Text + } } #[inline] @@ -445,27 +493,18 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { // Don't launch URLs if mouse has moved self.ctx.mouse_mut().block_url_launcher = true; - match self.mouse_position(point, modifiers) { - MousePosition::Url(url) => { + let mouse_state = self.mouse_state(point); + self.update_mouse_cursor(mouse_state); + match mouse_state { + MouseState::Url(url) => { let url_bounds = url.linear_bounds(self.ctx.terminal()); self.ctx.terminal_mut().set_url_highlight(url_bounds); - self.ctx.terminal_mut().set_mouse_cursor(MouseCursor::Hand); - self.ctx.terminal_mut().dirty = true; }, - MousePosition::MessageBar => { + MouseState::MessageBar | MouseState::MessageBarButton => { self.ctx.terminal_mut().reset_url_highlight(); - self.ctx.terminal_mut().set_mouse_cursor(MouseCursor::Default); return; }, - MousePosition::MessageBarButton => { - self.ctx.terminal_mut().reset_url_highlight(); - self.ctx.terminal_mut().set_mouse_cursor(MouseCursor::Hand); - return; - }, - MousePosition::Terminal => { - self.ctx.terminal_mut().reset_url_highlight(); - self.ctx.terminal_mut().reset_mouse_cursor(); - }, + _ => self.ctx.terminal_mut().reset_url_highlight(), } if self.ctx.mouse().left_button_state == ElementState::Pressed @@ -798,9 +837,20 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { /// Process key input pub fn process_key(&mut self, input: KeyboardInput) { + self.ctx.modifiers().update(input); + + // Update mouse cursor for temporarily disabling mouse mode + if input.virtual_keycode == Some(VirtualKeyCode::LShift) + || input.virtual_keycode == Some(VirtualKeyCode::RShift) + { + if let Some(point) = self.ctx.mouse_coords() { + let mouse_state = self.mouse_state(point); + self.update_mouse_cursor(mouse_state); + } + } + match input.state { ElementState::Pressed => { - *self.ctx.last_modifiers() = input.modifiers; *self.ctx.received_count() = 0; *self.ctx.suppress_chars() = false; @@ -830,7 +880,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { if self.alt_send_esc && *self.ctx.received_count() == 0 - && self.ctx.last_modifiers().alt + && self.ctx.modifiers().alt() && utf8_len == 1 { bytes.insert(0, b'\x1b'); @@ -934,8 +984,9 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { ElementState::Released => self.copy_selection(), ElementState::Pressed => { if self.message_close_at_point(point, message) { + let mouse_state = self.mouse_state(point); + self.update_mouse_cursor(mouse_state); self.ctx.terminal_mut().message_buffer_mut().pop(); - self.ctx.terminal_mut().reset_mouse_cursor(); } self.ctx.clear_selection(); @@ -950,6 +1001,17 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { } self.ctx.copy_selection(ClipboardType::Selection); } + + #[inline] + fn update_mouse_cursor(&mut self, mouse_state: MouseState) { + let mouse_cursor = match mouse_state { + MouseState::Url(_) | MouseState::MessageBarButton => MouseCursor::Hand, + MouseState::Text => MouseCursor::Text, + _ => MouseCursor::Default, + }; + + self.ctx.terminal_mut().set_mouse_cursor(mouse_cursor); + } } #[cfg(test)] @@ -968,7 +1030,7 @@ mod tests { use crate::selection::Selection; use crate::term::{SizeInfo, Term, TermMode}; - use super::{Action, Binding, Processor}; + use super::{Action, Binding, Modifiers, Processor}; const KEY: VirtualKeyCode = VirtualKeyCode::Key0; @@ -1112,7 +1174,7 @@ mod tests { pub last_action: MultiClick, pub received_count: usize, pub suppress_chars: bool, - pub last_modifiers: ModifiersState, + pub modifiers: Modifiers, pub window_changes: &'a mut WindowChanges, } @@ -1189,8 +1251,8 @@ mod tests { &mut self.suppress_chars } - fn last_modifiers(&mut self) -> &mut ModifiersState { - &mut self.last_modifiers + fn modifiers(&mut self) -> &mut Modifiers { + &mut self.modifiers } } @@ -1232,7 +1294,7 @@ mod tests { last_action: MultiClick::None, received_count: 0, suppress_chars: false, - last_modifiers: ModifiersState::default(), + modifiers: Default::default(), window_changes: &mut WindowChanges::default(), }; diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 2d1ec392..042ad1d0 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -841,9 +841,9 @@ impl Term { #[inline] pub fn scroll_display(&mut self, scroll: Scroll) { + self.set_mouse_cursor(MouseCursor::Text); self.grid.scroll_display(scroll); self.reset_url_highlight(); - self.reset_mouse_cursor(); self.dirty = true; } @@ -1296,18 +1296,7 @@ impl Term { #[inline] pub fn set_url_highlight(&mut self, hl: RangeInclusive) { self.grid.url_highlight = Some(hl); - } - - #[inline] - pub fn reset_mouse_cursor(&mut self) { - let mouse_mode = - TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG | TermMode::MOUSE_REPORT_CLICK; - let mouse_cursor = if self.mode().intersects(mouse_mode) { - MouseCursor::Default - } else { - MouseCursor::Text - }; - self.set_mouse_cursor(mouse_cursor); + self.dirty = true; } #[inline]