Merge pull request #165 from mahkoh/jorth/release-shortcut
config: implement on-release shortcuts
This commit is contained in:
commit
c235f026f0
14 changed files with 131 additions and 21 deletions
|
|
@ -903,6 +903,10 @@ impl Client {
|
||||||
(rate, delay)
|
(rate, delay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_forward(&self, seat: Seat, forward: bool) {
|
||||||
|
self.send(&ClientMessage::SetForward { seat, forward })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_keymap(&self, keymap: &str) -> Keymap {
|
pub fn parse_keymap(&self, keymap: &str) -> Keymap {
|
||||||
let res = self.send_with_response(&ClientMessage::ParseKeymap { keymap });
|
let res = self.send_with_response(&ClientMessage::ParseKeymap { keymap });
|
||||||
get_response!(res, Keymap(0), ParseKeymap { keymap });
|
get_response!(res, Keymap(0), ParseKeymap { keymap });
|
||||||
|
|
|
||||||
|
|
@ -436,6 +436,10 @@ pub enum ClientMessage<'a> {
|
||||||
device: InputDevice,
|
device: InputDevice,
|
||||||
keymap: Keymap,
|
keymap: Keymap,
|
||||||
},
|
},
|
||||||
|
SetForward {
|
||||||
|
seat: Seat,
|
||||||
|
forward: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -335,6 +335,26 @@ impl Seat {
|
||||||
pub fn move_to_output(self, connector: Connector) {
|
pub fn move_to_output(self, connector: Connector) {
|
||||||
get!().move_to_output(WorkspaceSource::Seat(self), connector);
|
get!().move_to_output(WorkspaceSource::Seat(self), connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set whether the current key event is forwarded to the focused client.
|
||||||
|
///
|
||||||
|
/// This only has an effect if called from a keyboard shortcut.
|
||||||
|
///
|
||||||
|
/// By default, release events are forwarded and press events are consumed. Note that
|
||||||
|
/// consuming release events can cause clients to get stuck in the pressed state.
|
||||||
|
pub fn set_forward(self, forward: bool) {
|
||||||
|
get!().set_forward(self, forward);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a shorthand for `set_forward(true)`.
|
||||||
|
pub fn forward(self) {
|
||||||
|
self.set_forward(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a shorthand for `set_forward(false)`.
|
||||||
|
pub fn consume(self) {
|
||||||
|
self.set_forward(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all seats.
|
/// Returns all seats.
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,11 @@ pub const NUM: Modifiers = MOD2;
|
||||||
/// Alias for `MOD4`.
|
/// Alias for `MOD4`.
|
||||||
pub const LOGO: Modifiers = MOD4;
|
pub const LOGO: Modifiers = MOD4;
|
||||||
|
|
||||||
|
/// Synthetic modifier matching key release events.
|
||||||
|
///
|
||||||
|
/// This can be used to execute a callback on key release.
|
||||||
|
pub const RELEASE: Modifiers = Modifiers(1 << 31);
|
||||||
|
|
||||||
impl BitOr for Modifiers {
|
impl BitOr for Modifiers {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,12 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_set_forward(&self, seat: Seat, forward: bool) -> Result<(), CphError> {
|
||||||
|
let seat = self.get_seat(seat)?;
|
||||||
|
seat.set_forward(forward);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_set_status(&self, status: &str) {
|
fn handle_set_status(&self, status: &str) {
|
||||||
self.state.set_status(status);
|
self.state.set_status(status);
|
||||||
}
|
}
|
||||||
|
|
@ -1764,6 +1770,9 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::DeviceSetKeymap { device, keymap } => self
|
ClientMessage::DeviceSetKeymap { device, keymap } => self
|
||||||
.handle_set_device_keymap(device, keymap)
|
.handle_set_device_keymap(device, keymap)
|
||||||
.wrn("set_device_keymap")?,
|
.wrn("set_device_keymap")?,
|
||||||
|
ClientMessage::SetForward { seat, forward } => {
|
||||||
|
self.handle_set_forward(seat, forward).wrn("set_forward")?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,7 @@ pub struct WlSeatGlobal {
|
||||||
text_input: CloneCell<Option<Rc<ZwpTextInputV3>>>,
|
text_input: CloneCell<Option<Rc<ZwpTextInputV3>>>,
|
||||||
input_method: CloneCell<Option<Rc<ZwpInputMethodV2>>>,
|
input_method: CloneCell<Option<Rc<ZwpInputMethodV2>>>,
|
||||||
input_method_grab: CloneCell<Option<Rc<ZwpInputMethodKeyboardGrabV2>>>,
|
input_method_grab: CloneCell<Option<Rc<ZwpInputMethodKeyboardGrabV2>>>,
|
||||||
|
forward: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
||||||
|
|
@ -243,6 +244,7 @@ impl WlSeatGlobal {
|
||||||
text_input: Default::default(),
|
text_input: Default::default(),
|
||||||
input_method: Default::default(),
|
input_method: Default::default(),
|
||||||
input_method_grab: Default::default(),
|
input_method_grab: Default::default(),
|
||||||
|
forward: Cell::new(false),
|
||||||
});
|
});
|
||||||
state.add_cursor_size(*DEFAULT_CURSOR_SIZE);
|
state.add_cursor_size(*DEFAULT_CURSOR_SIZE);
|
||||||
let seat = slf.clone();
|
let seat = slf.clone();
|
||||||
|
|
@ -1146,6 +1148,10 @@ impl WlSeatGlobal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_forward(&self, forward: bool) {
|
||||||
|
self.forward.set(forward);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global_base!(WlSeatGlobal, WlSeat, WlSeatError);
|
global_base!(WlSeatGlobal, WlSeat, WlSeatError);
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ use {
|
||||||
wire::WlDataOfferId,
|
wire::WlDataOfferId,
|
||||||
xkbcommon::{KeyboardState, XkbState, XKB_KEY_DOWN, XKB_KEY_UP},
|
xkbcommon::{KeyboardState, XkbState, XKB_KEY_DOWN, XKB_KEY_UP},
|
||||||
},
|
},
|
||||||
isnt::std_1::primitive::IsntSlice2Ext,
|
isnt::std_1::primitive::{IsntSlice2Ext, IsntSliceExt},
|
||||||
jay_config::keyboard::{
|
jay_config::keyboard::{
|
||||||
mods::{Modifiers, CAPS, NUM},
|
mods::{Modifiers, CAPS, NUM, RELEASE},
|
||||||
syms::KeySym,
|
syms::KeySym,
|
||||||
ModifiedKeySym,
|
ModifiedKeySym,
|
||||||
},
|
},
|
||||||
|
|
@ -375,11 +375,13 @@ impl WlSeatGlobal {
|
||||||
let mut shortcuts = SmallVec::<[_; 1]>::new();
|
let mut shortcuts = SmallVec::<[_; 1]>::new();
|
||||||
let new_mods;
|
let new_mods;
|
||||||
{
|
{
|
||||||
if !self.state.lock.locked.get() && state == wl_keyboard::PRESSED {
|
if !self.state.lock.locked.get() {
|
||||||
let old_mods = xkb_state.mods();
|
let mut mods = xkb_state.mods().mods_effective & !(CAPS.0 | NUM.0);
|
||||||
|
if state == wl_keyboard::RELEASED {
|
||||||
|
mods |= RELEASE.0;
|
||||||
|
}
|
||||||
let keysyms = xkb_state.unmodified_keysyms(key);
|
let keysyms = xkb_state.unmodified_keysyms(key);
|
||||||
for &sym in keysyms {
|
for &sym in keysyms {
|
||||||
let mods = old_mods.mods_effective & !(CAPS.0 | NUM.0);
|
|
||||||
if let Some(mods) = self.shortcuts.get(&(mods, sym)) {
|
if let Some(mods) = self.shortcuts.get(&(mods, sym)) {
|
||||||
shortcuts.push(ModifiedKeySym {
|
shortcuts.push(ModifiedKeySym {
|
||||||
mods,
|
mods,
|
||||||
|
|
@ -395,22 +397,28 @@ impl WlSeatGlobal {
|
||||||
});
|
});
|
||||||
let node = self.keyboard_node.get();
|
let node = self.keyboard_node.get();
|
||||||
let input_method_grab = self.input_method_grab.get();
|
let input_method_grab = self.input_method_grab.get();
|
||||||
if shortcuts.is_empty() {
|
let mut forward = true;
|
||||||
|
if shortcuts.is_not_empty() {
|
||||||
|
self.forward.set(state == wl_keyboard::RELEASED);
|
||||||
|
if let Some(config) = self.state.config.get() {
|
||||||
|
let id = xkb_state.kb_state.id;
|
||||||
|
drop(xkb_state);
|
||||||
|
for shortcut in shortcuts {
|
||||||
|
config.invoke_shortcut(self.id(), &shortcut);
|
||||||
|
}
|
||||||
|
xkb_state_rc = get_state();
|
||||||
|
xkb_state = xkb_state_rc.borrow_mut();
|
||||||
|
if id != xkb_state.kb_state.id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
forward = self.forward.get();
|
||||||
|
}
|
||||||
|
if forward {
|
||||||
match &input_method_grab {
|
match &input_method_grab {
|
||||||
Some(g) => g.on_key(time_usec, key, state, &xkb_state.kb_state),
|
Some(g) => g.on_key(time_usec, key, state, &xkb_state.kb_state),
|
||||||
_ => node.node_on_key(self, time_usec, key, state, &xkb_state.kb_state),
|
_ => node.node_on_key(self, time_usec, key, state, &xkb_state.kb_state),
|
||||||
}
|
}
|
||||||
} else if let Some(config) = self.state.config.get() {
|
|
||||||
let id = xkb_state.kb_state.id;
|
|
||||||
drop(xkb_state);
|
|
||||||
for shortcut in shortcuts {
|
|
||||||
config.invoke_shortcut(self.id(), &shortcut);
|
|
||||||
}
|
|
||||||
xkb_state_rc = get_state();
|
|
||||||
xkb_state = xkb_state_rc.borrow_mut();
|
|
||||||
if id != xkb_state.kb_state.id {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if new_mods {
|
if new_mods {
|
||||||
self.state.for_each_seat_tester(|t| {
|
self.state.for_each_seat_tester(|t| {
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ pub enum SimpleCommand {
|
||||||
ToggleFullscreen,
|
ToggleFullscreen,
|
||||||
ToggleMono,
|
ToggleMono,
|
||||||
ToggleSplit,
|
ToggleSplit,
|
||||||
|
Forward(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@ impl ActionParser<'_> {
|
||||||
"reload-config-toml" => ReloadConfigToml,
|
"reload-config-toml" => ReloadConfigToml,
|
||||||
"reload-config-so" => ReloadConfigSo,
|
"reload-config-so" => ReloadConfigSo,
|
||||||
"none" => None,
|
"none" => None,
|
||||||
|
"forward" => Forward(true),
|
||||||
|
"consume" => Forward(false),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span))
|
return Err(ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,10 @@ use {
|
||||||
toml::toml_span::{Span, SpannedExt},
|
toml::toml_span::{Span, SpannedExt},
|
||||||
},
|
},
|
||||||
jay_config::keyboard::{
|
jay_config::keyboard::{
|
||||||
mods::{Modifiers, ALT, CAPS, CTRL, LOCK, LOGO, MOD1, MOD2, MOD3, MOD4, MOD5, NUM, SHIFT},
|
mods::{
|
||||||
|
Modifiers, ALT, CAPS, CTRL, LOCK, LOGO, MOD1, MOD2, MOD3, MOD4, MOD5, NUM, RELEASE,
|
||||||
|
SHIFT,
|
||||||
|
},
|
||||||
ModifiedKeySym,
|
ModifiedKeySym,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
|
@ -49,6 +52,7 @@ impl Parser for ModifiedKeysymParser {
|
||||||
"alt" => ALT,
|
"alt" => ALT,
|
||||||
"num" => NUM,
|
"num" => NUM,
|
||||||
"logo" => LOGO,
|
"logo" => LOGO,
|
||||||
|
"release" => RELEASE,
|
||||||
_ => match KEYSYMS.get(part) {
|
_ => match KEYSYMS.get(part) {
|
||||||
Some(new) if sym.is_none() => {
|
Some(new) if sym.is_none() => {
|
||||||
sym = Some(*new);
|
sym = Some(*new);
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ impl Action {
|
||||||
}
|
}
|
||||||
SimpleCommand::ReloadConfigSo => Box::new(reload),
|
SimpleCommand::ReloadConfigSo => Box::new(reload),
|
||||||
SimpleCommand::None => Box::new(|| ()),
|
SimpleCommand::None => Box::new(|| ()),
|
||||||
|
SimpleCommand::Forward(bool) => Box::new(move || s.set_forward(bool)),
|
||||||
},
|
},
|
||||||
Action::Multi { actions } => {
|
Action::Multi { actions } => {
|
||||||
let mut actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
let mut actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||||
|
|
|
||||||
|
|
@ -440,7 +440,7 @@
|
||||||
"$ref": "#/$defs/RepeatRate"
|
"$ref": "#/$defs/RepeatRate"
|
||||||
},
|
},
|
||||||
"shortcuts": {
|
"shortcuts": {
|
||||||
"description": "The compositor shortcuts.\n\nThe keys should be in the following format:\n\n```\n(MOD-)*KEYSYM\n```\n\n`MOD` should be one of `shift`, `lock`, `ctrl`, `mod1`, `mod2`, `mod3`, `mod4`,\n`mod5`, `caps`, `alt`, `num`, or `logo`.\n\n`KEYSYM` should be the name of a keysym. The authorative location for these names\nis [1] with the `XKB_KEY_` prefix removed.\n\nThe keysym should be the unmodified keysym. E.g. `shift-q` not `shift-Q`.\n\n[1]: https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n",
|
"description": "The compositor shortcuts.\n\nThe keys should be in the following format:\n\n```\n(MOD-)*KEYSYM\n```\n\n`MOD` should be one of `shift`, `lock`, `ctrl`, `mod1`, `mod2`, `mod3`, `mod4`,\n`mod5`, `caps`, `alt`, `num`, `logo`, or `release`.\n\nUsing the `release` modifier causes the shortcut to trigger when the key is\nreleased.\n\n`KEYSYM` should be the name of a keysym. The authorative location for these names\nis [1] with the `XKB_KEY_` prefix removed.\n\nThe keysym should be the unmodified keysym. E.g. `shift-q` not `shift-Q`.\n\n[1]: https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h\n\n- Example:\n\n ```toml\n [shortcuts]\n alt-q = \"quit\"\n ```\n",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
@ -1053,6 +1053,8 @@
|
||||||
"quit",
|
"quit",
|
||||||
"reload-config-toml",
|
"reload-config-toml",
|
||||||
"reload-config-to",
|
"reload-config-to",
|
||||||
|
"consume",
|
||||||
|
"forward",
|
||||||
"none"
|
"none"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -694,7 +694,10 @@ The table has the following fields:
|
||||||
```
|
```
|
||||||
|
|
||||||
`MOD` should be one of `shift`, `lock`, `ctrl`, `mod1`, `mod2`, `mod3`, `mod4`,
|
`MOD` should be one of `shift`, `lock`, `ctrl`, `mod1`, `mod2`, `mod3`, `mod4`,
|
||||||
`mod5`, `caps`, `alt`, `num`, or `logo`.
|
`mod5`, `caps`, `alt`, `num`, `logo`, or `release`.
|
||||||
|
|
||||||
|
Using the `release` modifier causes the shortcut to trigger when the key is
|
||||||
|
released.
|
||||||
|
|
||||||
`KEYSYM` should be the name of a keysym. The authorative location for these names
|
`KEYSYM` should be the name of a keysym. The authorative location for these names
|
||||||
is [1] with the `XKB_KEY_` prefix removed.
|
is [1] with the `XKB_KEY_` prefix removed.
|
||||||
|
|
@ -2221,6 +2224,26 @@ The string should have one of the following values:
|
||||||
|
|
||||||
Reload the `config.so`.
|
Reload the `config.so`.
|
||||||
|
|
||||||
|
- `consume`:
|
||||||
|
|
||||||
|
Consume the current key event. Don't forward it to the focused application.
|
||||||
|
|
||||||
|
This action only has an effect in shortcuts.
|
||||||
|
|
||||||
|
Key-press events events that trigger shortcuts are consumed by default.
|
||||||
|
Key-release events events that trigger shortcuts are forwarded by default.
|
||||||
|
|
||||||
|
Note that consuming key-release events can cause keys to get stuck in the focused
|
||||||
|
application.
|
||||||
|
|
||||||
|
See the `forward` action to achieve the opposite effect.
|
||||||
|
|
||||||
|
- `forward`:
|
||||||
|
|
||||||
|
Forward the current key event to the focused application.
|
||||||
|
|
||||||
|
See the `consume` action for more details.
|
||||||
|
|
||||||
- `none`:
|
- `none`:
|
||||||
|
|
||||||
Perform no action.
|
Perform no action.
|
||||||
|
|
|
||||||
|
|
@ -668,6 +668,24 @@ SimpleActionName:
|
||||||
description: Reload the `config.toml`.
|
description: Reload the `config.toml`.
|
||||||
- value: reload-config-to
|
- value: reload-config-to
|
||||||
description: Reload the `config.so`.
|
description: Reload the `config.so`.
|
||||||
|
- value: consume
|
||||||
|
description: |
|
||||||
|
Consume the current key event. Don't forward it to the focused application.
|
||||||
|
|
||||||
|
This action only has an effect in shortcuts.
|
||||||
|
|
||||||
|
Key-press events events that trigger shortcuts are consumed by default.
|
||||||
|
Key-release events events that trigger shortcuts are forwarded by default.
|
||||||
|
|
||||||
|
Note that consuming key-release events can cause keys to get stuck in the focused
|
||||||
|
application.
|
||||||
|
|
||||||
|
See the `forward` action to achieve the opposite effect.
|
||||||
|
- value: forward
|
||||||
|
description: |
|
||||||
|
Forward the current key event to the focused application.
|
||||||
|
|
||||||
|
See the `consume` action for more details.
|
||||||
- value: none
|
- value: none
|
||||||
description: |
|
description: |
|
||||||
Perform no action.
|
Perform no action.
|
||||||
|
|
@ -1705,7 +1723,10 @@ Config:
|
||||||
```
|
```
|
||||||
|
|
||||||
`MOD` should be one of `shift`, `lock`, `ctrl`, `mod1`, `mod2`, `mod3`, `mod4`,
|
`MOD` should be one of `shift`, `lock`, `ctrl`, `mod1`, `mod2`, `mod3`, `mod4`,
|
||||||
`mod5`, `caps`, `alt`, `num`, or `logo`.
|
`mod5`, `caps`, `alt`, `num`, `logo`, or `release`.
|
||||||
|
|
||||||
|
Using the `release` modifier causes the shortcut to trigger when the key is
|
||||||
|
released.
|
||||||
|
|
||||||
`KEYSYM` should be the name of a keysym. The authorative location for these names
|
`KEYSYM` should be the name of a keysym. The authorative location for these names
|
||||||
is [1] with the `XKB_KEY_` prefix removed.
|
is [1] with the `XKB_KEY_` prefix removed.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue