i3-bind 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- i3_bind-1.0.0/PKG-INFO +46 -0
- i3_bind-1.0.0/README.md +31 -0
- i3_bind-1.0.0/i3_bind/__init__.py +0 -0
- i3_bind-1.0.0/i3_bind/main.py +228 -0
- i3_bind-1.0.0/i3_bind.egg-info/PKG-INFO +46 -0
- i3_bind-1.0.0/i3_bind.egg-info/SOURCES.txt +9 -0
- i3_bind-1.0.0/i3_bind.egg-info/dependency_links.txt +1 -0
- i3_bind-1.0.0/i3_bind.egg-info/entry_points.txt +2 -0
- i3_bind-1.0.0/i3_bind.egg-info/top_level.txt +1 -0
- i3_bind-1.0.0/pyproject.toml +21 -0
- i3_bind-1.0.0/setup.cfg +4 -0
i3_bind-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: i3-bind
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Add, remove, and list i3 keybindings from the command line
|
|
5
|
+
Author-email: "@readwith" <talwrii@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/talwrii/i3-bind
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Desktop Environment
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# i3-bind
|
|
17
|
+
Explore and create bindings for the i3wm window manager.
|
|
18
|
+
|
|
19
|
+
Unreviewed AI-generated code. ButI use it.
|
|
20
|
+
|
|
21
|
+
## Alternatives and prior work
|
|
22
|
+
You could edit `~/.config/i3/config` by hand or use `grep` on it. However, I am lazy. I could find now tool providing this functionality.
|
|
23
|
+
|
|
24
|
+
I made a tool called kde-bind which is this tool or kde. `i3keys-gui` which provides a gui for exploring bindings.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
`pipx install i3-bind`
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
List bindings:
|
|
31
|
+
|
|
32
|
+
`i3-bind`
|
|
33
|
+
|
|
34
|
+
Create a binding:
|
|
35
|
+
|
|
36
|
+
`i3-bind meta+shift+n gtk-launch focus-obsidina.desktop`
|
|
37
|
+
|
|
38
|
+
Run reload after you run the command. I have a keybinding in i3 for reload.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## About
|
|
42
|
+
I am @readwith. I make tools for reading research and agency with and without AI. You can follow me on [X](https://x.com/readwithai) or my [blog](https://readwithai.substack.com/).
|
|
43
|
+
|
|
44
|
+
I also produce a stream of small tools like this. If that sounds interesting I suggest following me on X.
|
|
45
|
+
|
|
46
|
+
|
i3_bind-1.0.0/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# i3-bind
|
|
2
|
+
Explore and create bindings for the i3wm window manager.
|
|
3
|
+
|
|
4
|
+
Unreviewed AI-generated code. ButI use it.
|
|
5
|
+
|
|
6
|
+
## Alternatives and prior work
|
|
7
|
+
You could edit `~/.config/i3/config` by hand or use `grep` on it. However, I am lazy. I could find now tool providing this functionality.
|
|
8
|
+
|
|
9
|
+
I made a tool called kde-bind which is this tool or kde. `i3keys-gui` which provides a gui for exploring bindings.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
`pipx install i3-bind`
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
List bindings:
|
|
16
|
+
|
|
17
|
+
`i3-bind`
|
|
18
|
+
|
|
19
|
+
Create a binding:
|
|
20
|
+
|
|
21
|
+
`i3-bind meta+shift+n gtk-launch focus-obsidina.desktop`
|
|
22
|
+
|
|
23
|
+
Run reload after you run the command. I have a keybinding in i3 for reload.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## About
|
|
27
|
+
I am @readwith. I make tools for reading research and agency with and without AI. You can follow me on [X](https://x.com/readwithai) or my [blog](https://readwithai.substack.com/).
|
|
28
|
+
|
|
29
|
+
I also produce a stream of small tools like this. If that sounds interesting I suggest following me on X.
|
|
30
|
+
|
|
31
|
+
|
|
File without changes
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
i3-bind — explore and create i3 keybindings from the command line.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
import argparse
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
# ── Config discovery ──────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
CONFIG_PATHS = [
|
|
19
|
+
Path.home() / ".config/i3/config",
|
|
20
|
+
Path.home() / ".i3/config",
|
|
21
|
+
Path("/etc/i3/config"),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def find_config(override=None):
|
|
26
|
+
if override:
|
|
27
|
+
p = Path(override)
|
|
28
|
+
if not p.exists():
|
|
29
|
+
die(f"config not found: {p}")
|
|
30
|
+
return p
|
|
31
|
+
for p in CONFIG_PATHS:
|
|
32
|
+
if p.exists():
|
|
33
|
+
return p
|
|
34
|
+
die("no i3 config found")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def die(msg):
|
|
38
|
+
print(f"i3-bind: {msg}", file=sys.stderr)
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ── Parsing ───────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
def get_mod_var(config_path):
|
|
45
|
+
for line in config_path.read_text().splitlines():
|
|
46
|
+
m = re.match(r'^set\s+\$mod\s+(\S+)', line)
|
|
47
|
+
if m:
|
|
48
|
+
return m.group(1)
|
|
49
|
+
return "Mod4"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def iter_bindings(config_path):
|
|
53
|
+
mod_var = get_mod_var(config_path)
|
|
54
|
+
current_mode = "default"
|
|
55
|
+
brace_depth = 0
|
|
56
|
+
|
|
57
|
+
for raw in config_path.read_text().splitlines():
|
|
58
|
+
line = raw.strip()
|
|
59
|
+
if line.startswith("# "):
|
|
60
|
+
continue
|
|
61
|
+
m = re.match(r'^mode\s+["\']?([^"\'{}\\s]+)["\']?\s*{?', line)
|
|
62
|
+
if m and not line.startswith("bindsym"):
|
|
63
|
+
current_mode = m.group(1)
|
|
64
|
+
if "{" in line:
|
|
65
|
+
brace_depth += 1
|
|
66
|
+
continue
|
|
67
|
+
opens = line.count("{")
|
|
68
|
+
closes = line.count("}")
|
|
69
|
+
if opens and not re.match(r'^mode\b', line):
|
|
70
|
+
brace_depth += opens
|
|
71
|
+
if closes:
|
|
72
|
+
brace_depth -= closes
|
|
73
|
+
if brace_depth <= 0:
|
|
74
|
+
brace_depth = 0
|
|
75
|
+
current_mode = "default"
|
|
76
|
+
continue
|
|
77
|
+
m = re.match(r'^bindsym\s+(\S+)\s+(.*)', line)
|
|
78
|
+
if m:
|
|
79
|
+
key = m.group(1).replace("$mod", mod_var)
|
|
80
|
+
yield current_mode, key, m.group(2).strip()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ── Config writing ────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
def backup(config_path):
|
|
86
|
+
shutil.copy2(config_path, config_path.with_suffix(".bak"))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def do_add(key, command, mode, config_path):
|
|
90
|
+
text = config_path.read_text()
|
|
91
|
+
line = f"bindsym {key} {command}\n"
|
|
92
|
+
backup(config_path)
|
|
93
|
+
if mode == "default":
|
|
94
|
+
text = text.rstrip("\n") + "\n\n" + line
|
|
95
|
+
else:
|
|
96
|
+
pattern = re.compile(
|
|
97
|
+
r'(^mode\s+["\']?' + re.escape(mode) + r'["\']?\s*\{[^}]*)(\})',
|
|
98
|
+
re.MULTILINE | re.DOTALL
|
|
99
|
+
)
|
|
100
|
+
m = pattern.search(text)
|
|
101
|
+
if not m:
|
|
102
|
+
die(f"mode '{mode}' not found in config")
|
|
103
|
+
text = text[:m.start(2)] + " " + line + text[m.start(2):]
|
|
104
|
+
config_path.write_text(text)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def do_delete(key, mode, config_path):
|
|
108
|
+
mod_var = get_mod_var(config_path)
|
|
109
|
+
lines = config_path.read_text().splitlines(keepends=True)
|
|
110
|
+
current_mode = "default"
|
|
111
|
+
brace_depth = 0
|
|
112
|
+
target = None
|
|
113
|
+
|
|
114
|
+
for i, raw in enumerate(lines):
|
|
115
|
+
line = raw.strip()
|
|
116
|
+
m = re.match(r'^mode\s+["\']?([^"\'{}\\s]+)["\']?\s*{?', line)
|
|
117
|
+
if m and not line.startswith("bindsym"):
|
|
118
|
+
current_mode = m.group(1)
|
|
119
|
+
if "{" in line:
|
|
120
|
+
brace_depth += 1
|
|
121
|
+
continue
|
|
122
|
+
opens = line.count("{")
|
|
123
|
+
closes = line.count("}")
|
|
124
|
+
if opens and not re.match(r'^mode\b', line):
|
|
125
|
+
brace_depth += opens
|
|
126
|
+
if closes:
|
|
127
|
+
brace_depth -= closes
|
|
128
|
+
if brace_depth <= 0:
|
|
129
|
+
brace_depth = 0
|
|
130
|
+
current_mode = "default"
|
|
131
|
+
continue
|
|
132
|
+
m = re.match(r'^bindsym\s+(\S+)', line)
|
|
133
|
+
if m and current_mode == mode:
|
|
134
|
+
if m.group(1).replace("$mod", mod_var) == key:
|
|
135
|
+
target = i
|
|
136
|
+
break
|
|
137
|
+
|
|
138
|
+
if target is None:
|
|
139
|
+
return False
|
|
140
|
+
backup(config_path)
|
|
141
|
+
lines[target] = "# [deleted] " + lines[target]
|
|
142
|
+
config_path.write_text("".join(lines))
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def reload_i3():
|
|
147
|
+
subprocess.Popen(["i3-msg", "reload"],
|
|
148
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ── Commands ──────────────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
def cmd_list(args):
|
|
154
|
+
config = find_config(args.config)
|
|
155
|
+
for mode, key, command in iter_bindings(config):
|
|
156
|
+
if args.mode and args.mode != "default" and mode != args.mode:
|
|
157
|
+
continue
|
|
158
|
+
if args.mode and args.mode != "default":
|
|
159
|
+
print(f"{key}\t{command}")
|
|
160
|
+
else:
|
|
161
|
+
print(f"{mode}\t{key}\t{command}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def cmd_add(args):
|
|
165
|
+
config = find_config(args.config)
|
|
166
|
+
do_add(args.key, args.command, args.mode, config)
|
|
167
|
+
suffix = f" (mode: {args.mode})" if args.mode != "default" else ""
|
|
168
|
+
print(f"added: bindsym {args.key} {args.command}{suffix}")
|
|
169
|
+
if not args.no_reload:
|
|
170
|
+
reload_i3()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def cmd_delete(args):
|
|
174
|
+
config = find_config(args.config)
|
|
175
|
+
ok = do_delete(args.key, args.mode, config)
|
|
176
|
+
if not ok:
|
|
177
|
+
suffix = f" in mode {args.mode}" if args.mode != "default" else ""
|
|
178
|
+
die(f"binding not found: {args.key}{suffix}")
|
|
179
|
+
suffix = f" (mode: {args.mode})" if args.mode != "default" else ""
|
|
180
|
+
print(f"deleted: {args.key}{suffix}")
|
|
181
|
+
if not args.no_reload:
|
|
182
|
+
reload_i3()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def cmd_modes(args):
|
|
186
|
+
config = find_config(args.config)
|
|
187
|
+
seen = dict.fromkeys(mode for mode, *_ in iter_bindings(config))
|
|
188
|
+
for m in seen:
|
|
189
|
+
print(m)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# ── Entry point ───────────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
def main():
|
|
195
|
+
parser = argparse.ArgumentParser(
|
|
196
|
+
prog="i3-bind",
|
|
197
|
+
description="Explore and create i3 keybindings from the command line.")
|
|
198
|
+
parser.add_argument("--config", default=None)
|
|
199
|
+
parser.add_argument("--no-reload", action="store_true")
|
|
200
|
+
parser.add_argument("--mode", default="default",
|
|
201
|
+
help="i3 mode (default: default)")
|
|
202
|
+
|
|
203
|
+
sub = parser.add_subparsers(dest="command")
|
|
204
|
+
|
|
205
|
+
p_add = sub.add_parser("add", help="Add a keybinding")
|
|
206
|
+
p_add.add_argument("key")
|
|
207
|
+
p_add.add_argument("command")
|
|
208
|
+
p_add.set_defaults(func=cmd_add)
|
|
209
|
+
|
|
210
|
+
p_del = sub.add_parser("delete", aliases=["del", "rm"])
|
|
211
|
+
p_del.add_argument("key")
|
|
212
|
+
p_del.set_defaults(func=cmd_delete)
|
|
213
|
+
|
|
214
|
+
p_list = sub.add_parser("list", aliases=["ls"])
|
|
215
|
+
p_list.set_defaults(func=cmd_list)
|
|
216
|
+
|
|
217
|
+
p_modes = sub.add_parser("modes")
|
|
218
|
+
p_modes.set_defaults(func=cmd_modes)
|
|
219
|
+
|
|
220
|
+
args = parser.parse_args()
|
|
221
|
+
if args.command is None:
|
|
222
|
+
cmd_list(args)
|
|
223
|
+
else:
|
|
224
|
+
args.func(args)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if __name__ == "__main__":
|
|
228
|
+
main()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: i3-bind
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Add, remove, and list i3 keybindings from the command line
|
|
5
|
+
Author-email: "@readwith" <talwrii@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/talwrii/i3-bind
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Desktop Environment
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# i3-bind
|
|
17
|
+
Explore and create bindings for the i3wm window manager.
|
|
18
|
+
|
|
19
|
+
Unreviewed AI-generated code. ButI use it.
|
|
20
|
+
|
|
21
|
+
## Alternatives and prior work
|
|
22
|
+
You could edit `~/.config/i3/config` by hand or use `grep` on it. However, I am lazy. I could find now tool providing this functionality.
|
|
23
|
+
|
|
24
|
+
I made a tool called kde-bind which is this tool or kde. `i3keys-gui` which provides a gui for exploring bindings.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
`pipx install i3-bind`
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
List bindings:
|
|
31
|
+
|
|
32
|
+
`i3-bind`
|
|
33
|
+
|
|
34
|
+
Create a binding:
|
|
35
|
+
|
|
36
|
+
`i3-bind meta+shift+n gtk-launch focus-obsidina.desktop`
|
|
37
|
+
|
|
38
|
+
Run reload after you run the command. I have a keybinding in i3 for reload.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## About
|
|
42
|
+
I am @readwith. I make tools for reading research and agency with and without AI. You can follow me on [X](https://x.com/readwithai) or my [blog](https://readwithai.substack.com/).
|
|
43
|
+
|
|
44
|
+
I also produce a stream of small tools like this. If that sounds interesting I suggest following me on X.
|
|
45
|
+
|
|
46
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
i3_bind
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [ "setuptools>=68.0",]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "i3-bind"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Add, remove, and list i3 keybindings from the command line"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Topic :: Desktop Environment",]
|
|
13
|
+
[[project.authors]]
|
|
14
|
+
name = "@readwith"
|
|
15
|
+
email = "talwrii@gmail.com"
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Homepage = "https://github.com/talwrii/i3-bind"
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
i3-bind = "i3_bind.main:main"
|
i3_bind-1.0.0/setup.cfg
ADDED