mirror of
https://github.com/wez/wezterm.git
synced 2024-12-24 22:01:47 +03:00
gui: allow matching keys with a physical position
```lua local wezterm = require 'wezterm' return { debug_key_events = true, keys = { {key="phys:Q", action=wezterm.action{SendString="woot"}}, }, } ``` The above will send "woot" to the pane whenever the key in same physical position as Q on an ANSI standard US keyboard is pressed. refs: https://github.com/wez/wezterm/issues/1483
This commit is contained in:
parent
822202b7be
commit
69e8d9144c
@ -1,4 +1,6 @@
|
||||
use ::window::{KeyCode, KeyEvent, Modifiers, WindowOps};
|
||||
use mux::pane::Pane;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn window_mods_to_termwiz_mods(modifiers: ::window::Modifiers) -> termwiz::input::Modifiers {
|
||||
let mut result = termwiz::input::Modifiers::NONE;
|
||||
@ -34,6 +36,87 @@ pub enum Key {
|
||||
}
|
||||
|
||||
impl super::TermWindow {
|
||||
fn process_key(
|
||||
&mut self,
|
||||
pane: &Rc<dyn Pane>,
|
||||
context: &dyn WindowOps,
|
||||
keycode: &KeyCode,
|
||||
raw_modifiers: Modifiers,
|
||||
leader_active: bool,
|
||||
leader_mod: Modifiers,
|
||||
) -> bool {
|
||||
if !leader_active {
|
||||
// Check to see if this key-press is the leader activating
|
||||
if let Some(duration) = self.input_map.is_leader(&keycode, raw_modifiers) {
|
||||
// Yes; record its expiration
|
||||
self.leader_is_down
|
||||
.replace(std::time::Instant::now() + duration);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(assignment) = self
|
||||
.input_map
|
||||
.lookup_key(&keycode, raw_modifiers | leader_mod)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &assignment).ok();
|
||||
context.invalidate();
|
||||
|
||||
if leader_active {
|
||||
// A successful leader key-lookup cancels the leader
|
||||
// virtual modifier state
|
||||
self.leader_is_down.take();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// While the leader modifier is active, only registered
|
||||
// keybindings are recognized.
|
||||
if !leader_active {
|
||||
let config = &self.config;
|
||||
|
||||
// This is a bit ugly.
|
||||
// Not all of our platforms report LEFT|RIGHT ALT; most report just ALT.
|
||||
// For those that do distinguish between them we want to respect the left vs.
|
||||
// right settings for the compose behavior.
|
||||
// Otherwise, if the event didn't include left vs. right then we want to
|
||||
// respect the generic compose behavior.
|
||||
let bypass_compose =
|
||||
// Left ALT and they disabled compose
|
||||
(raw_modifiers.contains(Modifiers::LEFT_ALT)
|
||||
&& !config.send_composed_key_when_left_alt_is_pressed)
|
||||
// Right ALT and they disabled compose
|
||||
|| (raw_modifiers.contains(Modifiers::RIGHT_ALT)
|
||||
&& !config.send_composed_key_when_right_alt_is_pressed)
|
||||
// Generic ALT and they disabled generic compose
|
||||
|| (!raw_modifiers.contains(Modifiers::RIGHT_ALT)
|
||||
&& !raw_modifiers.contains(Modifiers::LEFT_ALT)
|
||||
&& raw_modifiers.contains(Modifiers::ALT)
|
||||
&& !config.send_composed_key_when_alt_is_pressed);
|
||||
|
||||
if bypass_compose {
|
||||
if let Key::Code(term_key) = self.win_key_code_to_termwiz_key_code(keycode) {
|
||||
let tw_raw_modifiers = window_mods_to_termwiz_mods(raw_modifiers);
|
||||
if pane.key_down(term_key, tw_raw_modifiers).is_ok() {
|
||||
if !keycode.is_modifier()
|
||||
&& self.pane_state(pane.pane_id()).overlay.is_none()
|
||||
{
|
||||
self.maybe_scroll_to_bottom_for_input(&pane);
|
||||
}
|
||||
context.set_cursor(None);
|
||||
if !keycode.is_modifier() {
|
||||
context.invalidate();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn key_event_impl(&mut self, window_key: KeyEvent, context: &dyn WindowOps) -> bool {
|
||||
if !window_key.key_is_down {
|
||||
return false;
|
||||
@ -67,42 +150,36 @@ impl super::TermWindow {
|
||||
};
|
||||
|
||||
let modifiers = window_mods_to_termwiz_mods(window_key.modifiers);
|
||||
let raw_modifiers = window_mods_to_termwiz_mods(window_key.raw_modifiers);
|
||||
|
||||
// If we know the underlying raw code, let's first try any mappings
|
||||
// defined for those. By their nature, we don't know anything useful
|
||||
// about their position or meaning in code here, so we don't have
|
||||
// any built-in mappings defined with raw_codes.
|
||||
// That means that we only need check for user-defined values in
|
||||
// this block.
|
||||
// defined for those.
|
||||
if let Some(raw_code) = window_key.raw_code {
|
||||
let raw_code_key = KeyCode::RawCode(raw_code);
|
||||
|
||||
if !leader_active {
|
||||
// Check to see if this key-press is the leader activating
|
||||
if let Some(duration) = self
|
||||
.input_map
|
||||
.is_leader(&raw_code_key, window_key.raw_modifiers)
|
||||
{
|
||||
// Yes; record its expiration
|
||||
self.leader_is_down
|
||||
.replace(std::time::Instant::now() + duration);
|
||||
return true;
|
||||
}
|
||||
if self.process_key(
|
||||
&pane,
|
||||
context,
|
||||
&raw_code_key,
|
||||
window_key.raw_modifiers,
|
||||
leader_active,
|
||||
leader_mod,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(assignment) = self
|
||||
.input_map
|
||||
.lookup_key(&raw_code_key, window_key.raw_modifiers | leader_mod)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &assignment).ok();
|
||||
context.invalidate();
|
||||
// Then, check for mappings using physical key location
|
||||
if let Some(phys) = window_key.phys_code {
|
||||
let phys_code_key = KeyCode::Physical(phys);
|
||||
|
||||
if leader_active {
|
||||
// A successful leader key-lookup cancels the leader
|
||||
// virtual modifier state
|
||||
self.leader_is_down.take();
|
||||
}
|
||||
if self.process_key(
|
||||
&pane,
|
||||
context,
|
||||
&phys_code_key,
|
||||
window_key.raw_modifiers,
|
||||
leader_active,
|
||||
leader_mod,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -110,136 +187,59 @@ impl super::TermWindow {
|
||||
// We may know the decoded platform key, but prior to any composition
|
||||
// defined by the system (eg: prior to dead key expansion).
|
||||
if let Some(key) = &window_key.raw_key {
|
||||
if !leader_active {
|
||||
// Check to see if this key-press is the leader activating
|
||||
if let Some(duration) = self.input_map.is_leader(key, window_key.raw_modifiers) {
|
||||
// Yes; record its expiration
|
||||
self.leader_is_down
|
||||
.replace(std::time::Instant::now() + duration);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(assignment) = self
|
||||
.input_map
|
||||
.lookup_key(key, window_key.raw_modifiers | leader_mod)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &assignment).ok();
|
||||
context.invalidate();
|
||||
|
||||
if leader_active {
|
||||
// A successful leader key-lookup cancels the leader
|
||||
// virtual modifier state
|
||||
self.leader_is_down.take();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// While the leader modifier is active, only registered
|
||||
// keybindings are recognized.
|
||||
if !leader_active {
|
||||
let config = &self.config;
|
||||
|
||||
// This is a bit ugly.
|
||||
// Not all of our platforms report LEFT|RIGHT ALT; most report just ALT.
|
||||
// For those that do distinguish between them we want to respect the left vs.
|
||||
// right settings for the compose behavior.
|
||||
// Otherwise, if the event didn't include left vs. right then we want to
|
||||
// respect the generic compose behavior.
|
||||
let bypass_compose =
|
||||
// Left ALT and they disabled compose
|
||||
(window_key.raw_modifiers.contains(Modifiers::LEFT_ALT)
|
||||
&& !config.send_composed_key_when_left_alt_is_pressed)
|
||||
// Right ALT and they disabled compose
|
||||
|| (window_key.raw_modifiers.contains(Modifiers::RIGHT_ALT)
|
||||
&& !config.send_composed_key_when_right_alt_is_pressed)
|
||||
// Generic ALT and they disabled generic compose
|
||||
|| (!window_key.raw_modifiers.contains(Modifiers::RIGHT_ALT)
|
||||
&& !window_key.raw_modifiers.contains(Modifiers::LEFT_ALT)
|
||||
&& window_key.raw_modifiers.contains(Modifiers::ALT)
|
||||
&& !config.send_composed_key_when_alt_is_pressed);
|
||||
|
||||
if let Key::Code(term_key) = self.win_key_code_to_termwiz_key_code(&key) {
|
||||
if bypass_compose && pane.key_down(term_key, raw_modifiers).is_ok() {
|
||||
if !key.is_modifier() && self.pane_state(pane.pane_id()).overlay.is_none() {
|
||||
self.maybe_scroll_to_bottom_for_input(&pane);
|
||||
}
|
||||
context.set_cursor(None);
|
||||
if !key.is_modifier() {
|
||||
context.invalidate();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !leader_active {
|
||||
// Check to see if this key-press is the leader activating
|
||||
if let Some(duration) = self
|
||||
.input_map
|
||||
.is_leader(&window_key.key, window_key.modifiers)
|
||||
{
|
||||
// Yes; record its expiration
|
||||
self.leader_is_down
|
||||
.replace(std::time::Instant::now() + duration);
|
||||
if self.process_key(
|
||||
&pane,
|
||||
context,
|
||||
key,
|
||||
window_key.raw_modifiers,
|
||||
leader_active,
|
||||
leader_mod,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(assignment) = self
|
||||
.input_map
|
||||
.lookup_key(&window_key.key, window_key.modifiers | leader_mod)
|
||||
{
|
||||
self.perform_key_assignment(&pane, &assignment).ok();
|
||||
context.invalidate();
|
||||
if leader_active {
|
||||
// A successful leader key-lookup cancels the leader
|
||||
// virtual modifier state
|
||||
self.leader_is_down.take();
|
||||
}
|
||||
true
|
||||
} else if leader_active {
|
||||
if !window_key.key.is_modifier() {
|
||||
// Leader was pressed and this non-modifier keypress isn't
|
||||
// a registered key binding; swallow this event and cancel
|
||||
// the leader modifier
|
||||
self.leader_is_down.take();
|
||||
}
|
||||
true
|
||||
} else {
|
||||
let key = self.win_key_code_to_termwiz_key_code(&window_key.key);
|
||||
match key {
|
||||
Key::Code(key) => {
|
||||
if pane.key_down(key, modifiers).is_ok() {
|
||||
if !key.is_modifier() && self.pane_state(pane.pane_id()).overlay.is_none() {
|
||||
self.maybe_scroll_to_bottom_for_input(&pane);
|
||||
}
|
||||
context.set_cursor(None);
|
||||
if !key.is_modifier() {
|
||||
context.invalidate();
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Key::Composed(s) => {
|
||||
if leader_active {
|
||||
// Leader was pressed and this non-modifier keypress isn't
|
||||
// a registered key binding; swallow this event and cancel
|
||||
// the leader modifier.
|
||||
self.leader_is_down.take();
|
||||
} else {
|
||||
pane.writer().write_all(s.as_bytes()).ok();
|
||||
if self.process_key(
|
||||
&pane,
|
||||
context,
|
||||
&window_key.key,
|
||||
window_key.modifiers,
|
||||
leader_active,
|
||||
leader_mod,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let key = self.win_key_code_to_termwiz_key_code(&window_key.key);
|
||||
match key {
|
||||
Key::Code(key) => {
|
||||
if pane.key_down(key, modifiers).is_ok() {
|
||||
if !key.is_modifier() && self.pane_state(pane.pane_id()).overlay.is_none() {
|
||||
self.maybe_scroll_to_bottom_for_input(&pane);
|
||||
}
|
||||
context.set_cursor(None);
|
||||
if !key.is_modifier() {
|
||||
context.invalidate();
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
Key::None => false,
|
||||
}
|
||||
Key::Composed(s) => {
|
||||
if leader_active {
|
||||
// Leader was pressed and this non-modifier keypress isn't
|
||||
// a registered key binding; swallow this event and cancel
|
||||
// the leader modifier.
|
||||
self.leader_is_down.take();
|
||||
} else {
|
||||
pane.writer().write_all(s.as_bytes()).ok();
|
||||
self.maybe_scroll_to_bottom_for_input(&pane);
|
||||
context.invalidate();
|
||||
}
|
||||
true
|
||||
}
|
||||
Key::None => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user