Skip to content

Commit

Permalink
Merge pull request #135 from waycrate/keymodes_devel
Browse files Browse the repository at this point in the history
This is a PR that has merged all the other recent PRs.
  • Loading branch information
Shinyzenith committed Jun 24, 2022
2 parents d4d7f76 + b34ea17 commit e7a3cce
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 79 deletions.
11 changes: 7 additions & 4 deletions swhkd.1.scd
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ The config file goes in */etc/swhkd/swhkdrc*. Since swhkd is written with a pkex
*$XDG_CONFIG_HOME*.

This can be avoided as such:
- Using the *include* statement in your config file
- Using the *include* statement in your config file.
- Using the *-c* flag to mention a custom config file at runtime.
- Symlinking the config file from *~/.config/swhkd/swhkdrc* or any other directory of choice to */etc/swhkd/swhkdrc*
- Symlinking the config file from *~/.config/swhkd/swhkdrc* or any other directory of choice to */etc/swhkd/swhkdrc*.

More about the config file syntax in `swhkd(5)`

Expand All @@ -28,16 +28,19 @@ More about the config file syntax in `swhkd(5)`
*-v*, *--version*
Print version information.

*-c*, *--config*
*-c*, *--config* <CONFIG_FILE_PATH>
Set a custom config file path.

*-C*, *--cooldown*
*-C*, *--cooldown* <COOLDOWN_IN_MS>
Set a custom repeat cooldown duration. Default is 250ms. Most wayland
compositors handle this server side however, either way works.

*-d*, *--debug*
Enable debug mode.

-D --device <DEVICE_NAME>
Manually set the keyboard devices to use. Can occur multiple times.

# SIGNALS

