Skip to content

Conversation

zzhaolei
Copy link
Contributor

@zzhaolei zzhaolei commented Aug 5, 2025

This is a preliminary implementation and doesn't work yet. I'm not sure what the problem is.

expect:

┌──────────────────────────────────┬──────────────────────────────────┐
│                                  │                                  │
│                                  │         Right Top Pane           │
│                                  │        (50% of right)            │
│                                  │                                  │
│                                  ├──────────────────────────────────┤
│           Left Pane              │                                  │
│         (50% width)              │       Right Bottom Pane          │
│                                  │        (50% of right)            │
│                                  │                                  │
│                                  │                                  │
└──────────────────────────────────┴──────────────────────────────────┘

./kitty/launcher/kitty -c NONE --session ~/.config/kitty/startup.conf

startup.conf:

new_tab complex tab

layout splits:split_axis=horizontal
# enabled_layouts splits:split_axis=horizontal

launch --var=kitty_serialize_window_id=4 --location=hsplit
launch --var=kitty_serialize_window_id=5 --location=vsplit
launch --var=kitty_serialize_window_id=6 --location=vsplit
set_layout_state {"all_windows":{"active_group_history":[2,3,1,4,5,6],"active_group_idx":0,"window_groups":[{"id":4,"window_ids":[4]},{"id":5,"window_ids":[5]},{"id":6,"window_ids":[6]}]},"class":"Splits","opts":{"default_axis_is_horizontal":true},"pairs":{"one":4,"two":{"horizontal":false,"one":5,"two":6}}}

@zzhaolei zzhaolei changed the title wip: restore session restore session Aug 5, 2025
@zzhaolei zzhaolei marked this pull request as draft August 5, 2025 10:27
@zzhaolei zzhaolei marked this pull request as ready for review August 5, 2025 13:15
@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 6, 2025

@kovidgoyal I printed it, and it was different from what I expected, can you give me some more guidance on how to deal with it?

kitty/tabs.py:238:

    def _set_current_layout(self, layout_name: str, layout_state: dict[str, Any] | None = None) -> None:
        self._last_used_layout = self._current_layout_name
        self.current_layout = self.create_layout_object(layout_name)
        self._current_layout_name = layout_name
        if layout_state:
            print("before -- ", layout_state)
            v = self.current_layout.unserialize(layout_state, self.windows)
            print("serialize -- ", v)
            print("after -- ", self.current_layout.serialize(self.windows))
        self.mark_tab_bar_dirty()

output:

before --  {'all_windows': {'active_group_history': [2, 3, 1, 4, 5, 6], 'active_group_idx': 0, 'window_groups': [{'id': 4, 'window_ids': [4]}, {'id': 5, 'window_ids': [5]}, {'id': 6, 'window_ids': [6]}]}, 'class': 'Splits', 'opts': {'default_axis_is_horizontal': True}, 'pairs': {'one': 4, 'two': {'horizontal': False, 'one': 5, 'two': 6}}}
serialize --  True
after --  {'pairs': {}, 'opts': {'default_axis_is_horizontal': True}, 'class': 'Splits', 'all_windows': {'active_group_idx': -1, 'active_group_history': [], 'window_groups': []}}

@zzhaolei zzhaolei marked this pull request as draft August 6, 2025 10:32
@kovidgoyal
Copy link
Owner

There you go: 97f1d7f

and the variable name is wrong in your sample session file.

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 7, 2025

Now it works fine.

@kovidgoyal I'm planning to integrate kitty-save-session into kitty @ ls --output-session=[filepath], what do you think? This will generate a session file(like startup.conf)

Can it be generated in the ~/.config/kitty/ directory by default? When the user specifies the filepath, it will be generated to the specified path.

@zzhaolei zzhaolei marked this pull request as ready for review August 7, 2025 02:10
@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 7, 2025 via email

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 7, 2025

I will implement it step by step and finally achieve the effect of 6).

Instead of --output-session, use --output-format which can take choices json and session. Then output to stdout, the user can redirect the output wherever they like.

This is a good idea, I'll implement it.

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 7, 2025

@kovidgoyal On Mac, every time I start ./kitty/launcher/kitty, an Icon\r is created. What is this?

