├── .gitignore ├── README.md ├── golden-demo.gif ├── golden.py └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | error.log 4 | output 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bspwm golden-ratio expansion for focused windows, like in the [golden-ratio.el](https://github.com/roman/golden-ratio.el) plugin for emacs 2 | 3 | Whenever you focus a window, this script will increase its size considerably (by about 1.6x). Whatever you're working with gets more real estate on the screen. As you interact with different windows, the active one gets expanded. 4 | 5 | ![Demo](https://github.com/sumeet/golden/raw/master/golden-demo.gif) 6 | 7 | Usage: 8 | 9 | Stick this in your bspwmrc: 10 | 11 | ```sh 12 | exec ~/Projects/golden/run.sh & 13 | ``` 14 | 15 | For example, here's my full bspwmrc: 16 | 17 | ```sh 18 | #!/bin/sh 19 | killall sxhkd 20 | sxhkd & 21 | 22 | bspc monitor -d 1 2 3 4 5 6 23 | 24 | bspc config border_width 1 25 | bspc config top_padding 43 26 | bspc config window_gap 0 27 | 28 | bspc config split_ratio 0.50 29 | bspc config borderless_monocle true 30 | bspc config gapless_monocle true 31 | 32 | bspc config pointer_modifier mod1 33 | bspc config pointer_action1 move 34 | bspc config pointer_action2 resize_side 35 | bspc config pointer_action3 resize_corner 36 | bspc config focus_follows_pointer true 37 | 38 | bspc rule -a retroarch state=floating 39 | bspc rule -a plasmashell state=floating border=off layer=normal manage=off center=true 40 | bspc rule -a krunner state=floating 41 | 42 | exec ~/Projects/golden/run.sh & 43 | ``` 44 | 45 | Bugs, issues and questions all welcome in the Issues section. Thank you! 46 | -------------------------------------------------------------------------------- /golden-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sumeet/golden/567f535b87a8d2e12c354e3161aae50052f90c12/golden-demo.gif -------------------------------------------------------------------------------- /golden.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import json 3 | import sys 4 | import pprint 5 | import subprocess 6 | 7 | GOLDEN = (1 + 5 ** 0.5) / 2 8 | GOLDEN_RATIO = 1 / GOLDEN 9 | GOLDEN_RATIO_INVERSE = 1 - GOLDEN_RATIO 10 | 11 | 12 | def query_current_desktop(): 13 | result = subprocess.run(['bspc', 'query', '-T', '-n', '@/'], 14 | stdout=subprocess.PIPE) 15 | return Node(json.loads(result.stdout)) 16 | 17 | 18 | def set_node_ratio(node_id, ratio): 19 | subprocess.run(['bspc', 'node', str(node_id), '-r', str(ratio)]) 20 | 21 | 22 | class Node: 23 | 24 | def __init__(self, raw_output, parent=None): 25 | self._raw_output = raw_output 26 | self.parent = parent 27 | 28 | @property 29 | def is_private(self): 30 | return self._raw_output['private'] 31 | 32 | @property 33 | def id(self): 34 | return self._raw_output['id'] 35 | 36 | @property 37 | def first_child(self): 38 | if self._raw_output['firstChild']: 39 | return Node(self._raw_output['firstChild'], parent=self) 40 | return None 41 | 42 | @property 43 | def second_child(self): 44 | if self._raw_output['secondChild']: 45 | return Node(self._raw_output['secondChild'], parent=self) 46 | return None 47 | 48 | @property 49 | def all_children(self): 50 | first_child = self.first_child 51 | if first_child: 52 | yield first_child 53 | for child in first_child.all_children: 54 | yield child 55 | 56 | second_child = self.second_child 57 | if second_child: 58 | yield second_child 59 | for child in second_child.all_children: 60 | yield child 61 | 62 | def find_node(self, node_id): 63 | if self.id == node_id: 64 | return self 65 | if self.first_child: 66 | node = self.first_child.find_node(node_id) 67 | if node: 68 | return node 69 | if self.second_child: 70 | node = self.second_child.find_node(node_id) 71 | if node: 72 | return node 73 | 74 | 75 | def enlarge_by_golden_ratio(node): 76 | parent = node.parent 77 | 78 | if not parent: 79 | return 80 | 81 | # don't resize if either child contains a window marked as private: that 82 | # means the user wants to keep this window from changing position or size 83 | # if possible 84 | if any(child.is_private for child in parent.all_children): 85 | return 86 | 87 | # in bspwm, nodes each have two children: 88 | # 89 | # if the node we're trying to enlarge is the first parent, then set the 90 | # node's ratio to the golden ratio. that will make it larger than the second 91 | # node. otherwise, set the ratio to the inverse of the golden ratio, and 92 | # then the second child (our node) will appear larger than the first child 93 | if parent.first_child.id == node.id: 94 | ratio = GOLDEN_RATIO 95 | if parent.second_child.id == node.id: 96 | ratio = GOLDEN_RATIO_INVERSE 97 | 98 | set_node_ratio(parent.id, ratio) 99 | enlarge_by_golden_ratio(parent) 100 | 101 | 102 | def parse_node_focus_event(line): 103 | if not line.startswith('node_focus'): 104 | return None 105 | 106 | _, monitor_id, desktop_id, node_id = line.split() 107 | return int(monitor_id, 16), int(desktop_id, 16), int(node_id, 16) 108 | 109 | 110 | if __name__ == '__main__': 111 | node_focus_events = filter(None, map(parse_node_focus_event, sys.stdin)) 112 | 113 | 114 | for monitor_id, desktop_id, focused_node_id in node_focus_events: 115 | current_desktop_node = query_current_desktop() 116 | 117 | focused_node = current_desktop_node.find_node(focused_node_id) 118 | enlarge_by_golden_ratio(focused_node) 119 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # from https://stackoverflow.com/a/22644006/149987 4 | trap "exit" INT TERM 5 | trap "kill 0" EXIT 6 | 7 | # grabbed this badboy from https://stackoverflow.com/a/246128/149987 8 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" 9 | 10 | while true 11 | do 12 | bspc subscribe node_focus | $DIR/golden.py 13 | done 14 | --------------------------------------------------------------------------------