tabbed-run now requests a new tab, blocks for a reply containing the tab id and the compositor's Wayland socket path, then execs the command with WAYLAND_DISPLAY pointing at the new tab. The command's stdout/stderr and exit status stay in the invoking tab. Each compositor gets a stable socket path under XDG_RUNTIME_DIR; startup tabs still use spawn_async against that path.
49 lines
1.4 KiB
Python
Executable file
49 lines
1.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
import os
|
|
import socket
|
|
import sys
|
|
|
|
from tabbed_protocol import serialize, parse_reply
|
|
|
|
ctrl = os.environ.get("TABBED_CASILDA_CTRL")
|
|
if not ctrl:
|
|
print(
|
|
"tabbed-run: TABBED_CASILDA_CTRL not set (run inside a tabbed-casilda terminal)",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
if len(sys.argv) < 2:
|
|
print("usage: tabbed-run COMMAND [ARGS...]", file=sys.stderr)
|
|
sys.exit(1)
|
|
creator_id = os.environ.get("TABBED_CASILDA_TAB_ID", "")
|
|
payload = serialize(creator_id, sys.argv[1:])
|
|
|
|
try:
|
|
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
|
|
s.connect(ctrl)
|
|
s.sendall(payload)
|
|
data = b""
|
|
while True:
|
|
chunk = s.recv(4096)
|
|
if not chunk:
|
|
break
|
|
data += chunk
|
|
except OSError as e:
|
|
print(f"tabbed-run: cannot reach host: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
tab_id, socket_path = parse_reply(data.decode())
|
|
if not socket_path:
|
|
print("tabbed-run: no socket path in reply from host", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
env = dict(os.environ)
|
|
env["WAYLAND_DISPLAY"] = socket_path
|
|
env["TABBED_CASILDA_TAB_ID"] = str(tab_id) if tab_id is not None else ""
|
|
env["G_APPLICATION_NON_UNIQUE"] = "1"
|
|
|
|
try:
|
|
os.execvpe(sys.argv[1], sys.argv[1:], env)
|
|
except OSError as e:
|
|
print(f"tabbed-run: cannot exec {sys.argv[1]}: {e}", file=sys.stderr)
|
|
sys.exit(127)
|