1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-10 15:04:32 +03:00

allow wheel events with no mods. allow mouse events in mouse reporting mode

Adjusts how mouse events are matched so that we can now indicate whether
mouse reporting and alt-screen should be considered as part of the event
trigger criteria.

refs: #2173
refs: #581
This commit is contained in:
Wez Furlong 2022-07-31 09:13:26 -07:00
parent 3db7e72e54
commit bd846975fa
10 changed files with 336 additions and 70 deletions

View File

@ -22,8 +22,8 @@ use crate::unix::UnixDomain;
use crate::wsl::WslDomain;
use crate::{
default_config_with_overrides_applied, default_one_point_oh, default_one_point_oh_f64,
default_true, KeyMapPreference, LoadedConfig, RgbaColor, CONFIG_DIR, CONFIG_FILE_OVERRIDE,
CONFIG_OVERRIDES, CONFIG_SKIP, HOME_DIR,
default_true, KeyMapPreference, LoadedConfig, MouseEventTriggerMods, RgbaColor, CONFIG_DIR,
CONFIG_FILE_OVERRIDE, CONFIG_OVERRIDES, CONFIG_SKIP, HOME_DIR,
};
use anyhow::Context;
use luahelper::impl_lua_conversion_dynamic;
@ -908,7 +908,9 @@ impl Config {
tables
}
pub fn mouse_bindings(&self) -> HashMap<(MouseEventTrigger, Modifiers), KeyAssignment> {
pub fn mouse_bindings(
&self,
) -> HashMap<(MouseEventTrigger, MouseEventTriggerMods), KeyAssignment> {
let mut map = HashMap::new();
for m in &self.mouse_bindings {

View File

@ -396,6 +396,7 @@ pub enum KeyAssignment {
MoveTab(usize),
ScrollByPage(NotNan<f64>),
ScrollByLine(isize),
ScrollByCurrentEventWheelDelta,
ScrollToPrompt(isize),
ScrollToTop,
ScrollToBottom,

View File

@ -1,6 +1,6 @@
use crate::keyassignment::{KeyAssignment, MouseEventTrigger};
use std::convert::TryFrom;
use wezterm_dynamic::{FromDynamic, ToDynamic};
use wezterm_dynamic::{Error as DynError, FromDynamic, FromDynamicOptions, ToDynamic, Value};
use wezterm_input_types::{KeyCode, Modifiers, PhysKeyCode};
#[derive(Debug, Clone, Copy, Eq, PartialEq, FromDynamic, ToDynamic)]
@ -140,7 +140,55 @@ fn default_leader_timeout() -> u64 {
#[derive(Debug, Clone, FromDynamic, ToDynamic)]
pub struct Mouse {
pub event: MouseEventTrigger,
#[dynamic(default, into = "String", try_from = "String")]
pub mods: Modifiers,
#[dynamic(flatten)]
pub mods: MouseEventTriggerMods,
pub action: KeyAssignment,
}
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub enum MouseEventAltScreen {
True,
False,
Any,
}
impl FromDynamic for MouseEventAltScreen {
fn from_dynamic(value: &Value, _options: FromDynamicOptions) -> Result<Self, DynError> {
match value {
Value::Bool(true) => Ok(Self::True),
Value::Bool(false) => Ok(Self::False),
Value::String(s) if s == "Both" => Ok(Self::Any),
_ => Err(DynError::Message(
"must be either true, false or 'Any'".to_string(),
)),
}
}
}
impl ToDynamic for MouseEventAltScreen {
fn to_dynamic(&self) -> Value {
match self {
Self::True => true.to_dynamic(),
Self::False => false.to_dynamic(),
Self::Any => "Any".to_dynamic(),
}
}
}
impl Default for MouseEventAltScreen {
fn default() -> Self {
Self::Any
}
}
#[derive(
Debug, Default, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, FromDynamic, ToDynamic,
)]
pub struct MouseEventTriggerMods {
#[dynamic(default, into = "String", try_from = "String")]
pub mods: Modifiers,
#[dynamic(default)]
pub mouse_reporting: bool,
#[dynamic(default)]
pub alt_screen: MouseEventAltScreen,
}

View File

@ -34,6 +34,7 @@ As features stabilize some brief notes about them will accumulate here.
* X11/Wayland: [XDG desktop portal](https://flatpak.github.io/xdg-desktop-portal/) is now used to determine whether dark mode is in use [#2258](https://github.com/wez/wezterm/issues/2258)
* [SetPaneZoomState](config/lua/keyassignment/SetPaneZoomState.md) key assignment and [MuxTab:set_zoomed()](config/lua/MuxTab/set_zoomed.md) for explicitly setting the zoom state of a pane. [#2284](https://github.com/wez/wezterm/discussions/2284)
* [mouse_bindings](config/mouse.md) can now handle scroll events. Thanks to [@Funami580](https://github.com/Funami580)! [#2173](https://github.com/wez/wezterm/issues/2173) [#2296](https://github.com/wez/wezterm/pull/2296)
* [mouse_bindings](config/mouse.md) may now also be defined based on whether the alt-screen is active and/or whether the application in the pane has enabled mouse reporting. [#581](https://github.com/wez/wezterm/issues/581)
#### Changed
* If `timeout_milliseconds` is specified in

View File

@ -0,0 +1,5 @@
# disable_default_mouse_bindings = false
If set to true, the default mouse assignments will not be used, allowing
you to tightly control those assignments.

View File

@ -0,0 +1,33 @@
# ScrollByCurrentEventWheelDelta
*Since: nightly builds only*
Adjusts the scroll position by the number of lines in the vertical mouse
wheel delta field of the current mouse event, provided that it is a
vertical mouse wheel event.
This example demonstrates a mouse assignment that is actually the default, so
there's not much point adding this to your config unless you also have set
[disable_default_mouse_bindings](../config/disable_default_mouse_bindings.md)
to `true`.
```lua
local wezterm = require 'wezterm'
local act = wezterm.action
return {
mouse_bindings = {
{
event = 'WheelUp',
mods = 'NONE',
action = act.ScrollByCurrentEventWheelDelta,
},
{
event = 'WheelDown',
mods = 'NONE',
action = act.ScrollByCurrentEventWheelDelta,
},
},
}
```

View File

@ -19,6 +19,8 @@ bypassing mouse reporting capture.
## Default Mouse Assignments
*Note: you can run `wezterm show-keys` to show the effective key and mouse assignments*.
In the table below, `Triple Left Down` means that the left mouse button is
being triple clicked and that the event matches the downstroke of the third
quick consecutive press. `Triple Left Up` matches the subsequent release event
@ -99,6 +101,29 @@ return {
}
```
Each entry in the mouse binding table can have the following fields:
* `event` - the mouse event on which to trigger. Described in detail below.
* `mods` - the keyboard modifier keys that must be active in order to match the event.
`mods` have the same definition and meaning as for key assignments and are described
in more detail in [Configuring Key Assignments](keys.md#configuring-key-assignments).
* `action` - the action to take when this mouse binding is matched
* `mouse_reporting` - an optional boolean that defaults to `false`. This mouse binding
entry will only be considered if the current pane's mouse reporting state matches.
In general, you should avoid defining assignments that have
`mouse_reporting=true` as it will prevent the application running in the
pane from receiving that mouse event. You can, of course, define these and
still send your mouse event to the pane by holding down the configured
[mouse reporting bypass modifier
key](lua/config/bypass_mouse_reporting_modifiers.md). (*Since: nightly builds only*)
* `alt_screen` - an optional field that defaults to `'Any'`, but that can also
be set to either `true` or `false`. This mouse binding entry will only be
considered if the current pane's alt screen state matches this field. Most
of the default mouse assignments are defined as `alt_screen='Any'`, a notable
exception being that mouse wheel scrolling only applies when
`alt_screen=false`, as the mouse wheel is typically mapped to arrow keys by
the terminal in alt screen mode. (*Since: nightly builds only*).
The `action` and `mods` portions are described in more detail in the key assignment
information below.
@ -127,7 +152,6 @@ you wanted quadruple-click bindings you can specify `streak=4`.
*since: nightly builds only*
You can handle scroll events by using `'WheelUp'` or `'WheelDown'` as event.
Currently, this only works with at least one modifier being present.
```lua
local wezterm = require 'wezterm'

View File

@ -3,7 +3,7 @@ use config::keyassignment::{
ClipboardCopyDestination, ClipboardPasteSource, KeyAssignment, KeyTableEntry, KeyTables,
MouseEventTrigger, SelectionMode,
};
use config::ConfigHandle;
use config::{ConfigHandle, MouseEventAltScreen, MouseEventTriggerMods};
use std::collections::{BTreeMap, HashMap};
use std::time::Duration;
use wezterm_term::input::MouseButton;
@ -11,7 +11,7 @@ use window::{KeyCode, Modifiers};
pub struct InputMap {
pub keys: KeyTables,
pub mouse: HashMap<(MouseEventTrigger, Modifiers), KeyAssignment>,
pub mouse: HashMap<(MouseEventTrigger, MouseEventTriggerMods), KeyAssignment>,
leader: Option<(KeyCode, Modifiers, Duration)>,
}
@ -52,7 +52,35 @@ impl InputMap {
if !config.disable_default_mouse_bindings {
m!(
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::False,
},
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::WheelUp(1),
},
ScrollByCurrentEventWheelDelta
],
[
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::False,
},
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::WheelDown(1),
},
ScrollByCurrentEventWheelDelta
],
[
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Down {
streak: 3,
button: MouseButton::Left
@ -60,7 +88,11 @@ impl InputMap {
SelectTextAtMouseCursor(SelectionMode::Line)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Down {
streak: 2,
button: MouseButton::Left
@ -68,7 +100,11 @@ impl InputMap {
SelectTextAtMouseCursor(SelectionMode::Word)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::Left
@ -76,7 +112,11 @@ impl InputMap {
SelectTextAtMouseCursor(SelectionMode::Cell)
],
[
Modifiers::ALT,
MouseEventTriggerMods {
mods: Modifiers::ALT,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::Left
@ -84,7 +124,11 @@ impl InputMap {
SelectTextAtMouseCursor(SelectionMode::Block)
],
[
Modifiers::SHIFT,
MouseEventTriggerMods {
mods: Modifiers::SHIFT,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::Left
@ -92,7 +136,11 @@ impl InputMap {
ExtendSelectionToMouseCursor(SelectionMode::Cell)
],
[
Modifiers::SHIFT,
MouseEventTriggerMods {
mods: Modifiers::SHIFT,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Up {
streak: 1,
button: MouseButton::Left
@ -102,7 +150,11 @@ impl InputMap {
)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Up {
streak: 1,
button: MouseButton::Left
@ -112,7 +164,11 @@ impl InputMap {
)
],
[
Modifiers::ALT,
MouseEventTriggerMods {
mods: Modifiers::ALT,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Up {
streak: 1,
button: MouseButton::Left
@ -120,7 +176,11 @@ impl InputMap {
CompleteSelection(ClipboardCopyDestination::PrimarySelection)
],
[
Modifiers::ALT | Modifiers::SHIFT,
MouseEventTriggerMods {
mods: Modifiers::ALT | Modifiers::SHIFT,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::Left
@ -128,7 +188,11 @@ impl InputMap {
ExtendSelectionToMouseCursor(SelectionMode::Block)
],
[
Modifiers::ALT | Modifiers::SHIFT,
MouseEventTriggerMods {
mods: Modifiers::ALT | Modifiers::SHIFT,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Up {
streak: 1,
button: MouseButton::Left
@ -138,7 +202,11 @@ impl InputMap {
)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Up {
streak: 2,
button: MouseButton::Left
@ -146,7 +214,11 @@ impl InputMap {
CompleteSelection(ClipboardCopyDestination::PrimarySelection)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Up {
streak: 3,
button: MouseButton::Left
@ -154,7 +226,11 @@ impl InputMap {
CompleteSelection(ClipboardCopyDestination::PrimarySelection)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Drag {
streak: 1,
button: MouseButton::Left
@ -162,7 +238,11 @@ impl InputMap {
ExtendSelectionToMouseCursor(SelectionMode::Cell)
],
[
Modifiers::ALT,
MouseEventTriggerMods {
mods: Modifiers::ALT,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Drag {
streak: 1,
button: MouseButton::Left
@ -170,7 +250,11 @@ impl InputMap {
ExtendSelectionToMouseCursor(SelectionMode::Block)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Drag {
streak: 2,
button: MouseButton::Left
@ -178,7 +262,11 @@ impl InputMap {
ExtendSelectionToMouseCursor(SelectionMode::Word)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Drag {
streak: 3,
button: MouseButton::Left
@ -186,7 +274,11 @@ impl InputMap {
ExtendSelectionToMouseCursor(SelectionMode::Line)
],
[
Modifiers::NONE,
MouseEventTriggerMods {
mods: Modifiers::NONE,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::Middle
@ -194,7 +286,11 @@ impl InputMap {
PasteFrom(ClipboardPasteSource::PrimarySelection)
],
[
Modifiers::SUPER,
MouseEventTriggerMods {
mods: Modifiers::SUPER,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Drag {
streak: 1,
button: MouseButton::Left,
@ -202,7 +298,11 @@ impl InputMap {
StartWindowDrag
],
[
ctrl_shift,
MouseEventTriggerMods {
mods: ctrl_shift,
mouse_reporting: false,
alt_screen: MouseEventAltScreen::Any,
},
MouseEventTrigger::Drag {
streak: 1,
button: MouseButton::Left,
@ -214,7 +314,29 @@ impl InputMap {
keys.default
.retain(|_, v| v.action != KeyAssignment::DisableDefaultAssignment);
mouse.retain(|_, v| *v != KeyAssignment::DisableDefaultAssignment);
// Expand MouseEventAltScreen::Any to individual True/False entries
let mut expanded_mouse = vec![];
for ((code, mods), v) in &mouse {
if mods.alt_screen == MouseEventAltScreen::Any {
let mods_true = MouseEventTriggerMods {
alt_screen: MouseEventAltScreen::True,
..*mods
};
let mods_false = MouseEventTriggerMods {
alt_screen: MouseEventAltScreen::False,
..*mods
};
expanded_mouse.push((code.clone(), mods_true, v.clone()));
expanded_mouse.push((code.clone(), mods_false, v.clone()));
}
}
// Eliminate ::Any
mouse.retain(|(_, mods), _| mods.alt_screen != MouseEventAltScreen::Any);
for (code, mods, v) in expanded_mouse {
mouse.insert((code, mods), v);
}
keys.by_name
.entry("copy_mode".to_string())
@ -259,10 +381,13 @@ impl InputMap {
.cloned()
}
pub fn lookup_mouse(&self, event: MouseEventTrigger, mods: Modifiers) -> Option<KeyAssignment> {
self.mouse
.get(&(event, mods.remove_positional_mods()))
.cloned()
pub fn lookup_mouse(
&self,
event: MouseEventTrigger,
mut mods: MouseEventTriggerMods,
) -> Option<KeyAssignment> {
mods.mods = mods.mods.remove_positional_mods();
self.mouse.get(&(event, mods)).cloned()
}
pub fn show_keys(&self) {
@ -284,28 +409,52 @@ impl InputMap {
}
}
section_header("Mouse");
self.show_mouse();
}
fn show_mouse(&self) {
let ordered = self.mouse.iter().collect::<BTreeMap<_, _>>();
for (label, alt_screen, mouse_reporting) in [
("Mouse", MouseEventAltScreen::False, false),
("Mouse: alt_screen", MouseEventAltScreen::True, false),
("Mouse: mouse_reporting", MouseEventAltScreen::False, true),
(
"Mouse: mouse_reporting + alt_screen",
MouseEventAltScreen::True,
true,
),
] {
let ordered = self
.mouse
.iter()
.filter(|((_, m), _)| {
m.alt_screen == alt_screen && m.mouse_reporting == mouse_reporting
})
.collect::<BTreeMap<_, _>>();
let mut trigger_width = 0;
let mut mod_width = 0;
for (trigger, mods) in ordered.keys() {
mod_width = mod_width.max(format!("{mods:?}").len());
trigger_width = trigger_width.max(format!("{trigger:?}").len());
}
if ordered.is_empty() {
continue;
}
for ((trigger, mods), action) in ordered {
let mods = if *mods == Modifiers::NONE {
String::new()
} else {
format!("{mods:?}")
};
let trigger = format!("{trigger:?}");
println!("\t{mods:mod_width$} {trigger:trigger_width$} -> {action:?}");
section_header(label);
let mut trigger_width = 0;
let mut mod_width = 0;
for (trigger, mods) in ordered.keys() {
mod_width = mod_width.max(format!("{:?}", mods.mods).len());
trigger_width = trigger_width.max(format!("{trigger:?}").len());
}
for ((trigger, mods), action) in ordered {
let mods = if mods.mods == Modifiers::NONE {
String::new()
} else {
format!("{:?}", mods.mods)
};
let trigger = format!("{trigger:?}");
println!("\t{mods:mod_width$} {trigger:trigger_width$} -> {action:?}");
}
println!();
}
}
}

View File

@ -1990,6 +1990,17 @@ impl TermWindow {
Ok(())
}
fn scroll_by_current_event_wheel_delta(&mut self) -> anyhow::Result<()> {
if let Some(event) = &self.current_mouse_event {
let amount = match event.kind {
MouseEventKind::VertWheel(amount) => -amount,
_ => return Ok(()),
};
self.scroll_by_line(amount.into())?;
}
Ok(())
}
fn scroll_by_line(&mut self, amount: isize) -> anyhow::Result<()> {
let pane = match self.get_active_pane_or_overlay() {
Some(pane) => pane,
@ -2201,6 +2212,7 @@ impl TermWindow {
MoveTabRelative(n) => self.move_tab_relative(*n)?,
ScrollByPage(n) => self.scroll_by_page(**n)?,
ScrollByLine(n) => self.scroll_by_line(*n)?,
ScrollByCurrentEventWheelDelta => self.scroll_by_current_event_wheel_delta()?,
ScrollToPrompt(n) => self.scroll_to_prompt(*n)?,
ScrollToTop => self.scroll_to_top(pane),
ScrollToBottom => self.scroll_to_bottom(pane),

View File

@ -6,6 +6,7 @@ use ::window::{
WindowState,
};
use config::keyassignment::{MouseEventTrigger, SpawnTabDomain};
use config::MouseEventAltScreen;
use mux::pane::Pane;
use mux::tab::SplitDirection;
use mux::Mux;
@ -755,21 +756,6 @@ impl super::TermWindow {
None
}
}
WMEK::VertWheel(amount)
if !pane.is_mouse_grabbed()
&& !pane.is_alt_screen_active()
&& event.modifiers.is_empty() =>
{
// adjust viewport
let dims = pane.get_dimensions();
let position = self
.get_viewport(pane.pane_id())
.unwrap_or(dims.physical_top)
.saturating_sub((*amount).into());
self.set_viewport(pane.pane_id(), Some(position), dims);
context.invalidate();
return;
}
WMEK::VertWheel(amount) => Some(match *amount {
0 => return,
1.. => MouseEventTrigger::Down {
@ -784,12 +770,7 @@ impl super::TermWindow {
WMEK::HorzWheel(_) => None,
};
if allow_action
&& (!pane.is_mouse_grabbed()
|| event
.modifiers
.contains(self.config.bypass_mouse_reporting_modifiers))
{
if allow_action {
if let Some(mut event_trigger_type) = event_trigger_type {
self.current_event = Some(event_trigger_type.to_dynamic());
let mut modifiers = event.modifiers;
@ -826,7 +807,17 @@ impl super::TermWindow {
_ => {}
};
if let Some(action) = self.input_map.lookup_mouse(event_trigger_type, modifiers) {
let mouse_mods = config::MouseEventTriggerMods {
mods: modifiers,
mouse_reporting: pane.is_mouse_grabbed(),
alt_screen: if pane.is_alt_screen_active() {
MouseEventAltScreen::True
} else {
MouseEventAltScreen::False
},
};
if let Some(action) = self.input_map.lookup_mouse(event_trigger_type, mouse_mods) {
self.perform_key_assignment(&pane, &action).ok();
return;
}