GithubHelp home page GithubHelp logo

jonnyhaystack / i3-resurrect Goto Github PK

View Code? Open in Web Editor NEW
362.0 15.0 18.0 229 KB

Simple solution to saving and restoring i3 workspaces

License: GNU General Public License v3.0

Python 98.33% Shell 1.67%

i3-resurrect's Introduction

i3-resurrect

A simple but flexible solution to saving and restoring i3 workspaces

Build Status Coverage Status GitHub issues GitHub pull requests PyPI - Version

Table of Contents

Introduction

i3-resurrect is a program which can save and restore the layout and running programs in your i3 workspaces.

Layouts are saved by using i3ipc to take necessary information from the workspace tree and write it to a JSON file.

Programs are saved by looking up each process in the workspace and writing their cmdline (the command used to launch the program) and cwd (current working directory) to a JSON file.

When restoring programs, Python's subprocess module is used to launch the saved programs with the correct working directory.

When restoring layouts, i3's built-in ability layout restoring functionality is used. This creates placeholder windows where each one will "swallow" any new window that matches specified criteria (window class, instance, title etc).

xdotool is used to make i3 see existing windows as new windows. This is necessary on older i3 versions for matching by window title because the title must match when the window first appears and programs usually only update the title after the window is created. This is fixed on newer i3 versions, but it is also necessary to do this to apply a layout to existing windows if they are created before the placeholder windows.

Background

This project originated as a mixture of hacked together Python and bash scripts that I wrote in order to be able to quickly save and load workspaces on the fly.

I hate having to reboot my computer because it disrupts everything I have open (which tends to be a lot).

To cope with this problem, I try to make it as easy as possible for myself to get everything back to its pre-reboot state.

I quickly found out about the i3-save-tree utility and i3's append-layout command, but these weren't much use to me on their own, as you are expected to customise a layout manually after saving it and relaunch all your programs manually when you restore the layout.

My solution was to create a script that would extract just the bits from i3-save-tree that are needed, and use i3ipc, xprop, and psutil to obtain the commands necessary to launch the programs in a saved workspace.

Since I decided to release this publicly, I have improved the standard of the code a great deal and gotten rid of the hacky bash parts. The code is all Python now, and i3-save-tree is no longer needed as I have reimplemented it in Python.

Getting Started

Requirements

  • Python 3.6+
  • i3
  • xprop
  • xdotool

Installation

From the AUR using yay (recommended for Arch Linux users)

Latest release:

yay -S i3-resurrect

Latest development version:

yay -S i3-resurrect-git

From PyPI (recommended for everyone else)

pip3 install --user --upgrade i3-resurrect

Make sure ~/.local/bin is in your PATH environment variable.

Manual

Obtain source code

git clone [email protected]:JonnyHaystack/i3-resurrect.git

Install locally using pip

cd i3-resurrect
pip3 install --user .

Usage

Command line

Full command line documentation:

Usage: i3-resurrect save [OPTIONS]

  Save an i3 workspace's layout and running programs to a file.

Options:
  -w, --workspace TEXT       The workspace to save.
                             [default: current workspace]
  -n, --numeric              Select workspace by number instead of name.
  -d, --directory DIRECTORY  The directory to save the workspace to.
                             [default: ~/.i3/i3-resurrect]
  -p, --profile TEXT         The profile to save the workspace to.
  -s, --swallow TEXT         The swallow criteria to use.
                             [options: class,instance,title,window_role]
                             [default: class,instance]
  --layout-only              Only save layout.
  --programs-only            Only save running programs.


Usage: i3-resurrect restore [OPTIONS]

  Restore i3 workspace layout and programs.

Options:
  -w, --workspace TEXT       The workspace to restore.
                             [default: current workspace]
  -n, --numeric              Select workspace by number instead of name.
  -d, --directory DIRECTORY  The directory to restore the workspace from.
                             [default: ~/.i3/i3-resurrect]
  -p, --profile TEXT         The profile to restore the workspace from.
  --layout-only              Only restore layout.
  --programs-only            Only restore running programs.


Usage: i3-resurrect ls [OPTIONS] [[workspaces|profiles]]

  List saved workspaces or profiles.

Options:
  -d, --directory DIRECTORY  The directory to search in.
                             [default: ~/.i3/i3-resurrect]


Usage: i3-resurrect rm [OPTIONS]

  Remove saved layout or programs.

Options:
  -w, --workspace TEXT       The saved workspace to delete.
  -d, --directory DIRECTORY  The directory to delete from.
                             [default: ~/.i3/i3-resurrect]
  -p, --profile TEXT         The profile to delete.
  --layout-only              Only delete saved layout.
  --programs-only            Only delete saved programs.

Basic usage, matching only window class/instance:

# Save workspace '1'
i3-resurrect save -w 1

# Restore workspace '1'
i3-resurrect restore -w 1

More accurate layout restoring by matching title:

# Save workspace '1'
i3-resurrect save -w 1 --swallow=class,instance,title

# Restore workspace '1' programs
i3-resurrect restore -w 1 --programs-only

# Apply workspace '1' layout
i3-resurrect restore -w 1 --layout-only

When matching windows by title, the programs must be restored before the layout, because the title often won't match when the window first appears.

When restoring a layout, i3-resurrect uses xdotool to unmap and remap every window on the workspace which causes i3 to see them as new windows so they will be swallowed by the placeholder windows.

Scratchpad

The scratchpad can be saved and restored like so:

i3-resurrect save -w __i3_scratch
i3-resurrect restore -w __i3_scratch

Example configuration in i3

A very basic setup without window title matching:

set $i3_resurrect i3-resurrect

