diff --git a/kitty/boss.py b/kitty/boss.py index 46ce5d13b..ce155d33d 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -95,6 +95,13 @@ class Boss: if dpi_changed: self.on_dpi_change(os_window_id) + def list_os_windows(self): + for os_window_id, tm in self.os_window_map.items(): + yield { + 'id': os_window_id, + 'tabs': list(tm.list_tabs()), + } + def _new_os_window(self, args, cwd_from=None): sw = self.args_to_special_window(args, cwd_from) if args else None startup_session = create_session(self.opts, special_window=sw, cwd_from=cwd_from) diff --git a/kitty/child.py b/kitty/child.py index c7acaf639..834102ff4 100644 --- a/kitty/child.py +++ b/kitty/child.py @@ -19,6 +19,13 @@ def cwd_of_process(pid): return os.path.realpath(ans) +def cmdline_of_process(pid): + if is_macos: + # TODO: macOS implementation, see DarwinProcess.c in htop for inspiration + raise NotImplementedError() + return open('/proc/{}/cmdline'.format(pid), 'rb').read().decode('utf-8').split('\0') + + def remove_cloexec(fd): fcntl.fcntl(fd, fcntl.F_SETFD, fcntl.fcntl(fd, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC) diff --git a/kitty/remote_control.py b/kitty/remote_control.py index 97a55bca8..99f5cdf1f 100644 --- a/kitty/remote_control.py +++ b/kitty/remote_control.py @@ -36,7 +36,9 @@ def cmd_ls(global_opts, opts, args): def ls(boss, window): - raise NotImplementedError() + data = list(boss.list_os_windows()) + data = json.dumps(data, indent=2, sort_keys=True) + return data def handle_cmd(boss, window, cmd): diff --git a/kitty/tabs.py b/kitty/tabs.py index 536cc440a..4adc7d902 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -202,6 +202,10 @@ class Tab: # {{{ def move_window_backward(self): self.move_window(-1) + def list_windows(self): + for w in self: + yield w.as_dict() + def __iter__(self): yield from iter(self.windows) @@ -379,6 +383,14 @@ class TabManager: # {{{ def __len__(self): return len(self.tabs) + def list_tabs(self): + for tab in self: + yield { + 'id': tab.id, + 'title': tab.name or tab.title, + 'windows': list(tab.list_windows()), + } + @property def active_tab(self): return self.tabs[self.active_tab_idx] if self.tabs else None diff --git a/kitty/window.py b/kitty/window.py index 3c0d71ff2..6bc15087d 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -8,6 +8,7 @@ import weakref from collections import deque from enum import Enum +from .child import cmdline_of_process, cwd_of_process from .config import build_ansi_color_table, parse_send_text_bytes from .constants import ( ScreenGeometry, WindowGeometry, appname, get_boss, wakeup @@ -107,6 +108,22 @@ class Window: def __repr__(self): return 'Window(title={}, id={})'.format(self.title, self.id) + def as_dict(self): + try: + cwd = cwd_of_process(self.child.pid) + except Exception: + cwd = None + try: + cmdline = cmdline_of_process(self.child.pid) + except Exception: + cmdline = None + return dict( + id=self.id, + title=self.override_title or self.title, + pid=self.child.pid, + cwd=cwd, cmdline=cmdline + ) + def set_visible_in_layout(self, window_idx, val): val = bool(val) if val is not self.is_visible_in_layout: