Allow multiple programs at once with a config file.

This commit is contained in:
redxef 2022-10-20 13:45:39 +02:00
parent ec8bb73d1c
commit 089fc6fe22
Signed by: redxef
GPG key ID: 7DAC3AA211CBD921
3 changed files with 83 additions and 9 deletions

View file

@ -2,6 +2,26 @@
Launch a program and move it to the correct workspace. Launch a program and move it to the correct workspace.
## Usage
- **simple:** `i3toolwait simple ...`
- **config:** `i3toolwait config ...`
### Simple
Run only one program.
### Config
Run multiple programs by specifying a yaml list of the form:
```yaml
---
- filter: <your filter>
workspace: <target workspace>
program: <program to execute>
```
## Installing ## Installing
Use the makefile: `INSTALL_BASE=/usr/local/ make install` or install all dependencies Use the makefile: `INSTALL_BASE=/usr/local/ make install` or install all dependencies

View file

@ -6,9 +6,15 @@ import functools
import json import json
import time import time
import yaml
import click import click
import i3ipc import i3ipc
try:
from yaml import CSafeLoader as SafeLoader
except ImportError:
from yaml import SafeLoader
class Expression: class Expression:
def __init__(self): def __init__(self):
pass pass
@ -263,25 +269,37 @@ def parse(s: str) -> Expression:
return build_expression(tokens) return build_expression(tokens)
def window_new(filter, *, workspace, debug): def window_new(configs, debug):
def callback(ipc, e): def callback(ipc, e):
assert e.change == 'new' assert e.change == 'new'
if debug: if debug:
print(json.dumps(e.ipc_data)) print(json.dumps(e.ipc_data))
for i, cfg in enumerate(configs):
filter = cfg['filter']
workspace = cfg['workspace']
if filter.reduce(e.ipc_data): if filter.reduce(e.ipc_data):
container_id = e.ipc_data['container']['id'] container_id = e.ipc_data['container']['id']
ipc.command(f'for_window [con_id="{container_id}"] focus')
ipc.command(f'for_window [con_id="{container_id}"] move container to workspace {workspace}') ipc.command(f'for_window [con_id="{container_id}"] move container to workspace {workspace}')
configs.pop(i)
if not configs:
ipc.main_quit() ipc.main_quit()
return callback return callback
@click.command() @click.group()
@click.pass_context
@click.option('--debug', '-d', default=False, is_flag=True, help="Enable debug mode, will log ipc dictionary.")
def main(ctx, debug):
ctx.ensure_object(dict)
ctx.obj['DEBUG'] = debug
@main.command()
@click.pass_context @click.pass_context
@click.option('--filter', '-f', default='True', help="A filter expression for the raw ipc dictionary.") @click.option('--filter', '-f', default='True', help="A filter expression for the raw ipc dictionary.")
@click.option('--debug', '-d', default=False, is_flag=True, help="Enable debug mode, will log ipc dictionary.")
@click.option('--timeout', '-t', default=3000, help="Wait time for a window to appear (and match) in milliseconds.") @click.option('--timeout', '-t', default=3000, help="Wait time for a window to appear (and match) in milliseconds.")
@click.option('--workspace', '-w', required=True, help="The workspace to move to.") @click.option('--workspace', '-w', required=True, help="The workspace to move to.")
@click.argument('program', nargs=-1) @click.argument('program', nargs=-1)
def main(ctx, filter, debug, timeout, workspace, program): def simple(ctx, filter, timeout, workspace, program):
""" """
Start a program and move it's created window to the desired i3 workspace. Start a program and move it's created window to the desired i3 workspace.
@ -290,17 +308,52 @@ def main(ctx, filter, debug, timeout, workspace, program):
0 on success, 0 on success,
1 when no window has been found. 1 when no window has been found.
""" """
debug = ctx.obj['DEBUG']
filter = parse(filter) filter = parse(filter)
program = ' '.join(program) program = ' '.join(program)
configs=[
{
"filter": filter,
"workspace": workspace,
},
]
ipc = i3ipc.Connection() ipc = i3ipc.Connection()
ipc.on('window::new', window_new(filter, workspace=workspace, debug=debug)) ipc.on('window::new', window_new(configs, debug=debug))
ipc.command(f'exec {program}') ipc.command(f'exec {program}')
started_at = time.monotonic_ns() // (1000*1000) started_at = time.monotonic_ns() // (1000*1000)
ipc.main(timeout=timeout / 1000) ipc.main(timeout=timeout / 1000)
total_time = time.monotonic_ns() // (1000*1000) - started_at total_time = time.monotonic_ns() // (1000*1000) - started_at
ctx.exit(int(total_time >= timeout)) ctx.exit(int(total_time >= timeout))
@main.command()
@click.pass_context
@click.option('--timeout', '-t', default=3000, help="Wait time for a window to appear (and match) in milliseconds.")
@click.option('--configs', '-c', default='-', type=click.File('r'), help="A list of startup programs in json")
@click.argument('program', nargs=-1)
def config(ctx, timeout, configs, program):
"""
Start a program and move it's created window to the desired i3 workspace.
\b
Exist status:
0 on success,
1 when no window has been found.
"""
debug = ctx.obj['DEBUG']
program = ' '.join(program)
configs = yaml.load(configs, Loader=SafeLoader)
ipc = i3ipc.Connection()
ipc.on('window::new', window_new(configs, debug=debug))
for cfg in configs:
cfg['filter'] = parse(cfg['filter'])
ipc.command(f'exec {cfg["program"]}')
started_at = time.monotonic_ns() // (1000*1000)
ipc.main(timeout=timeout / 1000)
total_time = time.monotonic_ns() // (1000*1000) - started_at
ctx.exit(int(total_time >= timeout))
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -1,3 +1,4 @@
click click
i3ipc i3ipc
pyyaml