1
0
Fork 0
forked from wry/wry

Make Super_L chordable and implement hyprland-global-shortcuts-v1

This commit is contained in:
entailz 2026-04-30 23:21:35 -07:00
parent 8ff17aca1e
commit 6d3bff952e
16 changed files with 363 additions and 16 deletions

View file

@ -204,6 +204,10 @@ pub enum Action {
dx2: i32,
dy2: i32,
},
TriggerGlobalShortcut {
app_id: String,
id: String,
},
}
#[derive(Debug, Clone, Default)]

View file

@ -93,6 +93,10 @@ pub enum ActionParserError {
UnknownDirection(String),
#[error("Exactly one of `output` or `direction` must be specified")]
OutputAndDirectionMutuallyExclusive,
#[error("Specify either `name = \"app_id:id\"` or both `app_id` and `id`")]
GlobalShortcutNeedsName,
#[error("Global shortcut `name` must be of the form `app_id:id`, got `{0}`")]
GlobalShortcutBadName(String),
}
pub struct ActionParser<'a>(pub &'a Context<'a>);
@ -516,6 +520,36 @@ impl ActionParser<'_> {
dy2: dy2.despan().unwrap_or(0),
})
}
fn parse_global_shortcut(
&mut self,
span: Span,
ext: &mut Extractor<'_>,
) -> ParseResult<Self> {
let (name_opt, app_id_opt, id_opt) = ext.extract((
opt(str("name")),
opt(str("app_id")),
opt(str("id")),
))?;
let (app_id, id) = match (app_id_opt, id_opt, name_opt) {
(Some(a), Some(i), _) => (a.value.to_string(), i.value.to_string()),
(None, None, Some(n)) => match n.value.split_once(':') {
Some((a, i)) if !a.is_empty() && !i.is_empty() => {
(a.to_string(), i.to_string())
}
_ => {
return Err(
ActionParserError::GlobalShortcutBadName(n.value.to_string())
.spanned(n.span),
);
}
},
_ => {
return Err(ActionParserError::GlobalShortcutNeedsName.spanned(span));
}
};
Ok(Action::TriggerGlobalShortcut { app_id, id })
}
}
impl Parser for ActionParser<'_> {
@ -578,6 +612,7 @@ impl Parser for ActionParser<'_> {
"create-virtual-output" => self.parse_create_virtual_output(&mut ext),
"remove-virtual-output" => self.parse_remove_virtual_output(&mut ext),
"resize" => self.parse_resize(&mut ext),
"global-shortcut" => self.parse_global_shortcut(span, &mut ext),
v => {
ext.ignore_unused();
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));

View file

@ -9,7 +9,10 @@ use {
ALT, CAPS, CTRL, LOCK, LOGO, MOD1, MOD2, MOD3, MOD4, MOD5, Modifiers, NUM, RELEASE,
SHIFT,
},
syms::KeySym,
syms::{
KeySym, SYM_Alt_L, SYM_Alt_R, SYM_Control_L, SYM_Control_R, SYM_Hyper_L, SYM_Hyper_R,
SYM_Meta_L, SYM_Meta_R, SYM_Shift_L, SYM_Shift_R, SYM_Super_L, SYM_Super_R,
},
},
kbvm::Keysym,
thiserror::Error,
@ -38,23 +41,28 @@ impl Parser for ModifiedKeysymParser {
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
let mut modifiers = Modifiers(0);
let mut sym = None;
let mut sym: Option<KeySym> = None;
for part in string.split("-") {
let modifier = match parse_mod(part) {
Some(m) => m,
_ => match Keysym::from_str(part) {
Some(new) if sym.is_none() => {
sym = Some(KeySym(new.0));
continue;
}
Some(_) => return Err(ModifiedKeysymParserError::MoreThanOneSym.spanned(span)),
_ => {
return Err(ModifiedKeysymParserError::UnknownKeysym(part.to_string())
.spanned(span));
}
},
if let Some(m) = parse_mod(part) {
modifiers |= m;
continue;
}
let Some(new) = Keysym::from_str(part) else {
return Err(
ModifiedKeysymParserError::UnknownKeysym(part.to_string()).spanned(span),
);
};
modifiers |= modifier;
let new = KeySym(new.0);
match sym {
None => sym = Some(new),
Some(prev) => {
let Some(m) = modifier_key_to_mod(prev) else {
return Err(ModifiedKeysymParserError::MoreThanOneSym.spanned(span));
};
modifiers |= m;
sym = Some(new);
}
}
}
match sym {
Some(s) => Ok(modifiers | s),
@ -63,6 +71,20 @@ impl Parser for ModifiedKeysymParser {
}
}
#[allow(non_upper_case_globals)]
fn modifier_key_to_mod(sym: KeySym) -> Option<Modifiers> {
let m = match sym {
SYM_Super_L | SYM_Super_R => LOGO,
SYM_Alt_L | SYM_Alt_R => ALT,
SYM_Control_L | SYM_Control_R => CTRL,
SYM_Shift_L | SYM_Shift_R => SHIFT,
SYM_Meta_L | SYM_Meta_R => MOD1,
SYM_Hyper_L | SYM_Hyper_R => MOD4,
_ => return None,
};
Some(m)
}
pub struct ModifiersParser;
impl Parser for ModifiersParser {

View file

@ -508,6 +508,11 @@ impl Action {
Action::Resize { dx1, dy1, dx2, dy2 } => {
window_or_seat!(s, s.resize(dx1, dy1, dx2, dy2))
}
Action::TriggerGlobalShortcut { app_id, id } => {
b.new(move || {
jay_config::trigger_global_shortcut(&app_id, &id);
})
}
}
}
}