feature(mouse): forward mouse events (#1191)

* handle sgr mouse mode enable by applications

* forward mouse events

* fix scroll events not forwarded to floating panes

* improve mouse hold/release with floating panes
This commit is contained in:
Thomas Linford 2022-03-10 13:14:02 +01:00 committed by GitHub
parent c38981c655
commit 9961a28cb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 30 deletions

View file

@ -306,6 +306,7 @@ pub struct Grid {
pub link_handler: Rc<RefCell<LinkHandler>>, pub link_handler: Rc<RefCell<LinkHandler>>,
pub ring_bell: bool, pub ring_bell: bool,
scrollback_buffer_lines: usize, scrollback_buffer_lines: usize,
pub mouse_mode: bool,
} }
impl Debug for Grid { impl Debug for Grid {
@ -363,6 +364,7 @@ impl Grid {
link_handler, link_handler,
ring_bell: false, ring_bell: false,
scrollback_buffer_lines: 0, scrollback_buffer_lines: 0,
mouse_mode: false,
} }
} }
pub fn render_full_viewport(&mut self) { pub fn render_full_viewport(&mut self) {
@ -1773,6 +1775,9 @@ impl Perform for Grid {
Some(7) => { Some(7) => {
self.disable_linewrap = true; self.disable_linewrap = true;
} }
Some(1006) => {
self.mouse_mode = false;
}
_ => {} _ => {}
}; };
} else if let Some(4) = params_iter.next().map(|param| param[0]) { } else if let Some(4) = params_iter.next().map(|param| param[0]) {
@ -1826,6 +1831,9 @@ impl Perform for Grid {
Some(7) => { Some(7) => {
self.disable_linewrap = false; self.disable_linewrap = false;
} }
Some(1006) => {
self.mouse_mode = true;
}
_ => {} _ => {}
}; };
} else if let Some(4) = params_iter.next().map(|param| param[0]) { } else if let Some(4) = params_iter.next().map(|param| param[0]) {

View file

@ -401,4 +401,7 @@ impl Pane for PluginPane {
)) ))
.unwrap(); .unwrap();
} }
fn mouse_mode(&self) -> bool {
false
}
} }

View file

