From 302db2c9764375fb524e20d1a3406fcc92358e77 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Sat, 15 Jun 2019 13:48:28 -0700 Subject: [PATCH] a lighter way to pass selection ranges for client tabs --- src/frontend/guicommon/localtab.rs | 9 ++++++++ src/mux/tab.rs | 6 ++++++ src/server/codec.rs | 2 ++ src/server/listener.rs | 10 +++++---- src/server/tab.rs | 17 ++++++++++++++- term/src/selection.rs | 34 +++++++++++++++++++++++++++--- term/src/terminalstate.rs | 4 ++++ 7 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/frontend/guicommon/localtab.rs b/src/frontend/guicommon/localtab.rs index 234f91658..b262e536b 100644 --- a/src/frontend/guicommon/localtab.rs +++ b/src/frontend/guicommon/localtab.rs @@ -5,6 +5,7 @@ use failure::Error; use portable_pty::{Child, MasterPty, PtySize}; use std::cell::{RefCell, RefMut}; use term::color::ColorPalette; +use term::selection::SelectionRange; use term::{KeyCode, KeyModifiers, MouseEvent, Terminal, TerminalHost}; pub struct LocalTab { @@ -81,6 +82,14 @@ impl Tab for LocalTab { fn domain_id(&self) -> DomainId { self.domain_id } + + fn selection_range(&self) -> Option { + let terminal = self.terminal.borrow(); + let rows = terminal.screen().physical_rows; + terminal + .selection_range() + .map(|r| r.clip_to_viewport(terminal.get_viewport_offset(), rows)) + } } impl LocalTab { diff --git a/src/mux/tab.rs b/src/mux/tab.rs index dc82ae98a..88f31b240 100644 --- a/src/mux/tab.rs +++ b/src/mux/tab.rs @@ -5,6 +5,7 @@ use failure::Fallible; use portable_pty::PtySize; use std::cell::RefMut; use term::color::ColorPalette; +use term::selection::SelectionRange; use term::{KeyCode, KeyModifiers, MouseEvent, TerminalHost}; static TAB_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(0); @@ -28,5 +29,10 @@ pub trait Tab: Downcast { fn is_dead(&self) -> bool; fn palette(&self) -> ColorPalette; fn domain_id(&self) -> DomainId; + + /// Returns the selection range adjusted to the viewport + /// (eg: it has been normalized and had clip_to_viewport called + /// on it prior to being returned) + fn selection_range(&self) -> Option; } impl_downcast!(Tab); diff --git a/src/server/codec.rs b/src/server/codec.rs index 9e2c933f2..7e1036517 100644 --- a/src/server/codec.rs +++ b/src/server/codec.rs @@ -19,6 +19,7 @@ use log::debug; use portable_pty::{CommandBuilder, PtySize}; use serde_derive::*; use std::sync::Arc; +use term::selection::SelectionRange; use term::{CursorPosition, Line}; use termwiz::hyperlink::Hyperlink; use termwiz::surface::{Change, SequenceNo}; @@ -330,6 +331,7 @@ pub struct SendMouseEvent { #[derive(Deserialize, Serialize, PartialEq, Debug)] pub struct SendMouseEventResponse { pub clipboard: Option, + pub selection_range: Option, } #[derive(Deserialize, Serialize, PartialEq, Debug)] diff --git a/src/server/listener.rs b/src/server/listener.rs index 35eda6037..73c94b88f 100644 --- a/src/server/listener.rs +++ b/src/server/listener.rs @@ -383,7 +383,7 @@ impl ClientSession { Pdu::UnitResponse(UnitResponse {}) } Pdu::SendMouseEvent(SendMouseEvent { tab_id, event }) => { - let clipboard = Future::with_executor(self.executor.clone_executor(), move || { + Future::with_executor(self.executor.clone_executor(), move || { let mux = Mux::get().unwrap(); let tab = mux .get_tab(tab_id) @@ -394,10 +394,12 @@ impl ClientSession { title: None, }; tab.mouse_event(event, &mut host)?; - Ok(host.clipboard) + Ok(Pdu::SendMouseEventResponse(SendMouseEventResponse { + clipboard: host.clipboard, + selection_range: tab.selection_range(), + })) }) - .wait()?; - Pdu::SendMouseEventResponse(SendMouseEventResponse { clipboard }) + .wait()? } Pdu::Spawn(spawn) => { diff --git a/src/server/tab.rs b/src/server/tab.rs index 5a5571b76..57945a615 100644 --- a/src/server/tab.rs +++ b/src/server/tab.rs @@ -14,6 +14,7 @@ use std::ops::Range; use std::sync::Arc; use std::time::{Duration, Instant}; use term::color::ColorPalette; +use term::selection::SelectionRange; use term::{CursorPosition, Line}; use term::{KeyCode, KeyModifiers, MouseEvent, TerminalHost}; use termwiz::hyperlink::Hyperlink; @@ -45,6 +46,7 @@ impl ClientTab { surface: RefCell::new(Surface::new(80, 24)), remote_sequence: RefCell::new(0), local_sequence: RefCell::new(0), + selection_range: RefCell::new(None), }; let reader = Pipe::new().expect("Pipe::new failed"); @@ -130,6 +132,7 @@ impl Tab for ClientTab { if resp.clipboard.is_some() { host.set_clipboard(resp.clipboard)?; } + *self.renderable.borrow().selection_range.borrow_mut() = resp.selection_range; Ok(()) } @@ -153,6 +156,10 @@ impl Tab for ClientTab { fn domain_id(&self) -> DomainId { self.client.local_domain_id } + + fn selection_range(&self) -> Option { + self.renderable.borrow().selection_range.borrow().clone() + } } struct RenderableState { @@ -164,6 +171,7 @@ struct RenderableState { surface: RefCell, remote_sequence: RefCell, local_sequence: RefCell, + selection_range: RefCell>, } const POLL_INTERVAL: Duration = Duration::from_millis(50); @@ -228,11 +236,18 @@ impl Renderable for RenderableState { let mut surface = self.surface.borrow_mut(); let seq = surface.current_seqno(); surface.flush_changes_older_than(seq); + let selection = *self.selection_range.borrow(); surface .screen_lines() .into_iter() .enumerate() - .map(|(idx, line)| (idx, line, 0..0)) + .map(|(idx, line)| { + let r = match selection { + None => 0..0, + Some(sel) => sel.cols_for_row(idx as i32), + }; + (idx, line, r) + }) .collect() } diff --git a/term/src/selection.rs b/term/src/selection.rs index bb3bd90a0..de95c0a6b 100644 --- a/term/src/selection.rs +++ b/term/src/selection.rs @@ -1,11 +1,12 @@ // The range_plus_one lint can't see when the LHS is not compatible with // and inclusive range #![cfg_attr(feature = "cargo-clippy", allow(clippy::range_plus_one))] -use super::ScrollbackOrVisibleRowIndex; +use super::{ScrollbackOrVisibleRowIndex, VisibleRowIndex}; +use serde_derive::*; use std::ops::Range; /// The x,y coordinates of either the start or end of a selection region -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct SelectionCoordinate { pub x: usize, pub y: ScrollbackOrVisibleRowIndex, @@ -13,7 +14,7 @@ pub struct SelectionCoordinate { /// Represents the selected text range. /// The end coordinates are inclusive. -#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct SelectionRange { pub start: SelectionCoordinate, pub end: SelectionCoordinate, @@ -26,6 +27,33 @@ impl SelectionRange { Self { start, end } } + /// Returns a modified version of the selection that is adjusted + /// for a Surface that holds only the visible viewport. + /// The y values are adjusted such that 0 indicates the top of + /// the viewport. + pub fn clip_to_viewport( + &self, + viewport_offset: VisibleRowIndex, + height: usize, + ) -> SelectionRange { + let offset = -viewport_offset as ScrollbackOrVisibleRowIndex; + let res = SelectionRange { + start: SelectionCoordinate { + x: self.start.x, + y: self.start.y.max(offset) - offset, + }, + end: SelectionCoordinate { + x: self.end.x, + y: self + .end + .y + .min(offset + height as ScrollbackOrVisibleRowIndex) + - offset, + }, + }; + res + } + /// Returns an extended selection that it ends at the specified location pub fn extend(&self, end: SelectionCoordinate) -> Self { Self { diff --git a/term/src/terminalstate.rs b/term/src/terminalstate.rs index 458fbf9f3..cb8c7c149 100644 --- a/term/src/terminalstate.rs +++ b/term/src/terminalstate.rs @@ -590,6 +590,10 @@ impl TerminalState { } } + pub fn selection_range(&self) -> Option { + self.selection_range.clone().map(|r| r.normalize()) + } + fn mouse_drag_left(&mut self, event: MouseEvent) -> Result<(), Error> { // dragging out the selection region // TODO: may drag and change the viewport