From 82bfc41af7d6eff384bdf95cedaf0d62b5450979 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Sun, 1 Jan 2017 18:28:49 -0800 Subject: [PATCH] Add support for SGR mouse reporting According to: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking --- src/ansi.rs | 6 +++++ src/index.rs | 4 +-- src/input.rs | 66 ++++++++++++++++++++++++++++++++----------------- src/term/mod.rs | 6 +++++ 4 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/ansi.rs b/src/ansi.rs index 633a7e54..e5c129e3 100644 --- a/src/ansi.rs +++ b/src/ansi.rs @@ -249,6 +249,10 @@ pub enum Mode { ShowCursor = 25, /// ?1000 ReportMouseClicks = 1000, + /// ?1002 + ReportMouseMotion = 1002, + /// ?1006 + SgrMouse = 1006, /// ?1049 SwapScreenAndSetRestoreCursor = 1049, /// ?2004 @@ -267,6 +271,8 @@ impl Mode { 12 => Mode::BlinkingCursor, 25 => Mode::ShowCursor, 1000 => Mode::ReportMouseClicks, + 1002 => Mode::ReportMouseMotion, + 1006 => Mode::SgrMouse, 1049 => Mode::SwapScreenAndSetRestoreCursor, 2004 => Mode::BracketedPaste, _ => return None diff --git a/src/index.rs b/src/index.rs index 7cb6ee24..c80589da 100644 --- a/src/index.rs +++ b/src/index.rs @@ -62,7 +62,7 @@ pub struct Line(pub usize); impl fmt::Display for Line { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Line({})", self.0) + write!(f, "{}", self.0) } } @@ -74,7 +74,7 @@ pub struct Column(pub usize); impl fmt::Display for Column { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Column({})", self.0) + write!(f, "{}", self.0) } } diff --git a/src/input.rs b/src/input.rs index 60183098..960bc017 100644 --- a/src/input.rs +++ b/src/input.rs @@ -18,11 +18,8 @@ //! In order to figure that out, state about which modifier keys are pressed //! needs to be tracked. Additionally, we need a bit of a state machine to //! determine what to do when a non-modifier key is pressed. -//! -//! TODO would be nice to generalize this so it could work with other windowing -//! APIs -//! -//! TODO handling xmodmap would be good +use std::mem; + use copypasta::{Clipboard, Load, Store}; use glutin::{ElementState, VirtualKeyCode, MouseButton}; use glutin::{Mods, mods}; @@ -188,15 +185,12 @@ impl From<&'static str> for Action { impl<'a, N: Notify + 'a> Processor<'a, N> { #[inline] pub fn mouse_moved(&mut self, x: u32, y: u32) { - // Record mouse position within window. Pixel coordinates are *not* - // translated to grid coordinates here since grid coordinates are rarely - // needed and the mouse position updates frequently. self.ctx.mouse.x = x; self.ctx.mouse.y = y; if let Some(point) = self.ctx.size_info.pixels_to_coords(x as usize, y as usize) { - self.ctx.mouse.line = point.line; - self.ctx.mouse.column = point.col; + let prev_line = mem::replace(&mut self.ctx.mouse.line, point.line); + let prev_col = mem::replace(&mut self.ctx.mouse.column, point.col); let cell_x = x as usize % self.ctx.size_info.cell_width as usize; let half_cell_width = (self.ctx.size_info.cell_width / 2.0) as usize; @@ -207,18 +201,28 @@ impl<'a, N: Notify + 'a> Processor<'a, N> { Side::Left }; - if self.ctx.mouse.left_button_state == ElementState::Pressed && - !self.ctx.terminal.mode().contains(mode::MOUSE_REPORT_CLICK) - { - self.ctx.selection.update(Point { - line: point.line, - col: point.col - }, self.ctx.mouse.cell_side); + if self.ctx.mouse.left_button_state == ElementState::Pressed { + let report_mode = mode::MOUSE_REPORT_CLICK | mode::MOUSE_MOTION; + if !self.ctx.terminal.mode().intersects(report_mode) { + self.ctx.selection.update(Point { + line: point.line, + col: point.col + }, self.ctx.mouse.cell_side); + } else if self.ctx.terminal.mode().contains(mode::MOUSE_MOTION) { + // Only report motion when changing cells + if prev_line != self.ctx.mouse.line || prev_col != self.ctx.mouse.column { + self.mouse_report(0 + 32); + } + } } } } - pub fn mouse_report(&mut self, button: u8) { + pub fn mouse_moved_cells(&mut self) { + + } + + pub fn normal_mouse_report(&mut self, button: u8) { let (line, column) = (self.ctx.mouse.line, self.ctx.mouse.column); if line < Line(223) && column < Column(223) { @@ -235,8 +239,25 @@ impl<'a, N: Notify + 'a> Processor<'a, N> { } } + pub fn sgr_mouse_report(&mut self, button: u8, release: bool) { + let (line, column) = (self.ctx.mouse.line, self.ctx.mouse.column); + let c = if release { 'm' } else { 'M' }; + + let msg = format!("\x1b[<{};{};{}{}", button, column + 1, line + 1, c); + self.ctx.notifier.notify(msg.into_bytes()); + } + + pub fn mouse_report(&mut self, button: u8) { + if self.ctx.terminal.mode().contains(mode::SGR_MOUSE) { + let release = self.ctx.mouse.left_button_state != ElementState::Pressed; + self.sgr_mouse_report(button, release); + } else { + self.normal_mouse_report(button); + } + } + pub fn on_mouse_press(&mut self) { - if self.ctx.terminal.mode().contains(mode::MOUSE_REPORT_CLICK) { + if self.ctx.terminal.mode().intersects(mode::MOUSE_REPORT_CLICK | mode::MOUSE_MOTION) { self.mouse_report(0); return; } @@ -245,7 +266,7 @@ impl<'a, N: Notify + 'a> Processor<'a, N> { } pub fn on_mouse_release(&mut self) { - if self.ctx.terminal.mode().contains(mode::MOUSE_REPORT_CLICK) { + if self.ctx.terminal.mode().intersects(mode::MOUSE_REPORT_CLICK | mode::MOUSE_MOTION) { self.mouse_report(3); return; } @@ -304,10 +325,9 @@ impl<'a, N: Notify + 'a> Processor<'a, N> { pub fn mouse_input(&mut self, state: ElementState, button: MouseButton) { if let MouseButton::Left = button { - // TODO handle state changes + let state = mem::replace(&mut self.ctx.mouse.left_button_state, state); if self.ctx.mouse.left_button_state != state { - self.ctx.mouse.left_button_state = state; - match state { + match self.ctx.mouse.left_button_state { ElementState::Pressed => { self.on_mouse_press(); }, diff --git a/src/term/mod.rs b/src/term/mod.rs index 319f7520..c93ec963 100644 --- a/src/term/mod.rs +++ b/src/term/mod.rs @@ -180,6 +180,8 @@ pub mod mode { const APP_KEYPAD = 0b00000100, const MOUSE_REPORT_CLICK = 0b00001000, const BRACKETED_PASTE = 0b00010000, + const SGR_MOUSE = 0b00100000, + const MOUSE_MOTION = 0b01000000, const ANY = 0b11111111, const NONE = 0b00000000, } @@ -987,7 +989,9 @@ impl ansi::Handler for Term { ansi::Mode::ShowCursor => self.mode.insert(mode::SHOW_CURSOR), ansi::Mode::CursorKeys => self.mode.insert(mode::APP_CURSOR), ansi::Mode::ReportMouseClicks => self.mode.insert(mode::MOUSE_REPORT_CLICK), + ansi::Mode::ReportMouseMotion => self.mode.insert(mode::MOUSE_MOTION), ansi::Mode::BracketedPaste => self.mode.insert(mode::BRACKETED_PASTE), + ansi::Mode::SgrMouse => self.mode.insert(mode::SGR_MOUSE), _ => { debug_println!(".. ignoring set_mode"); } @@ -1002,7 +1006,9 @@ impl ansi::Handler for Term { ansi::Mode::ShowCursor => self.mode.remove(mode::SHOW_CURSOR), ansi::Mode::CursorKeys => self.mode.remove(mode::APP_CURSOR), ansi::Mode::ReportMouseClicks => self.mode.remove(mode::MOUSE_REPORT_CLICK), + ansi::Mode::ReportMouseMotion => self.mode.remove(mode::MOUSE_MOTION), ansi::Mode::BracketedPaste => self.mode.remove(mode::BRACKETED_PASTE), + ansi::Mode::SgrMouse => self.mode.remove(mode::SGR_MOUSE), _ => { debug_println!(".. ignoring unset_mode"); }