# Save workspace mode.
mode "save" {
  bindsym 1 exec $i3_resurrect save -w 1
  bindsym 2 exec $i3_resurrect save -w 2
  bindsym 3 exec $i3_resurrect save -w 3
  bindsym 4 exec $i3_resurrect save -w 4
  bindsym 5 exec $i3_resurrect save -w 5
  bindsym 6 exec $i3_resurrect save -w 6
  bindsym 7 exec $i3_resurrect save -w 7
  bindsym 8 exec $i3_resurrect save -w 8
  bindsym 9 exec $i3_resurrect save -w 9
  bindsym 0 exec $i3_resurrect save -w 0

  # Back to normal: Enter, Escape, or s
  bindsym Return mode "default"
  bindsym Escape mode "default"
  bindsym s mode "default"
  bindsym $mod+s mode "default"
}

bindsym $mod+s mode "save"

# Restore workspace mode.
mode "restore" {
  bindsym 1 exec $i3_resurrect restore -w 1
  bindsym 2 exec $i3_resurrect restore -w 2
  bindsym 3 exec $i3_resurrect restore -w 3
  bindsym 4 exec $i3_resurrect restore -w 4
  bindsym 5 exec $i3_resurrect restore -w 5
  bindsym 6 exec $i3_resurrect restore -w 6
  bindsym 7 exec $i3_resurrect restore -w 7
  bindsym 8 exec $i3_resurrect restore -w 8
  bindsym 9 exec $i3_resurrect restore -w 9
  bindsym 0 exec $i3_resurrect restore -w 0

  # Back to normal: Enter, Escape, or n
  bindsym Return mode "default"
  bindsym Escape mode "default"
  bindsym n mode "default"
  bindsym $mod+n mode "default"
}

bindsym $mod+n mode "restore"

A more advanced setup where windows are matched by title:

set $i3_resurrect i3-resurrect

# Save workspace mode.
mode "save" {
  bindsym 1 exec "$i3_resurrect save -w 1 --swallow=class,instance,title"
  bindsym 2 exec "$i3_resurrect save -w 2 --swallow=class,instance,title"
  bindsym 3 exec "$i3_resurrect save -w 3 --swallow=class,instance,title"
  bindsym 4 exec "$i3_resurrect save -w 4 --swallow=class,instance,title"
  bindsym 5 exec "$i3_resurrect save -w 5 --swallow=class,instance,title"
  bindsym 6 exec "$i3_resurrect save -w 6 --swallow=class,instance,title"
  bindsym 7 exec "$i3_resurrect save -w 7 --swallow=class,instance,title"
  bindsym 8 exec "$i3_resurrect save -w 8 --swallow=class,instance,title"
  bindsym 9 exec "$i3_resurrect save -w 9 --swallow=class,instance,title"
  bindsym 0 exec "$i3_resurrect save -w 10 --swallow=class,instance,title"

  # Back to normal: Enter, Escape, or s
  bindsym Return mode "default"
  bindsym Escape mode "default"
  bindsym s mode "default"
  bindsym $mod+s mode "default"
}

bindsym $mod+s mode "save"

# Restore workspace mode.
mode "restore" {
  bindsym 1 exec "$i3_resurrect restore -w 1 --programs-only"
  bindsym 2 exec "$i3_resurrect restore -w 2 --programs-only"
  bindsym 3 exec "$i3_resurrect restore -w 3 --programs-only"
  bindsym 4 exec "$i3_resurrect restore -w 4 --programs-only"
  bindsym 5 exec "$i3_resurrect restore -w 5 --programs-only"
  bindsym 6 exec "$i3_resurrect restore -w 6 --programs-only"
  bindsym 7 exec "$i3_resurrect restore -w 7 --programs-only"
  bindsym 8 exec "$i3_resurrect restore -w 8 --programs-only"
  bindsym 9 exec "$i3_resurrect restore -w 9 --programs-only"
  bindsym 0 exec "$i3_resurrect restore -w 10 --programs-only"

  bindsym $mod+1 exec "$i3_resurrect restore -w 1 --layout-only"
  bindsym $mod+2 exec "$i3_resurrect restore -w 2 --layout-only"
  bindsym $mod+3 exec "$i3_resurrect restore -w 3 --layout-only"
  bindsym $mod+4 exec "$i3_resurrect restore -w 4 --layout-only"
  bindsym $mod+5 exec "$i3_resurrect restore -w 5 --layout-only"
  bindsym $mod+6 exec "$i3_resurrect restore -w 6 --layout-only"
  bindsym $mod+7 exec "$i3_resurrect restore -w 7 --layout-only"
  bindsym $mod+8 exec "$i3_resurrect restore -w 8 --layout-only"
  bindsym $mod+9 exec "$i3_resurrect restore -w 9 --layout-only"
  bindsym $mod+0 exec "$i3_resurrect restore -w 10 --layout-only"

  # Back to normal: Enter, Escape, or n
  bindsym Return mode "default"
  bindsym Escape mode "default"
  bindsym n mode "default"
  bindsym $mod+n mode "default"
}

bindsym $mod+n mode "restore"

Example of usage with the second configuration:

Example of usage with the second configuration

rofi/dmenu

There is a sample rofi/dmenu script available here for convenience. This can be given a keybinding and allows easy saving, restoring, and deletion of workspaces and profiles.

Configuration

The config file should be located at ~/.config/i3-resurrect/config.json. A default config file will be created when you first run i3-resurrect.

Window command mappings

In the case of a window where the process cmdline is not the same as the command you must run to launch that program, you can add an explicit window command mapping in the config file.

For example, gnome-terminal's process is gnome-terminal-server, but we need to launch it with the command gnome-terminal. To get this working, you would put the following in your config file:

