108 lines
3.1 KiB
Python
Executable file
108 lines
3.1 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""
|
|
Simple script to launch an application or focus it if already running in niri
|
|
Usage: niri-launch-or-focus <command> [--class CLASS] [--title TITLE] [args...]
|
|
|
|
Examples:
|
|
niri-launch-or-focus firefox
|
|
niri-launch-or-focus code --class Code --title "My Project"
|
|
niri-launch-or-focus kitty --class kitty
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def find_window(class_name=None, title=None, app_name=None):
|
|
"""Find window by app_id (class), title, or app name."""
|
|
try:
|
|
result = subprocess.run(
|
|
["niri", "msg", "--json", "windows"],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
windows_json = result.stdout.strip()
|
|
|
|
if not windows_json or windows_json == "[]":
|
|
return None
|
|
|
|
windows = json.loads(windows_json)
|
|
|
|
for window in windows:
|
|
app_id = window.get("app_id", "").lower()
|
|
|
|
if class_name:
|
|
if app_id == class_name.lower():
|
|
return window.get("id")
|
|
elif title:
|
|
if window.get("title") == title:
|
|
return window.get("id")
|
|
elif app_name:
|
|
if app_id == app_name.lower():
|
|
return window.get("id")
|
|
|
|
return None
|
|
except (json.JSONDecodeError, subprocess.SubprocessError, KeyError):
|
|
return None
|
|
|
|
|
|
def focus_window(window_id):
|
|
"""Focus a window by ID."""
|
|
subprocess.run(
|
|
["niri", "msg", "action", "focus-window", "--id", str(window_id)],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
|
|
|
|
def launch_command(command, args):
|
|
"""Launch a command in the background."""
|
|
subprocess.Popen(
|
|
[command] + args,
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
start_new_session=True,
|
|
)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Launch an application or focus it if already running in niri"
|
|
)
|
|
parser.add_argument("command", help="Command to run")
|
|
parser.add_argument("--class", dest="class_name", help="Window class (app_id)")
|
|
parser.add_argument("--title", help="Window title")
|
|
parser.add_argument("args", nargs=argparse.REMAINDER, help="Additional arguments for command")
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Extract app name from command (basename)
|
|
app_name = args.command.split("/")[-1]
|
|
|
|
# Try to find existing window
|
|
window_id = find_window(
|
|
class_name=args.class_name,
|
|
title=args.title,
|
|
app_name=app_name if not args.class_name and not args.title else None,
|
|
)
|
|
|
|
# Build command arguments
|
|
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)
|
|
|
|
# Focus if found, otherwise launch
|
|
if window_id:
|
|
focus_window(window_id)
|
|
else:
|
|
launch_command(args.command, cmd_args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|