@ -472,6 +472,10 @@ impl Pane for TerminalPane {
fn borderless(&self) -> bool { fn borderless(&self) -> bool {
self.borderless self.borderless
} }
fn mouse_mode(&self) -> bool {
self.grid.mouse_mode
}
} }
impl TerminalPane { impl TerminalPane {

View file

@ -289,6 +289,7 @@ pub trait Pane {
fn set_borderless(&mut self, borderless: bool); fn set_borderless(&mut self, borderless: bool);
fn borderless(&self) -> bool; fn borderless(&self) -> bool;
fn handle_right_click(&mut self, _to: &Position, _client_id: ClientId) {} fn handle_right_click(&mut self, _to: &Position, _client_id: ClientId) {}
fn mouse_mode(&self) -> bool;
} }
impl Tab { impl Tab {
@ -975,6 +976,20 @@ impl Tab {
}; };
self.write_to_pane_id(input_bytes, pane_id); self.write_to_pane_id(input_bytes, pane_id);
} }
pub fn write_to_terminal_at(&mut self, input_bytes: Vec<u8>, position: &Position) {
if self.floating_panes.panes_are_visible() {
let pane_id = self.floating_panes.get_pane_id_at(position, false);
if let Some(pane_id) = pane_id {
self.write_to_pane_id(input_bytes, pane_id);
return;
}
}
let pane_id = self.get_pane_id_at(position, false);
if let Some(pane_id) = pane_id {
self.write_to_pane_id(input_bytes, pane_id);
}
}
pub fn write_to_pane_id(&mut self, input_bytes: Vec<u8>, pane_id: PaneId) { pub fn write_to_pane_id(&mut self, input_bytes: Vec<u8>, pane_id: PaneId) {
match pane_id { match pane_id {
PaneId::Terminal(active_terminal_id) => { PaneId::Terminal(active_terminal_id) => {
@ -2288,11 +2303,30 @@ impl Tab {
} }
pub fn scroll_terminal_up(&mut self, point: &Position, lines: usize, client_id: ClientId) { pub fn scroll_terminal_up(&mut self, point: &Position, lines: usize, client_id: ClientId) {
if let Some(pane) = self.get_pane_at(point, false) { if let Some(pane) = self.get_pane_at(point, false) {
if pane.mouse_mode() {
let relative_position = pane.relative_position(point);
let mouse_event = format!(
"\u{1b}[<64;{:?};{:?}M",
relative_position.column.0 + 1,
relative_position.line.0 + 1
);
self.write_to_terminal_at(mouse_event.into_bytes(), point);
} else {
pane.scroll_up(lines, client_id); pane.scroll_up(lines, client_id);
} }
} }
}
pub fn scroll_terminal_down(&mut self, point: &Position, lines: usize, client_id: ClientId) { pub fn scroll_terminal_down(&mut self, point: &Position, lines: usize, client_id: ClientId) {
if let Some(pane) = self.get_pane_at(point, false) { if let Some(pane) = self.get_pane_at(point, false) {
if pane.mouse_mode() {
let relative_position = pane.relative_position(point);
let mouse_event = format!(
"\u{1b}[<65;{:?};{:?}M",
relative_position.column.0 + 1,
relative_position.line.0 + 1
);
self.write_to_terminal_at(mouse_event.into_bytes(), point);
} else {
pane.scroll_down(lines, client_id); pane.scroll_down(lines, client_id);
if !pane.is_scrolled() { if !pane.is_scrolled() {
if let PaneId::Terminal(pid) = pane.pid() { if let PaneId::Terminal(pid) = pane.pid() {
@ -2301,6 +2335,7 @@ impl Tab {
} }
} }
} }
}
fn get_pane_at( fn get_pane_at(
&mut self, &mut self,
point: &Position, point: &Position,
@ -2348,8 +2383,18 @@ impl Tab {
if let Some(pane) = self.get_pane_at(position, false) { if let Some(pane) = self.get_pane_at(position, false) {
let relative_position = pane.relative_position(position); let relative_position = pane.relative_position(position);
if pane.mouse_mode() {
let mouse_event = format!(
"\u{1b}[<0;{:?};{:?}M",
relative_position.column.0 + 1,
relative_position.line.0 + 1
);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
} else {
pane.start_selection(&relative_position, client_id); pane.start_selection(&relative_position, client_id);
self.selecting_with_mouse = true; self.selecting_with_mouse = true;
}
}; };
} }
pub fn handle_right_click(&mut self, position: &Position, client_id: ClientId) { pub fn handle_right_click(&mut self, position: &Position, client_id: ClientId) {
@ -2357,7 +2402,16 @@ impl Tab {
if let Some(pane) = self.get_pane_at(position, false) { if let Some(pane) = self.get_pane_at(position, false) {
let relative_position = pane.relative_position(position); let relative_position = pane.relative_position(position);
if pane.mouse_mode() {
let mouse_event = format!(
"\u{1b}[<2;{:?};{:?}M",
relative_position.column.0 + 1,
relative_position.line.0 + 1
);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
} else {
pane.handle_right_click(&relative_position, client_id); pane.handle_right_click(&relative_position, client_id);
}
}; };
} }
fn focus_pane_at(&mut self, point: &Position, client_id: ClientId) { fn focus_pane_at(&mut self, point: &Position, client_id: ClientId) {
@ -2392,38 +2446,73 @@ impl Tab {
} }
} }
pub fn handle_mouse_release(&mut self, position: &Position, client_id: ClientId) { pub fn handle_mouse_release(&mut self, position: &Position, client_id: ClientId) {
if self.selecting_with_mouse { if self.floating_panes.panes_are_visible()
let mut selected_text = None; && self.floating_panes.pane_is_being_moved_with_mouse()
let active_pane = self.get_active_pane_or_floating_pane_mut(client_id); {
if let Some(active_pane) = active_pane { self.floating_panes.stop_moving_pane_with_mouse(*position);
let relative_position = active_pane.relative_position(position); return;
active_pane.end_selection(&relative_position, client_id);
selected_text = active_pane.get_selected_text();
active_pane.reset_selection();
} }
let selecting = self.selecting_with_mouse;
let active_pane = self.get_active_pane_or_floating_pane_mut(client_id);
if let Some(active_pane) = active_pane {
let relative_position = active_pane.relative_position(position);
if active_pane.mouse_mode() {
// ensure that coordinates are valid
let col = (relative_position.column.0 + 1)
.max(1)
.min(active_pane.get_content_columns());
let line = (relative_position.line.0 + 1)
.max(1)
.min(active_pane.get_content_rows() as isize);
let mouse_event = format!("\u{1b}[<0;{:?};{:?}m", col, line);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
} else if selecting {
active_pane.end_selection(&relative_position, client_id);
let selected_text = active_pane.get_selected_text();
active_pane.reset_selection();
if let Some(selected_text) = selected_text { if let Some(selected_text) = selected_text {
self.write_selection_to_clipboard(&selected_text); self.write_selection_to_clipboard(&selected_text);
} }
self.selecting_with_mouse = false; self.selecting_with_mouse = false;
} else if self.floating_panes.panes_are_visible() { }
self.floating_panes.stop_moving_pane_with_mouse(*position);
} }
} }
pub fn handle_mouse_hold(&mut self, position_on_screen: &Position, client_id: ClientId) { pub fn handle_mouse_hold(&mut self, position_on_screen: &Position, client_id: ClientId) {
let search_selectable = true; let search_selectable = true;
if self.selecting_with_mouse {
let active_pane = self.get_active_pane_or_floating_pane_mut(client_id); if self.floating_panes.panes_are_visible()
if let Some(active_pane) = active_pane { && self.floating_panes.pane_is_being_moved_with_mouse()
let relative_position = active_pane.relative_position(position_on_screen);
active_pane.update_selection(&relative_position, client_id);
}
} else if self.floating_panes.panes_are_visible()
&& self && self
.floating_panes .floating_panes
.move_pane_with_mouse(*position_on_screen, search_selectable) .move_pane_with_mouse(*position_on_screen, search_selectable)
{ {
self.set_force_render(); self.set_force_render();
return;
}
let selecting = self.selecting_with_mouse;
let active_pane = self.get_active_pane_or_floating_pane_mut(client_id);
if let Some(active_pane) = active_pane {
let relative_position = active_pane.relative_position(position_on_screen);
if active_pane.mouse_mode() {
// ensure that coordinates are valid
let col = (relative_position.column.0 + 1)
.max(1)
.min(active_pane.get_content_columns());
let line = (relative_position.line.0 + 1)
.max(1)
.min(active_pane.get_content_rows() as isize);
let mouse_event = format!("\u{1b}[<32;{:?};{:?}M", col, line);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
} else if selecting {
active_pane.update_selection(&relative_position, client_id);
}
} }
} }