{
  ...
  "window_command_mappings": [
    {
      "class": "Gnome-terminal",
      "command": "gnome-terminal"
    }
  ]
  ...
}

Another example use case is where:

  • You have multiple windows for a single instance of an application
  • When restoring, you only want one instance of the program to be launched for each instance of the application's main window

In this scenario, you could create one rule that by default maps the application's window class to have no command, and another that sets the command if it also matches a certain title:

{
  ...
  "window_command_mappings": [
    ...
    {
      "class": "Some-program"
    },
    {
      "class": "Some-program",
      "title": "Main window's title",
      "command": ["some-program", "arg1", "arg2"]
    }
    ...
  ]
  ...
}

Hint: If you need to find out a window's class/instance, type xprop | grep WM_CLASS in a terminal and then click on the desired window.

Argument interpolation

You can also interpolate arguments from the actual process's cmdline into a custom command mapping using Python format specifiers. This is useful when you want to keep parts of the original command. Example:

Command mapping:

{
  ...
  "window_command_mappings": [
    ...
    {
      "class": "Code",
      "command": "code -n {1}"
    }
    ...
  ]
  ...
}

Actual cmdline: ['code', '/path/to/file.txt']

Resulting command that gets saved: code -n /path/to/file.txt

Terminals

For terminal emulator windows, we must get the working directory from the first subprocess (usually this will be your shell) instead of the window's root process (the terminal emulator).

i3-resurrect deals with this by allowing you to specify a list of terminal emulator window classes in your config file.

For example, if you use both Alacritty and gnome-terminal and you want their working directories to be restored correctly, you would put the following in your config file:

{
  ...
  "terminals": [
    "Gnome-terminal",
    "Alacritty"
  ]
  ...
}

Some examples are included in the default config. If you would like me to add more command mappings or terminals to the default config, please open an issue for it.

Per window swallow criteria

It is also possible to configure swallow criteria on a per window basis, which will override the criteria set by the --swallow command line parameter.

Example use case:

  • I usually want to include the window title in the swallow criteria to more accurately restore layouts
  • Among other programs that I use, Ario (an mpd client) always has the currently playing song in the window title
  • This makes matching the layout by window title inconvenient, so I want to have Ario always be matched by only the window class/instance

This can be achieved by putting the following in your config file:

{
  ...
  "window_swallow_criteria": {
    "Ario": ["class", "instance"]
  }
  ...
}

Default directory

The default directory used for the saving and loading of workspaces can also be set in the config file:

{
  ...
  "directory": "~/.i3-resurrect/"
  ...
}

Troubleshooting

Programs with spaces in the executable path

If the process of a program you are saving has one only argument (the executable) and the executable path is a relative path containing spaces, it cannot be saved/restored correctly unless you create a custom command mapping for it.

See issue #55 for why this is the case.

I think this is a pretty far out edge case though and I'd be surprised if it caused anyone issues.

Manually editing programs files

If you manually edit a saved programs file, you must be aware of a few things:

If using an array to specify the command, each array element must be a distinct argument, otherwise it won't work. For example:

"command": ["some-program arg1 arg2"]

is invalid, but both

"command": ["some-program", "arg1", "arg2"]

and

"command": "some-program arg1 arg2"

are valid.

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Built With

  • Click - Used to create the command line interface
  • i3ipc - Used to get/build the workspace tree
  • xprop - Used to get the PIDs of the windows that are retrieved using i3ipc
  • psutil - Used to get the cmdline and cwd of each process
  • xdotool - Used to unmap and remap windows

Contributors

See also the list of contributors who participated in this project.

Acknowledgments

Related projects

For those interested, other excellent software I use to get things up and running quickly includes:

  • tmux-resurrect - which obviously also inspired the name of this project
  • tmux-continuum - an excellent companion to tmux-resurrect
  • qutebrowser - which has excellent session management, especially if you create bindings for saving and loading individual windows

License

This project is licensed under the GNU GPL Version 3 - see the LICENSE file for details

i3-resurrect's People

Contributors

ashish-yadav11 avatar infokiller avatar jonnyhaystack avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

i3-resurrect's Issues

Error when trying to save a workspace with placeholders

Describe the bug
When trying to save a workspace with placeholders, the python script fails with an error because the placeholder window doesn't have the "class" attribute. Traceback:

Traceback (most recent call last):
  File "/home/infokiller/.local/bin/i3-resurrect", line 10, in <module>
    sys.exit(main())
  File "/home/infokiller/.local/lib/python3.6/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/home/infokiller/.local/lib/python3.6/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/home/infokiller/.local/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/infokiller/.local/lib/python3.6/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/infokiller/.local/lib/python3.6/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/home/infokiller/.local/lib/python3.6/site-packages/i3_resurrect/main.py", line 54, in save_workspace
    save_layout(workspace, directory, swallow_criteria)
  File "/home/infokiller/.local/lib/python3.6/site-packages/i3_resurrect/main.py", line 74, in save_layout
    util.build_tree(workspace_tree, swallow_criteria),
  File "/home/infokiller/.local/lib/python3.6/site-packages/i3_resurrect/util.py", line 74, in build_tree
    container['nodes'] = build_tree(node, swallow)
  File "/home/infokiller/.local/lib/python3.6/site-packages/i3_resurrect/util.py", line 62, in build_tree
    window_class = node['window_properties']['class']
KeyError: 'class'

