diff --git a/README.md b/README.md index 56ec45a0..9421ed32 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ PaperWM is an experimental [Gnome Shell](https://wiki.gnome.org/Projects/GnomeShell) extension providing scrollable tiling of windows and per monitor workspaces. It's inspired by paper notebooks and tiling window managers. -Supports Gnome Shell from 3.28 to 43 on X11 and wayland. +Supports Gnome Shell from 3.28 to 44 on X11 and wayland. + +>**Note:** while PaperWM can be installed on a wide range of Gnome versions, new features aren't generally backported to prevous Gnome Shell versions. Fixes may be backported on request (please submit a [new issue](https://github.com/paperwm/PaperWM/issues/new/choose) if you've identified a recent fix that should be backported and you can help with testing). While technically an [extension](https://wiki.gnome.org/Projects/GnomeShell/Extensions) it's to a large extent built on top of the Gnome desktop rather than merely extending it. @@ -14,8 +16,8 @@ We hang out on [zulip](https://paperwm.zulipchat.com). Clone the repo and check out the branch supporting the Gnome Shell version you're running. -- 44 (experimental, not officially supported yet, please report bugs): https://github.com/paperwm/PaperWM/tree/develop -- 43 (experimental, please report bugs): https://github.com/paperwm/PaperWM/tree/develop +- 44 (targeted for current support): https://github.com/paperwm/PaperWM/tree/develop +- 43: https://github.com/paperwm/PaperWM/tree/gnome-43 - 42: https://github.com/paperwm/PaperWM/tree/gnome-42 - 40: https://github.com/paperwm/PaperWM/tree/gnome-40 - 3.28-3.38: https://github.com/paperwm/PaperWM/tree/gnome-3.38 @@ -430,6 +432,8 @@ There's a few Gnome Shell settings which works poorly with PaperWM. Namely spanning all monitors - `edge-tiling`: We don't support the native half tiled windows - `attach-modal-dialogs`: Attached modal dialogs can cause visual glitching +- `toggle-tiled-left`: Default GNOME keyboard shortcut `super+left` collides with a default PaperWM shortcut. We disable the GNOME shortcut +- `toggle-tiled-right`: Default GNOME keyboard shortcut `super+right` collides with a default PaperWM shortcut. We disable the GNOME shortcut To use the recommended settings run [`set-recommended-gnome-shell-settings.sh`](https://github.com/paperwm/PaperWM/blob/master/set-recommended-gnome-shell-settings.sh). A "restore previous settings" script is generated so the original settings is not lost. diff --git a/grab.js b/grab.js index d6073bb5..bbab2589 100644 --- a/grab.js +++ b/grab.js @@ -18,13 +18,13 @@ var Utils = Extension.imports.utils; var Tweener = Utils.tweener; var Navigator = Extension.imports.navigator; +var virtualPointer; function isInRect(x, y, r) { return r.x <= x && x < r.x + r.width && r.y <= y && y < r.y + r.height; } - function monitorAtPoint(gx, gy) { for (let monitor of Main.layoutManager.monitors) { if (isInRect(gx, gy, monitor)) @@ -33,6 +33,22 @@ function monitorAtPoint(gx, gy) { return null; } +/** + * Returns a virtual pointer (i.e. mouse) device that can be used to + * "clickout" of a drag operation when `grab_end_op` is unavailable + * (i.e. as of Gnome 44 where `grab_end_op` was removed). + * @returns Clutter.VirtualInputDevice + */ +function getVirtualPointer() { + if (!virtualPointer) { + virtualPointer = Clutter.get_default_backend() + .get_default_seat() + .create_virtual_device(Clutter.InputDeviceType.POINTER_DEVICE); + } + + return virtualPointer; +} + var MoveGrab = class MoveGrab { constructor(metaWindow, type, space) { this.window = metaWindow; @@ -42,6 +58,10 @@ var MoveGrab = class MoveGrab { this.initialSpace = space || Tiling.spaces.spaceOfWindow(metaWindow); this.zoneActors = new Set(); + + // save whether this was tiled window at start of grab + this.wasTiled = !(this.initialSpace.isFloating(metaWindow) || + Scratch.isScratchWindow(metaWindow)); } begin({center} = {}) { @@ -50,8 +70,11 @@ var MoveGrab = class MoveGrab { this.center = center; if (this.grabbed) return; + this.grabbed = true - global.display?.end_grab_op(global.get_current_time()); + + global.display.end_grab_op?.(global.get_current_time()); + global.display.set_cursor(Meta.Cursor.MOVE_OR_RESIZE_WINDOW); this.dispatcher = new Navigator.getActionDispatcher(Clutter.GrabState.POINTER) this.actor = this.dispatcher.actor @@ -370,7 +393,7 @@ var MoveGrab = class MoveGrab { time: prefs.animation_time, scale_x: 1, scale_y: 1, - opacity: clone.__oldOpacity || 255 + opacity: clone?.__oldOpacity ?? 255, }; if (this.dnd) { @@ -456,10 +479,9 @@ var MoveGrab = class MoveGrab { // and layout will work correctly etc. this.window = null; - this.initialSpace.layout(); // ensure window is properly activated after layout/ensureViewport tweens - Mainloop.timeout_add(0, () => { + Meta.later_add(Meta.LaterType.IDLE, () => { Main.activateWindow(metaWindow); }); @@ -473,6 +495,28 @@ var MoveGrab = class MoveGrab { } global.display.set_cursor(Meta.Cursor.DEFAULT); + + /** + * Gnome 44 removed the ability to manually end_grab_op. + * Previously we would end the grab_op before doing + * PaperWM grabs. In 44, we can't do this so the grab op + * may still be in progress, which is okay, but won't be ended + * until we "click out". We do this here if needed. + */ + Meta.later_add(Meta.LaterType.IDLE, () => { + if (!global.display.end_grab_op && this.wasTiled) { + // move to current cursort position + let [x, y, _mods] = global.get_pointer(); + getVirtualPointer().notify_absolute_motion( + Clutter.get_current_event_time(), + x, y); + + getVirtualPointer().notify_button(Clutter.get_current_event_time(), + Clutter.BUTTON_PRIMARY, Clutter.ButtonState.PRESSED); + getVirtualPointer().notify_button(Clutter.get_current_event_time(), + Clutter.BUTTON_PRIMARY, Clutter.ButtonState.RELEASED); + } + }); } activateDndTarget(zone, first) { diff --git a/keybindings.js b/keybindings.js index e5f9de72..d92737cf 100644 --- a/keybindings.js +++ b/keybindings.js @@ -10,6 +10,7 @@ var Gdk = imports.gi.Gdk; var Gtk = imports.gi.Gtk; var Gio = imports.gi.Gio; var Meta = imports.gi.Meta; +var GLib = imports.gi.GLib; var Utils = Extension.imports.utils; var Main = imports.ui.main; @@ -568,7 +569,7 @@ function getActionId(mutterName) { function overrideAction(mutterName, action) { let id = getActionId(mutterName); Main.wm.setCustomKeybindingHandler(mutterName, Shell.ActionMode.NORMAL, - action.keyHandler); + action.keyHandler); if (id === Meta.KeyBindingAction.NONE) return; actionIdMap[id] = action; @@ -600,8 +601,7 @@ function resetConflicts() { ) { Main.wm.setCustomKeybindingHandler( name, - Shell.ActionMode.NORMAL | - Shell.ActionMode.OVERVIEW, + Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW, Main.wm._showWorkspaceSwitcher.bind(Main.wm)); continue; } @@ -663,7 +663,7 @@ function resetConflicts() { break; case 'toggle-application-view': // overview._controls: Backward compatibility for 3.34 and below: - const viewSelector = (Main.overview._overview._controls || Main.overview.viewSelector || Main.overview._controls.viewSelector); + const viewSelector = (Main.overview._overview._controls || Main.overview.viewSelector || Main.overview._controls.viewSelector); Main.wm.setCustomKeybindingHandler( name, diff --git a/kludges.js b/kludges.js index eb4d4ad1..95e3c93c 100644 --- a/kludges.js +++ b/kludges.js @@ -43,29 +43,25 @@ function overrideHotCorners() { } } +// polyfill for 3.28 (`get_monitor_scale` first appeared in 3.31.92). if (!global.display.get_monitor_scale) { - // `get_monitor_scale` first appeared in 3.31.92. Polyfill a fallback for 3.28 global.display.constructor.prototype.get_monitor_scale = () => 1.0; } +// polyfill for 3.28 (`get_monitor_neighbor_index`) if (!global.display.get_monitor_neighbor_index) { - // `get_monitor_neighbor_index` polyfill a fallback for 3.28 global.display.constructor.prototype.get_monitor_neighbor_index = function(...args) { return global.screen.get_monitor_neighbor_index(...args); } } -if (!global.display.set_cursor) { - global.display.constructor.prototype.set_cursor = global.screen.set_cursor.bind(global.screen); -} - // polyfill for 3.28 if (!Meta.DisplayDirection && Meta.ScreenDirection) { Meta.DisplayDirection = Meta.ScreenDirection; } +// polyfill for 3.28 if (!St.Settings) { - // `St.Settings` doesn't exist in 3.28 - polyfill: let Gtk = imports.gi.Gtk; let gtkSettings = Gtk.Settings.get_default(); let polyfillSettings = new (class PolyfillStSettings { @@ -82,14 +78,14 @@ if (!St.Settings) { }; } +// polyfill for 3.28 if (!Clutter.Actor.prototype.set) { - // `set` doesn't exist in 3.28 - polyfill: Clutter.Actor.prototype.set = function(params) { Object.assign(this, params); } } -// Polyfill gnome-3.34 transition API, taken from gnome-shell/js/ui/environment.js +// polyfill 3.34 transition API, taken from gnome-shell/js/ui/environment.js if (version[0] >= 3 && version[1] < 34) { function _makeEaseCallback(params, cleanup) { let onComplete = params.onComplete; @@ -179,7 +175,12 @@ if (version[0] >= 3 && version[1] < 34) { }; } -// Polyfill +// polyfill +if (!global.display.set_cursor) { + global.display.constructor.prototype.set_cursor = global.screen.set_cursor.bind(global.screen); +} + +// polyfill if (!Clutter.Actor.prototype.raise) { Clutter.Actor.prototype.raise = function raise(above) { const parent = this.get_parent(); @@ -189,12 +190,14 @@ if (!Clutter.Actor.prototype.raise) { } } +// polyfill if (!Clutter.Actor.prototype.raise_top) { Clutter.Actor.prototype.raise_top = function raise_top() { this.raise(null); } } +// polyfill if (!Clutter.Actor.prototype.reparent) { Clutter.Actor.prototype.reparent = function reparent(newParent) { const parent = this.get_parent(); @@ -205,11 +208,19 @@ if (!Clutter.Actor.prototype.reparent) { } } -if (! Clutter.Vertex) { +// polyfill +if (!Clutter.Vertex) { const {Graphene} = imports.gi; Clutter.Vertex = Graphene.Point3D; } +// polyfill for 44 +if (!Meta.later_add && global.compositor?.get_laters()) { + Meta.later_add = function(...args) { + global.compositor.get_laters().add(...args); + } +} + // Workspace.Workspace._realRecalculateWindowPositions // Sort tiled windows in the correct order function _realRecalculateWindowPositions(flags) { diff --git a/metadata.json b/metadata.json index dc41ab26..c64cbe50 100644 --- a/metadata.json +++ b/metadata.json @@ -5,6 +5,6 @@ "url": "https://github.com/paperwm/PaperWM", "settings-schema": "org.gnome.Shell.Extensions.PaperWM", "shell-version": [ "42", "43", "44" ], - "version": "43.0", + "version": "44.0", "session-modes": [ "unlock-dialog", "user" ] } diff --git a/navigator.js b/navigator.js index 94811d44..d6090326 100644 --- a/navigator.js +++ b/navigator.js @@ -87,7 +87,6 @@ function getModLock(mods) { grab = Main.pushModal(this.actor) // We expect at least a keyboard grab here if ((grab.get_seat_state() & Clutter.GrabState.KEYBOARD) === 0) { - Main.popModal(grabHandle); log("Failed to grab modal"); throw new Error('Could not grab modal') } diff --git a/set-recommended-gnome-shell-settings.sh b/set-recommended-gnome-shell-settings.sh index 1b0cb90a..df13ed2f 100755 --- a/set-recommended-gnome-shell-settings.sh +++ b/set-recommended-gnome-shell-settings.sh @@ -51,7 +51,5 @@ set-with-backup org.gnome.shell.overrides edge-tiling false # Attached modal dialogs isn't handled very well set-with-backup org.gnome.shell.overrides attach-modal-dialogs false - - echo echo "Run $RESTORE_SETTINGS_SCRIPT to revert changes" diff --git a/settings.js b/settings.js index 4ab95cdc..57655599 100644 --- a/settings.js +++ b/settings.js @@ -242,7 +242,7 @@ function keystrToKeycombo(keystr) { keystr = keystr.replace('Above_Tab', 'A'); aboveTab = true; } - let [key, mask] = Gtk.accelerator_parse(keystr); + let [ok, key, mask] = Gtk.accelerator_parse(keystr); if (aboveTab) key = META_KEY_ABOVE_TAB; diff --git a/tiling.js b/tiling.js index d4ee4c65..189309ac 100644 --- a/tiling.js +++ b/tiling.js @@ -1387,24 +1387,24 @@ border-radius: ${borderWidth}px; this.actor.insert_child_below(this.background, null); - this.signals.connect( - this.background, 'button-press-event', + this.signals.connect(this.background, 'button-press-event', (actor, event) => { if (inGrab) { return; } + + /** + * if user clicks on window, then ensureViewport on that window before exiting + */ let [gx, gy, $] = global.get_pointer(); let [ok, x, y] = this.actor.transform_stage_point(gx, gy); let windowAtPoint = !Gestures.gliding && this.getWindowAtPoint(x, y); if (windowAtPoint) { ensureViewport(windowAtPoint, this); - spaces.selectedSpace = this - inGrab = new Extension.imports.grab.MoveGrab(windowAtPoint, Meta.GrabOp.MOVING, this); - inGrab.begin(); - } else if (inPreview) { - spaces.selectedSpace = this; - Navigator.getNavigator().finish(); } + + spaces.selectedSpace = this; + Navigator.getNavigator().finish(); }); this.signals.connect( @@ -1498,7 +1498,6 @@ border-radius: ${borderWidth}px; layout of oldSpace if present. */ addAll(oldSpace) { - // On gnome-shell-restarts the windows are moved into the viewport, but // they're moved minimally and the stacking is not changed, so the tiling // order is preserved (sans full-width windows..) @@ -1666,11 +1665,12 @@ var Spaces = class Spaces extends Map { this.signals.connect(display, 'window-created', this.window_created.bind(this)); + this.signals.connect(display, 'grab-op-begin', (display, mw, type) => grabBegin(mw, type)); this.signals.connect(display, 'grab-op-end', (display, mw, type) => grabEnd(mw, type)); - + this.signals.connect(Main.layoutManager, 'monitors-changed', this.monitorsChanged.bind(this)); this.signals.connect(global.window_manager, 'switch-workspace', @@ -2154,7 +2154,6 @@ var Spaces = class Spaces extends Map { const padding_percentage = 4; let last = monitorSpaces.length - 1; monitorSpaces.forEach((space, i) => { - let padding = (space.height * scale / 100) * padding_percentage; let center = (space.height - (space.height * scale)) / 2; let space_y; @@ -3145,6 +3144,7 @@ function grabBegin(metaWindow, type) { }) break; case Meta.GrabOp.MOVING: + case Meta.GrabOp.MOVING_UNCONSTRAINED: // introduced in Gnome 44 inGrab = new Extension.imports.grab.MoveGrab(metaWindow, type); if (utils.getModiferState() & Clutter.ModifierType.CONTROL_MASK) { @@ -3311,8 +3311,10 @@ function showWindow(metaWindow) { let actor = metaWindow.get_compositor_private(); if (!actor) return false; - metaWindow.clone.cloneActor.hide(); - metaWindow.clone.cloneActor.source = null; + if (metaWindow.clone?.cloneActor) { + metaWindow.clone.cloneActor.hide(); + metaWindow.clone.cloneActor.source = null; + } actor.show(); return true; } @@ -3321,8 +3323,10 @@ function animateWindow(metaWindow) { let actor = metaWindow.get_compositor_private(); if (!actor) return false; - metaWindow.clone.cloneActor.show(); - metaWindow.clone.cloneActor.source = actor; + if (metaWindow.clone?.cloneActor) { + metaWindow.clone.cloneActor.show(); + metaWindow.clone.cloneActor.source = actor; + } actor.hide(); return true; } @@ -3366,6 +3370,7 @@ function toggleMaximizeHorizontally(metaWindow) { } function resizeHInc(metaWindow) { + metaWindow = metaWindow || display.focus_window; let frame = metaWindow.get_frame_rect(); let monitor = Main.layoutManager.monitors[metaWindow.get_monitor()]; let space = spaces.spaceOfWindow(metaWindow); @@ -3386,6 +3391,7 @@ function resizeHInc(metaWindow) { } function resizeHDec(metaWindow) { + metaWindow = metaWindow || display.focus_window; let frame = metaWindow.get_frame_rect(); let monitor = Main.layoutManager.monitors[metaWindow.get_monitor()]; let space = spaces.spaceOfWindow(metaWindow); @@ -3407,6 +3413,7 @@ function resizeHDec(metaWindow) { } function resizeWInc(metaWindow) { + metaWindow = metaWindow || display.focus_window; let frame = metaWindow.get_frame_rect(); let monitor = Main.layoutManager.monitors[metaWindow.get_monitor()]; let space = spaces.spaceOfWindow(metaWindow); @@ -3427,6 +3434,7 @@ function resizeWInc(metaWindow) { } function resizeWDec(metaWindow) { + metaWindow = metaWindow || display.focus_window; let frame = metaWindow.get_frame_rect(); let monitor = Main.layoutManager.monitors[metaWindow.get_monitor()]; let space = spaces.spaceOfWindow(metaWindow); diff --git a/utils.js b/utils.js index 14ec2647..8fa52f06 100644 --- a/utils.js +++ b/utils.js @@ -270,7 +270,6 @@ function toggleCloneMarks() { } } - function sum(array) { return array.reduce((a, b) => a + b, 0); } @@ -285,18 +284,9 @@ function zip(...as) { } function warpPointer(x, y) { - // 3.36 added support for warping in wayland - if (Meta.is_wayland_compositor() && Clutter.Backend.prototype.get_default_seat) { - let backend = Clutter.get_default_backend(); - let seat = backend.get_default_seat(); - seat.warp_pointer(x, y); - return; - } else { - let display = Gdk.Display.get_default(); - let deviceManager = display.get_device_manager(); - let pointer = deviceManager.get_client_pointer(); - pointer.warp(Gdk.Screen.get_default(), x, y) - } + let backend = Clutter.get_default_backend(); + let seat = backend.get_default_seat(); + seat.warp_pointer(x, y); } /**