From e287b76595e1d8a9efb4ae3bc58ea8f0fcfe02e2 Mon Sep 17 00:00:00 2001 From: Nathan Poirier Date: Tue, 12 Sep 2023 01:19:37 +0200 Subject: [PATCH] Update back-to-monitor@nathan818fr (#483) Changelog at: https://github.com/nathan818fr/cinnamon-back-to-monitor/commits/main --- back-to-monitor@nathan818fr/.gitignore | 1 + back-to-monitor@nathan818fr/.prettierrc.yaml | 9 + back-to-monitor@nathan818fr/README.md | 10 +- .../4.0/src/utils.js | 29 -- .../4.0/src/window-utils.js | 199 --------- .../5.4/extension.js | 19 - .../5.4/settings-schema.json | 10 - .../{4.0 => }/extension.js | 0 .../back-to-monitor@nathan818fr/metadata.json | 3 +- .../po/back-to-monitor@nathan818fr.pot | 12 +- .../back-to-monitor@nathan818fr/po/da.po | 4 +- .../back-to-monitor@nathan818fr/po/fr.po | 12 +- .../back-to-monitor@nathan818fr/po/hu.po | 4 +- .../back-to-monitor@nathan818fr/po/it.po | 4 +- .../back-to-monitor@nathan818fr/po/pt_BR.po | 4 +- .../back-to-monitor@nathan818fr/po/ro.po | 4 +- .../{4.0 => }/settings-schema.json | 0 .../{4.0 => }/src/extension.js | 17 +- .../{4.0 => }/src/logger.js | 0 .../{4.0 => }/src/screen-watcher.js | 53 ++- .../back-to-monitor@nathan818fr/src/utils.js | 59 +++ .../src/window-saver.js | 415 ++++++++++++++++++ back-to-monitor@nathan818fr/package-lock.json | 28 ++ back-to-monitor@nathan818fr/package.json | 11 + 24 files changed, 591 insertions(+), 316 deletions(-) create mode 100644 back-to-monitor@nathan818fr/.gitignore create mode 100644 back-to-monitor@nathan818fr/.prettierrc.yaml delete mode 100644 back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/utils.js delete mode 100644 back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/window-utils.js delete mode 100644 back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/extension.js delete mode 100644 back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/settings-schema.json rename back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/{4.0 => }/extension.js (100%) rename back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/{4.0 => }/settings-schema.json (100%) rename back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/{4.0 => }/src/extension.js (89%) rename back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/{4.0 => }/src/logger.js (100%) rename back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/{4.0 => }/src/screen-watcher.js (71%) create mode 100644 back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/utils.js create mode 100644 back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/window-saver.js create mode 100644 back-to-monitor@nathan818fr/package-lock.json create mode 100644 back-to-monitor@nathan818fr/package.json diff --git a/back-to-monitor@nathan818fr/.gitignore b/back-to-monitor@nathan818fr/.gitignore new file mode 100644 index 00000000..07e6e472 --- /dev/null +++ b/back-to-monitor@nathan818fr/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/back-to-monitor@nathan818fr/.prettierrc.yaml b/back-to-monitor@nathan818fr/.prettierrc.yaml new file mode 100644 index 00000000..342f2fc3 --- /dev/null +++ b/back-to-monitor@nathan818fr/.prettierrc.yaml @@ -0,0 +1,9 @@ +trailingComma: es5 +printWidth: 120 +tabWidth: 4 +semi: true +singleQuote: true +bracketSpacing: false +overrides: + - files: ['*.json'] + options: {tabWidth: 2} diff --git a/back-to-monitor@nathan818fr/README.md b/back-to-monitor@nathan818fr/README.md index fcb0f5aa..d3578e03 100644 --- a/back-to-monitor@nathan818fr/README.md +++ b/back-to-monitor@nathan818fr/README.md @@ -4,8 +4,6 @@ A Cinnamon extension to move windows back to their original location when (re-)c ![Screenshot](./screenshot.png?raw=true) -> **IMPORTANT: Cinnamon 5.4+ natively provides the features of this extension.** - ## Features - Remember window locations based on monitor connection (can be switched on/off) @@ -27,7 +25,7 @@ To download the source and install it, execute the following as a normal user: ```bash git clone git@github.com:nathan818fr/cinnamon-back-to-monitor.git cd cinnamon-back-to-monitor -cinnamon-install-spice extension files/back-to-monitor@nathan818fr +npm run install-extension ``` ## Issues @@ -48,8 +46,10 @@ Therefore, tiled/snapped windows can only be restored to the predefined dimensio ### • Fullscreen windows are not restored -The required APIs are not exposed to cinnamon extensions.
-Therefore, fullscreen windows are ignored by this extension. +Before Cinnamon 5.4, the required APIs were not exposed to cinnamon extensions.
+In this case, the fullscreen windows will be ignored. + +With Cinnamon 5.4 and above, the fullscreen windows will be restored correctly! ### • The "Always on Visible Workspace" option is not restored diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/utils.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/utils.js deleted file mode 100644 index 0f2095f8..00000000 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/utils.js +++ /dev/null @@ -1,29 +0,0 @@ -function callSafely(fn) { - try { - return fn(); - } catch (err) { - globalThis.logError(err); - } -} - -function delayQueue(delayMs, fn) { - let timer; - return () => { - clearTimeout(timer); - timer = setTimeout(() => { - timer = undefined; - fn(); - }, delayMs); - }; -} - -function arrayRemoveIf(array, predicate) { - let i = array.length; - while (i--) { - if (predicate(array[i], i)) { - array.splice(i, 1); - } - } -} - -module.exports = {callSafely, delayQueue, arrayRemoveIf}; diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/window-utils.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/window-utils.js deleted file mode 100644 index 01a29e19..00000000 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/window-utils.js +++ /dev/null @@ -1,199 +0,0 @@ -const Meta = imports.gi.Meta; - -const META_MAXIMIZE_HORIZONTAL = Meta.MaximizeFlags.HORIZONTAL; -const META_MAXIMIZE_VERTICAL = Meta.MaximizeFlags.VERTICAL; - -const META_WINDOW_TILE_TYPE_NONE = Meta.WindowTileType.NONE; -const META_WINDOW_TILE_TYPE_TILED = Meta.WindowTileType.TILED; -const META_WINDOW_TILE_TYPE_SNAPPED = Meta.WindowTileType.SNAPPED; - -const META_TILE_NONE = Meta.TileMode.NONE; -const META_TILE_LEFT = Meta.TileMode.LEFT; -const META_TILE_RIGHT = Meta.TileMode.RIGHT; -const META_TILE_ULC = Meta.TileMode.ULC; -const META_TILE_LLC = Meta.TileMode.LLC; -const META_TILE_URC = Meta.TileMode.URC; -const META_TILE_LRC = Meta.TileMode.LRC; -const META_TILE_TOP = Meta.TileMode.TOP; -const META_TILE_BOTTOM = Meta.TileMode.BOTTOM; -const META_TILE_MAXIMIZE = Meta.TileMode.MAXIMIZE; - -function saveWindowState(metaWindow) { - const frameRect = _getFrameRect(metaWindow); - const workspace = metaWindow.get_workspace(); - - let tile, tileType, tileMode; - if ( - (tileType = metaWindow.tile_type) !== META_WINDOW_TILE_TYPE_NONE && - (tileMode = _computeTileMode(metaWindow)) !== META_TILE_NONE - ) { - tile = { - type: tileType, - mode: tileMode, - }; - } - - return { - x: frameRect.x, - y: frameRect.y, - width: frameRect.width, - height: frameRect.height, - minimized: metaWindow.minimized, - maximized: { - horizontally: metaWindow.maximized_horizontally, - vertically: metaWindow.maximized_vertically, - }, - tile, - fullscreen: metaWindow.fullscreen, - workspace: workspace ? workspace.index() : -1, - onAllWorkspaces: false, // TODO: Find a way to access to MetaWindow.on_all_workspaces_requested - }; -} - -function restoreWindowState(metaWindow, state, monitorRect) { - if (metaWindow.fullscreen || state.fullscreen) { - // Fullscreen is not supported yet, skip this window - // TODO: Fint a way to access to MetaWindow.make_fullscreen / MetaWindow.unmake_fullscreen - return; - } - - if (state.minimized) { - // Minimize first if needed - metaWindow.minimize(); - } - - // Always untile & unmaximize (otherwise move is impossible) - metaWindow.tile(META_WINDOW_TILE_TYPE_NONE, false); - metaWindow.unmaximize(META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); - - // FIX: Force-move the window; this prevent many strange placement bugs - // (-32768 is arbitrary: need a value that is not the current one nor the state one) - metaWindow.move_frame(false, -32768, -32768); - - // Move back to the correct monitor - if ((state.maximized.horizontally && state.maximized.vertically) || state.tile) { - // If it's a full maximize or tile, only move (to keep the saved width & height) - const frameRect = _getFrameRect(metaWindow); - metaWindow.move_frame( - true, - monitorRect.x + Math.floor(monitorRect.width / 2) - Math.floor(frameRect.width / 2), - monitorRect.y + Math.floor(monitorRect.height / 2) - Math.floor(frameRect.height / 2) - ); - } else { - // ... otherwise immediately move & resize correctly - metaWindow.move_resize_frame(true, state.x, state.y, state.width, state.height); - } - - // Change workspace (before maximize & tile) - if (state.onAllWorkspaces === true) { - metaWindow.change_workspace_by_index(-1, false, 0); - } else if (state.workspace !== -1) { - metaWindow.change_workspace_by_index(state.workspace, false, 0); - } - - if (state.maximized.horizontally || state.maximized.vertically) { - // Maximize if needed - metaWindow.maximize( - (state.maximized.horizontally ? META_MAXIMIZE_HORIZONTAL : 0) | - (state.maximized.vertically ? META_MAXIMIZE_VERTICAL : 0) - ); - } - - if (state.tile) { - // Tile if needed - metaWindow.tile(state.tile.mode, state.tile.type === META_WINDOW_TILE_TYPE_SNAPPED); - // TODO: Find a way to re-apply custom tile dimensions - } - - if (!state.minimized) { - // Unminimize at end if needed - metaWindow.unminimize(); - } -} - -function _getFrameRect(metaWindow) { - // Can't access to MetaWindow.get_frame_rect with Muffin - // So try our best to get the frame rect - const rect = metaWindow.get_rect(); - const inputRect = metaWindow.get_input_rect(); - const outerRect = metaWindow.get_outer_rect(); - if ( - rect.x === inputRect.x && - rect.y === inputRect.y && - rect.width === inputRect.width && - rect.height === inputRect.height - ) { - return inputRect; - } else { - return outerRect; - } -} - -function _computeTileMode(metaWindow) { - // Can't access to MetaWindow.tile_mode - // So try our best to re-compute it (unfortunately if the window is tiled with a custom size it is possible to make mistakes) - - if (metaWindow.tile_type === META_WINDOW_TILE_TYPE_NONE) { - return META_TILE_NONE; - } - - const monitorIndex = metaWindow.get_monitor(); - if (monitorIndex === -1) { - return META_TILE_NONE; - } - - const monitorGeo = metaWindow.get_screen().get_monitor_geometry(monitorIndex); - if (!monitorGeo) { - return META_TILE_NONE; - } - - let {x, y, width, height} = monitorGeo; - const halfWidth = width / 2; - const halfHeight = height / 2; - const tileAreas = [ - {mode: META_TILE_LEFT, x: x, y: y, width: halfWidth, height: height}, - {mode: META_TILE_RIGHT, x: x + halfWidth, y: y, width: halfWidth, height: height}, - {mode: META_TILE_ULC, x: x, y: y, width: halfWidth, height: halfHeight}, - {mode: META_TILE_LLC, x: x, y: y + halfHeight, width: halfWidth, height: halfHeight}, - {mode: META_TILE_URC, x: x + halfWidth, y: y, width: halfWidth, height: halfHeight}, - {mode: META_TILE_LRC, x: x + halfWidth, y: y + halfHeight, width: halfWidth, height: halfHeight}, - {mode: META_TILE_TOP, x: x, y: y, width: width, height: halfHeight}, - {mode: META_TILE_BOTTOM, x: x, y: y + halfHeight, width: width, height: halfHeight}, - // I don't known what META_TILE_MAXIMIZE is... Skip it for now. - ]; - - const rect = metaWindow.get_outer_rect(); - x = rect.x; - y = rect.y; - width = rect.width; - height = rect.height; - - let bestIou = 0; - let bestTileMode = META_TILE_NONE; - for (const tileArea of tileAreas) { - // Compute intersection area - const iLeft = Math.max(x, tileArea.x); - const iTop = Math.max(y, tileArea.y); - const iRight = Math.min(x + width, tileArea.x + tileArea.width); - const iBottom = Math.min(y + height, tileArea.y + tileArea.height); - if (iRight < iLeft || iBottom < iTop) { - // No intersection - continue; - } - const iArea = (iRight - iLeft) * (iBottom - iTop); - - // Compute union area - const uArea = Math.max(width * height + tileArea.width * tileArea.height - iArea, 1); - - // Compute intersection over union - const iou = iArea / uArea; - - if (iou > bestIou) { - bestIou = iou; - bestTileMode = tileArea.mode; - } - } - return bestTileMode; -} - -module.exports = {saveWindowState, restoreWindowState}; diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/extension.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/extension.js deleted file mode 100644 index 2f156934..00000000 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/extension.js +++ /dev/null @@ -1,19 +0,0 @@ -const Settings = imports.ui.settings; - -let uuid, settings; - -function init(meta) { - uuid = meta.uuid; -} - -function enable() { - globalThis.log(`[${uuid}]: This extension is not compatible with this version of Cinnamon.`); - settings = new Settings.ExtensionSettings({}, uuid); -} - -function disable() { - if (settings) { - settings.finalize(); - settings = null; - } -} diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/settings-schema.json b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/settings-schema.json deleted file mode 100644 index 7e95ebde..00000000 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/5.4/settings-schema.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "incompatibilityNotice1": { - "type": "header", - "description": "This extension is not compatible with this version of Cinnamon." - }, - "incompatibilityNotice2": { - "type": "header", - "description": "Cinnamon now natively provides the features of this extension." - } -} diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/extension.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/extension.js similarity index 100% rename from back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/extension.js rename to back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/extension.js diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/metadata.json b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/metadata.json index cda42c67..380d8f88 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/metadata.json +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/metadata.json @@ -3,7 +3,6 @@ "name": "Back to Monitor", "description": "Move windows back to their original location when (re-)connecting a monitor.", "author": "Nathan Poirier <nathan@poirier.io>", - "cinnamon-version": ["4.0", "4.2", "4.4", "4.6", "4.8", "5.0", "5.2"], - "multiversion": true, + "cinnamon-version": ["4.0", "4.2", "4.4", "4.6", "4.8", "5.0", "5.2", "5.4", "5.6"], "url": "https://github.com/linuxmint/cinnamon-spices-extensions/tree/master/back-to-monitor%40nathan818fr" } diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/back-to-monitor@nathan818fr.pot b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/back-to-monitor@nathan818fr.pot index 8e97f7ce..3a080d8c 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/back-to-monitor@nathan818fr.pot +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/back-to-monitor@nathan818fr.pot @@ -21,18 +21,10 @@ msgid "" "Move windows back to their original location when (re-)connecting a monitor." msgstr "" -#. 4.0->settings-schema.json->rememberState->description +#. settings-schema.json->rememberState->description msgid "Remember window locations based on monitor connection" msgstr "" -#. 4.0->settings-schema.json->minimize->description +#. settings-schema.json->minimize->description msgid "Minimize windows when a monitor is disconnected" msgstr "" - -#. 5.4->settings-schema.json->incompatibilityNotice1->description -msgid "This extension is not compatible with this version of Cinnamon." -msgstr "" - -#. 5.4->settings-schema.json->incompatibilityNotice2->description -msgid "Cinnamon now natively provides the features of this extension." -msgstr "" diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/da.po b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/da.po index a36f2e9e..b8077029 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/da.po +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/da.po @@ -21,10 +21,10 @@ msgstr "Back to Monitor" msgid "Move windows back to their original location when (re-)connecting a monitor." msgstr "Flyt vinduer tilbage til deres oprindelige placering, når en skærm tilkobles (igen)." -#. 4.0->settings-schema.json->rememberState->description +#. settings-schema.json->rememberState->description msgid "Remember window locations based on monitor connection" msgstr "Husk vinduesplacering baseret på skærmforbindelse" -#. 4.0->settings-schema.json->minimize->description +#. settings-schema.json->minimize->description msgid "Minimize windows when a monitor is disconnected" msgstr "Minimér vinduer, når en skærm frakobles" diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/fr.po b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/fr.po index 9e906bdd..4cd187d6 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/fr.po +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/fr.po @@ -22,18 +22,10 @@ msgstr "Back to Monitor" msgid "Move windows back to their original location when (re-)connecting a monitor." msgstr "Déplacer les fenêtres à leur emplacement d'origine lors de la (re)connexion d'un moniteur." -#. 4.0->settings-schema.json->rememberState->description +#. settings-schema.json->rememberState->description msgid "Remember window locations based on monitor connection" msgstr "Mémoriser les emplacements des fenêtres en fonction de la connexion au moniteur" -#. 4.0->settings-schema.json->minimize->description +#. settings-schema.json->minimize->description msgid "Minimize windows when a monitor is disconnected" msgstr "Réduire les fenêtres lorsqu'un moniteur est déconnecté" - -#. 5.4->settings-schema.json->incompatibilityNotice1->description -msgid "This extension is not compatible with this version of Cinnamon." -msgstr "Cette extension n'est pas compatible avec cette version de Cinnamon." - -#. 5.4->settings-schema.json->incompatibilityNotice2->description -msgid "Cinnamon now natively provides the features of this extension." -msgstr "Cinnamon fournit maintenant nativement les fonctionnalités de cette extension." diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/hu.po b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/hu.po index 4b7b8132..3ac66f9d 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/hu.po +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/hu.po @@ -20,10 +20,10 @@ msgstr "Vissza a kijelzőre" msgid "Move windows back to their original location when (re-)connecting a monitor." msgstr "Visszahelyezi az ablakokat az eredeti helyére, amikor újracsatlakoztat egy kijelzőt." -#. 4.0->settings-schema.json->rememberState->description +#. settings-schema.json->rememberState->description msgid "Remember window locations based on monitor connection" msgstr "Feljegyzi az ablakok helyét az adott kijelzőn" -#. 4.0->settings-schema.json->minimize->description +#. settings-schema.json->minimize->description msgid "Minimize windows when a monitor is disconnected" msgstr "Az ablak minimalizálása, amikor a kijelző nincsen csatlakoztatva" diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/it.po b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/it.po index 1fc71337..49c08a4b 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/it.po +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/it.po @@ -21,10 +21,10 @@ msgstr "Torna al Monitor" msgid "Move windows back to their original location when (re-)connecting a monitor." msgstr "Riporta le finestre nella loro posizione originale quando (ri)connetti un monitor." -#. 4.0->settings-schema.json->rememberState->description +#. settings-schema.json->rememberState->description msgid "Remember window locations based on monitor connection" msgstr "Ricorda le posizioni delle finestre in base alla connessione del monitor" -#. 4.0->settings-schema.json->minimize->description +#. settings-schema.json->minimize->description msgid "Minimize windows when a monitor is disconnected" msgstr "Riduci a icona le finestre quando un monitor è disconnesso" diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/pt_BR.po b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/pt_BR.po index 49ca3fa2..10c175f4 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/pt_BR.po +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/pt_BR.po @@ -21,10 +21,10 @@ msgstr "De volta ao monitor" msgid "Move windows back to their original location when (re-)connecting a monitor." msgstr "Mova as janelas para suas posições originais ao (re)conectar um monitor." -#. 4.0->settings-schema.json->rememberState->description +#. settings-schema.json->rememberState->description msgid "Remember window locations based on monitor connection" msgstr "Memorizar a posição das janelas de acordo com o monitor conectado" -#. 4.0->settings-schema.json->minimize->description +#. settings-schema.json->minimize->description msgid "Minimize windows when a monitor is disconnected" msgstr "Minimizar as janelas quando um monitor for desconectado" diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/ro.po b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/ro.po index 451fe4f3..9730c058 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/ro.po +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/po/ro.po @@ -21,10 +21,10 @@ msgstr "Înapoi la Monitor" msgid "Move windows back to their original location when (re-)connecting a monitor." msgstr "Mută ferestrele înapoi la locația lor inițială atunci când se (re)conectează un monitor." -#. 4.0->settings-schema.json->rememberState->description +#. settings-schema.json->rememberState->description msgid "Remember window locations based on monitor connection" msgstr "Reține locațiile ferestrelor în funcție de conexiunea monitorului" -#. 4.0->settings-schema.json->minimize->description +#. settings-schema.json->minimize->description msgid "Minimize windows when a monitor is disconnected" msgstr "Minimizează ferestrele atunci când un monitor este deconectat" diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/settings-schema.json b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/settings-schema.json similarity index 100% rename from back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/settings-schema.json rename to back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/settings-schema.json diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/extension.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/extension.js similarity index 89% rename from back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/extension.js rename to back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/extension.js index 8ddea19d..61795d4b 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/extension.js +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/extension.js @@ -1,11 +1,9 @@ const Settings = imports.ui.settings; -const Gdk = imports.gi.Gdk; -const CinnamonDesktop = imports.gi.CinnamonDesktop; const SignalManager = imports.misc.signalManager; const {globalLogger: logger} = require('src/logger'); const {ScreenWatcher} = require('src/screen-watcher'); const {callSafely} = require('src/utils'); -const {saveWindowState, restoreWindowState} = require('src/window-utils'); +const {windowSaver} = require('src/window-saver'); class BackToMonitorExtension { constructor(meta) { @@ -24,8 +22,7 @@ class BackToMonitorExtension { this._settingsDb.bind('rememberState', 'rememberState', this._onRememberStateChange); this._settingsDb.bind('minimize', 'minimize', this._onMinimizeChange); - const rrScreen = CinnamonDesktop.RRScreen.new(Gdk.Screen.get_default()); - this._screenWatcher = new ScreenWatcher(global.screen, rrScreen); + this._screenWatcher = new ScreenWatcher(); this._screenWatcher.register(); this._signalManager = new SignalManager.SignalManager(null); @@ -55,6 +52,8 @@ class BackToMonitorExtension { this._settingsDb.finalize(); this._settingsDb = null; } + + logger.log('Disabled'); } _onRememberStateChange = () => { @@ -77,12 +76,12 @@ class BackToMonitorExtension { this._monitorDisconnectedWindows.set(outputName, disconnectedWindows); for (const metaWindow of global.display.list_windows(0)) { - if (metaWindow.get_monitor() !== monitorIndex) { + if (!windowSaver.isInside(metaWindow, monitorRect, monitorIndex)) { continue; } - if (this._settings.rememberState && metaWindow.can_move()) { - const windowState = callSafely(() => saveWindowState(metaWindow)); + if (this._settings.rememberState && windowSaver.allowsMove(metaWindow)) { + const windowState = callSafely(() => windowSaver.save(metaWindow)); if (windowState) { // Transform x and y to relative positions windowState.x -= monitorRect.x; @@ -142,7 +141,7 @@ class BackToMonitorExtension { // Restore logger.log(`Restore '${metaWindow.get_title()}' to ${outputName}: ${JSON.stringify(windowState)}`); - callSafely(() => restoreWindowState(metaWindow, windowState, monitorRect)); + callSafely(() => windowSaver.restore(metaWindow, windowState, monitorRect)); } } }; diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/logger.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/logger.js similarity index 100% rename from back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/logger.js rename to back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/logger.js diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/screen-watcher.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/screen-watcher.js similarity index 71% rename from back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/screen-watcher.js rename to back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/screen-watcher.js index d818aecb..89614c06 100644 --- a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/4.0/src/screen-watcher.js +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/screen-watcher.js @@ -3,29 +3,45 @@ const GLib = imports.gi.GLib; const Meta = imports.gi.Meta; const SignalManager = imports.misc.signalManager; const Signals = imports.signals; +const Gdk = imports.gi.Gdk; +const CinnamonDesktop = imports.gi.CinnamonDesktop; const {globalLogger: logger} = require('src/logger'); -const {callSafely, delayQueue} = require('src/utils'); +const {callSafely, delayQueue, addSignalHook, removeSignalHooks} = require('src/utils'); class ScreenWatcher { - constructor(metaScreen, rrScreen) { - this._metaScreen = metaScreen; - this._rrScreen = rrScreen; + constructor() { + this._metaScreen = global.screen; + this._rrScreen = CinnamonDesktop.RRScreen.new(Gdk.Screen.get_default()); } register() { this._outputsRect = new Map(); this._pendingMonitors = new Map(); this._signalManager = new SignalManager.SignalManager(null); + this._signalHooks = []; this._loading = true; try { - this._signalManager.connect(this._rrScreen, 'changed', this._onRRScreenChanged); + if (typeof Meta.WindowTileType === 'undefined') { + // Cinnamon 5.4+ move and resize windows before emitting documented signals. + // Internally it uses MonitorManager:monitors-changed-internal to detect monitor changes, so we hook + // this signal to act before Cinnamon. + addSignalHook( + this._signalHooks, + Meta.MonitorManager.get(), + 'monitors-changed-internal', + this._onRRScreenChanged + ); + } else { + this._signalManager.connect(this._rrScreen, 'changed', this._onRRScreenChanged); + } this._signalManager.connect( this._metaScreen, 'monitors-changed', delayQueue(1000, this._onMonitorsChanged) ); + // Call _onRRScreenChanged() immediately to initialize _outputsRect. this._onRRScreenChanged(this._rrScreen); } finally { this._loading = false; @@ -37,13 +53,14 @@ class ScreenWatcher { this._signalManager.disconnectAllSignals(); this._signalManager = null; } + if (this._signalHooks) { + removeSignalHooks(this._signalHooks); + this._signalHooks = null; + } } _onRRScreenChanged = () => { - // NOTE: Can't use RROutput.get_position because it requires output arguments (not available with CJS). - // So instead call the xrandr command :'( const rrOutputsRect = this._captureRROutputsRect(); - const rrOutputs = this._rrScreen.list_outputs(); for (const rrOutput of rrOutputs) { const name = rrOutput.get_name(); @@ -66,7 +83,9 @@ class ScreenWatcher { _onOutputConnected = (name, rect) => { const monitorIndex = this._getMonitorIndexAt(rect.x, rect.y); - logger.log(`Output connected: ${name} (x: ${rect.x}, y: ${rect.y}, index: ${monitorIndex})`); + logger.log( + `Output connected: ${name} (x: ${rect.x}, y: ${rect.y}, w: ${rect.width}, h: ${rect.height}, index: ${monitorIndex})` + ); const monitorChangeCancelled = this._pendingMonitors.has(name); if (monitorChangeCancelled) { @@ -80,7 +99,9 @@ class ScreenWatcher { _onOutputDisconnected = (name, rect) => { const monitorIndex = this._getMonitorIndexAt(rect.x, rect.y); - logger.log(`Output disconnected: ${name} (x: ${rect.x}, y: ${rect.y}, index: ${monitorIndex})`); + logger.log( + `Output disconnected: ${name} (x: ${rect.x}, y: ${rect.y}, w: ${rect.width}, h: ${rect.height}, index: ${monitorIndex})` + ); const monitorChangeCancelled = this._pendingMonitors.has(name); if (monitorChangeCancelled) { @@ -116,23 +137,29 @@ class ScreenWatcher { _onMonitorLoaded = (name, rect) => { const monitorIndex = this._getMonitorIndexAt(rect.x, rect.y); - logger.log(`Monitor loaded: ${name} (x: ${rect.x}, y: ${rect.y}, index: ${monitorIndex})`); + logger.log( + `Monitor loaded: ${name} (x: ${rect.x}, y: ${rect.y}, w: ${rect.width}, h: ${rect.height}, index: ${monitorIndex})` + ); this.emit('monitor-loaded', {outputName: name, monitorRect: rect, monitorIndex}); }; _onMonitorUnloaded = (name, rect) => { const monitorIndex = this._getMonitorIndexAt(rect.x, rect.y); - logger.log(`Monitor unloaded: ${name} (x: ${rect.x}, y: ${rect.y}, index: ${monitorIndex})`); + logger.log( + `Monitor unloaded: ${name} (x: ${rect.x}, y: ${rect.y}, w: ${rect.width}, h: ${rect.height}, index: ${monitorIndex})` + ); this.emit('monitor-unloaded', {outputName: name, monitorRect: rect, monitorIndex}); }; _captureRROutputsRect = () => { + // NOTE: Can't use RROutput.get_position because it requires output arguments (not available with CJS). + // So instead call the xrandr command :'( let [, xrandrStdout] = GLib.spawn_command_line_sync('xrandr --current'); xrandrStdout = xrandrStdout ? ByteArray.toString(xrandrStdout) : ''; - // See xrandr output sources: https://github.com/freedesktop/xorg-xrandr/blob/8969b3c651eaae3e3a2370ec45f4eeae9750111d/xrandr.c#L3697 + // See xrandr output sources: https://gitlab.freedesktop.org/xorg/app/xrandr/-/blob/8969b3c651eaae3e3a2370ec45f4eeae9750111d/xrandr.c#L3697 const pattern = /^([^ ]+) (?:connected|disconnected|unknown connection)(?: primary)? ([0-9]+)x([0-9]+)\+(-?[0-9]+)\+(-?[0-9]+)/gm; const ret = {}; diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/utils.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/utils.js new file mode 100644 index 00000000..2d080841 --- /dev/null +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/utils.js @@ -0,0 +1,59 @@ +const GObject = imports.gi.GObject; + +function callSafely(fn) { + try { + return fn(); + } catch (err) { + globalThis.logError(err); + } +} + +function delayQueue(delayMs, fn) { + let timer; + return () => { + clearTimeout(timer); + timer = setTimeout(() => { + timer = undefined; + fn(); + }, delayMs); + }; +} + +function arrayRemoveIf(array, predicate) { + let i = array.length; + while (i--) { + if (predicate(array[i], i)) { + array.splice(i, 1); + } + } +} + +function addSignalHook(storage, object, signalName, callback) { + const signalId = GObject.signal_lookup(signalName, object); + if (!signalId) { + return; + } + + const hookId = GObject.signal_add_emission_hook(signalId, undefined, () => { + try { + callback(); + } catch (err) { + globalThis.log(err); + } + return true; + }); + if (!hookId) { + return; + } + + storage.push([signalId, hookId]); +} + +function removeSignalHooks(storage) { + for (const [signalId, hookId] of storage) { + GObject.signal_remove_emission_hook(signalId, hookId); + } + storage.length = 0; +} + +module.exports = {callSafely, delayQueue, arrayRemoveIf, addSignalHook, removeSignalHooks}; diff --git a/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/window-saver.js b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/window-saver.js new file mode 100644 index 00000000..e3a8d1b6 --- /dev/null +++ b/back-to-monitor@nathan818fr/files/back-to-monitor@nathan818fr/src/window-saver.js @@ -0,0 +1,415 @@ +const Meta = imports.gi.Meta; +const {globalLogger: logger} = require('src/logger'); + +const META_MAXIMIZE_HORIZONTAL = Meta.MaximizeFlags.HORIZONTAL; +const META_MAXIMIZE_VERTICAL = Meta.MaximizeFlags.VERTICAL; + +const META_TILE_NONE = Meta.TileMode.NONE; +const META_TILE_LEFT = Meta.TileMode.LEFT; +const META_TILE_RIGHT = Meta.TileMode.RIGHT; +const META_TILE_ULC = Meta.TileMode.ULC; +const META_TILE_LLC = Meta.TileMode.LLC; +const META_TILE_URC = Meta.TileMode.URC; +const META_TILE_LRC = Meta.TileMode.LRC; +const META_TILE_TOP = Meta.TileMode.TOP; +const META_TILE_BOTTOM = Meta.TileMode.BOTTOM; +const META_TILE_MAXIMIZE = Meta.TileMode.MAXIMIZE; + +class WindowSaverBase { + constructor(version) { + this.version = version; + } + + save(metaWindow) { + const frameRect = this._getFrameRect(metaWindow); + const workspace = metaWindow.get_workspace(); + const tile = this._saveTile(metaWindow); + + return { + x: frameRect.x, + y: frameRect.y, + width: frameRect.width, + height: frameRect.height, + minimized: metaWindow.minimized, + maximized: { + horizontally: metaWindow.maximized_horizontally, + vertically: metaWindow.maximized_vertically, + }, + tile, + fullscreen: metaWindow.fullscreen, + workspace: workspace ? workspace.index() : -1, + onAllWorkspaces: false, // MetaWindow.on_all_workspaces_requested is not accessible + }; + } + + restore(metaWindow, state, monitorRect) { + if (!this._supportsFullscreen() && (metaWindow.fullscreen || state.fullscreen)) { + // Fullscreen is not supported, skip this window + return; + } + + if (state.minimized) { + // Minimize first if needed + metaWindow.minimize(); + } + + // Always unfullscreen, untile & unmaximize (otherwise move is impossible) + if (metaWindow.fullscreen) { + this._unmakeFullscreen(metaWindow); + } + this._untile(metaWindow); + metaWindow.unmaximize(META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL); + + // FIX: Force-move the window; this prevent many strange placement bugs + // (-32768 is arbitrary: need a value that is not the current one nor the state one) + metaWindow.move_frame(false, -32768, -32768); + + // Move back to the correct monitor + if ((state.maximized.horizontally && state.maximized.vertically) || state.tile || state.fullscreen) { + // If it's a full-maximize/tile/fullscreen, only move (to keep the saved width & height) + const frameRect = this._getFrameRect(metaWindow); + metaWindow.move_frame( + true, + monitorRect.x + Math.floor(monitorRect.width / 2) - Math.floor(frameRect.width / 2), + monitorRect.y + Math.floor(monitorRect.height / 2) - Math.floor(frameRect.height / 2) + ); + } else { + // ... otherwise immediately move & resize correctly + metaWindow.move_resize_frame(true, state.x, state.y, state.width, state.height); + } + + // Change workspace (before maximize, tile & fullscreen) + if (state.onAllWorkspaces === true) { + this._changeWorkspaceByIndex(metaWindow, -1, false); + } else if (state.workspace !== -1) { + this._changeWorkspaceByIndex(metaWindow, state.workspace, false); + } + + if (state.maximized.horizontally || state.maximized.vertically) { + // Maximize if needed + metaWindow.maximize( + (state.maximized.horizontally ? META_MAXIMIZE_HORIZONTAL : 0) | + (state.maximized.vertically ? META_MAXIMIZE_VERTICAL : 0) + ); + } + + if (state.tile) { + // Tile if needed + this._retile(metaWindow, state.tile); + } + + if (state.fullscreen) { + // Fullscreen if needed + this._makeFullscreen(metaWindow); + } + + if (!state.minimized) { + // Unminimize at end if needed + metaWindow.unminimize(); + } + } + + /** + * @abstract + */ + isInside(metaWindow, monitorRect, monitorIndex) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + allowsMove(metaWindow) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _getFrameRect(metaWindow) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _untile(metaWindow) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _retile(metaWindow, tileState) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _saveTile(metaWindow) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _supportsFullscreen() { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _makeFullscreen(metaWindow) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _unmakeFullscreen(metaWindow) { + throw new Error('not implemented'); + } + + /** + * @abstract + */ + _changeWorkspaceByIndex(metaWindow, workspaceIndex, append) { + throw new Error('not implemented'); + } +} + +class WindowSaver4_8 extends WindowSaverBase { + constructor() { + super('4.8<>5.2'); + this.MetaWindowTileTypeNone = Meta.WindowTileType.NONE; + this.MetaWindowTileTypeTiled = Meta.WindowTileType.TILED; + this.MetaWindowTileTypeSnapped = Meta.WindowTileType.SNAPPED; + } + + isInside(metaWindow, monitorRect, monitorIndex) { + return metaWindow.get_monitor() === monitorIndex; + } + + allowsMove(metaWindow) { + return metaWindow.can_move(); + } + + _getFrameRect(metaWindow) { + // Can't access to MetaWindow.get_frame_rect with Muffin + // So try our best to get the frame rect + const rect = metaWindow.get_rect(); + const inputRect = metaWindow.get_input_rect(); + const outerRect = metaWindow.get_outer_rect(); + if ( + rect.x === inputRect.x && + rect.y === inputRect.y && + rect.width === inputRect.width && + rect.height === inputRect.height + ) { + return inputRect; + } else { + return outerRect; + } + } + + _untile(metaWindow) { + metaWindow.tile(META_TILE_NONE, false); + } + + _retile(metaWindow, tileState) { + metaWindow.tile(tileState.mode, tileState.type === this.MetaWindowTileTypeSnapped); + // TODO: Find a way to re-apply custom tile dimensions + } + + _saveTile(metaWindow) { + const tileType = metaWindow.tile_type; + if (tileType === this.MetaWindowTileTypeNone) { + return undefined; + } + + const tileMode = this._computeTileMode(metaWindow); + if (tileMode === META_TILE_NONE) { + return undefined; + } + + return { + type: tileType, + mode: tileMode, + }; + } + + _computeTileMode(metaWindow) { + // Can't access to MetaWindow.tile_mode + // So try our best to re-compute it (unfortunately if the window is tiled with a custom size it is possible to make mistakes) + + if (metaWindow.tile_type === this.MetaWindowTileTypeNone) { + return META_TILE_NONE; + } + + const monitorIndex = metaWindow.get_monitor(); + if (monitorIndex === -1) { + return META_TILE_NONE; + } + + const monitorGeo = metaWindow.get_screen().get_monitor_geometry(monitorIndex); + if (!monitorGeo) { + return META_TILE_NONE; + } + + let {x, y, width, height} = monitorGeo; + const halfWidth = width / 2; + const halfHeight = height / 2; + const tileAreas = [ + {mode: META_TILE_LEFT, x: x, y: y, width: halfWidth, height: height}, + {mode: META_TILE_RIGHT, x: x + halfWidth, y: y, width: halfWidth, height: height}, + {mode: META_TILE_ULC, x: x, y: y, width: halfWidth, height: halfHeight}, + {mode: META_TILE_LLC, x: x, y: y + halfHeight, width: halfWidth, height: halfHeight}, + {mode: META_TILE_URC, x: x + halfWidth, y: y, width: halfWidth, height: halfHeight}, + {mode: META_TILE_LRC, x: x + halfWidth, y: y + halfHeight, width: halfWidth, height: halfHeight}, + {mode: META_TILE_TOP, x: x, y: y, width: width, height: halfHeight}, + {mode: META_TILE_BOTTOM, x: x, y: y + halfHeight, width: width, height: halfHeight}, + // I don't known what META_TILE_MAXIMIZE is... Skip it for now. + ]; + + const rect = metaWindow.get_outer_rect(); + x = rect.x; + y = rect.y; + width = rect.width; + height = rect.height; + + let bestIou = 0; + let bestTileMode = META_TILE_NONE; + for (const tileArea of tileAreas) { + // Compute intersection area + const iLeft = Math.max(x, tileArea.x); + const iTop = Math.max(y, tileArea.y); + const iRight = Math.min(x + width, tileArea.x + tileArea.width); + const iBottom = Math.min(y + height, tileArea.y + tileArea.height); + if (iRight < iLeft || iBottom < iTop) { + // No intersection + continue; + } + const iArea = (iRight - iLeft) * (iBottom - iTop); + + // Compute union area + const uArea = Math.max(width * height + tileArea.width * tileArea.height - iArea, 1); + + // Compute intersection over union + const iou = iArea / uArea; + + if (iou > bestIou) { + bestIou = iou; + bestTileMode = tileArea.mode; + } + } + return bestTileMode; + } + + /** + * @abstract + */ + _supportsFullscreen() { + // MetaWindow.make_fullscreen & MetaWindow.unmake_fullscreen are not accessible + return false; + } + + /** + * @abstract + */ + _makeFullscreen(metaWindow) { + // MetaWindow.make_fullscreen is not accessible + } + + /** + * @abstract + */ + _unmakeFullscreen(metaWindow) { + // MetaWindow.unmake_fullscreen is not accessible + } + + _changeWorkspaceByIndex(metaWindow, workspaceIndex, append) { + metaWindow.change_workspace_by_index(workspaceIndex, append, 0); + } +} + +class WindowSaver5_4 extends WindowSaverBase { + constructor() { + super('5.4+'); + } + + isInside(metaWindow, monitorRect) { + const frameRect = this._getFrameRect(metaWindow); + const frameCenterX = frameRect.x + frameRect.width / 2; + const frameCenterY = frameRect.y + frameRect.height / 2; + return ( + frameCenterX >= monitorRect.x && + frameCenterY >= monitorRect.y && + frameCenterX < monitorRect.x + monitorRect.width && + frameCenterY < monitorRect.y + monitorRect.height + ); + } + + allowsMove(metaWindow) { + return metaWindow.allows_move() || metaWindow.fullscreen; + } + + _getFrameRect(metaWindow) { + return metaWindow.get_frame_rect(); + } + + _untile(metaWindow) { + // meta_window_tile is not exported in Cinnamon 5.4... :'( + // TODO: Open an issue on muffin to ask to export it again. + // Take the opportunity to ask to export the other missing APIs. + } + + _retile(metaWindow, tileState) { + // Noting-to-do (see below) + } + + _saveTile(metaWindow) { + // Still cannot access tile_mode in Cinnamon 5.4. + // But since tile_type doesn't exist anymore, we can't use it to detect tilling like in Cinnamon 4.8. + return undefined; + } + + /** + * @abstract + */ + _supportsFullscreen() { + return true; + } + + /** + * @abstract + */ + _makeFullscreen(metaWindow) { + metaWindow.make_fullscreen(); + } + + /** + * @abstract + */ + _unmakeFullscreen(metaWindow) { + metaWindow.unmake_fullscreen(); + } + + _changeWorkspaceByIndex(metaWindow, workspaceIndex, append) { + metaWindow.change_workspace_by_index(workspaceIndex, append); + } +} + +const windowSaver = (() => { + let ret; + if (typeof Meta.WindowTileType === 'undefined') { + ret = new WindowSaver5_4(); + } else { + ret = new WindowSaver4_8(); + } + logger.log('Using WindowSaver for Cinnamon ' + ret.version); + return ret; +})(); + +module.exports = {windowSaver}; diff --git a/back-to-monitor@nathan818fr/package-lock.json b/back-to-monitor@nathan818fr/package-lock.json new file mode 100644 index 00000000..7d658a1b --- /dev/null +++ b/back-to-monitor@nathan818fr/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "cinnamon-back-to-monitor", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cinnamon-back-to-monitor", + "devDependencies": { + "prettier": "^3.0.3" + } + }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + } + } +} diff --git a/back-to-monitor@nathan818fr/package.json b/back-to-monitor@nathan818fr/package.json new file mode 100644 index 00000000..2180c67d --- /dev/null +++ b/back-to-monitor@nathan818fr/package.json @@ -0,0 +1,11 @@ +{ + "name": "cinnamon-back-to-monitor", + "private": "true", + "scripts": { + "format": "prettier -w .", + "install-extension": "cinnamon-install-spice extension files/back-to-monitor@nathan818fr" + }, + "devDependencies": { + "prettier": "^3.0.3" + } +}