To Reproduce
Steps to reproduce the behavior:

  1. Create a placeholder container (for example by using i3-resurrect restore with placeholders that don't match a container)
  2. Run i3-resurrect save on that workspace
  3. See error

Expected behavior
Workspace saved correctly.

System information (please complete the following information):

  • Linux distribution: Archlinux

  • i3-resurrect version (output of i3-resurrect --version): i3-resurrect, version 1.3.2

  • i3 version (output of i3 -v): i3 version 4.17.1 (2019-08-30) © 2009 Michael Stapelberg and contributors

  • Python version (output of python -V): Python 3.6.7

Implement proper logging

Would be good to use Python's logging module to do proper logging with varying severity levels so that I can debug more easily and users will be able to send better bug reports.

This should not replace existing print statements that print to stdout.

Failure with workspaces with slashes in their names

Describe the bug
My workspaces are as follows: 1/home, 2/www, 3/rsc. This causes a FileNotFoundError because the slash in the name is interpreted as a directory separator.

To Reproduce
Run i3-resurrect -w '1/home'

Expected behavior
Work w/o problems, using a slug for the filename.

System information (please complete the following information):

  • Linux distribution: Debian Testing
  • i3-resurrect version (output of i3-resurrect --version): i3-resurrect, version 1.3.2
  • i3 version (output of i3 -v): i3 version 4.17.1 (2019-08-30) © 2009 Michael Stapelberg and contributors
  • Python version (output of python -V): Python 3.7.5

Additional context
Traceback:

Traceback (most recent call last):
  File "/home/g/.local/bin/i3-resurrect", line 10, in <module>
    sys.exit(main())
  File "/home/g/.local/lib/python3.7/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/home/g/.local/lib/python3.7/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/home/g/.local/lib/python3.7/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/g/.local/lib/python3.7/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/g/.local/lib/python3.7/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/home/g/.local/lib/python3.7/site-packages/i3_resurrect/main.py", line 54, in save_workspace
    save_layout(workspace, directory, swallow_criteria)
  File "/home/g/.local/lib/python3.7/site-packages/i3_resurrect/main.py", line 69, in save_layout
    with layout_file.open('w') as f:
  File "/usr/lib/python3.7/pathlib.py", line 1193, in open
    opener=self._opener)
  File "/usr/lib/python3.7/pathlib.py", line 1046, in _opener
    return self._accessor.open(self, flags, mode)
FileNotFoundError: [Errno 2] No such file or directory: '/home/g/.i3/i3-resurrect/workspace_1/home_layout.json'

Restoring a workspace with programs assigned to a another workspace.

When restoring a workspace (not workspace 8) with more than 1 okular windows (okular is assigned to workspace 8), every once in a while one of the windows opens in workspace 8.

Steps to reproduce the behavior:

  1. Assign okular to workspace 8
  2. Go to workspace 1
  3. Open 3 okular windows in workspace 1 (They will open in workspace 8, move them to workspace 1)
  4. Save the workspace
  5. Close all windows in workspace 1
  6. Restore workspace 1
  7. Every once in a while you will observe that some of the okular windows opened workspace 8.

Expected behavior
Every okular window should open in workspace 1

System information (please complete the following information):

  • Linux distribution: Arch
  • i3-resurrect version (output of i3-resurrect --version): 1.4.2
  • i3 version (output of i3 -v): 4.18
  • Python version (output of python -V): 3.8.1

Improve code coverage

I need to add some more unit tests and get better coverage because right now it is really low (48%). It would be nice to get it to 70-80%.

Right now the problem is that a lot of the code isn't really testable because it's been left in the Click command methods. Most of this logic could be extracted out into more testable functions.

The module organisation also probably needs rethinking, because more and more stuff just ends up being dumped in the "util" module.

Note that most things which are not covered by unit tests are still covered by my tox tests, so overall the coverage is not as bad as it looks.

Some windows not restored

I use multiple instances of VS Code across my workspaces. When restoring, my first VS Code will be restored, but other VS Code instances appear like this:

image

What causes this? Given that multiple VS Code windows can be opened, how come they are not restored successfully? Other applications such as Chrome open just fine across multiple workspaces.

Sorry for my lack of understanding of the inner workings of your tool. Thanks for your work!

Saving and restoring AppImage windows fail

Describe the bug
Saving a workspace containing AppImage windows works, but their launch command is not correct and therefore restoring will fail.

To Reproduce
Steps to reproduce the behavior:

  1. Launch some AppImage applications (using Rofi drun modi for instance)
  2. Save workspace with i3-resurrect
  3. Close everything
  4. Try restoring workspace with i3-resurrect

