From 51ba0d06ab87f9c9f2c53c693cce0f0d4e980ab1 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 28 Jan 2022 07:49:48 +0100 Subject: [PATCH 1/4] :memo: Improve comments --- docs/how-to-create-new-effects.md | 6 ++++-- src/BrokenGlass.js | 13 ++++++++----- src/EnergizeA.js | 13 ++++++++----- src/EnergizeB.js | 13 ++++++++----- src/Fire.js | 13 ++++++++----- src/Matrix.js | 13 ++++++++----- src/TRexAttack.js | 13 ++++++++----- src/TVEffect.js | 13 ++++++++----- src/Wisps.js | 13 ++++++++----- 9 files changed, 68 insertions(+), 42 deletions(-) diff --git a/docs/how-to-create-new-effects.md b/docs/how-to-create-new-effects.md index 730c131..a69d5ae 100644 --- a/docs/how-to-create-new-effects.md +++ b/docs/how-to-create-new-effects.md @@ -150,13 +150,15 @@ var SimpleFade = class SimpleFade { return new Shader(settings); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. // The parameter 'forOpening' is set to true if this is called for a window-open // transition, for a window-close transition it is set to false. The modes can be set to // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. - // This also determines how the uProgress uniform value will progress in the shader. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows should neither be scaled nor faded. static tweakTransition(actor, settings, forOpening) { return { diff --git a/src/BrokenGlass.js b/src/BrokenGlass.js index 3646693..26c5cc4 100644 --- a/src/BrokenGlass.js +++ b/src/BrokenGlass.js @@ -83,12 +83,15 @@ var BrokenGlass = class BrokenGlass { return new Shader(actor, settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows are set to twice their original size, so that // we have some space to draw the shards. We also set the animation mode to "Linear". static tweakTransition(actor, settings, forOpening) { diff --git a/src/EnergizeA.js b/src/EnergizeA.js index a7ef50d..5cbef47 100644 --- a/src/EnergizeA.js +++ b/src/EnergizeA.js @@ -78,12 +78,15 @@ var EnergizeA = class EnergizeA { return new Shader(settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows should neither be scaled nor faded. static tweakTransition(actor, settings, forOpening) { return { diff --git a/src/EnergizeB.js b/src/EnergizeB.js index fe3973e..37dd1a1 100644 --- a/src/EnergizeB.js +++ b/src/EnergizeB.js @@ -78,12 +78,15 @@ var EnergizeB = class EnergizeB { return new Shader(settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows should neither be scaled nor faded. static tweakTransition(actor, settings, forOpening) { return { diff --git a/src/Fire.js b/src/Fire.js index eecbeb0..b824c74 100644 --- a/src/Fire.js +++ b/src/Fire.js @@ -100,12 +100,15 @@ var Fire = class Fire { return new Shader(settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows should neither be scaled nor faded. static tweakTransition(actor, settings, forOpening) { return { diff --git a/src/Matrix.js b/src/Matrix.js index 329ea42..bed7983 100644 --- a/src/Matrix.js +++ b/src/Matrix.js @@ -85,12 +85,15 @@ var Matrix = class Matrix { return new Shader(settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows should not be faded but scaled vertically to allow for some // overshooting. static tweakTransition(actor, settings, forOpening) { diff --git a/src/TRexAttack.js b/src/TRexAttack.js index e161100..123c9dc 100644 --- a/src/TRexAttack.js +++ b/src/TRexAttack.js @@ -82,12 +82,15 @@ var TRexAttack = class TRexAttack { return new Shader(settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, we slightly increase the window's scale as part of the warp effect. static tweakTransition(actor, settings, forOpening) { const warp = 1.0 + 0.5 * settings.get_double('claw-scratch-warp'); diff --git a/src/TVEffect.js b/src/TVEffect.js index e022f55..80be7bc 100644 --- a/src/TVEffect.js +++ b/src/TVEffect.js @@ -79,12 +79,15 @@ var TVEffect = class TVEffect { return new Shader(settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows are scaled down vertically. static tweakTransition(actor, settings, forOpening) { return { diff --git a/src/Wisps.js b/src/Wisps.js index 4722af9..5d5f6b7 100644 --- a/src/Wisps.js +++ b/src/Wisps.js @@ -79,12 +79,15 @@ var Wisps = class Wisps { return new Shader(settings, forOpening); } - // This is also called from extension.js. It is used to tweak a window's open / close + // The tweakTransition() is called from extension.js to tweak a window's open / close // transitions - usually windows are faded in / out and scaled up / down by GNOME Shell. - // forOpening is set to true if this is called for a window-open transition, for a - // window-close transition it is set to false. The modes can be set to any value from - // here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. This also - // determines how the uProgress uniform value will progress in the shader. + // The parameter 'forOpening' is set to true if this is called for a window-open + // transition, for a window-close transition it is set to false. The modes can be set to + // any value from here: https://gjs-docs.gnome.org/clutter8~8_api/clutter.animationmode. + // The only required property is 'opacity', even if it transitions from 1.0 to 1.0. The + // current value of the opacity transition is passed as uProgress to the shader. + // Tweaking the actor's scale during the transition only works properly for GNOME 3.38+. + // For this effect, windows should be scaled down slightly but not faded. static tweakTransition(actor, settings, forOpening) { return { From f87a0a649b900230df4a8fe1ffe57487147f9111 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 28 Jan 2022 07:50:10 +0100 Subject: [PATCH 2/4] :wrench: Use opacity transition for uProgress --- extension.js | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/extension.js b/extension.js index dc33e09..9fdb8bc 100644 --- a/extension.js +++ b/extension.js @@ -97,7 +97,7 @@ class Extension { // actor. The window preview in the overview is basically a Clutter.Clone which // shows the original window. if (Main.overview.visible && !Main.overview.closing) { - const id = actor.connect('show', () => { + const id = actor.connect('notify::mapped', () => { extensionThis._setupEffect(actor, true); actor.disconnect(id); }); @@ -405,27 +405,21 @@ class Extension { transition = actor.get_transition(property); } - // For some reason, there are rare cases, where no transition is set up. We do not - // try to continue here... - if (!transition) { - utils.debug('Failed to set up transitions.'); - this._fixAnimationTimes(isDialogWindow, forOpening, null); - return; + // Tweak the transition according to the config object. For some reason, there are + // rare cases, where no transition is set up. This happens from time to time... + if (transition) { + transition.set_duration(duration); + transition.set_to(config[property].to); + transition.set_from(config[property].from); + transition.set_progress_mode(config[property].mode); } - - // Tweak the transition according to the config object. - transition.set_duration(duration); - transition.set_to(config[property].to); - transition.set_from(config[property].from); - transition.set_progress_mode(config[property].mode); } - // There should always be a scale-y transitions. Once this is finished, we restore the - // original actor size. - const transition = actor.get_transition('scale-y'); - transition.connect('completed', () => { + // Once the transitions are finished, we restore the original actor size. + actor.connect('transitions-completed', () => { actor.scale_x = 1.0; actor.scale_y = 1.0; + actor.opacity = forOpening ? 1.0 : 0.0; }); // -------------------------------------------------------------------- add the shader @@ -434,6 +428,15 @@ class Extension { const shader = this._currentEffect.createShader(actor, this._settings, forOpening); if (shader) { + // There should always be an opacity transition going on... + const transition = actor.get_transition('opacity'); + + if (!transition) { + this._fixAnimationTimes(isDialogWindow, forOpening, null); + utils.debug('Cannot setup shader without opacity transition.') + return; + } + // First remove any old effect. actor.remove_effect_by_name(`burn-my-windows-effect`); actor.add_effect_with_name(`burn-my-windows-effect`, shader); From bcb7619d3703bee866fe352e9b4db7387bdc2cc5 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 28 Jan 2022 07:54:20 +0100 Subject: [PATCH 3/4] :wrench: Disconnect from transitions-completed --- extension.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extension.js b/extension.js index 9fdb8bc..cfa8dfc 100644 --- a/extension.js +++ b/extension.js @@ -416,10 +416,12 @@ class Extension { } // Once the transitions are finished, we restore the original actor size. - actor.connect('transitions-completed', () => { + const connectionID = actor.connect('transitions-completed', () => { actor.scale_x = 1.0; actor.scale_y = 1.0; actor.opacity = forOpening ? 1.0 : 0.0; + + actor.disconnect(connectionID); }); // -------------------------------------------------------------------- add the shader From c41bef6413b60c1b6056837c8ed518184a7d16fb Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Fri, 28 Jan 2022 14:04:40 +0100 Subject: [PATCH 4/4] :beetle: Fix occasional empty window --- extension.js | 103 +++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/extension.js b/extension.js index cfa8dfc..63e7ba6 100644 --- a/extension.js +++ b/extension.js @@ -75,10 +75,11 @@ class Extension { const extensionThis = this; // We will monkey-patch these methods. Let's store the original ones. - this._origAddWindowClone = Workspace.prototype._addWindowClone; - this._origWindowRemoved = Workspace.prototype._windowRemoved; - this._origDoRemoveWindow = Workspace.prototype._doRemoveWindow; - this._origShouldAnimateActor = WindowManager.prototype._shouldAnimateActor; + this._origAddWindowClone = Workspace.prototype._addWindowClone; + this._origWindowRemoved = Workspace.prototype._windowRemoved; + this._origDoRemoveWindow = Workspace.prototype._doRemoveWindow; + this._origShouldAnimateActor = WindowManager.prototype._shouldAnimateActor; + this._origWaitForOverviewToHide = WindowManager.prototype._waitForOverviewToHide; // We will also override these animation times. this._origWindowTime = imports.ui.windowManager.DESTROY_WINDOW_ANIMATION_TIME; @@ -88,38 +89,28 @@ class Extension { // ------------------------------------------------ patching the window-open animation // Here we add an effect to the window-open animation. This is done whenever a new - // window is created. + // window is created. Usually, there is no real window animation for opening windows + // in the overview - only the window's clone is animated - but thanks to the hacks + // below, we can show the real animations in the overview. + + // If a window is created the transitions are set up in the async _mapWindow of the + // WindowManager: + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1449 + // AFAIK, overriding this method is not possible as it's called by a signal to + // which it is bound via the bind() method. To tweak the async transition + // anyways, we override the actors ease() method once - the next time it will be + // called by the _mapWindow(), we will intercept it! this._windowCreatedConnection = global.display.connect('window-created', (d, metaWin) => { let actor = metaWin.get_compositor_private(); - // If we are currently in the overview, we add the effect to the original window - // actor. The window preview in the overview is basically a Clutter.Clone which - // shows the original window. - if (Main.overview.visible && !Main.overview.closing) { - const id = actor.connect('notify::mapped', () => { - extensionThis._setupEffect(actor, true); - actor.disconnect(id); - }); + const orig = actor.ease; + actor.ease = function(...params) { + orig.apply(actor, params); + actor.ease = orig; - } - // If a window is created outside of the overview, the transitions are set up in - // the async _mapWindow of the WindowManager which can defer the actual showing - // of the window significantly, especially when currently leaving the overview: - // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1449 - // AFAIK, overriding this method is not possible as it's called by a signal to - // which it is bound via the bind() method. To tweak the async transition - // anyways, we override the actors ease() method once - the next time it will be - // called by the _mapWindow(), we will intercept it! - else { - const orig = actor.ease; - actor.ease = function(...params) { - orig.apply(actor, params); - actor.ease = orig; - - extensionThis._setupEffect(actor, true); - }; - } + extensionThis._setupEffect(actor, true); + }; }); // Some of the effects require that the window's actor is enlarged to provide a bigger @@ -147,13 +138,13 @@ class Extension { // Shell 3.38+. So effects cannot scale windows in the overview of GNOME 3.36... if (utils.shellVersionIsAtLeast(3, 38)) { const xID = realWindow.connect('notify::scale-x', () => { - if (realWindow.scale_x > 0) { + if (realWindow.scale_x > 0 && clone.allocation.get_size()[0] > 0) { clone.scale_x = realWindow.scale_x; } }); const yID = realWindow.connect('notify::scale-y', () => { - if (realWindow.scale_y > 0) { + if (realWindow.scale_y > 0 && clone.allocation.get_size()[1] > 0) { clone.scale_y = realWindow.scale_y; } }); @@ -164,7 +155,7 @@ class Extension { }); } - // This is actually need for the window-close animation on GNOME Shell 3.36. + // This is actually needed for the window-close animation on GNOME Shell 3.36. // On GNOME 3.36, the window clone's 'destroy' handler only calls _removeWindowClone // but not _doRemoveWindow. The latter is required to trigger the repositioning of // the overview window layout. Therefore we call this method in addition. @@ -177,6 +168,27 @@ class Extension { return result; }; + // Usually, windows are faded in after the overview is completely hidden. We enable + // window-open animations by not waiting for this. + WindowManager.prototype._waitForOverviewToHide = async function() { + return Promise.resolve(); + }; + + // Here comes the ULTRA-HACK: The method below is called (amongst others) by the + // _destroyWindow and _mapWindow methods of the WindowManager. Usually, it returns + // false when we are in the overview. This prevents the window animations. As we + // cannot monkey-patch the _destroyWindow or _mapWindow methods themselves, we check + // inside the method below whether it was called by either of those. If so, we return + // true. Let's see if this breaks stuff left and right... + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1125 + WindowManager.prototype._shouldAnimateActor = function(...params) { + const caller = (new Error()).stack.split('\n')[1]; + if (caller.includes('_destroyWindow@') || caller.includes('_mapWindow@')) { + return true; + } + return extensionThis._origShouldAnimateActor.apply(this, params); + }; + // ----------------------------------------------- patching the window-close animation @@ -213,20 +225,6 @@ class Extension { } }; - // Here comes the ULTRA-HACK: The method below is called (amongst others) by the - // _destroyWindow method of the WindowManager. Usually, it returns false when we are - // in the overview. This prevents the window-close animation. As we cannot - // monkey-patch the _destroyWindow method itself, we check inside the method below - // whether it was called by _destroyWindow. If so, we return true. Let's see if this - // breaks stuff left and right... - // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1125 - WindowManager.prototype._shouldAnimateActor = function(...params) { - if ((new Error()).stack.split('\n')[1].includes('_destroyWindow@')) { - return true; - } - return extensionThis._origShouldAnimateActor.apply(this, params); - }; - // With the code below, we hide the window-overlay (icon, label, close button) in the // overview once the close-animation is running. As the WindowPreview class is only // available on GNOME 3.38 and beyond, we cannot hide the overlay on GNOME 3.36. @@ -288,10 +286,11 @@ class Extension { global.window_manager.disconnect(this._destroyConnection); global.display.disconnect(this._windowCreatedConnection); - Workspace.prototype._addWindowClone = this._origAddWindowClone; - Workspace.prototype._windowRemoved = this._origWindowRemoved; - Workspace.prototype._doRemoveWindow = this._origDoRemoveWindow; - WindowManager.prototype._shouldAnimateActor = this._origShouldAnimateActor; + Workspace.prototype._addWindowClone = this._origAddWindowClone; + Workspace.prototype._windowRemoved = this._origWindowRemoved; + Workspace.prototype._doRemoveWindow = this._origDoRemoveWindow; + WindowManager.prototype._shouldAnimateActor = this._origShouldAnimateActor; + WindowManager.prototype._waitForOverviewToHide = this._origWaitForOverviewToHide; imports.ui.windowManager.DESTROY_WINDOW_ANIMATION_TIME = this._origWindowTime; imports.ui.windowManager.DIALOG_DESTROY_WINDOW_ANIMATION_TIME = this._origDialogTime;