Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it so root workspace layout mode is restored #42

Merged
merged 11 commits into from
Nov 2, 2019
7 changes: 6 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ dist: disco
services:
- xvfb
before_install:
- sudo apt-get install -y i3 i3status
- sudo /usr/lib/apt/apt-helper download-file https://debian.sur5r.net/i3/pool/main/s/sur5r-keyring/sur5r-keyring_2019.02.01_all.deb keyring.deb SHA256:176af52de1a976f103f9809920d80d02411ac5e763f695327de9fa6aff23f416
- sudo dpkg -i ./keyring.deb
- echo "deb https://debian.sur5r.net/i3/ $(grep '^DISTRIB_CODENAME=' /etc/lsb-release | cut -f2 -d=) universe" | sudo tee -a /etc/apt/sources.list.d/sur5r-i3.list
- sudo apt update
- sudo apt install -y i3 i3status wmctrl xdotool
install:
- pip install -r requirements.txt
before_script:
- i3 &
- sleep 3
script:
- i3 -v
- pytest -vv --cov=i3_resurrect tests/
- tox
after_success:
Expand Down
49 changes: 39 additions & 10 deletions i3_resurrect/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,26 +207,55 @@ def restore_layout(workspace, directory):
placeholder_window_ids = []
for (_, window) in util.windows_in_workspace(workspace):
pid = window.pid

# If window has no process, add it to list of placeholder windows.
if pid == 0:
# If window has no process, add it to list of placeholder windows.
placeholder_window_ids.append(int(window.id, 16))
continue

# Otherwise, add it to the list of regular windows.
window_ids.append(int(window.id, 16))
else:
# Otherwise, add it to the list of regular windows.
window_ids.append(int(window.id, 16))

# Unmap all windows in workspace.
# Unmap all non-placeholder windows in workspace.
for window_id in window_ids:
util.xdo_unmap_window(window_id)

# Remove any remaining placeholder windows in workspace.
# Remove any remaining placeholder windows in workspace so that we don't
# have duplicates.
for window_id in placeholder_window_ids:
util.xdo_kill_window(window_id)

# Read saved layout file.
layout_file = Path(directory) / f'workspace_{workspace}_layout.json'
with layout_file.open('r') as f:
layout = json.load(f)

# append_layout can only insert nodes so we must separately change the
# layout mode of the workspace node.
ws_layout_mode = layout.get('layout', 'default')
tree = i3.get_tree()
focused = tree.find_focused()
workspace_node = focused.workspace()
workspace_node.command(f'layout {ws_layout_mode}')

# We don't want to pass the whole layout file because we don't want to
# append a new workspace, but append_layout requires a file path so we must
# extract the part of the json that we want and store it in a temporary
# file.
restorable_layout = (
layout.get('nodes', []) + layout.get('floating_nodes', [])
)
restorable_layout_file = Path(
f'/tmp/i3-resurrect/workspace_{workspace}_layout.json'
)
# Create tempfile directory if non-existent.
restorable_layout_file.parent.mkdir(parents=True, exist_ok=True)
with restorable_layout_file.open('w') as f:
f.write(json.dumps(restorable_layout))

# Create fresh placeholder windows by appending layout to workspace.
layout_file = str(Path(directory) / f'workspace_{workspace}_layout.json')
i3.command(f'append_layout {layout_file}')
i3.command(f'append_layout {str(restorable_layout_file)}')

# Delete tempfile.
restorable_layout_file.unlink()

