1
0
Fork 0
forked from wry/wry

config: implement shortcut latching

This commit is contained in:
Julian Orth 2024-04-16 18:47:40 +02:00
parent 90dbde99ab
commit 6f55675bdb
14 changed files with 367 additions and 93 deletions

View file

@ -285,6 +285,7 @@ pub struct Shortcut {
pub mask: Modifiers,
pub keysym: ModifiedKeySym,
pub action: Action,
pub latch: Option<Action>,
}
#[derive(Debug, Clone)]

View file

@ -34,6 +34,8 @@ pub enum ShortcutsParserError {
ModMask(#[source] ModifiedKeysymParserError),
#[error("Could not parse the action")]
ActionParserError(#[source] ActionParserError),
#[error("Could not parse the latch action")]
LatchError(#[source] ActionParserError),
}
pub struct ShortcutsParser<'a, 'b> {
@ -65,6 +67,7 @@ impl Parser for ShortcutsParser<'_, '_> {
mask: Modifiers(!0),
keysym,
action,
latch: None,
});
}
Ok(())
@ -129,7 +132,8 @@ impl Parser for ComplexShortcutParser<'_> {
table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> {
let mut ext = Extractor::new(self.cx, span, table);
let (mod_mask_val, action_val) = ext.extract((opt(str("mod-mask")), opt(val("action"))))?;
let (mod_mask_val, action_val, latch_val) =
ext.extract((opt(str("mod-mask")), opt(val("action")), opt(val("latch"))))?;
let mod_mask = match mod_mask_val {
None => Modifiers(!0),
Some(v) => ModifiersParser
@ -144,10 +148,18 @@ impl Parser for ComplexShortcutParser<'_> {
.parse(&mut ActionParser(self.cx))
.map_spanned_err(ShortcutsParserError::ActionParserError)?,
};
let mut latch = None;
if let Some(v) = latch_val {
latch = Some(
v.parse(&mut ActionParser(self.cx))
.map_spanned_err(ShortcutsParserError::LatchError)?,
);
}
Ok(Shortcut {
mask: mod_mask,
keysym: self.keysym,
action,
latch,
})
}
}

View file

@ -37,51 +37,75 @@ fn default_seat() -> Seat {
get_seat("default")
}
trait FnBuilder: Sized {
fn new<F: Fn() + 'static>(f: F) -> Self;
}
impl FnBuilder for Box<dyn Fn()> {
fn new<F: Fn() + 'static>(f: F) -> Self {
Box::new(f)
}
}
impl FnBuilder for Rc<dyn Fn()> {
fn new<F: Fn() + 'static>(f: F) -> Self {
Rc::new(f)
}
}
impl Action {
fn into_fn(self, state: &Rc<State>) -> Box<dyn FnMut()> {
fn into_fn(self, state: &Rc<State>) -> Box<dyn Fn()> {
self.into_fn_impl(state)
}
fn into_rc_fn(self, state: &Rc<State>) -> Rc<dyn Fn()> {
self.into_fn_impl(state)
}
fn into_fn_impl<B: FnBuilder>(self, state: &Rc<State>) -> B {
let s = state.persistent.seat;
match self {
Action::SimpleCommand { cmd } => match cmd {
SimpleCommand::Focus(dir) => Box::new(move || s.focus(dir)),
SimpleCommand::Move(dir) => Box::new(move || s.move_(dir)),
SimpleCommand::Split(axis) => Box::new(move || s.create_split(axis)),
SimpleCommand::ToggleSplit => Box::new(move || s.toggle_split()),
SimpleCommand::ToggleMono => Box::new(move || s.toggle_mono()),
SimpleCommand::ToggleFullscreen => Box::new(move || s.toggle_fullscreen()),
SimpleCommand::FocusParent => Box::new(move || s.focus_parent()),
SimpleCommand::Close => Box::new(move || s.close()),
SimpleCommand::Focus(dir) => B::new(move || s.focus(dir)),
SimpleCommand::Move(dir) => B::new(move || s.move_(dir)),
SimpleCommand::Split(axis) => B::new(move || s.create_split(axis)),
SimpleCommand::ToggleSplit => B::new(move || s.toggle_split()),
SimpleCommand::ToggleMono => B::new(move || s.toggle_mono()),
SimpleCommand::ToggleFullscreen => B::new(move || s.toggle_fullscreen()),
SimpleCommand::FocusParent => B::new(move || s.focus_parent()),
SimpleCommand::Close => B::new(move || s.close()),
SimpleCommand::DisablePointerConstraint => {
Box::new(move || s.disable_pointer_constraint())
B::new(move || s.disable_pointer_constraint())
}
SimpleCommand::ToggleFloating => Box::new(move || s.toggle_floating()),
SimpleCommand::Quit => Box::new(quit),
SimpleCommand::ToggleFloating => B::new(move || s.toggle_floating()),
SimpleCommand::Quit => B::new(quit),
SimpleCommand::ReloadConfigToml => {
let persistent = state.persistent.clone();
Box::new(move || load_config(false, &persistent))
B::new(move || load_config(false, &persistent))
}
SimpleCommand::ReloadConfigSo => Box::new(reload),
SimpleCommand::None => Box::new(|| ()),
SimpleCommand::Forward(bool) => Box::new(move || s.set_forward(bool)),
SimpleCommand::ReloadConfigSo => B::new(reload),
SimpleCommand::None => B::new(|| ()),
SimpleCommand::Forward(bool) => B::new(move || s.set_forward(bool)),
},
Action::Multi { actions } => {
let mut actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
Box::new(move || {
for action in &mut actions {
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
B::new(move || {
for action in &actions {
action();
}
})
}
Action::Exec { exec } => Box::new(move || create_command(&exec).spawn()),
Action::SwitchToVt { num } => Box::new(move || switch_to_vt(num)),
Action::Exec { exec } => B::new(move || create_command(&exec).spawn()),
Action::SwitchToVt { num } => B::new(move || switch_to_vt(num)),
Action::ShowWorkspace { name } => {
let workspace = get_workspace(&name);
Box::new(move || s.show_workspace(workspace))
B::new(move || s.show_workspace(workspace))
}
Action::MoveToWorkspace { name } => {
let workspace = get_workspace(&name);
Box::new(move || s.set_workspace(workspace))
B::new(move || s.set_workspace(workspace))
}
Action::ConfigureConnector { con } => Box::new(move || {
Action::ConfigureConnector { con } => B::new(move || {
for c in connectors() {
if con.match_.matches(c) {
con.apply(c);
@ -90,7 +114,7 @@ impl Action {
}),
Action::ConfigureInput { input } => {
let state = state.clone();
Box::new(move || {
B::new(move || {
for c in input_devices() {
if input.match_.matches(c, &state) {
input.apply(c, &state);
@ -100,7 +124,7 @@ impl Action {
}
Action::ConfigureOutput { out } => {
let state = state.clone();
Box::new(move || {
B::new(move || {
for c in connectors() {
if out.match_.matches(c, &state) {
out.apply(c);
@ -108,36 +132,36 @@ impl Action {
}
})
}
Action::SetEnv { env } => Box::new(move || {
Action::SetEnv { env } => B::new(move || {
for (k, v) in &env {
set_env(k, v);
}
}),
Action::UnsetEnv { env } => Box::new(move || {
Action::UnsetEnv { env } => B::new(move || {
for k in &env {
unset_env(k);
}
}),
Action::SetKeymap { map } => {
let state = state.clone();
Box::new(move || state.set_keymap(&map))
B::new(move || state.set_keymap(&map))
}
Action::SetStatus { status } => {
let state = state.clone();
Box::new(move || state.set_status(&status))
B::new(move || state.set_status(&status))
}
Action::SetTheme { theme } => {
let state = state.clone();
Box::new(move || state.apply_theme(&theme))
B::new(move || state.apply_theme(&theme))
}
Action::SetLogLevel { level } => Box::new(move || set_log_level(level)),
Action::SetGfxApi { api } => Box::new(move || set_gfx_api(api)),
Action::SetLogLevel { level } => B::new(move || set_log_level(level)),
Action::SetGfxApi { api } => B::new(move || set_gfx_api(api)),
Action::ConfigureDirectScanout { enabled } => {
Box::new(move || set_direct_scanout_enabled(enabled))
B::new(move || set_direct_scanout_enabled(enabled))
}
Action::ConfigureDrmDevice { dev } => {
let state = state.clone();
Box::new(move || {
B::new(move || {
for d in drm_devices() {
if dev.match_.matches(d, &state) {
dev.apply(d);
@ -147,7 +171,7 @@ impl Action {
}
Action::SetRenderDevice { dev } => {
let state = state.clone();
Box::new(move || {
B::new(move || {
for d in drm_devices() {
if dev.matches(d, &state) {
d.make_render_device();
@ -155,10 +179,10 @@ impl Action {
}
})
}
Action::ConfigureIdle { idle } => Box::new(move || set_idle(Some(idle))),
Action::ConfigureIdle { idle } => B::new(move || set_idle(Some(idle))),
Action::MoveToOutput { output, workspace } => {
let state = state.clone();
Box::new(move || {
B::new(move || {
let output = 'get_output: {
for connector in connectors() {
if connector.connected() && output.matches(connector, &state) {
@ -174,7 +198,7 @@ impl Action {
})
}
Action::SetRepeatRate { rate } => {
Box::new(move || s.set_repeat_rate(rate.rate, rate.delay))
B::new(move || s.set_repeat_rate(rate.rate, rate.delay))
}
}
}
@ -548,16 +572,26 @@ impl State {
cmd: SimpleCommand::None,
} = shortcut.action
{
self.persistent.seat.unbind(shortcut.keysym);
binds.remove(&shortcut.keysym);
} else {
self.persistent.seat.bind_masked(
shortcut.mask,
shortcut.keysym,
shortcut.action.into_fn(self),
);
binds.insert(shortcut.keysym);
if shortcut.latch.is_none() {
self.persistent.seat.unbind(shortcut.keysym);
binds.remove(&shortcut.keysym);
continue;
}
}
let mut f = shortcut.action.into_fn(self);
if let Some(l) = shortcut.latch {
let l = l.into_rc_fn(self);
let s = self.persistent.seat;
f = Box::new(move || {
f();
let l = l.clone();
s.latch(move || l());
});
}
self.persistent
.seat
.bind_masked(shortcut.mask, shortcut.keysym, move || f());
binds.insert(shortcut.keysym);
}
}