Add tabbed-run control channel to open apps in new tabs
Host listens on a unix socket; tabbed-run sends a command to open it as a new maximized casilda tab. Spawned children inherit the full environment plus TABBED_CASILDA_CTRL so tabbed-run works from any tab.
This commit is contained in:
parent
88778ecbb4
commit
134de7bb4d
2 changed files with 82 additions and 15 deletions
24
tabbed-run
Executable file
24
tabbed-run
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env python3
|
||||
import os
|
||||
import shlex
|
||||
import socket
|
||||
import sys
|
||||
|
||||
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)
|
||||
payload = (shlex.join(sys.argv[1:]) + "\n").encode()
|
||||
try:
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
|
||||
s.connect(ctrl)
|
||||
s.sendall(payload)
|
||||
except OSError as e:
|
||||
print(f"tabbed-run: cannot reach host: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
|
@ -6,26 +6,17 @@
|
|||
# ]
|
||||
# ///
|
||||
import argparse
|
||||
import gi
|
||||
import atexit
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "4.0")
|
||||
gi.require_version("Casilda", "1.0")
|
||||
from gi.repository import GLib, Gtk, Casilda
|
||||
|
||||
|
||||
def make_tab(notebook, command):
|
||||
compositor = Casilda.Compositor()
|
||||
compositor.props.maximized = True
|
||||
label = Gtk.Label(label=command)
|
||||
notebook.append_page(compositor, label)
|
||||
argv = shlex.split(command)
|
||||
if not argv:
|
||||
return
|
||||
argv[0] = shutil.which(argv[0]) or argv[0]
|
||||
compositor.spawn_async(None, argv, None, GLib.SpawnFlags.DEFAULT)
|
||||
from gi.repository import Gio, GLib, Gtk, Casilda
|
||||
|
||||
|
||||
class TabbedCasilda(Gtk.Application):
|
||||
|
|
@ -33,6 +24,56 @@ class TabbedCasilda(Gtk.Application):
|
|||
super().__init__(application_id="local.tabbed-casilda")
|
||||
self.win = None
|
||||
self.commands = commands
|
||||
self.notebook = None
|
||||
self.ctrl_path = os.path.join(
|
||||
os.environ.get("XDG_RUNTIME_DIR", "/tmp"), "tabbed-casilda.sock"
|
||||
)
|
||||
|
||||
def add_tab(self, command, ctrl_spawn=False):
|
||||
compositor = Casilda.Compositor()
|
||||
compositor.props.maximized = True
|
||||
label = Gtk.Label(label=command)
|
||||
self.notebook.append_page(compositor, label)
|
||||
argv = shlex.split(command)
|
||||
if not argv:
|
||||
return
|
||||
argv[0] = shutil.which(argv[0]) or argv[0]
|
||||
repo_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
env = dict(os.environ)
|
||||
env["PATH"] = f"{repo_dir}:{env.get('PATH', '')}"
|
||||
env["TABBED_CASILDA_CTRL"] = self.ctrl_path
|
||||
if ctrl_spawn:
|
||||
env["G_APPLICATION_NON_UNIQUE"] = "1"
|
||||
envp = [f"{k}={v}" for k, v in env.items()]
|
||||
compositor.spawn_async(None, argv, envp, GLib.SpawnFlags.DEFAULT)
|
||||
|
||||
def start_control_channel(self):
|
||||
try:
|
||||
os.unlink(self.ctrl_path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
service = Gio.SocketService()
|
||||
address = Gio.UnixSocketAddress.new(self.ctrl_path)
|
||||
service.add_address(
|
||||
address, Gio.SocketType.STREAM, Gio.SocketProtocol.DEFAULT, None
|
||||
)
|
||||
service.connect("incoming", self.on_incoming)
|
||||
service.start()
|
||||
self.ctrl_service = service
|
||||
atexit.register(self.cleanup_socket)
|
||||
|
||||
def cleanup_socket(self):
|
||||
try:
|
||||
os.unlink(self.ctrl_path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def on_incoming(self, service, connection, source_object):
|
||||
stream = Gio.DataInputStream.new(connection.get_input_stream())
|
||||
line, _ = stream.read_line()
|
||||
if line:
|
||||
self.add_tab(line.decode(), ctrl_spawn=True)
|
||||
return False
|
||||
|
||||
def do_activate(self):
|
||||
if self.win:
|
||||
|
|
@ -46,8 +87,10 @@ class TabbedCasilda(Gtk.Application):
|
|||
)
|
||||
nb = Gtk.Notebook()
|
||||
win.set_child(nb)
|
||||
self.notebook = nb
|
||||
self.start_control_channel()
|
||||
for command in self.commands:
|
||||
make_tab(nb, command)
|
||||
self.add_tab(command)
|
||||
win.present()
|
||||
self.win = win
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue