X-Git-Url: https://iankelling.org/git/?a=blobdiff_plain;f=i3-event-hook;h=196e6081296f73f0fe343f86918226f223294c25;hb=refs%2Fheads%2Fmaster;hp=01514a648d6017aa0b2f5bf58f9c6b38433946d7;hpb=dab96f8fa4c701db13ba734fa0c07b5d12fc8fae;p=distro-setup diff --git a/i3-event-hook b/i3-event-hook index 01514a6..87487c6 100755 --- a/i3-event-hook +++ b/i3-event-hook @@ -1,26 +1,86 @@ #!/usr/bin/env python3 +# I, Ian Kelling, follow the GNU license recommendations at +# https://www.gnu.org/licenses/license-recommendations.en.html. They +# recommend that small programs, < 300 lines, be licensed under the +# Apache License 2.0. This file contains or is part of one or more small +# programs. If a small program grows beyond 300 lines, I plan to change +# to a recommended GPL license. + +# Copyright 2024 Ian Kelling + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + + +# This gets rid of all single window containers. +# If a single window container was nested in another, +# it ignores that, but I don't generally expect to create them and +# we change focus enough that we would kill them off. + +# Note: I spent a lot of time figuring out how to do this properly, +# https://github.com/i3/i3/issues/3808 there are a bunch of links +# which suggest either float toggle; float toggle, which doesn't put windows back in the same place, or doing a move, which only actually works if you are moving in a direction which does not have a container there, else your window joins the container, and . + + import sys import os from i3ipc import Connection, Event from pprint import pprint -def find_parent(i3, window_id): +def find_workspace(i3, window_id): """ - Find the parent of a given window id + Find the workspace of a given window id """ - def finder(con, parent, gp): + def finder(con, workspace): if con.id == window_id: - return (parent, gp) + return (workspace) for node in con.nodes: - res = finder(node, con, parent) + res = finder(node, con if con and con.type == 'workspace' else workspace) if res: return res return None - return finder(i3.get_tree(), None, None) + return finder(i3.get_tree(), None) + + +def kill_single_win_containers(i3, e, node, parent): + if len(parent.nodes) == 1 and len(node.nodes) == 0: + #print("d1: killing parent") + # parent is a single window container, kill it. + + # Note: based on testing, + # i3 takes care of not calling this program for + # events which we create within it. Otherwise, + # we could create our disabling file here + # and delete it later if it wasn't already there. + i3.command('[con_id=%s] focus' % node.id) + i3.command('mark --add i3ha') + i3.command('focus parent') + i3.command('focus parent') + i3.command('mark --add i3hb') + i3.command('[con_mark="i3ha"] focus') + i3.command('move window to mark i3hb') + i3.command('unmark i3ha') + i3.command('unmark i3hb') + # back to our original focus + i3.command('[con_id=%s] focus' % e.container.id) + elif len(node.nodes) >= 1: + for child in node.nodes: + kill_single_win_containers(i3, e, child, node) + def focus_hook(i3, e): @@ -33,27 +93,36 @@ def focus_hook(i3, e): if os.path.isfile("/tmp/iank-i3-no-auto"): return + # I identify container vs a real windows by the fact that it has nodes. + # looking through the data, another notable difference is that it has + # 'window': None, + # 'window_type': None, + + workspace = find_workspace(i3, e.container.id) # debugging - #pprint(vars(e)) + #exit(0) - parent, gp = find_parent(i3, e.container.id) + if not workspace: + return + #pprint(vars(workspace)) + #print() + for pnode in workspace.nodes: + # debugging + # if (len(pnode.nodes) >= 1): + # print("pnodes: ", pnode.nodes) - # This gets rid of tabbed container with single windows. - #if (parent and gp and parent.layout == 'tabbed' and len(parent.nodes) == 1): - # This gets rid of all single window containers. - if (parent and gp and len(parent.nodes) == 1): - i3.command('mark i3ha') - i3.command('focus parent') - i3.command('focus parent') - i3.command('mark i3hb') - i3.command('[con_mark="i3ha"] focus') - i3.command('move window to mark i3hb') - i3.command('unmark i3ha') - i3.command('unmark i3hb') + for node in pnode.nodes: + kill_single_win_containers(i3, e, node, pnode) def main(): i3 = Connection() i3.on(Event.WINDOW_FOCUS, focus_hook) + # if we don't have move, and we move a window out of a container, + # leaving behind a single window container, then we move it back, it + # will go into the container. We could expect that if we do it + # quickly, but it would be unexpected after a few seconds and we + # forget that it was a container. + i3.on(Event.WINDOW_MOVE, focus_hook) i3.main()