initial commit
This commit is contained in:
commit
3d676babd5
3 changed files with 168 additions and 0 deletions
24
LICENSE
Normal file
24
LICENSE
Normal file
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2025 Daniel Fichtinger <daniel@ficd.sh>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the author nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
37
README.md
Normal file
37
README.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# foot scripts
|
||||
|
||||
[foot]: https://codeberg.org/dnkl/foot
|
||||
[`wtype`]: https://github.com/atx/wtype
|
||||
|
||||
This repository contains scripts for [foot].
|
||||
|
||||
## `foot-command`
|
||||
|
||||
This script implements a command palette for Foot. It allows you to
|
||||
interactively pick a command defined in your `foot.ini` keybindings, and
|
||||
simulate its corresponding keypress with [`wtype`]. Here's how it works:
|
||||
|
||||
1. Parses keybindings from the `[key-bindings]` section of `foot.ini`.
|
||||
2. Pipes the list of commands to a picker and waits for a selection.
|
||||
3. Sends the mapped keypress to to the focused window using `wtype`.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [`wtype`].
|
||||
- A dmenu-compatible picker (e.g. `fuzzel`).
|
||||
|
||||
### Usage
|
||||
|
||||
```sh
|
||||
foot-command.py [-c PATH_TO_CONFIG] [-p PICKER_COMMAND]
|
||||
```
|
||||
|
||||
I recommend binding this to a key in your window manager. You need to invoke the
|
||||
command when Foot is the focused window.
|
||||
|
||||
### Options
|
||||
|
||||
- `-c`, `--config`: Path to your `foot.ini` file. Defaults to
|
||||
`$HOME/.config/foot/foot.ini`.
|
||||
- `-p`, `--picker`: Picker to use (must support `dmenu` style input). Defaults
|
||||
to `fuzzel --dmenu --placeholder=Select a command:`.
|
107
foot-command.py
Executable file
107
foot-command.py
Executable file
|
@ -0,0 +1,107 @@
|
|||
#!/bin/env python3
|
||||
|
||||
# Depends: wtype
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from argparse import ArgumentParser
|
||||
from configparser import ConfigParser
|
||||
|
||||
ENV_CONFIG = "FOOT_CONFIG"
|
||||
ENV_PICKER = "FOOT_PICKER"
|
||||
DEFAULT_CONFIG = "$HOME/.config/foot.ini"
|
||||
|
||||
|
||||
def parse_config(path: str):
|
||||
config = ConfigParser(allow_unnamed_section=True)
|
||||
config.read(path)
|
||||
section = "key-bindings"
|
||||
pairs: list[tuple[str, str]] = config.items(section, raw=True)
|
||||
out: dict[str, str] = {}
|
||||
for pair in pairs:
|
||||
command = pair[0]
|
||||
bindings = pair[1].split(" ")
|
||||
if bindings[0] == "none":
|
||||
continue
|
||||
elif bindings[0][0] == "[":
|
||||
out[command] = bindings[1]
|
||||
else:
|
||||
out[command] = bindings[0]
|
||||
return out
|
||||
|
||||
|
||||
def get_query_string(mapping: dict[str, str]) -> str:
|
||||
return "\n".join(mapping.keys())
|
||||
|
||||
|
||||
def spawn_picker(cmd, query_string):
|
||||
process = subprocess.Popen(
|
||||
cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE
|
||||
)
|
||||
selection = process.communicate(input=query_string.encode("UTF-8"))
|
||||
if process.returncode != 2:
|
||||
return selection[0].decode("UTF-8").strip("\n")
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
MOD_MAP = {
|
||||
"Control": "ctrl",
|
||||
"Shift": "shift",
|
||||
"Mod1": "alt",
|
||||
}
|
||||
|
||||
|
||||
def get_wtype_args(selection: str, mapping: dict[str, str]):
|
||||
binding = mapping[selection]
|
||||
args: list[str] = ["wtype"]
|
||||
for k in binding.split("+"):
|
||||
if k in MOD_MAP:
|
||||
args += ["-M", MOD_MAP[k]]
|
||||
else:
|
||||
args += ["-k", k]
|
||||
return args
|
||||
|
||||
|
||||
def send_keys(args):
|
||||
process = subprocess.run(args)
|
||||
process.check_returncode()
|
||||
|
||||
|
||||
def validate_path(path: str) -> str:
|
||||
if path == DEFAULT_CONFIG:
|
||||
return str(os.environ["HOME"]) + "/.config/foot/foot.ini"
|
||||
else:
|
||||
return path
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--config",
|
||||
required=False,
|
||||
help="Absolute path to foot.ini file. (default: %(default)s)",
|
||||
default=DEFAULT_CONFIG,
|
||||
type=str,
|
||||
metavar=ENV_CONFIG,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--picker",
|
||||
required=False,
|
||||
help="Picker command to be used. (default: %(default)s)",
|
||||
type=str,
|
||||
default="fuzzel --dmenu --placeholder=Select a command:",
|
||||
metavar=ENV_PICKER,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
picker = args.picker
|
||||
foot_path = validate_path(args.config)
|
||||
mapping = parse_config(foot_path)
|
||||
query = get_query_string(mapping)
|
||||
selection = spawn_picker(picker, query)
|
||||
if selection is not None:
|
||||
wtype_args = get_wtype_args(selection, mapping)
|
||||
print(wtype_args)
|
||||
send_keys(wtype_args)
|
Loading…
Add table
Add a link
Reference in a new issue