~/D/kitty> git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   kitty/rc/ls.py

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	"Icon\r"

~/D/kitty> file Icon\r
: empty

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 7, 2025

Now you can generate a session file by running the following command:

kitty @ ls --output-format session

@zzhaolei zzhaolei force-pushed the master branch 2 times, most recently from 8ea0bd7 to bac0a24 Compare August 7, 2025 05:40
@kovidgoyal
Copy link
Owner

No idea, doesnt happen on my mac, but in any case you should be running kitty/launcher/kitty.app/Contents/MacOS/kitty

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 8, 2025

  1. Exclude a bunch of unsafe env vars when serilizing, see the parse_message() method in kitty/launch.py probably factor that out into a common function and use it.

To be honest, I don't quite understand the logic of parse_message. Is the logic in kitty/launch.py:1005 what I need to focus on?

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 8, 2025

2/3/4/5/6

@kovidgoyal Sorry, I'm afraid I don't fully understand these requirements. Could you please implement them when you have time?

Not sure if the current implementation meets certain requirements

@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 8, 2025 via email

@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 8, 2025 via email

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 8, 2025

0afa0c4

4) added

@kovidgoyal
Copy link
Owner

0afa0c4

4) added

There's also a whole bunch of display manager related env vars. A subset:
X11 (Xorg/X11)
DISPLAY
Specifies the X server to connect to.
Example: :0, localhost:10.0

XAUTHORITY
Path to the X authentication file (e.g., .Xauthority).

XDG_SESSION_TYPE
Sometimes set to x11 to indicate an X11 session.

XMODIFIERS
Input Method configuration for X11 applications.
Example: @im=ibus

XSERVTransSocketUNIXPath
(Rarely set directly) Path for UNIX socket transport.

Wayland
WAYLAND_DISPLAY
Specifies the Wayland socket to connect to.
Example: wayland-0

XDG_SESSION_TYPE
Set to wayland to indicate a Wayland session.

XDG_RUNTIME_DIR
Directory for user-specific non-essential runtime files (used for Wayland sockets).

WAYLAND_DEBUG
Enables debug output for Wayland clients if set (e.g., 1).

QT_QPA_PLATFORM
For Qt apps, can be set to wayland, wayland-egl, or xcb to specify platform backend.

GDK_BACKEND
For GTK apps, can be set to wayland or x11 (or both, comma-separated) to specify backend.

Other Related Environment Variables
DBUS_SESSION_BUS_ADDRESS
Used by both X11 and Wayland environments for D-Bus communication.

CLUTTER_BACKEND
For Clutter-based apps, can be set to wayland or x11.

Thinking about it, it may be better to only serialize env vars that are not present in kitty's own environment. Or even better, only serialize env vars that were specified when the window itself was created, this will require some plumbing to track those env vars on the window object.

@zzhaolei
Copy link
Contributor Author

zzhaolei commented Aug 8, 2025

This is a bit beyond my capabilities, so I'm afraid I'll just have to rely on you to figure this out.🙁

@mike-lloyd03
Copy link

Could we just snapshot the env vars when a window is created and diff them when serialization happens?

Furthermore, could we move forward with no env var support for now and add that is a later feature? I personally don't want env vars being recorded in my sessions.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 9, 2025 via email

@mike-lloyd03
Copy link

Could we target an MVP that just restores tabs, windows, layouts, and cwds? What other data, besides env vars are you hoping to capture during a user's session?

If we want to restore actively running commands like nvim, we may want to include a prompt in each window for the user to confirm running the command when a session is restored. That's how Zellij does it for reference.

@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 9, 2025 via email

@mike-lloyd03
Copy link

I'm not asking for "half-baked" solutions. I'm merely making a suggestion and trying to figure out how I can best contribute. You said the approach needs rethinking. So I threw some ideas out there.

Anyway, I'm happy to contribute to this feature so you're free to focus on more pressing issues. But I don't want to waste your or my time pursuing an approach you're not a fan of. If you can define the required elements for session restoration, that gives me a clear roadmap to work towards. My understanding right now is that built-in session save/restore needs the following elements:

  • Tab
  • Windows
  • Layouts
  • Environment variables
  • CWDs
  • Active commands

Does this look right?

@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 10, 2025 via email