Expected behavior
Save the correct launch commands for AppImage applications (not sure how to do that; they are in the .desltop files but I don't think i3-resurrect parses those files to get the launch commands).

Screenshots
ss-2020-03-08_034132

Note the profile_programs.json on the top left: Molotov and mattermost-desktop both are AppImage applications.

System information (please complete the following information):

  • Linux distribution: Solus
  • i3-resurrect version (output of i3-resurrect --version): version 1.4.2
  • i3 version (output of i3 -v): 4.17.1 (2019-08-30)
  • Python version (output of python -V): Python 2.7.17 (weird, I don't understand why it defaults to the old Python; I have Python 3 installedtoo)

Add a way to map from a window properties filter to a new set of swallow values

This would allow us to match a window that could have a dynamic title and set it up so that its placeholder will swallow any window matching a custom regex.

Actually there's a simple way to achieve this:

  1. Allow window_swallow_criteria to map using a window property filter just like window_command_mappings (#93)
  2. Implement regular expressions for window filters (including swallow criteria) (#92)
  3. Make it so the placeholder window uses the regex filters used for the swallow criteria as the values to swallow for those criteria

Sounds like the perfect solution to me. It should work so well that users don't even notice, and it doesn't add any extra confusing configuration to the config file.

To provide a somewhat similar example to #92 and #93, imagine this scenario:

  • One window we want to save is the game window for PCSX2, a PlayStation 2 emulator
  • The window title changes dynamically and contains the current framerate among other things

This means that the window cannot be swallowed by title correctly unless the framerate happens
to exactly match what it was when the workspace layout was saved.

Here is a configuration example that (with this new feature being implemented) would solve this:

{
  ...
  "window_swallow_criteria": {
    {
      "class": "^PCSX2$",
      "title": "^Kingdom Hearts [0-9]*FPS - PCSX2$",
      "swallows": ["class", "instance", "title"]
    }
  }
  ...
}

When saving the layout, the swallow criteria rule would match the game window and overwrite the title of the placeholder window with the regex that we used for the filter. This means that the placeholder window will now swallow any window that matches our regex, so the game window will be swallowed correctly regardless of the current framerate of the game.

Note: The class name and title regex in the example above were made up on the spot and in reality the regex could be much simpler. I just wanted to give an example that shows a glimpse of how precisely you could match windows.

Handle scratchpad windows

It would be great to be able to treat all the running scratchpads as a workspace that can be saved and restored like the others. I often have a dozen of them going.

Add a way to configure swallow criteria per window class in the config file

The problem:

Sometimes a user may want to have different swallow criteria for different applications in the same workspace. Currently they would have to edit the file manually after each time they saved the workspace layout, which rather defeats the point of i3-resurrect.

Proposed solution:

Add a window_swallow_criteria option to the config file which can specify swallow criteria per window class. Here is an example:

  ...
  "window_swallow_criteria": {
    "Ario": ["class", "instance"]
  }
  ...

The config file should take priority over the --swallow option when deciding swallow criteria.

Programs having names consisting comma do not launch while restoring

Okular open with file having name starting with comma doesn't launch while restoring.

Steps to reproduce the behavior;

  1. run okular -- "file name having comma in it"
  2. save the workspace having the okular window
  3. restore it
  4. okular will not launch

Expected behavior
okular should launch

System information (please complete the following information):

  • Linux distribution: Arch
  • i3-resurrect version (output of i3-resurrect --version): 1.4.2
  • i3 version (output of i3 -v): 4.18
  • Python version (output of python -V): 3.8.1

Command mappings by window title

It would be nice to be able to create command mappings by window title as well as window class.

Window title command mappings would take priority over window class command mappings.

My main use case for this is:

  • Sometimes I'll have multiple windows for one instance of an application on a workspace, but each window has a different role
  • When I restore the programs I don't want it to launch multiple instances of the application. I'd rather it just restored the main window.
  • If I can create window mappings by title, I can just map the title of the extra windows to a blank command

E.g.

  ...
  "window_title_command_mappings": {
    "Dolphin NetPlay Setup": ""
  },
  ...

Alternatively you could create a window class command mapping to an empty command, then override that for a specific window title. That would probably work for more applications.

Eventually I'll probably merge it into one config parameter where you can specify whichever criteria you want for each rule, but that will be something for 2.0.0 because right now I want to keep things backwards compatible.

EDIT:
On second thought, I could merge them into one config parameter for 1.3.0, but make it backwards compatible by simply checking if that config parameter is a dict or a list, and print a message indicating that the dict way will be deprecated in the next major version.

Windows get left behind when restoring programs for workspace that is not yet focused

Describe the bug
Say you are on workspace 1 and you execute i3-resurrect restore -w 2 --programs-only, sometimes one or more program windows may get launched on workspace 1 instead of workspace 2.

I believe this is because the launching of programs with python's subprocess module is not synchronous with the workspace switching using i3.command(f'workspace --no-auto-back-and-forth {workspace}'). I'm experimenting right now using i3's exec command to launch programs instead and that seems to work, although unfortunately I have to do some slightly hacky things to make it work right..

To Reproduce
Described above.

Expected behavior
All programs should be launched on the workspace whose programs you are restoring.

System information (please complete the following information):

  • Linux distribution: Arch Linux
  • i3-resurrect version (output of i3-resurrect --version): 1.3.2
  • i3 version (output of i3 -v): 4.17.1
  • Python version (output of python -V): 3.7.4

support restoring current directory for kitty terminal

Is your feature request related to a problem? Please describe.
Documentation indicates that gnome-terminal and alacrittyare supported, however there is no mention of support for kitty

Describe the solution you'd like
adding the ability to provide "Kitty" as a valid value under terminals in the config file, and

Describe alternatives you've considered
Manually scanning all the kitty windows and modifying the launch with --location, but that is very labor intensive.

Additional context
kitty is becoming a very popular terminal, support would be greatly welcomed.

Time is wasted by unnecessarily getting window info with wmctrl when restoring layout

Describe the bug
Time taken to restore a layout increases rapidly with the number of windows currently open on the workspace. This is because I am unnecessarily using wmctrl to get window info that I do not need for restoring the layout, due to having reused the same function that is used when saving programs.

Expected behavior
The time delay added by each window on the workspace should be much smaller and layouts should be restored much faster.

~/.i3/i3-resurrect does not exist

Describe the bug
I just packaged i3-resurrect for nixos, and tried to launch ls as my first command and it failed.

result/bin/i3-resurrect ls                                                                                          ~/nixpkgs
Traceback (most recent call last):
  File "/nix/store/2gjiv1kl57nb1ji9w2gjawiradhqjnxh-i3-resurrect-1.4.3/bin/.i3-resurrect-wrapped", line 9, in <module>
    sys.exit(main())
  File "/nix/store/s27sv0bd4aq9id6qcg18sr2h6g3fmrsy-python3.7-click-7.1.1/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/nix/store/s27sv0bd4aq9id6qcg18sr2h6g3fmrsy-python3.7-click-7.1.1/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/nix/store/s27sv0bd4aq9id6qcg18sr2h6g3fmrsy-python3.7-click-7.1.1/lib/python3.7/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/nix/store/s27sv0bd4aq9id6qcg18sr2h6g3fmrsy-python3.7-click-7.1.1/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/nix/store/s27sv0bd4aq9id6qcg18sr2h6g3fmrsy-python3.7-click-7.1.1/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/nix/store/2gjiv1kl57nb1ji9w2gjawiradhqjnxh-i3-resurrect-1.4.3/lib/python3.7/site-packages/i3_resurrect/main.py", line 141, in list_workspaces
    for entry in directory.iterdir():
  File "/nix/store/lm0w36273v76nnph1518gwbv1g45sl8w-python3-3.7.7/lib/python3.7/pathlib.py", line 1102, in iterdir
    for name in self._accessor.listdir(self):
FileNotFoundError: [Errno 2] No such file or directory: '/home/teto/.i3/i3-resurrect'

Seems like one has to call save/load first.

Expected behavior
no error

Screenshots
If applicable, add screenshots to help explain your problem.

System information (please complete the following information):

  • Linux distribution: www.nixos.org
  • i3-resurrect version (output of i3-resurrect --version): 1.4.c
  • i3 version (output of i3 -v): 4.18

Additional context
Also my i3 config is in $XDG_CONFIG_HOME/i3 (~/.config/i3) so I would prefer i3-resurrect to use that one (or its own ~/.config/i3-resurrect)

Probelm with workspaces with names starting with space

change
i3.command(f'workspace --no-auto-back-and-forth {workspace_name}')
to
i3.command(f'workspace --no-auto-back-and-forth "{workspace_name}"')
on line 113 of i3_resurrect/main.py
The prior takes to workspace "1: terminal" instead of " 1: terminal" even when workspace_name is " 1: terminal".

AttributeError: 'Connection' object has no attribute 'message'

Hi,

I can invoke i3-resurrect just fine:

└─$   i3-resurrect
Usage: i3-resurrect [OPTIONS] COMMAND [ARGS]...

Options:
  --version   Show the version and exit.
  -h, --help  Show this message and exit.

Commands:
  restore  Restore i3 workspace layout and programs.
  save     Save an i3 workspace's layout and running programs to a file.

...but when I try to save a workspace, it prints the following error:

└─$  i3-resurrect save -w 6
Traceback (most recent call last):
  File "/home/me/.local/bin/i3-resurrect", line 11, in <module>
    sys.exit(main())
  File "/home/me/.local/lib/python3.6/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/home/me/.local/lib/python3.6/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/home/me/.local/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/me/.local/lib/python3.6/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/me/.local/lib/python3.6/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/home/me/.local/lib/python3.6/site-packages/i3_resurrect/main.py", line 54, in save_workspace
    save_layout(workspace, directory, swallow_criteria)
  File "/home/me/.local/lib/python3.6/site-packages/i3_resurrect/main.py", line 67, in save_layout
    workspace_tree = util.get_workspace_tree(workspace)
  File "/home/me/.local/lib/python3.6/site-packages/i3_resurrect/util.py", line 88, in get_workspace_tree
    root = json.loads(i3.message(i3ipc.MessageType.GET_TREE, ''))
AttributeError: 'Connection' object has no attribute 'message'

I have ~/.local/bin in my $PATH var.

Since I'm on Ubuntu 16.04, which only has Python 3.5, I've installed Python 3.6 using pyenv according to this AskUbuntu answer.

Can't install `yay -S i3-resurrect-git` in manjaro

Describe the bug
I3-resurrect can't be installed via yay as it depends on yet unreleased python-i3ipc

To Reproduce
Steps to reproduce the behavior:

  1. open terminel
  2. yay -S i3-resurrect-git`
  3. See error

Expected behavior

  1. open terminel
  2. yay -S i3-resurrect-git`
  3. Program installs

Additional context
though python-i3ipc does not exist on yay just yet, the library aur/i3ipc-python-git should do just fine and which is installed and can be used as import i3ipc.

Note
Works if I just clone and use python setup.py install

Use regular expressions for window filters in config

This would be very useful in order to make different window command mappings and swallow criteria mappings for windows that have the same class and instance and also have dynamic titles. This is also a necessary step on the road towards mapping windows matching filter criteria (such as the current window_command_mappings criteria to placeholder windows that swallow custom regex values (mainly just for the title).

An example use case for this feature would be:

  • I usually have my Alacritty terminal windows swallowed by class,instance,title
  • I sometimes have a file open in neovim in Alacritty, and when the layout and windows are restored, the window in which neovim was running is not immediately swallowed because neovim is not reopened (because restoring shell subprocesses is not practical to do), and so the newly restored Alacritty terminal window's title does not match the old neovim window.

With this new feature, I would be able to create a swallow criteria mapping like so:

{
  ...
  "window_swallow_criteria": {
    {
      "class": "^Alacritty$",
      "title": "nvim",
      "swallows": ["class", "instance"]
    }
  }
  ...
}

This rule would match any window with the class "Alacritty" and a title
containing "nvim" and override the swallow criteria of those windows so that
the title would not be required to match.

One might have observed that this is not that helpful on its own, seeing as that
window might now end up swallowing any other Alacritty window, which is most
likely undesirable. However, the main goal here is actually to enable #94.

invalid literal

Hi,

I can store and load layouts just find. However when trying to store programs I always get error like:

nick@nick:~/.i3/i3-resurrect$ i3-resurrect save -w 1 --programs-only
invalid literal for int() with base 10: '1:'
invalid literal for int() with base 10: '1:

And inside the .json only an empty list is saved [].

Please let me know how I can alleviate this, or assist in debugging.
Thanks

Only restore programs that are not running?

Is your feature request related to a problem? Please describe.
It can be a bit annoying that all programs are restored regardless of whether they are already open or not. It would be more convenient to have only missing programs restored.

Describe the solution you'd like

  1. Get list of programs to restore
  2. Get open windows
  3. Get commands for open windows
  4. Loop through open programs
    • Use list.remove() to delete matching programs from list of programs to restore

This would change existing behaviour though, so it might be best to add a flag to make it optional. I would appreciate feedback on this.

Handle floating windows

Describe the bug
Floating windows are not preserved in the saved window tree. This is because I only recurse over nodes but floating windows are stored in a separate array called floating_nodes.

To Reproduce
Steps to reproduce the behavior:

  1. Save a workspace layout containing a floating window using i3-resurrect save -w <workspace number>
  2. Make the floating window not floating.
  3. Try appling that layout with i3-resurrect restore -w <workspace number> --layout-only
  4. The window that was floating will not be restored to floating.

Expected behavior
The window that was floating when the layout was saved should be restored as a floating window in its original position.

I should be able to just recurse over floating_nodes exactly the same as I do with nodes, unless there are more dimension and positioning attributes that I need to include.

Add "add" saving mode

Because scratchpad windows are not a fixed workspace, but rather used like a palette or library of windows that can be displayed one or several at a time, it would be useful to be able to add elements to a resurrectable list of scratchpad windows, rather than rebuild the entire list on each save.

So I propose a command flag like i3-resurrect save -w __i3_scratch --add that would keep the current contents of the saved (scratch) workspace, but add to it any additional unique windows that are open on that workspace.

Windows loading with ?

On the latest release, when reloading the following is observed:

image

image

The expected layout is a 50/50 split with terminal and browser with no gap in between.

Workspace 1 Layout
Workspace 1 Programs

  • Linux distribution: Disco Dingo
  • i3-resurrect version (output of i3-resurrect --version): i3-resurrect, version 1.4.0
  • i3 version (output of i3 -v): i3 version 4.17.1
  • Python version (output of python -V): Python 2.7.16 and Python 3.7.3

Add a way to interpolate actual cmdline args into custom command mappings

Is your feature request related to a problem? Please describe.
Currently, custom command mappings only allow a fixed command. This is limiting in cases where you might only want to change part of the command, but keep the rest.

Describe the solution you'd like
Python's str.format() function makes it possible for users to use Python format specifiers in custom commands, which will then be replaced by the corresponding argument from the actual command.

Example:

Command mapping:

{
  "window_command_mappings": [
    {
      "class": "Code",
      "command": "code -n {1}"
    }
  ]
}

Actual cmdline: ['code', '/path/to/file.txt']

Resulting command that gets saved: code -n /path/to/file.txt

Additional context
#60 (comment)

terminal emulator terminology not recognized properly

Description:
My saved layout with terminology instances cannot be restored properly.

To Reproduce

-install terminology (e.g. apt install terminology)
-open windows on workspace
-save layout
-restore layout

Result
instead of having the applications open just the containers are displayed. I guess that the window recognition
is not working, checked the class and it should be fine.
The same procedure is working with gnome-terminal for me.

System information (please complete the following information):

  • Linux distribution: Ubuntu
  • i3-resurrect version (output of i3-resurrect --version): 1.4.3
  • i3 version (output of i3 -v): 4.18.2
  • Python version (output of python -V): 3.8.2

Support for saving and restoring workspaces by number

I'd like to be able to save/restore workspace by displaying number to the --workspace option for easier usage.
This is especially useful when icon are used in workspace names that can't easily be typed with keyboard.

Extra options for more flexibility

Over time I am realising that I often switch between different tasks which means different programs/layouts which I will want to save/restore. It's starting to become limiting that I have to use a different workspace for every different task that I might want to work on. It's hard to keep track of which workspace I use for what and the muscle memory is a factor too.

So now I'm thinking it would be nice to have these new options:

  • Option to save workspace to a custom named "session"
  • Option to restore named "session" programs/layout on the currently focused workspace (currently it is assumed that a workspace name will be given and i3-resurrect will switch the focus to that workspace before restoring)

A command to list saved sessions would also be nice for scripts. With this I envision a rofi script that allows you to search your saved sessions and restore the selected one into the current workspace.

I want the current usage/workflow to still work the same, because it's quick and works well for the most part, but I'd also like the flexibility of being able to restore an arbitrary named session on any workspace if I need to.

I'd be interested to hear other people's thoughts on this, as it may end up requiring some significant changes to the interface (might be a 2.0.0 feature I'm not sure yet).

Python wmctrl wrapper is slow

Using the wmctrl-python library to get window info has a big performance overhead. The only thing I need to get is the PID which can be doing using xprop like so:

xprop _NET_WM_PID -id <window_id>

My tests show that this is much faster. It will also eliminate a (completely unmaintained) dependency.

Crashes due to failed dependancy

The script crashed on launch due to pkg_resources.DistributionNotFound: The 'enum-compat' distribution was not found and is required by i3ipc

To Reproduce

  1. run latest i3-resurrect

Screenshots
If applicable, add screenshots to help explain your problem.
img

System information (please complete the following information):

  • Linux distribution: 5.3.13-arch1-1
  • i3-resurrect version (output of i3-resurrect --version):
    Traceback (most recent call last): File "/usr/bin/i3-resurrect", line 6, in <module> from pkg_resources import load_entry_point File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 3252, in <module> def _initialize_master_working_set(): File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 3235, in _call_aside f(*args, **kwargs) File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 3264, in _initialize_master_working_set working_set = WorkingSet._build_master() File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 583, in _build_master ws.require(__requires__) File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 900, in require needed = self.resolve(parse_requirements(requirements)) File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 786, in resolve raise DistributionNotFound(req, requirers) pkg_resources.DistributionNotFound: The 'enum-compat' distribution was not found and is required by i3ipc
  • i3 version (output of i3 -v): i3 version 4.17.1 (2019-08-30)
  • Python version (output of python -V): Python 3.8.0

google-chrome --app mode resurrection

Description
Windows started with google-chrome --app=https://yourdomain.com do not resurrect to the same state as when they were saved.

Describe the solution you'd like
R&D: Investivate google-chrome instances started in --app mode for unique properties that can be used to restore the window in --app mode.

Additional context
JSON for google-chrome --app=... instance:

[
  {
    "command": [
      "/usr/bin/google-chrome"
    ],
    "working_directory": "/home/jackson"
  }
]

screenshot (2)

Add a way to force a swallow when windows and placeholders already exist

Currently there is a problem when you use the title in your swallow criteria:
If the title does not match when the window first appears, it will never be swallowed

Proposed solution:

  • Use xdotool to unmap then remap all the windows on the workspace (except the placeholder windows)
  • When the windows are mapped again, i3 will treat them as new windows and so they will be swallowed by matching placeholder windows, as one would desire

I plan to implement this using python-libxdo in order to keep i3-resurrect's code Python-only.
This library doesn't look very actively maintained but I think it still works. If there are any issues with it in future, I will just use Python's subprocess module. It's only two commands so it's not a particularly big deal.

Get terminal emulator working directory from subprocess

Currently I've used an ugly hack where I try to get the working directory of a terminal emulator from the window title.
For most programs, I get the working directory from the process itself. This didn't work for terminal emulators however, as their working directory doesn't update when you change directory in the shell, so I came up with this silly hack.

I have since realised that while the terminal emulator's working directory does not change, the shell's working directory does. The shell is usually a direct child of the terminal emulator, so I can easily get the working directory from there instead.

So my solution will be to simply get the working directory from the first subprocess instead of the main process, if the window class is in the pre-defined (or - in future - user-defined) list of terminals.

Allow saving whole session with all workspaces

Saving whole user session in one command would be convenient for the user in my opinion.

I propose this implementation made in my repo :

  • i3-resurrect save/restore --session to save whole session
  • i3-resurrect save/restore --workspace list-of-workspaces for saving multiples workspaces
  • i3-resurrect restore --session --clear for deleting active workspaces before restoring
  • Save profile in directory instead of a file to allow saving multiples workspaces or whole session profile

Glade to have your feedback about this.

Mark not restored on compound floating window

Describe the bug
I created a lovely floating + tiled window and I saved it using i3-resurrect. The mark on the outer frame is present in the json file (attached), but it is not restored when I restore the window.

To Reproduce
Steps to reproduce the behavior:

  1. Create a floating window containing multiple tiles.
  2. Mark its outermost parent, so that you can send it to the scratchpad.
  3. i3-resurrect save -w __i3_scratch -p bunchawindows
  4. Close float.
  5. i3-resurrect restore -p bunchawindows

Expected behavior
The restored window should have the mark, but it doesn't.

System information (please complete the following information):

  • Linux distribution: Ubuntu 18.04
  • i3-resurrect version (output of i3-resurrect --version): 1.4.3
  • i3 version (output of i3 -v): 4.19
  • Python version (output of python -V): 3.6.9

Broken saving with -w and -p flag together

Describe the bug
When I try to save a specific workspace to a specific profile, the saved json is empty.

To Reproduce
Steps to reproduce the behavior:

  1. i3-resurrect save -w 1 -p desk
  2. Observe that profiles/desk_layout.json and profiles/desk_programs.json have been created but they are both 2-byte [] files.
  3. With one flag at a time, i3-resurrect save -w 1 works great and so does i3-resurrect save -p desk

Expected behavior
The workspace specified with the -w flag should be saved to the profile specified with the -p flag.

System information (please complete the following information):

  • Linux distribution: Ubuntu 18.04
  • i3-resurrect version (output of i3-resurrect --version): Running version 1.4.3.
  • i3 version (output of i3 -v): 4.19
  • Python version (output of python -V): 3.6.9

restoring would get mixed up if the commands are run without delay

I've written a bash script to restore a number of my workspaces. If I execute the script, some of the programs in a workspace would get sucked into another workspace. This behavior does not occur if there is a delay involved.

#!/bin/bash

i3-resurrect restore -w 1 -d ~/.i3/i3-resurrect/project-name/ --programs-only
sleep 1
i3-resurrect restore -w 2 -d ~/.i3/i3-resurrect/project-name/
sleep 1
i3-resurrect restore -w 3 -d ~/.i3/i3-resurrect/project-name/
sleep 1
i3-resurrect restore -w 4 -d ~/.i3/i3-resurrect/project-name/
sleep 1
i3-resurrect restore -w 9 -d ~/.i3/i3-resurrect/project-name/

To Reproduce
If I omit the "delay"s , a terminal in w2 would get spawned in w3.

System information (please complete the following information):

  • Linux distribution: Xubuntu 18.04
  • i3-resurrect version (output of i3-resurrect --version): 1.4.3
  • i3 version (output of i3 -v): 4.18.2 (2020-07-26)
  • Python version (output of python -V): 3.8.2

Additional context
I'm also using i3-focus-last and i3-auto-orientation scripts.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.