Skip to content

Commit

Permalink
fix: save/restore state on un/tiling instead of session lock/unlock
Browse files Browse the repository at this point in the history
While I don't know why if it's expected that my extension crashes GNOME
Shell when trying to access some window props during a screen lock or if
it's a bug in mutter... but either way. This should workaround the crash.

Fixes: #267
  • Loading branch information
Leleat committed Aug 4, 2024
1 parent 9c9b058 commit 6d48a47
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 37 deletions.
54 changes: 18 additions & 36 deletions tiling-assistant@leleat-on-github/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import KeybindingHandler from './src/extension/keybindingHandler.js';
import LayoutsManager from './src/extension/layoutsManager.js';
import ActiveWindowHint from './src/extension/activeWindowHint.js';
import AltTabOverride from './src/extension/altTab.js';
import { Rect } from './src/extension/utility.js';
import { Rect, TilingStates } from './src/extension/utility.js';

/**
* 2 entry points:
Expand Down Expand Up @@ -229,6 +229,8 @@ export default class TilingAssistantExtension extends Extension {
// them after the session is unlocked again.
this._saveBeforeSessionLock();

TilingStates.clear();

this._settingsOverrider.destroy();
this._settingsOverrider = null;
this._moveHandler.destroy();
Expand Down Expand Up @@ -274,30 +276,6 @@ export default class TilingAssistantExtension extends Extension {

this._wasLocked = true;

const rectToJsObj = rect => rect && {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height
};

// can't just check for isTiled because maximized windows may
// have an untiledRect as well in case window gaps are used
const openWindows = this._twm.getWindows(true);
const savedWindows = openWindows.filter(w => w.untiledRect).map(w => {
return {
windowId: w.get_stable_sequence(),
isTiled: w.isTiled,
tiledRect: rectToJsObj(w.tiledRect),
untiledRect: rectToJsObj(w.untiledRect)
};
});

const saveObj = {
'windows': savedWindows,
'tileGroups': Array.from(this._twm.getTileGroups())
};

const userPath = GLib.get_user_config_dir();
const parentPath = GLib.build_filenamev([userPath, '/tiling-assistant']);
const parent = Gio.File.new_for_path(parentPath);
Expand All @@ -310,7 +288,7 @@ export default class TilingAssistantExtension extends Extension {
}
}

const path = GLib.build_filenamev([parentPath, '/tiledSessionRestore.json']);
const path = GLib.build_filenamev([parentPath, '/tiledSessionRestore2.json']);
const file = Gio.File.new_for_path(path);

try {
Expand All @@ -321,7 +299,7 @@ export default class TilingAssistantExtension extends Extension {
}
}

file.replace_contents(JSON.stringify(saveObj), null, false,
file.replace_contents(JSON.stringify(Object.fromEntries(TilingStates)), null, false,
Gio.FileCreateFlags.REPLACE_DESTINATION, null);
}

Expand All @@ -336,7 +314,7 @@ export default class TilingAssistantExtension extends Extension {
this._wasLocked = false;

const userPath = GLib.get_user_config_dir();
const path = GLib.build_filenamev([userPath, '/tiling-assistant/tiledSessionRestore.json']);
const path = GLib.build_filenamev([userPath, '/tiling-assistant/tiledSessionRestore2.json']);
const file = Gio.File.new_for_path(path);
if (!file.query_exists(null))
return;
Expand All @@ -353,16 +331,17 @@ export default class TilingAssistantExtension extends Extension {
if (!success || !contents.length)
return;

const openWindows = this._twm.getWindows(true);
const saveObj = JSON.parse(new TextDecoder().decode(contents));
const states = JSON.parse(new TextDecoder().decode(contents));
const windowObjects = states.windows;
const openWindows = global.display.list_all_windows();

const windowObjects = saveObj['windows'];
windowObjects.forEach(wObj => {
const { windowId, isTiled, tiledRect, untiledRect } = wObj;
const window = openWindows.find(w => w.get_stable_sequence() === windowId);
if (!window)
openWindows.forEach(window => {
const savedState = windowObjects[window.get_id()];

if (!savedState)
return;

const { isTiled, tiledRect, untiledRect } = savedState;
const jsToRect = jsRect => jsRect && new Rect(
jsRect.x, jsRect.y, jsRect.width, jsRect.height
);
Expand All @@ -372,13 +351,16 @@ export default class TilingAssistantExtension extends Extension {
window.untiledRect = jsToRect(untiledRect);
});

const tileGroups = new Map(saveObj['tileGroups']);
const tileGroups = new Map(states.tileGroups);
this._twm.setTileGroups(tileGroups);
openWindows.forEach(w => {
if (tileGroups.has(w.get_id())) {
const group = this._twm.getTileGroupFor(w);
this._twm.updateTileGroup(group);
}
});

TilingStates.set('windows', states.windows);
TilingStates.set('tileGroups', states.tileGroups);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ export default class TilingResizeHandler {
newGrabbedTiledRectHeight
);

Twm.updateSavedTilingState(window);

// Now calculate the new tiledRects for the windows, which were resized
// along the window based on the diff of the window's tiledRect pre
// and after the grab.
Expand Down Expand Up @@ -379,6 +381,8 @@ export default class TilingResizeHandler {
win.tiledRect.y += isResizingS ? tiledRectDiffHeight : 0;
win.tiledRect.height -= tiledRectDiffHeight;
}

Twm.updateSavedTilingState(win);
});

this._preGrabRects.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Main } from '../dependencies/shell.js';
import { getWindows } from '../dependencies/unexported/altTab.js';

import { Orientation, Settings } from '../common.js';
import { Rect, Util } from './utility.js';
import { Rect, TilingStates, Util } from './utility.js';

/**
* Singleton responsible for tiling. Implement the signals in a separate Clutter
Expand Down Expand Up @@ -220,19 +220,22 @@ export class TilingWindowManager {
// Maximized with gaps
if (maximize) {
this._updateGappedMaxWindowSignals(window);
this.updateSavedTilingState(window);

// Tiled window
} else if (!fakeTile) {
// Make the tile group only consist of the window itself to stop
// resizing or raising together. Also don't call the Tiling Popup.
if (Settings.getBoolean('disable-tile-groups') || ignoreTA) {
this.updateTileGroup([window]);
this.updateSavedTilingState(window);
return;
}

// Setup the (new) tileGroup to raise tiled windows as a group
const topTileGroup = this._getWindowsForBuildingTileGroup(monitor);
this.updateTileGroup(topTileGroup);
this.updateSavedTilingState(window);

this.emit('window-tiled', window);

Expand Down Expand Up @@ -308,6 +311,8 @@ export class TilingWindowManager {
window.tiledRect = null;
window.untiledRect = null;

this.deleteSavedTilingState(window);

this.emit('window-untiled', window);
}

Expand Down Expand Up @@ -1032,6 +1037,55 @@ export class TilingWindowManager {
app.open_new_window(-1);
}

static updateSavedTilingState(window) {
// window state
const windowState = TilingStates.get('windows')[window.get_id()];
const rectToJsObject = rect => {
return rect
? { x: rect.x, y: rect.y, width: rect.width, height: rect.height }
: undefined;
};

if (windowState) {
windowState.isTiled = window.isTiled;
windowState.tiledRect = rectToJsObject(window.tiledRect);
windowState.untiledRect = rectToJsObject(window.untiledRect);
} else {
TilingStates.get('windows')[window.get_id()] = {
isTiled: window.isTiled,
tiledRect: rectToJsObject(window.tiledRect),
untiledRect: rectToJsObject(window.untiledRect)
};
}

// tile groups
const tileGroups = TilingStates.get('tileGroups');
const group = tileGroups.find(g => g[0] === window.get_id());

if (group) {
group.splice(1);
group.push(this._tileGroups.get(window.get_id()));
} else {
tileGroups.push([
window.get_id(),
this._tileGroups.get(window.get_id())
]);
}
}

static deleteSavedTilingState(window) {
const savedTilingState = TilingStates.get('windows');

if (!savedTilingState[window.get_id()])
return;

delete savedTilingState[window.get_id()];

TilingStates.set('tileGroups', TilingStates.get('tileGroups').filter(group => {
return group[0] !== window.get_id();
}));
}

/**
* Gets the top windows, which are supposed to be in a tile group. That
* means windows, which are tiled, and don't overlap each other.
Expand Down
14 changes: 14 additions & 0 deletions tiling-assistant@leleat-on-github/src/extension/utility.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ import { Main } from '../dependencies/shell.js';

import { Direction, Orientation, Settings } from '../common.js';

/**
* The states of the windows tiling states during the runtime of the extension.
* It is used for for saving/restoring the states when un/locking the session.
*/
export const TilingStates = new Map([
// `windows` value will map the window.id to an object with isTiled,
// tiledRect, and untiledRect props
['windows', {}],
// `tileGroups` value will be an array of tuples (arrays). The first value
// in a tuple will be the window.id. The second tuple value is an array of
// window.ids of the windows in the tile group.
['tileGroups', []]
]);

/**
* Library of commonly used functions for the extension.js' files
* (and *not* the prefs files)
Expand Down

0 comments on commit 6d48a47

Please sign in to comment.