@mike-lloyd03
Copy link

Fine, man. I'm just trying to help. I understand that FOSS software is difficult with people trying to demand things of you free of charge. I'm grateful for this project and what you've build here so I'm trying to do the opposite of that by contributing. But you seem pretty set on what you want and that isn't very clear to me. I'll leave it be and you can implement it if and when and how you want.

@kovidgoyal
Copy link
Owner

If you want to help, be patient, wait till I implement it, then once
that's done, I will welcome discussion and further contributions, but
asking me to have this discussion now just means I have to go look at
the code and think about things and have this discussion and then do it
all over again later when it's time for actual implementation/review.
That is not being respectful of my time.

@kovidgoyal
Copy link
Owner

Have added boss.serialize_state_as_session()

I leave testing/refinement to you. Any comments/changes/discussion is welcome now.

@zzhaolei
Copy link
Contributor Author

kitty/tabs.py:1194

When there is only one tab, self.active_tab_history is empty, so the tab cannot be serialized. Is this expected?

@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 12, 2025 via email

@zzhaolei
Copy link
Contributor Author

kitty/boss.py:512

This will disrupt the user's current tab position and reorder them accordingly, but this shouldn't be necessary.

kitty/tabs.py:1201

Also, a new_os_window will be created here, but this isn't necessary when there's only one OS window.

@kovidgoyal
Copy link
Owner

Both issues should be fixed now.

@zzhaolei zzhaolei force-pushed the master branch 2 times, most recently from c1d434f to f121981 Compare August 12, 2025 10:53
@zzhaolei
Copy link
Contributor Author

Is there a way to distinguish between tab and window focus?
I'm currently using the following code, but it focuses on the last tab. My cursor might be on tab 2 of 1, 2, or 3. The following code was added after kitty/tabs.py:306:

if window.os_window_id == current_focused_os_window_id() and window is self.active_window:
    gw.append('focus')

@zzhaolei
Copy link
Contributor Author

Now the main functionality should be implemented. The only thing missing is the focus distinction between tabs and windows, or how to focus the last open tab

@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 12, 2025 via email

@zzhaolei
Copy link
Contributor Author

Now generate the following session content:

os_window_class kitty
os_window_name kitty
new_tab
layout splits:split_axis=horizontal
enabled_layouts splits:split_axis=horizontal
launch --cwd=/Users/user/Test/1 --var=kitty_serialize_window_id=1 /opt/homebrew/bin/fish --login
set_layout_state {"pairs": {"one": 1}, "opts": {"default_axis_is_horizontal": true}, "class": "Splits", "all_windows": {"active_group_idx": 0, "active_group_history": [1], "window_groups": [{"id": 1, "window_ids": [1]}]}}

new_tab
layout splits:split_axis=horizontal
enabled_layouts splits:split_axis=horizontal
launch --cwd=/Users/user/Test/3 --var=kitty_serialize_window_id=3
set_layout_state {"pairs": {"one": 3}, "opts": {"default_axis_is_horizontal": true}, "class": "Splits", "all_windows": {"active_group_idx": 0, "active_group_history": [3], "window_groups": [{"id": 3, "window_ids": [3]}]}}

new_tab
layout splits:split_axis=horizontal
enabled_layouts splits:split_axis=horizontal
launch --cwd=/Users/user/Test/4 --var=kitty_serialize_window_id=4
set_layout_state {"pairs": {"one": 4}, "opts": {"default_axis_is_horizontal": true}, "class": "Splits", "all_windows": {"active_group_idx": 0, "active_group_history": [4], "window_groups": [{"id": 4, "window_ids": [4]}]}}

new_tab
layout splits:split_axis=horizontal
enabled_layouts splits:split_axis=horizontal
launch --cwd=/Users/user/Test/2 --var=kitty_serialize_window_id=2
set_layout_state {"pairs": {"one": 2}, "opts": {"default_axis_is_horizontal": true}, "class": "Splits", "all_windows": {"active_group_idx": 0, "active_group_history": [2], "window_groups": [{"id": 2, "window_ids": [2]}]}}

Visiting directories 1, 2, 3, 4, 2 (also tab numbers) will result in a session with tabs in 1, 3, 4, 2. Most recently visited will be placed last? This makes sense, but it should probably be documented somewhere.