- Reload config file: `sudo pkill -HUP swhkd`
Expand Down
22 changes: 21 additions & 1 deletion swhkd.5.scd
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ For valid keys and modifiers, check *swhkd-keys(5)*
# NOTE: the path provided must be absolute and not relative such as `~`.
include /home/YourUserName/.config/swhkd/swhkdrc
ignore alt + print # globally ignore a key binding
# terminal
super + ReTuRn # case insensitive
alacritty
Expand Down Expand Up @@ -63,7 +65,7 @@ super + {\,, .}
print
scrot
ctrl + print
any + print # any represent at least one of the valid modifiers
scrot -s
# Append with @ to run on key-release.
Expand All @@ -73,6 +75,24 @@ ctrl + print
# Append with ~ to emit the hotkey after the command is triggered. Aka, don't swallow the hotkey.
~super + shift + f
pcmanfm
super + m
# commands starting with @ are internal commands.
# internal commands can be combined with normal commands with '&&'.
# '@enter' pushes a mode into the mode stack and starts listening only the
# key bindings defined in that mode
@enter music && echo "music" > ~/.config/waybar/swhkd-mode
mode music # use the mode statement to define a mode
q
# '@escape' pops the current mode out of the mode stack
# the default mode is 'normal mode', which is always on the bottom of the mode
# stack and can never be escaped
@escape && echo "normal" > ~/.config/waybar/swhkd-mode
{n, p, space, r, z, y}
mpc {next, prev, toggle, repeat, random, single}
endmode # use endmode if you want to set more key bindings for normal mode
```
# AUTHORS

Expand Down
106 changes: 82 additions & 24 deletions swhkd/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ impl fmt::Display for Error {
}

pub const IMPORT_STATEMENT: &str = "include";
pub const UNBIND_STATEMENT: &str = "ignore";
pub const MODE_STATEMENT: &str = "mode";
pub const MODE_END_STATEMENT: &str = "endmode";
pub const MODE_ENTER_STATEMENT: &str = "@enter";
pub const MODE_ESCAPE_STATEMENT: &str = "@escape";

#[derive(Debug, PartialEq, Clone)]
pub struct Config {
Expand Down Expand Up @@ -102,7 +107,8 @@ impl Config {
Ok(configs)
}

pub fn load_and_merge(mut configs: Vec<Self>) -> Result<Vec<Self>, Error> {
pub fn load_and_merge(config: Self) -> Result<Vec<Self>, Error> {
let mut configs = vec![config];
let mut prev_count = 0;
let mut current_count = configs.len();
while prev_count != current_count {
Expand All @@ -120,17 +126,28 @@ impl Config {
}
}

pub fn load(path: &Path) -> Result<Vec<Hotkey>, Error> {
let mut hotkeys = Vec::new();
let configs = vec![Config::new(path)?];
for config in Config::load_and_merge(configs)? {
for hotkey in parse_contents(path.to_path_buf(), config.contents)? {
if !hotkeys.contains(&hotkey) {
hotkeys.push(hotkey);
}
pub fn load(path: &Path) -> Result<Vec<Mode>, Error> {
let config_self = Config::new(path)?;
let mut configs: Vec<Config> = Config::load_and_merge(config_self.clone())?;
configs.remove(0);
configs.push(config_self);
let mut modes: Vec<Mode> = vec![Mode::default()];
for config in configs {
let mut output = parse_contents(path.to_path_buf(), config.contents)?;
for hotkey in output[0].hotkeys.drain(..) {
modes[0].hotkeys.retain(|hk| hk.keybinding != hotkey.keybinding);
modes[0].hotkeys.push(hotkey);
}
for unbind in output[0].unbinds.drain(..) {
modes[0].hotkeys.retain(|hk| hk.keybinding != unbind);
}
output.remove(0);
for mut mode in output {
mode.hotkeys.retain(|x| !mode.unbinds.contains(&x.keybinding));
modes.push(mode);
}
}
Ok(hotkeys)
Ok(modes)
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -211,6 +228,7 @@ pub enum Modifier {
Alt,
Control,
Shift,
Any,
}

impl Hotkey {
Expand Down Expand Up @@ -249,7 +267,24 @@ impl Value for &Hotkey {
}
}

pub fn parse_contents(path: PathBuf, contents: String) -> Result<Vec<Hotkey>, Error> {
#[derive(Debug, Clone, PartialEq)]
pub struct Mode {
pub name: String,
pub hotkeys: Vec<Hotkey>,
pub unbinds: Vec<KeyBinding>,
}

impl Mode {
pub fn new(name: String) -> Self {
Mode { name, hotkeys: Vec::new(), unbinds: Vec::new() }
}

pub fn default() -> Self {
Self::new("normal".to_string())
}
}

pub fn parse_contents(path: PathBuf, contents: String) -> Result<Vec<Mode>, Error> {
// Don't forget to update valid key list on the man page if you do change this list.
let key_to_evdev_key: HashMap<&str, evdev::Key> = HashMap::from([
("q", evdev::Key::KEY_Q),
Expand Down Expand Up @@ -409,9 +444,12 @@ pub fn parse_contents(path: PathBuf, contents: String) -> Result<Vec<Hotkey>, Er
("alt", Modifier::Alt),
("mod1", Modifier::Alt),
("shift", Modifier::Shift),
("any", Modifier::Any),
]);

let lines: Vec<&str> = contents.split('\n').collect();
let mut modes: Vec<Mode> = vec![Mode::default()];
let mut current_mode: usize = 0;

// Go through each line, ignore comments and empty lines, mark lines starting with whitespace
// as commands, and mark the other lines as keysyms. Mark means storing a line's type and the
Expand All @@ -426,14 +464,20 @@ pub fn parse_contents(path: PathBuf, contents: String) -> Result<Vec<Hotkey>, Er
}
if line.starts_with(' ') || line.starts_with('\t') {
lines_with_types.push(("command", line_number as u32));
} else if line.starts_with(UNBIND_STATEMENT) {
lines_with_types.push(("unbind", line_number as u32));
} else if line.starts_with(MODE_STATEMENT) {
lines_with_types.push(("modestart", line_number as u32));
} else if line.starts_with(MODE_END_STATEMENT) {
lines_with_types.push(("modeend", line_number as u32));
} else {
lines_with_types.push(("keysym", line_number as u32));
}
}

// Edge case: return a blank vector if no lines detected
if lines_with_types.is_empty() {
return Ok(vec![]);
return Ok(modes);
}

let mut actual_lines: Vec<(&str, u32, String)> = Vec::new();
Expand Down Expand Up @@ -479,13 +523,32 @@ pub fn parse_contents(path: PathBuf, contents: String) -> Result<Vec<Hotkey>, Er

drop(lines);

let mut hotkeys: Vec<Hotkey> = Vec::new();

for (i, item) in actual_lines.iter().enumerate() {
let line_type = item.0;
let line_number = item.1;
let line = &item.2;

if line_type == "unbind" {
let to_unbind = line.trim_start_matches(UNBIND_STATEMENT).trim();
modes[current_mode].unbinds.push(parse_keybind(
path.clone(),
to_unbind,
line_number + 1,
&key_to_evdev_key,
&mod_to_mod_enum,
)?);
}

if line_type == "modestart" {
let modename = line.split(' ').nth(1).unwrap();
modes.push(Mode::new(modename.to_string()));
current_mode = modes.len() - 1;
}

if line_type == "modeend" {
current_mode = 0;
}

if line_type != "keysym" {
continue;
}
Expand All @@ -503,7 +566,7 @@ pub fn parse_contents(path: PathBuf, contents: String) -> Result<Vec<Hotkey>, Er
let extracted_keys = extract_curly_brace(line);
let extracted_commands = extract_curly_brace(&next_line.2);

'hotkey_parse: for (key, command) in extracted_keys.iter().zip(extracted_commands.iter()) {
for (key, command) in extracted_keys.iter().zip(extracted_commands.iter()) {
let keybinding = parse_keybind(
path.clone(),
key,
Expand All @@ -513,18 +576,13 @@ pub fn parse_contents(path: PathBuf, contents: String) -> Result<Vec<Hotkey>, Er
)?;
let hotkey = Hotkey::from_keybinding(keybinding, command.to_string());

// Ignore duplicate hotkeys
for i in hotkeys.iter() {
if i.keybinding == hotkey.keybinding {
continue 'hotkey_parse;
}
}

hotkeys.push(hotkey);
// Override latter
modes[current_mode].hotkeys.retain(|h| h.keybinding != hotkey.keybinding);
modes[current_mode].hotkeys.push(hotkey);
}
}

Ok(hotkeys)
Ok(modes)
}

// We need to get the reference to key_to_evdev_key
Expand Down
Loading

0 comments on commit e7a3cce

Please sign in to comment.