From 28fa76afa4ed47698dabe65e0eddbad0f04e40de Mon Sep 17 00:00:00 2001 From: vrde Date: Mon, 17 Feb 2020 01:02:21 +0100 Subject: [PATCH] Consolidate CLI and config --- README.md | 45 ++++++++++++++++--- quiet-cmd.py | 123 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 130 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index f91833d..a1eb726 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,52 @@ This is accomplished by two scripts, one to enable *quiet-mode*, and another one ## Install -Make sure to install [`i3ipc-python`][3], then copy the two python scripts to your `.config/i3` directory, and configure `i3`. I use `mod+q` to move a window to the quiet workspace, and `mod+shift+q` to move it back to its original place. (I use `mod+Shift+backslash` to kill the focused window.) - -My configuration looks like this: +Make sure to install [`i3ipc-python`][3], then copy the two python scripts to your `.config/i3` directory, and configure `i3`. I use `mod+q` to switch to the quiet workspace, and `mod+shift+q` to start the `quiet mode`, see the configuration below: ``` # Quiet mode -bindsym $mod+q exec $HOME/.config/i3/quiet-cmd.py -bindsym $mod+Shift+q exec $HOME/.config/i3/quiet-cmd.py toggle + +mode "quiet" { + # These bindings trigger as soon as you enter the resize mode + + # Move a window in and out the quiet mode + bindsym q exec ~/.config/i3/quiet-cmd.py toggle + + # Resize the window in quiet mode + bindsym s exec ~/.config/i3/quiet-cmd.py resize small + bindsym m exec ~/.config/i3/quiet-cmd.py resize medium + bindsym l exec ~/.config/i3/quiet-cmd.py resize large + + # back to normal: Enter or Escape or $mod+r + bindsym Return mode "default" + bindsym Caps_Lock mode "default" + bindsym $mod+d mode "default" +} + +bindsym $mod+q exec $HOME/.config/i3/quiet-cmd.py switch +bindsym $mod+Shift+q mode "quiet" exec_always $HOME/.config/i3/quiet-toggle-bar.py ``` +You can also play with the script from the CLI: + +``` +➜ i3-quiet git:(improve-cli) ✗ ./quiet-cmd.py -h +usage: quiet-cmd.py [-h] {toggle,switch,resize} ... + +Control i3-quiet. + +optional arguments: + -h, --help show this help message and exit + +subcommands: + valid subcommands + + {toggle,switch,resize} + additional help +``` + + [1]: https://i3wm.org/docs/userguide.html#_toggling_fullscreen_mode_for_a_window [2]: https://github.com/polybar/polybar/ [3]: https://github.com/altdesktop/i3ipc-python/ diff --git a/quiet-cmd.py b/quiet-cmd.py index cf49117..ea41ea4 100755 --- a/quiet-cmd.py +++ b/quiet-cmd.py @@ -1,70 +1,127 @@ #!/usr/bin/env python3 +import argparse import sys import json -import subprocess +from subprocess import run, check_output import os ZEN_NUMBER = 99 ZEN_NAME = 'zen' +ZEN_WORKSPACE = '{}: {}'.format(ZEN_NUMBER, ZEN_NAME) WIDTH_PERC=50 MAX_WIDTH=800 HEIGHT_PERC=90 MAX_HEIGHT=1024 +SIZES = { + 'small': [50, 70, 800, 800], + 'medium': [65, 80, 1024, 1024], + 'large': [90, 90, 1600, 1600], +} + ZEN_BORDER = 'none' -DEFAULT_BORDER = 'none' +DEFAULT_BORDER = 'normal' ZEN_FILE_PATH = os.path.join(os.environ['HOME'], '.config/i3/.i3-quiet.json') + def enable_zen_mode(workspace): width = round(min(workspace['rect']['width'] * WIDTH_PERC / 100, MAX_WIDTH)) height = round(min(workspace['rect']['height'] * HEIGHT_PERC / 100, MAX_HEIGHT)) - workspace_name = '{}: {}'.format(ZEN_NUMBER, workspace['name']) - with open(ZEN_FILE_PATH, 'w') as zen_file: - json.dump(workspace, zen_file) - return ';'.join([ - 'move container to workspace {}'.format(workspace_name), - 'workspace {}'.format(workspace_name), + msg = ';'.join([ + 'move container to workspace {}'.format(ZEN_WORKSPACE), + 'workspace {}'.format(ZEN_WORKSPACE), 'floating enable', - 'border {}'.format(ZEN_BORDER), 'resize set width {}'.format(width), 'resize set height {}'.format(height), 'move position center' ]) + run(['i3-msg', msg]) + layout = check_output(['i3-save-tree', '--workspace', ZEN_WORKSPACE]) + msg = ';'.join([ + 'border {}'.format(ZEN_BORDER) + ]) + run(['i3-msg', msg]) + layout = layout.decode() + layout = layout.split('\n') + layout = filter(lambda l: not l.strip().startswith('//'), layout) + layout = json.loads(''.join(layout)) + + with open(ZEN_FILE_PATH, 'w') as zen_file: + json.dump({'workspace': workspace, 'layout': layout}, zen_file) def disable_zen_mode(workspace): - return ';'.join([ + try: + with open(ZEN_FILE_PATH, 'r') as zen_file: + border = json.load(zen_file)['layout']['border'] + except (FileNotFoundError, KeyError): + border = DEFAULT_BORDER + + msg = ';'.join([ 'move container to workspace number {}'.format(workspace['num']), 'workspace number {}'.format(workspace['num']), 'floating disable', - 'border {}'.format(DEFAULT_BORDER) + 'border {}'.format(border) ]) + run(['i3-msg', msg]) -def goto_zen(workspace_name): - return 'workspace {}'.format(workspace_name) +def resize(args): + cw, _, _= get_workspaces() + workspace_width = cw['rect']['width'] + workspace_height = cw['rect']['height'] + wp, hp, wm, hm = SIZES[args.size] + width = round(min(workspace_width * wp / 100, wm)) + height = round(min(workspace_height * hp / 100, hm)) + msg = ';'.join([ + 'resize set width {}'.format(width), + 'resize set height {}'.format(height), + 'move position center' + ]) + run(['i3-msg', msg]) -out = subprocess.run(['i3-msg', '-t', 'get_workspaces'], capture_output=True) -workspaces = json.loads(out.stdout) +def goto_zen(): + msg = 'workspace {}'.format(ZEN_WORKSPACE) + run(['i3-msg', msg]) -with open(ZEN_FILE_PATH, 'r') as zen_file: - try: - workspace_previous = json.load(zen_file) - except: - workspace_previous = None -workspace_current = list(filter(lambda w: w['focused'], workspaces))[0] -workspace_zen = list(filter(lambda w: w['num'] == ZEN_NUMBER, workspaces)) - -if __name__ == '__main__': - if len(sys.argv) > 1: - if len(workspace_zen): - msg = disable_zen_mode(workspace_previous) +def switch_workspace(args): + goto_zen() + +def toggle(args): + cw, pw, zw = get_workspaces() + if zw: + disable_zen_mode(pw) else: - if len(workspace_zen): - msg = goto_zen(workspace_zen[0]['name']) - else: - msg = enable_zen_mode(workspace_current) - print(msg) - subprocess.run(['i3-msg', msg]) + enable_zen_mode(cw) + +def get_workspaces(): + out = run(['i3-msg', '-t', 'get_workspaces'], capture_output=True) + workspaces = json.loads(out.stdout) + + try: + with open(ZEN_FILE_PATH, 'r') as zen_file: + workspace_previous = json.load(zen_file)['workspace'] + except (FileNotFoundError, KeyError): + workspace_previous = {'num': 1} + workspace_current = list(filter(lambda w: w['focused'], workspaces))[0] + workspace_zen = list(filter(lambda w: w['num'] == ZEN_NUMBER, workspaces)) + return workspace_current, workspace_previous, workspace_zen + + +parser = argparse.ArgumentParser(description='Control i3-quiet.') +parser.set_defaults(func=lambda args: parser.print_help()) +subparsers = parser.add_subparsers(title='subcommands', + description='valid subcommands', + help='additional help') +parser_toggle = subparsers.add_parser('toggle') +parser_toggle.set_defaults(func=toggle) +parser_switch = subparsers.add_parser('switch') +parser_switch.set_defaults(func=switch_workspace) +parser_resize = subparsers.add_parser('resize') +parser_resize.add_argument('size', type=str, + help='Size of the window specified as "small", "medium", or "large"') +parser_resize.set_defaults(func=resize) +args = parser.parse_args() +args.func(args)