Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: implement on-release shortcuts #165

Merged
merged 2 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions jay-config/src/_private/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,10 @@ impl Client {
(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 {
let res = self.send_with_response(&ClientMessage::ParseKeymap { keymap });
get_response!(res, Keymap(0), ParseKeymap { keymap });
Expand Down
4 changes: 4 additions & 0 deletions jay-config/src/_private/ipc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,10 @@ pub enum ClientMessage<'a> {
device: InputDevice,
keymap: Keymap,
},
SetForward {
seat: Seat,
forward: bool,
},
}

#[derive(Serialize, Deserialize, Debug)]
Expand Down
20 changes: 20 additions & 0 deletions jay-config/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,26 @@ impl Seat {
pub fn move_to_output(self, connector: 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.
Expand Down
5 changes: 5 additions & 0 deletions jay-config/src/keyboard/mods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ pub const NUM: Modifiers = MOD2;
/// Alias for `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 {
type Output = Self;

Expand Down
9 changes: 9 additions & 0 deletions src/config/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ impl ConfigProxyHandler {
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) {
self.state.set_status(status);
}
Expand Down Expand Up @@ -1764,6 +1770,9 @@ impl ConfigProxyHandler {
ClientMessage::DeviceSetKeymap { device, keymap } => self
.handle_set_device_keymap(device, keymap)
.wrn("set_device_keymap")?,
ClientMessage::SetForward { seat, forward } => {
self.handle_set_forward(seat, forward).wrn("set_forward")?
}
}
Ok(())
}
Expand Down
6 changes: 6 additions & 0 deletions src/ifs/wl_seat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ pub struct WlSeatGlobal {
text_input: CloneCell<Option<Rc<ZwpTextInputV3>>>,
input_method: CloneCell<Option<Rc<ZwpInputMethodV2>>>,
input_method_grab: CloneCell<Option<Rc<ZwpInputMethodKeyboardGrabV2>>>,
forward: Cell<bool>,
}

const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
Expand Down Expand Up @@ -243,6 +244,7 @@ impl WlSeatGlobal {
text_input: Default::default(),
input_method: Default::default(),
input_method_grab: Default::default(),
forward: Cell::new(false),
});
state.add_cursor_size(*DEFAULT_CURSOR_SIZE);
let seat = slf.clone();
Expand Down Expand Up @@ -1146,6 +1148,10 @@ impl WlSeatGlobal {
}
}
}

pub fn set_forward(&self, forward: bool) {
self.forward.set(forward);
}
}

global_base!(WlSeatGlobal, WlSeat, WlSeatError);
Expand Down
42 changes: 25 additions & 17 deletions src/ifs/wl_seat/event_handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ use {
wire::WlDataOfferId,
xkbcommon::{KeyboardState, XkbState, XKB_KEY_DOWN, XKB_KEY_UP},
},
isnt::std_1::primitive::IsntSlice2Ext,
isnt::std_1::primitive::{IsntSlice2Ext, IsntSliceExt},
jay_config::keyboard::{
mods::{Modifiers, CAPS, NUM},
mods::{Modifiers, CAPS, NUM, RELEASE},
syms::KeySym,
ModifiedKeySym,
},
Expand Down Expand Up @@ -375,11 +375,13 @@ impl WlSeatGlobal {
let mut shortcuts = SmallVec::<[_; 1]>::new();
let new_mods;
{
if !self.state.lock.locked.get() && state == wl_keyboard::PRESSED {
let old_mods = xkb_state.mods();
if !self.state.lock.locked.get() {
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);
for &sym in keysyms {
let mods = old_mods.mods_effective & !(CAPS.0 | NUM.0);
if let Some(mods) = self.shortcuts.get(&(mods, sym)) {
shortcuts.push(ModifiedKeySym {
mods,
Expand All @@ -395,22 +397,28 @@ impl WlSeatGlobal {
});
let node = self.keyboard_node.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 {
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),
}
} 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 {
self.state.for_each_seat_tester(|t| {
Expand Down
1 change: 1 addition & 0 deletions toml-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub enum SimpleCommand {
ToggleFullscreen,
ToggleMono,
ToggleSplit,
Forward(bool),
}

#[derive(Debug, Clone)]
Expand Down
2 changes: 2 additions & 0 deletions toml-config/src/config/parsers/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ impl ActionParser<'_> {
"reload-config-toml" => ReloadConfigToml,
"reload-config-so" => ReloadConfigSo,
"none" => None,
"forward" => Forward(true),
"consume" => Forward(false),
_ => {
return Err(ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span))
}
Expand Down
6 changes: 5 additions & 1 deletion toml-config/src/config/parsers/modified_keysym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use {
toml::toml_span::{Span, SpannedExt},
},
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,
},
thiserror::Error,
Expand Down Expand Up @@ -49,6 +52,7 @@ impl Parser for ModifiedKeysymParser {
"alt" => ALT,
"num" => NUM,
"logo" => LOGO,
"release" => RELEASE,
_ => match KEYSYMS.get(part) {
Some(new) if sym.is_none() => {
sym = Some(*new);
Expand Down
1 change: 1 addition & 0 deletions toml-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl Action {
}
SimpleCommand::ReloadConfigSo => Box::new(reload),
SimpleCommand::None => Box::new(|| ()),
SimpleCommand::Forward(bool) => Box::new(move || s.set_forward(bool)),
},
Action::Multi { actions } => {
let mut actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
Expand Down
4 changes: 3 additions & 1 deletion toml-spec/spec/spec.generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@
"$ref": "#/$defs/RepeatRate"
},
"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",
"additionalProperties": {
"description": "",
Expand Down Expand Up @@ -1053,6 +1053,8 @@
"quit",
"reload-config-toml",
"reload-config-to",
"consume",
"forward",
"none"
]
},
Expand Down
25 changes: 24 additions & 1 deletion toml-spec/spec/spec.generated.md
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,10 @@ The table has the following fields:
```

`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
is [1] with the `XKB_KEY_` prefix removed.
Expand Down Expand Up @@ -2221,6 +2224,26 @@ The string should have one of the following values:

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`:

Perform no action.
Expand Down
23 changes: 22 additions & 1 deletion toml-spec/spec/spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,24 @@ SimpleActionName:
description: Reload the `config.toml`.
- value: reload-config-to
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
description: |
Perform no action.
Expand Down Expand Up @@ -1705,7 +1723,10 @@ Config:
```

`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
is [1] with the `XKB_KEY_` prefix removed.
Expand Down
Loading