0a98c90b3927ed8a499b56f05441b3959bd4fc54
[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. There are 2 options of
26 # when to do it: just after a window is created, or just before a window
27 # is created.
28 #
29 # * Doing it after a window is created allows you to move a window into
30 # the split that only has 1 window, whereas the other way doesn't. For
31 # my use cases, I think I don't really want to move it into the split if
32 # it is a tabbed split. upon further reflection, I've determined that
33 # single window containers are inherently confusing because they tend to
34 # exist and get nested at unexpected times and then it is unclear how to
35 # get rid of them and what is going on and the benefit is generally not
36 # worth it. This command helps identify single window containers during
37 # testing: /a/opt/i3ipc-python/examples/i3-debug-console.py
38 #
39 # * Doing it just before a windows is created, you need to call this
40 # script, which means wrapping launch of a program, which I have no way
41 # to do for all cases, I just do it for the common programs I have bound
42 # to keys in i3.
43 #
44 # * Note: doing it just before a window is created also leaves that split behind if
45 # the window is closed, and I don't want single window splits hanging around,
46 # so I close them out in
47 #
48 # I have a keybind which disables both, it runs /b/ds/i3-auto-layout-toggle
49 #
50
51 import sys
52 import os
53 from i3ipc import Connection, Event
54 # for debugging
55 #from pprint import pprint
56
57
58 def find_parent(i3, window_id):
59 """
60 Find the parent of a given window id
61 """
62
63 def finder(con, parent, gp):
64 if con.id == window_id:
65 return (parent, gp)
66 for node in con.nodes:
67 res = finder(node, con, parent)
68 if res:
69 return res
70 return None
71
72 return finder(i3.get_tree(), None, None)
73
74
75 def set_layout(i3):
76 """
77 Set the layout/split for the currently
78 focused window to either vertical or
79 horizontal, depending on its width/height
80 """
81
82 if os.path.isfile("/tmp/iank-i3-no-auto"):
83 return
84
85 win = i3.get_tree().find_focused()
86 parent, gp = find_parent(i3, win.id)
87
88
89 workspace = win.workspace()
90 #pprint(vars(workspace.rect))
91
92 screen_width = workspace.rect.width
93 screen_height = workspace.rect.height
94 half_w = screen_width / 2 + 1
95 half_h = screen_height / 2 + 1
96
97 w = win.rect.width
98 h = win.rect.height
99 ph = parent.rect.height
100 pw = parent.rect.width
101
102 # There is potential for future use with < 1920, but I'm
103 # not thinking about it yet.
104 if ( screen_width < 1920 or parent.layout == 'tabbed' or gp.layout == 'tabbed'):
105 return
106
107 # print('d2: len(parent.nodes)', len(parent.nodes),' > 1',
108 # 'and ( ph ',ph,' > h + 10',h + 10,' or pw',pw,' > w',w,' )',
109 # 'and w <= half_w',half_w,'+ and h <= half_h',half_h)
110
111 # h + 10 because a tabbed window loses high compared to its parent.
112 # Note, it is redundant since we check above if the parent is tabbed,
113 # but just being cautious.
114 if (len(parent.nodes) > 1
115 and ( ph > h + 10 or pw > w )
116 and w <= half_w and h <= half_h ):
117 i3.command('split vertical, layout tabbed')
118 # print('d1: tabbed')
119
120
121 ### further potential use cases:
122
123 # We could automatically do a vertical split when there are 2 or 3
124 # horizontal windows.
125
126 # We could undo a vertical split when we close out windows.
127 # elif (( w == screen_width )); then
128 # # if we had 2 windows on screen, made them vertical splits, then
129 # # closed one, it stays vertical split, but we want it horizontal at
130 # # that point. So, make it horizontal here.
131 # m i3-msg "split horizontal"
132
133
134 def main():
135 i3 = Connection()
136 set_layout(i3)
137
138
139 if __name__ == "__main__":
140 main()