z_mirror/dotfiles
git clone https://git.raiguard.me/z_mirror/dotfiles
880f5bc
noctalia
1#!/usr/bin/env python3 2 3""" 4This script uses the i3ipc python module to switch the layout splith / splitv 5for the currently focused window, depending on its dimensions. 6It works on both sway and i3 window managers. 7 8Inspired by https://github.com/olemartinorg/i3-alternating-layout 9 10Copyright: 2019-2021 Piotr Miller & Contributors 11e-mail: nwg.piotr@gmail.com 12Project: https://github.com/nwg-piotr/autotiling 13License: GPL3 14 15Dependencies: python-i3ipc>=2.0.1 (i3ipc-python) 16""" 17import argparse 18import os 19import sys 20from functools import partial 21 22from i3ipc import Connection, Event 23 24try: 25 from .__about__ import __version__ 26except ImportError: 27 __version__ = "unknown" 28 29 30def temp_dir(): 31 return os.getenv("TMPDIR") or os.getenv("TEMP") or os.getenv("TMP") or "/tmp" 32 33 34def save_string(string, file_path): 35 try: 36 with open(file_path, "wt") as file: 37 file.write(string) 38 except Exception as e: 39 print(e) 40 41 42def output_name(con): 43 if con.type == "root": 44 return None 45 46 if p := con.parent: 47 if p.type == "output": 48 return p.name 49 else: 50 return output_name(p) 51 52 53def switch_splitting(i3, e, debug, outputs, workspaces, depth_limit, splitwidth, splitheight, splitratio): 54 try: 55 con = i3.get_tree().find_focused() 56 output = output_name(con) 57 # Stop, if outputs is set and current output is not in the selection 58 if outputs and output not in outputs: 59 if debug: 60 print(f"Debug: Autotiling turned off on output {output}", file=sys.stderr) 61 return 62 63 if con and not workspaces or (str(con.workspace().num) in workspaces): 64 if con.floating: 65 # We're on i3: on sway it would be None 66 # May be 'auto_on' or 'user_on' 67 is_floating = "_on" in con.floating 68 else: 69 # We are on sway 70 is_floating = con.type == "floating_con" 71 72 if depth_limit: 73 # Assume we reached the depth limit, unless we can find a workspace 74 depth_limit_reached = True 75 current_con = con 76 current_depth = 0 77 while current_depth < depth_limit: 78 # Check if we found the workspace of the current container 79 if current_con.type == "workspace": 80 # Found the workspace within the depth limitation 81 depth_limit_reached = False 82 break 83 84 # Look at the parent for next iteration 85 current_con = current_con.parent 86 87 # Only count up the depth, if the container has more than 88 # one container as child 89 if len(current_con.nodes) > 1: 90 current_depth += 1 91 92 if depth_limit_reached: 93 if debug: 94 print("Debug: Depth limit reached") 95 return 96 97 is_full_screen = con.fullscreen_mode == 1 98 is_stacked = con.parent.layout == "stacked" 99 is_tabbed = con.parent.layout == "tabbed" 100 101 # Exclude floating containers, stacked layouts, tabbed layouts and full screen mode 102 if (not is_floating 103 and not is_stacked 104 and not is_tabbed 105 and not is_full_screen): 106 new_layout = "splitv" if con.rect.height > con.rect.width / splitratio else "splith" 107 108 if new_layout != con.parent.layout: 109 result = i3.command(new_layout) 110 if result[0].success and debug: 111 print(f"Debug: Switched to {new_layout}", file=sys.stderr) 112 elif debug: 113 print(f"Error: Switch failed with err {result[0].error}", file=sys.stderr) 114 115 if e.change in ["new", "move"] and con.percent: 116 if con.parent.layout == "splitv" and splitheight != 1.0: # top / bottom 117 # print(f"split top fac {splitheight*100}") 118 i3.command(f"resize set height {int(con.percent * splitheight * 100)} ppt") 119 elif con.parent.layout == "splith" and splitwidth != 1.0: # top / bottom: # left / right 120 # print(f"split right fac {splitwidth*100} ") 121 i3.command(f"resize set width {int(con.percent * splitwidth * 100)} ppt") 122 123 elif debug: 124 print("Debug: No focused container found or autotiling on the workspace turned off", file=sys.stderr) 125 126 except Exception as e: 127 print(f"Error: {e}", file=sys.stderr) 128 129 130def get_parser(): 131 parser = argparse.ArgumentParser(prog="autotiling", description="Script for sway and i3 to automatically switch the horizontal / vertical window split orientation") 132 133 parser.add_argument("-d", "--debug", action="store_true", 134 help="print debug messages to stderr") 135 parser.add_argument("-v", "--version", action="version", 136 version=f"%(prog)s {__version__}, Python {sys.version}", 137 help="display version information") 138 parser.add_argument("-o", "--outputs", nargs="*", type=str, default=[], 139 help="restricts autotiling to certain output; example: autotiling --output DP-1 HDMI-0") 140 parser.add_argument("-w", "--workspaces", nargs="*", type=str, default=[], 141 help="restricts autotiling to certain workspaces; example: autotiling --workspaces 8 9") 142 parser.add_argument("-l", "--limit", type=int, default=0, 143 help='limit how often autotiling will split a container; ' 144 'try "2" if you like master-stack layouts; default: 0 (no limit)') 145 parser.add_argument("-sw", 146 "--splitwidth", 147 help='set the width of the vertical split (as factor); default: 1.0;', 148 type=float, 149 default=1.0, ) 150 parser.add_argument("-sh", 151 "--splitheight", 152 help='set the height of the horizontal split (as factor); default: 1.0;', 153 type=float, 154 default=1.0, ) 155 parser.add_argument("-sr", 156 "--splitratio", 157 help='Split direction ratio - based on window height/width; default: 1;' 158 'try "1.61", for golden ratio - window has to be 61%% wider for left/right split; default: 1.0;', 159 type=float, 160 default=1.0, ) 161 162 """ 163 Changing event subscription has already been the objective of several pull request. To avoid doing this again 164 and again, let's allow to specify them in the `--events` argument. 165 """ 166 parser.add_argument("-e", "--events", nargs="*", type=str, default=["WINDOW", "MODE"], 167 help="list of events to trigger switching split orientation; default: WINDOW MODE") 168 169 return parser 170 171def main(): 172 args = get_parser().parse_args() 173 174 if args.debug: 175 if args.outputs: 176 print(f"autotiling is only active on outputs: {','.join(args.outputs)}") 177 if args.workspaces: 178 print(f"autotiling is only active on workspaces: {','.join(args.workspaces)}") 179 180 # For use w/ nwg-panel 181 ws_file = os.path.join(temp_dir(), "autotiling") 182 if args.workspaces: 183 save_string(','.join(args.workspaces), ws_file) 184 else: 185 if os.path.isfile(ws_file): 186 os.remove(ws_file) 187 188 if not args.events: 189 print("No events specified", file=sys.stderr) 190 sys.exit(1) 191 192 handler = partial( 193 switch_splitting, 194 debug=args.debug, 195 outputs=args.outputs, 196 workspaces=args.workspaces, 197 depth_limit=args.limit, 198 splitwidth=args.splitwidth, 199 splitheight=args.splitheight, 200 splitratio=args.splitratio 201 ) 202 i3 = Connection() 203 for e in args.events: 204 try: 205 i3.on(Event[e], handler) 206 print(f"{Event[e]} subscribed") 207 except KeyError: 208 print(f"'{e}' is not a valid event", file=sys.stderr) 209 210 i3.main() 211 212 213if __name__ == "__main__": 214 main() 215