# Map all unmapped windows.
for window_id in window_ids:
Expand Down
2 changes: 1 addition & 1 deletion i3_resurrect/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def build_layout(tree, swallow):
JSON serialisable.
"""
processed = process_node(tree, swallow)
return processed.get('nodes', []) + processed.get('floating_nodes', [])
return processed


def process_node(original, swallow):
Expand Down
220 changes: 120 additions & 100 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,110 +288,130 @@ def test_build_layout(monkeypatch):
"floating": "auto_off",
"swallows": []
}
expected_tree = [
{
"border": "pixel",
"current_border_width": 2,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 2049,
"y": 486,
"width": 553,
"height": 107
},
"layout": "splith",
"name": "Ario",
"orientation": "none",
"percent": 0.5,
"scratchpad_state": "none",
"type": "con",
"workspace_layout": "default",
"swallows": [
{
"class": "^Ario$",
"instance": "^ario$"
}
],
"sticky": False
expected_tree = {
"type": "workspace",
"orientation": "horizontal",
"scratchpad_state": "none",
"percent": None,
"layout": "splith",
"workspace_layout": "default",
"sticky": False,
"border": "normal",
"current_border_width": -1,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0
},
{
"border": "normal",
"current_border_width": -1,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0
"name": "8",
"nodes": [
{
"border": "pixel",
"current_border_width": 2,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 2049,
"y": 486,
"width": 553,
"height": 107
},
"layout": "splith",
"name": "Ario",
"orientation": "none",
"percent": 0.5,
"scratchpad_state": "none",
"type": "con",
"workspace_layout": "default",
"swallows": [
{
"class": "^Ario$",
"instance": "^ario$"
}
],
"sticky": False
},
"layout": "splitv",
"name": None,
"orientation": "vertical",
"percent": 0.5,
"scratchpad_state": "none",
"sticky": False,
"type": "con",
"workspace_layout": "default",
"nodes": [
{
"border": "pixel",
"current_border_width": 2,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 2331,
"y": 10,
"width": 941,
"height": 1024
},
"layout": "splith",
"name": "Faster Melee - Slippi (r18)",
"orientation": "none",
"percent": 0.5,
"scratchpad_state": "none",
"sticky": False,
"type": "con",
"workspace_layout": "default",
"swallows": [
{
"class": "^Dolphin\\-emu$",
"instance": "^dolphin\\-emu$",
"title": "^Faster\\ Melee\\ \\-\\ Slippi\\ \\(r18\\)$"
}
]
{
"border": "normal",
"current_border_width": -1,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 0,
"y": 0,
"width": 0,
"height": 0
},
{
"border": "pixel",
"current_border_width": 2,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 2542,
"y": 344,
"width": 518,
"height": 356
"layout": "splitv",
"name": None,
"orientation": "vertical",
"percent": 0.5,
"scratchpad_state": "none",
"sticky": False,
"type": "con",
"workspace_layout": "default",
"nodes": [
{
"border": "pixel",
"current_border_width": 2,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 2331,
"y": 10,
"width": 941,
"height": 1024
},
"layout": "splith",
"name": "Faster Melee - Slippi (r18)",
"orientation": "none",
"percent": 0.5,
"scratchpad_state": "none",
"sticky": False,
"type": "con",
"workspace_layout": "default",
"swallows": [
{
"class": "^Dolphin\\-emu$",
"instance": "^dolphin\\-emu$",
"title": "^Faster\\ Melee\\ \\-\\ Slippi\\ \\(r18\\)$"
}
]
},
"layout": "splith",
"name": "Dolphin NetPlay Setup",
"orientation": "none",
"percent": 0.5,
"scratchpad_state": "none",
"sticky": False,
"type": "con",
"workspace_layout": "default",
"swallows": [
{
"class": "^Dolphin\\-emu$",
"instance": "^dolphin\\-emu$",
"title": "^Dolphin\\ NetPlay\\ Setup$"
}
]
}
]
}
]
{
"border": "pixel",
"current_border_width": 2,
"floating": "auto_off",
"fullscreen_mode": 0,
"geometry": {
"x": 2542,
"y": 344,
"width": 518,
"height": 356
},
"layout": "splith",
"name": "Dolphin NetPlay Setup",
"orientation": "none",
"percent": 0.5,
"scratchpad_state": "none",
"sticky": False,
"type": "con",
"workspace_layout": "default",
"swallows": [
{
"class": "^Dolphin\\-emu$",
"instance": "^dolphin\\-emu$",
"title": "^Dolphin\\ NetPlay\\ Setup$"
}
]
}
]
}
]
}
tree = util.build_layout(workspace_container, ['class', 'instance', 'title'])
assert tree == expected_tree

Expand Down
16 changes: 8 additions & 8 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ commands =
i3-resurrect save --help
i3-resurrect restore -h
i3-resurrect restore --help
i3-resurrect save -d /tmp/i3-resurrect
i3-resurrect save -d /tmp/i3-resurrect --swallow=class,instance,title
i3-resurrect save -d /tmp/i3-resurrect --swallow=class,instance,title --layout-only
i3-resurrect save -d /tmp/i3-resurrect --swallow=class,instance,title --programs-only
i3-resurrect restore -d /tmp/i3-resurrect --programs-only
i3-resurrect restore -d /tmp/i3-resurrect --layout-only
i3-resurrect save -d /tmp/i3-resurrect -w "2 " --swallow=class,instance,title
i3-resurrect restore -d /tmp/i3-resurrect -w "2 "
i3-resurrect save -d /tmp/i3-resurrect-saves
i3-resurrect save -d /tmp/i3-resurrect-saves --swallow=class,instance,title
i3-resurrect save -d /tmp/i3-resurrect-saves --swallow=class,instance,title --layout-only
i3-resurrect save -d /tmp/i3-resurrect-saves --swallow=class,instance,title --programs-only
i3-resurrect restore -d /tmp/i3-resurrect-saves --programs-only
i3-resurrect restore -d /tmp/i3-resurrect-saves --layout-only
i3-resurrect save -d /tmp/i3-resurrect-saves -w "2 " --swallow=class,instance,title
i3-resurrect restore -d /tmp/i3-resurrect-saves -w "2 "