@zzhaolei
Copy link
Contributor Author

Is there anything I haven't done yet?

@zzhaolei zzhaolei changed the title restore session kitty @ ls output session Aug 12, 2025
@kovidgoyal kovidgoyal merged commit c8e4126 into kovidgoyal:master Aug 12, 2025
@kovidgoyal
Copy link
Owner

kovidgoyal commented Aug 12, 2025 via email

@mike-lloyd03
Copy link

I'm getting an error when trying to use a desktop session created with kitten @ ls --output-format session:

[0.214] Traceback (most recent call last):
  File "/usr/bin/../lib/kitty/kitty/main.py", line 588, in main
    kitty_main(called_from_panel)
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/main.py", line 570, in kitty_main
    run_app(opts, cli_opts, bad_lines, talk_fd)
    ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/main.py", line 325, in __call__
    _run_app(opts, args, bad_lines, talk_fd)
    ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/main.py", line 299, in _run_app
    boss.start(window_id, startup_sessions)
    ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/boss.py", line 1283, in start
    self.startup_first_child(first_os_window_id, startup_sessions=startup_sessions)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/boss.py", line 424, in startup_first_child
    wid = self.add_os_window(startup_session, window_state=wstate, os_window_id=os_window_id)
  File "/usr/bin/../lib/kitty/kitty/boss.py", line 461, in add_os_window
    tm = TabManager(os_window_id, self.args, wclass, wname, startup_session)
  File "/usr/bin/../lib/kitty/kitty/tabs.py", line 992, in __init__
    self.add_tabs_from_session(startup_session)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/tabs.py", line 997, in add_tabs_from_session
    tab = Tab(self, session_tab=t)
  File "/usr/bin/../lib/kitty/kitty/tabs.py", line 170, in __init__
    self.startup(session_tab)
    ~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/tabs.py", line 246, in startup
    self._startup(session_tab)
    ~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/tabs.py", line 281, in _startup
    self.current_layout.unserialize(session_tab.layout_state, self.windows)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/layout/base.py", line 473, in unserialize
    return self.set_layout_state(s, m.get)
           ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/layout/tall.py", line 357, in set_layout_state
    self.layout_opts = TallLayoutOpts(layout_state['opts'])
                       ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/layout/tall.py", line 79, in __init__
    self.mirrored = to_bool(data.get('mirrored', 'false'))
                    ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/bin/../lib/kitty/kitty/conf/utils.py", line 84, in to_bool
    return x.lower() in ('y', 'yes', 'true')
           ^^^^^^^
AttributeError: 'bool' object has no attribute 'lower'

Here's the session I'm trying to restore from:

os_window_class kitty
os_window_name kitty

new_tab jib
layout fat:bias=80
enabled_layouts fat:bias=80,splits,stack
set_layout_state {"main_bias": [0.8, 0.19999999999999996], "biased_map": {}, "opts": {"full_size": 1, "bias": 80, "mirrored": false}, "class": "Fat", "all_windows": {"active_group_idx": 2, "active_group_history": [1, 2], "window_groups": [{"id": 1, "window_ids": [1]}, {"id": 2, "window_ids": [3]}, {"id": 3, "window_ids": [4]}]}}

launch --cwd=/home/mike --var=kitty_serialize_window_id=1
launch --cwd=/home/mike --var=kitty_serialize_window_id=3
launch --cwd=/home/mike --var=kitty_serialize_window_id=4
focus

new_tab
layout fat:bias=80
enabled_layouts fat:bias=80,splits,stack
set_layout_state {"main_bias": [0.8, 0.19999999999999996], "biased_map": {}, "opts": {"full_size": 1, "bias": 80, "mirrored": false}, "class": "Fat", "all_windows": {"active_group_idx": 1, "active_group_history": [4], "window_groups": [{"id": 4, "window_ids": [5]}, {"id": 5, "window_ids": [6]}]}}

launch --cwd=/home/mike --var=kitty_serialize_window_id=5
launch --cwd=/home/mike --var=kitty_serialize_window_id=6
focus

Running 1:0.42.2.r125.ga0b58ef20-1 built from the AUR.

@kovidgoyal
Copy link
Owner

a8de8e4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants