distro specific fixes
[distro-setup] / i3-set-layout
1 #!/usr/bin/python3
2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to change
7 # to a recommended GPL license.
8
9 # Copyright 2024 Ian Kelling
10
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23
24 import sys
25 from i3ipc import Connection, Event
26
27
28 def find_parent(i3, window_id):
29 """
30 Find the parent of a given window id
31 """
32
33 def finder(con, parent, gp):
34 if con.id == window_id:
35 return (parent, gp)
36 for node in con.nodes:
37 res = finder(node, con, parent)
38 if res:
39 return res
40 return None
41
42 return finder(i3.get_tree(), None, None)
43
44
45 def set_layout(i3):
46 """
47 Set the layout/split for the currently
48 focused window to either vertical or
49 horizontal, depending on its width/height
50 """
51
52 win = i3.get_tree().find_focused()
53 # i don't use gp: todo: revert to original alternating_layout function
54 # which did not return gp.
55 parent, gp = find_parent(i3, win.id)
56
57
58 # We never want to set the layout of a single window container,
59 # there are already keys for that. I don't know why i3 even does
60 # this, it is stupid. So, eliminate single window container if we
61 # are focused on one.
62 #
63 # Alternatively, we could first focus the parent, but I think when
64 # layout changes, we expect new windows to be created within that
65 # layout.
66 #
67 # Todo: if the direction we are moving has a split/tabbed container
68 # as a peer to parent, this will move our window into that container
69 # instead of what we want. And in fact, the whole logic below is
70 # incorrect and based on testing which did not realize that fact.
71 #
72 if (parent and parent.type == 'con' and len(parent.nodes) == 1):
73 # https://unix.stackexchange.com/questions/173754/how-to-move-a-window-up-to-the-level-of-its-parent-window-in-i3wm
74 i3.command('mark i3ha')
75 i3.command('focus parent')
76 i3.command('focus parent')
77 i3.command('mark i3hb')
78 i3.command('[con_mark="i3ha"] focus')
79 i3.command('move window to mark i3hb')
80 i3.command('unmark i3ha')
81 i3.command('unmark i3hb')
82 i3.command('layout ' + sys.argv[1])
83
84 def main():
85 i3 = Connection()
86 set_layout(i3)
87
88
89 if __name__ == "__main__":
90 main()