#!/usr/bin/env python3 """ Launch an application or focus it if already running in i3. Usage: i3-launch-or-focus --class CLASS [args...] Examples: i3-launch-or-focus --class sdm-connect alacritty -e ~/.local/bin/sdm-ui.sh i3-launch-or-focus --class dropdown alacritty """ import argparse import json import subprocess import sys def find_window(tree, instance=None, title=None): """Recursively search the i3 tree for a window matching criteria.""" props = tree.get("window_properties", {}) if instance and props.get("instance", "").lower() == instance.lower(): return tree.get("id") if title and tree.get("name", "") == title: return tree.get("id") for node in tree.get("nodes", []) + tree.get("floating_nodes", []): result = find_window(node, instance=instance, title=title) if result: return result return None def focus_window(instance=None, title=None): """Focus a window using i3-msg criteria.""" if instance: criteria = f'[instance="{instance}"]' elif title: criteria = f'[title="{title}"]' else: return subprocess.run( ["i3-msg", f"{criteria} focus"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) def launch_command(command, args): """Launch a command, replacing this process.""" import os os.execvp(command, [command] + args) def main(): parser = argparse.ArgumentParser( description="Launch an application or focus it if already running in i3" ) parser.add_argument("command", help="Command to run") parser.add_argument("--class", dest="class_name", help="Window instance name") parser.add_argument("--title", help="Window title") parser.add_argument( "args", nargs=argparse.REMAINDER, help="Additional arguments for command" ) args = parser.parse_args() try: result = subprocess.run( ["i3-msg", "-t", "get_tree"], capture_output=True, text=True, check=False, ) tree = json.loads(result.stdout) except (json.JSONDecodeError, subprocess.SubprocessError): tree = {} window_id = find_window(tree, instance=args.class_name, title=args.title) if window_id: focus_window(instance=args.class_name, title=args.title) else: cmd_args = [] if args.class_name: cmd_args.append(f"--class={args.class_name}") if args.title: cmd_args.append(f"--title={args.title}") cmd_args.extend(args.args) launch_command(args.command, cmd_args) if __name__ == "__main__": main()