0e052599d2b524619bf8e9a05c1a994cf9971d64
[distro-setup] / i3-split-maybe
1 #!/usr/bin/python3
2
3 # I, Ian Kelling, follow the GNU license recommendations at
4 # https://www.gnu.org/licenses/license-recommendations.en.html. They
5 # recommend that small programs, < 300 lines, be licensed under the
6 # Apache License 2.0. This file contains or is part of one or more small
7 # programs. If a small program grows beyond 300 lines, I plan to change
8 # to a recommended GPL license.
9
10 # Copyright 2024 Ian Kelling
11
12 # Licensed under the Apache License, Version 2.0 (the "License");
13 # you may not use this file except in compliance with the License.
14 # You may obtain a copy of the License at
15
16 # http://www.apache.org/licenses/LICENSE-2.0
17
18 # Unless required by applicable law or agreed to in writing, software
19 # distributed under the License is distributed on an "AS IS" BASIS,
20 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 # See the License for the specific language governing permissions and
22 # limitations under the License.
23
24
25 # This anticipates when we want to tab windows.
26 #
27 # There are 2 options of when to do it: just after a window is created,
28 # or just before a window is created.
29 #
30 # * Doing it after a window is created allows you to move a window into
31 # the split that only has 1 window, whereas the other way doesn't. For
32 # my use cases, I think I don't really want to move it into the split if
33 # it is a tabbed split. upon further reflection, I've determined that
34 # single window containers are inherently confusing because they tend to
35 # exist and get nested at unexpected times and then it is unclear how to
36 # get rid of them and what is going on and the benefit is generally not
37 # worth it. This command helps identify single window containers during
38 # testing: /a/opt/i3ipc-python/examples/i3-debug-console.py
39 #
40 # * Doing it just before a windows is created, you need to call this
41 # script, which means wrapping launch of a program, which I have no way
42 # to do for all cases, I just do it for the common programs I have bound
43 # to keys in i3.
44 #
45 # * Note: doing it just before a window is created also leaves that split behind if
46 # the window is closed, and I don't want single window splits hanging around,
47 # so I close them out in
48 #
49 # I have a keybind which disables both, super+shift+u, it runs
50 # /b/ds/i3-auto-layout-toggle
51 #
52
53 import sys
54 import os
55 from i3ipc import Connection, Event
56 # for debugging
57 #from pprint import pprint
58
59
60 def find_parent(i3, window_id):
61 """
62 Find the parent of a given window id
63 """
64
65 def finder(con, parent, gp):
66 if con.id == window_id:
67 return (parent, gp)
68 for node in con.nodes:
69 res = finder(node, con, parent)
70 if res:
71 return res
72 return None
73
74 return finder(i3.get_tree(), None, None)
75
76
77 def set_layout(i3):
78 """
79 Set the layout/split for the currently
80 focused window to either vertical or
81 horizontal, depending on its width/height
82 """
83
84 if os.path.isfile("/tmp/iank-i3-no-auto"):
85 return
86
87 win = i3.get_tree().find_focused()
88 parent, gp = find_parent(i3, win.id)
89
90
91 workspace = win.workspace()
92 #pprint(vars(workspace.rect))
93
94 screen_w = workspace.rect.width
95 screen_h = workspace.rect.height
96 half_w = screen_w / 2 + 1
97 half_h = screen_h / 2 + 1
98
99 w = win.rect.width
100 h = win.rect.height
101 ph = parent.rect.height
102 pw = parent.rect.width
103
104 if ( parent.layout == 'tabbed' or gp.layout == 'tabbed'):
105 return
106
107 # debug
108 print('d2: len(parent.nodes):', len(parent.nodes),' > 1',
109 'and ( ph:',ph,' > h + 10:',h + 10,' or pw:',pw,' > w:',w,' )',
110 'and (screen_w:',screen_w,' < screen_h:',screen_h,' or w <= half_w:',half_w,')',
111 'and h <= half_h:',half_h,')')
112
113 # h + 10 because a tabbed window loses high compared to its parent.
114 # Note, it is redundant since we check above if the parent is tabbed,
115 # but just being cautious.
116 #
117 # condition in english
118 # the parent container is bigger
119 # and we are on a vertical screen or our window is <= half the screen width
120 # and we are <= half the screen height
121 if (len(parent.nodes) > 1
122 and ( ph > h + 10 or pw > w )
123 and ( screen_w < screen_h or w <= half_w )
124 and h <= half_h ):
125 i3.command('split vertical, layout tabbed')
126 # print('d1: tabbed')
127
128
129
130 ### further potential use cases:
131
132 # We could automatically do a vertical split when there are 2 or 3
133 # horizontal windows.
134
135 # We could undo a vertical split when we close out windows.
136 # elif (( w == screen_width )); then
137 # # if we had 2 windows on screen, made them vertical splits, then
138 # # closed one, it stays vertical split, but we want it horizontal at
139 # # that point. So, make it horizontal here.
140 # m i3-msg "split horizontal"
141
142
143 def main():
144 i3 = Connection()
145 set_layout(i3)
146
147
148 if __name__ == "__main__":
149 main()