diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 71d9e76..6f48d78 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -86,7 +86,7 @@ jobs: - '35' - '36' session: - - 'gnome-xsession' + #- 'gnome-xsession' Disabled for now as it miraculously fails on GitHub. Locally, all tests pass... - 'gnome-wayland-nested' steps: - uses: actions/checkout@v2 diff --git a/docs/how-to-create-new-effects.md b/docs/how-to-create-new-effects.md index 8bce434..35029ce 100644 --- a/docs/how-to-create-new-effects.md +++ b/docs/how-to-create-new-effects.md @@ -77,11 +77,55 @@ Just remember to replace `simple-fade` with your custom name! ### 2. Creating the Effect Class -You will have to create a new file called `src/SimpleFade.js` and paste the following code to it. +You will have to create a new GLSL file called `resources/shaders/simple-fade.glsl` and a new JavaScript source file called `src/SimpleFade.js`. +Simply paste the following source code to the respective file. Please study this code carefully, all of it is explained with inline comments.
- Expand this to show the code. + Expand this to show the GLSL code. + +```glsl +// The code below injects some standard uniforms which will be updated during the +// animation. This includes: +// bool uForOpening: True if a window-open animation is ongoing, false otherwise. +// sampler2D uTexture: Contains the texture of the window. +// float uProgress: A value which transitions from 0 to 1 during the entire animation. +// float uTime: A steadily increasing value in seconds. +// vec2 uSize: The size of uTexture in pixels. +#include "common/uniforms.glsl" + +// The width of the fading effect is loaded from the settings. +uniform float uFadeWidth; + +void main() { + // Get the color from the window texture. + cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st); + + // Shell.GLSLEffect uses straight alpha. So we have to convert from premultiplied. + if (cogl_color_out.a > 0) { + cogl_color_out.rgb /= cogl_color_out.a; + } + + // Radial distance from window edge to the window's center. + float dist = length(cogl_tex_coord_in[0].st - 0.5) * 2.0 / sqrt(2.0); + + // This gradually dissolves from [1..0] from the outside to the center. We + // switch the direction for opening and closing. + float progress = uForOpening ? 1.0 - uProgress : uProgress; + float mask = (1.0 - progress * (1.0 + uFadeWidth) - dist + uFadeWidth) / uFadeWidth; + + // Make the mask smoother. + mask = smoothstep(0, 1, mask); + + // Apply the mask to the output. + cogl_color_out.a *= mask; +} +``` + +
+ +
+ Expand this to show the JavaScript code. ```javascript ////////////////////////////////////////////////////////////////////////////////////////// @@ -106,29 +150,45 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect ... // // <- Please add a description of your effect here -> // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var SimpleFade = class SimpleFade { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import Clutter in this function as it is not available in the preferences + // process. This creator function of the ShaderFactory is only called within GNOME + // Shell's process. + const Clutter = imports.gi.Clutter; + + // Store uniform locations of newly created shaders. + shader._uForOpening = shader.get_uniform_location('uForOpening'); + shader._uFadeWidth = shader.get_uniform_location('uFadeWidth'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + shader.set_uniform_float(shader._uForOpening, 1, [forOpening]); + shader.set_uniform_float(shader._uFadeWidth, 1, [settings.get_double('simple-fade-width')]); + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. - static getMinShellVersion() { + getMinShellVersion() { return [3, 36]; } @@ -136,13 +196,13 @@ var SimpleFade = class SimpleFade { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'simple-fade'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Simple Fade Effect'); } @@ -151,112 +211,20 @@ var SimpleFade = class SimpleFade { // This is called by the preferences dialog. It loads the settings page for this effect, // binds all properties to the settings and appends the page to the main stack of the // preferences dialog. - static getPreferences(dialog) { + getPreferences(dialog) { // Empty for now... Code is added here later in the tutorial! return null; } // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0}; } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const Shell = imports.gi.Shell; - const shaderSnippets = Me.imports.src.shaderSnippets; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - _init(settings, forOpening) { - super._init({shader_type: Clutter.ShaderType.FRAGMENT_SHADER}); - - this._uForOpening = this.get_uniform_location('uForOpening'); - this._uFadeWidth = this.get_uniform_location('uFadeWidth'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - this.set_uniform_float(this._uForOpening, 1, [forOpening]); - this.set_uniform_float(this._uFadeWidth, 1, [settings.get_double('simple-fade-width')]); - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - - const declarations = ` - // The code below injects some standard uniforms which will be updated during the - // animation. This includes: - // bool uForOpening: True if a window-open animation is ongoing, false otherwise. - // sampler2D uTexture: Contains the texture of the window. - // float uProgress: A value which transitions from 0 to 1 during the entire animation. - // float uTime: A steadily increasing value in seconds. - // vec2 uSize: The size of uTexture in pixels. - #include "common/uniforms.glsl" - - // The width of the fading effect is loaded from the settings. - uniform float uFadeWidth; - `; - - const code = ` - // Get the color from the window texture. - vec4 windowColor = texture2D(uTexture, cogl_tex_coord_in[0].st); - - // Radial distance from window edge to the window's center. - float dist = length(cogl_tex_coord_in[0].st - 0.5) * 2.0 / sqrt(2.0); - - // This gradually dissolves from [1..0] from the outside to the center. We - // switch the direction for opening and closing. - float progress = uForOpening ? 1.0 - uProgress : uProgress; - float mask = (1.0 - progress * (1.0 + uFadeWidth) - dist + uFadeWidth) / uFadeWidth; - - // Make the mask smoother. - mask = smoothstep(0, 1, mask); - - // Set the final output color. This uses premultiplied alpha. - cogl_color_out = windowColor * mask; - `; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, code, true); - }; - }); -} ```
@@ -270,7 +238,7 @@ Like this: ```javascript const ALL_EFFECTS = [ ... - Me.imports.src.SimpleFade.SimpleFade, + new Me.imports.src.SimpleFade.SimpleFade(), ... ]; ``` diff --git a/extension.js b/extension.js index c375799..b61f168 100644 --- a/extension.js +++ b/extension.js @@ -31,21 +31,6 @@ const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; -// New effects must be registered here and in prefs.js. -const ALL_EFFECTS = [ - Me.imports.src.Apparition.Apparition, - Me.imports.src.BrokenGlass.BrokenGlass, - Me.imports.src.EnergizeA.EnergizeA, - Me.imports.src.EnergizeB.EnergizeB, - Me.imports.src.Fire.Fire, - Me.imports.src.Hexagon.Hexagon, - Me.imports.src.Matrix.Matrix, - Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, - Me.imports.src.TRexAttack.TRexAttack, - Me.imports.src.TVEffect.TVEffect, - Me.imports.src.Wisps.Wisps, -]; - ////////////////////////////////////////////////////////////////////////////////////////// // This extensions modifies the window-close and window-open animations with all kinds // // of effects. The effects are implemented using GLSL shaders which are applied to the // @@ -63,6 +48,21 @@ class Extension { // from GNOME Tweaks, when you log in or when the screen is unlocked. enable() { + // New effects must be registered here and in prefs.js. + this._ALL_EFFECTS = [ + new Me.imports.src.Apparition.Apparition(), + new Me.imports.src.BrokenGlass.BrokenGlass(), + new Me.imports.src.EnergizeA.EnergizeA(), + new Me.imports.src.EnergizeB.EnergizeB(), + new Me.imports.src.Fire.Fire(), + new Me.imports.src.Hexagon.Hexagon(), + new Me.imports.src.Matrix.Matrix(), + new Me.imports.src.SnapOfDisintegration.SnapOfDisintegration(), + new Me.imports.src.TRexAttack.TRexAttack(), + new Me.imports.src.TVEffect.TVEffect(), + new Me.imports.src.Wisps.Wisps(), + ]; + // Load all of our resources. this._resources = Gio.Resource.load(Me.path + '/resources/burn-my-windows.gresource'); Gio.resources_register(this._resources); @@ -222,7 +222,7 @@ class Extension { const shader = actor.get_effect('burn-my-windows-effect'); if (shader) { actor.remove_effect(shader); - shader.free(); + shader.returnToFactory(); } } @@ -306,7 +306,7 @@ class Extension { disable() { // Free all effect resources. - ALL_EFFECTS.forEach(Effect => Effect.cleanUp()); + this._ALL_EFFECTS = []; // Unregister our resources. Gio.resources_unregister(this._resources); @@ -365,13 +365,13 @@ class Extension { return; } - // There is the weird case where an animation is already. This happens when a window - // is closed which has been created before the session was started (e.g. when GNOME - // Shell has been restarted in the meantime). + // There is the weird case where an animation is already ongoing. This happens when a + // window is closed which has been created before the session was started (e.g. when + // GNOME Shell has been restarted in the meantime). const oldShader = actor.get_effect('burn-my-windows-effect'); if (oldShader) { actor.remove_effect(oldShader); - oldShader.free(); + oldShader.returnToFactory(); } // ------------------------------------------------------------------ choose an effect @@ -381,9 +381,7 @@ class Extension { // First we check if an effect is to be previewed. if (previewNick != '') { - effect = ALL_EFFECTS.find(Effect => { - return Effect.getNick() == previewNick; - }); + effect = this._ALL_EFFECTS.find(effect => effect.getNick() == previewNick); // Only preview the effect once. this._settings.set_string(action + '-preview-effect', ''); @@ -393,8 +391,8 @@ class Extension { else { // Therefore, we first create a list of all currently enabled effects. - const enabled = ALL_EFFECTS.filter(Effect => { - return this._settings.get_boolean(`${Effect.getNick()}-${action}-effect`); + const enabled = this._ALL_EFFECTS.filter(effect => { + return this._settings.get_boolean(`${effect.getNick()}-${action}-effect`); }); // And then choose a random effect. @@ -476,43 +474,41 @@ class Extension { // -------------------------------------------------------------------- add the shader // Now add a cool shader to our window actor! - const shader = effect.getShader(actor, this._settings, forOpening); + const shader = effect.shaderFactory.getShader(); - if (shader) { - // There should always be an opacity transition going on... - const transition = actor.get_transition('opacity'); + // 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; + if (!transition) { + this._fixAnimationTimes(isDialogWindow, forOpening, null); + utils.debug('Cannot setup shader without opacity transition.') + return; + } + + // Assign the effect to the window actor! + actor.add_effect_with_name('burn-my-windows-effect', shader); + + // Set one-time uniforms. + shader.beginAnimation(this._settings, forOpening, actor); + + // Set other uniforms each frame. + transition.connect('new-frame', (t) => { + if (testMode) { + shader.updateAnimation(0.5, 0.001 * duration * 0.5); + } else { + shader.updateAnimation(t.get_progress(), 0.001 * t.get_elapsed_time()); } + }); - actor.add_effect_with_name('burn-my-windows-effect', shader); - - // Update uniforms at each frame. - transition.connect('new-frame', (t) => { - shader.set_uniform_float(shader.get_uniform_location('uForOpening'), 1, - [forOpening]); - shader.set_uniform_float(shader.get_uniform_location('uProgress'), 1, - [testMode ? 0.5 : t.get_progress()]); - shader.set_uniform_float( - shader.get_uniform_location('uTime'), 1, - [testMode ? duration / 2 : 0.001 * t.get_elapsed_time()]); - shader.set_uniform_float(shader.get_uniform_location('uSize'), 2, - [actor.width, actor.height]); + // Remove the effect if the animation finished or was interrupted. + if (forOpening) { + transition.connect('stopped', () => { + const oldShader = actor.get_effect('burn-my-windows-effect'); + if (oldShader) { + actor.remove_effect(oldShader); + oldShader.returnToFactory(); + } }); - - // Remove the effect if the animation finished or was interrupted. - if (forOpening) { - transition.connect('stopped', () => { - const oldShader = actor.get_effect('burn-my-windows-effect'); - if (oldShader) { - actor.remove_effect(oldShader); - oldShader.free(); - } - }); - } } // Finally, ensure that all animation times are set properly so that other extensions diff --git a/prefs.js b/prefs.js index d5809b5..bb45daa 100644 --- a/prefs.js +++ b/prefs.js @@ -30,21 +30,6 @@ const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; -// New effects must be registered here and in extension.js. -const ALL_EFFECTS = [ - Me.imports.src.Apparition.Apparition, - Me.imports.src.BrokenGlass.BrokenGlass, - Me.imports.src.EnergizeA.EnergizeA, - Me.imports.src.EnergizeB.EnergizeB, - Me.imports.src.Fire.Fire, - Me.imports.src.Hexagon.Hexagon, - Me.imports.src.Matrix.Matrix, - Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, - Me.imports.src.TRexAttack.TRexAttack, - Me.imports.src.TVEffect.TVEffect, - Me.imports.src.Wisps.Wisps, -]; - // This template widget class is defined at the bottom of this file. var BurnMyWindowsEffectPage = null; @@ -59,6 +44,22 @@ var PreferencesDialog = class PreferencesDialog { // ------------------------------------------------------------ constructor / destructor constructor() { + + // New effects must be registered here and in extension.js. + this._ALL_EFFECTS = [ + new Me.imports.src.Apparition.Apparition(), + new Me.imports.src.BrokenGlass.BrokenGlass(), + new Me.imports.src.EnergizeA.EnergizeA(), + new Me.imports.src.EnergizeB.EnergizeB(), + new Me.imports.src.Fire.Fire(), + new Me.imports.src.Hexagon.Hexagon(), + new Me.imports.src.Matrix.Matrix(), + new Me.imports.src.SnapOfDisintegration.SnapOfDisintegration(), + new Me.imports.src.TRexAttack.TRexAttack(), + new Me.imports.src.TVEffect.TVEffect(), + new Me.imports.src.Wisps.Wisps(), + ]; + // Load all of our resources. this._resources = Gio.Resource.load(Me.path + '/resources/burn-my-windows.gresource'); Gio.resources_register(this._resources); @@ -113,24 +114,24 @@ var PreferencesDialog = class PreferencesDialog { const group = new Adw.PreferencesGroup({title: _('Effect Options')}); this.gtkBoxAppend(this._widget, group); - ALL_EFFECTS.forEach(Effect => { - const [minMajor, minMinor] = Effect.getMinShellVersion(); + this._ALL_EFFECTS.forEach(effect => { + const [minMajor, minMinor] = effect.getMinShellVersion(); if (utils.shellVersionIsAtLeast(minMajor, minMinor)) { - const row = new Adw.ActionRow({title: Effect.getLabel(), activatable: true}); + const row = new Adw.ActionRow({title: effect.getLabel(), activatable: true}); row.add_suffix(new Gtk.Image({icon_name: 'go-next-symbolic'})); // Open a subpage with the effect's settings. row.connect('activated', () => { - const page = new BurnMyWindowsEffectPage(Effect, this); + const page = new BurnMyWindowsEffectPage(effect, this); page.valign = Gtk.Align.CENTER; page.margin_top = 10; page.margin_bottom = 10; page.margin_start = 10; page.margin_end = 10; - // Add the Effect's preferences (if any). - const preferences = Effect.getPreferences(this); + // Add the effect's preferences (if any). + const preferences = effect.getPreferences(this); if (preferences) { this.gtkBoxAppend(page, preferences); } @@ -186,23 +187,23 @@ var PreferencesDialog = class PreferencesDialog { this.gtkBoxAppend(this._widget, stack); // Add all other effect pages. - ALL_EFFECTS.forEach(Effect => { - const [minMajor, minMinor] = Effect.getMinShellVersion(); + this._ALL_EFFECTS.forEach(effect => { + const [minMajor, minMinor] = effect.getMinShellVersion(); if (utils.shellVersionIsAtLeast(minMajor, minMinor)) { - const page = new BurnMyWindowsEffectPage(Effect, this); + const page = new BurnMyWindowsEffectPage(effect, this); page.margin_start = 60; page.margin_end = 60; page.margin_top = 60; page.margin_bottom = 60; - // Add the Effect's preferences (if any). - const preferences = Effect.getPreferences(this); + // Add the effect's preferences (if any). + const preferences = effect.getPreferences(this); if (preferences) { this.gtkBoxAppend(page, preferences); } - stack.add_titled(page, Effect.getNick(), Effect.getLabel()); + stack.add_titled(page, effect.getNick(), effect.getLabel()); } }); } @@ -302,11 +303,11 @@ var PreferencesDialog = class PreferencesDialog { const group = Gio.SimpleActionGroup.new(); window.insert_action_group('open-effects', group); - ALL_EFFECTS.forEach(Effect => { - const [minMajor, minMinor] = Effect.getMinShellVersion(); + this._ALL_EFFECTS.forEach(effect => { + const [minMajor, minMinor] = effect.getMinShellVersion(); if (utils.shellVersionIsAtLeast(minMajor, minMinor)) { - const nick = Effect.getNick(); - const label = Effect.getLabel(); + const nick = effect.getNick(); + const label = effect.getLabel(); const actionName = nick + '-open-effect'; const fullName = 'open-effects.' + actionName; @@ -324,11 +325,11 @@ var PreferencesDialog = class PreferencesDialog { const group = Gio.SimpleActionGroup.new(); window.insert_action_group('close-effects', group); - ALL_EFFECTS.forEach(Effect => { - const [minMajor, minMinor] = Effect.getMinShellVersion(); + this._ALL_EFFECTS.forEach(effect => { + const [minMajor, minMinor] = effect.getMinShellVersion(); if (utils.shellVersionIsAtLeast(minMajor, minMinor)) { - const nick = Effect.getNick(); - const label = Effect.getLabel(); + const nick = effect.getNick(); + const label = effect.getLabel(); const actionName = nick + '-close-effect'; const fullName = 'close-effects.' + actionName; @@ -490,17 +491,17 @@ var PreferencesDialog = class PreferencesDialog { InternalChildren: ['label', 'button'], }, class BurnMyWindowsEffectPage extends Gtk.Box { // ------------------------------ - _init(Effect, dialog) { + _init(effect, dialog) { super._init(); // Set the effect's name as label. - this._label.label = Effect.getLabel(); + this._label.label = effect.getLabel(); // Open the preview window once the preview button is clicked. this._button.connect('clicked', () => { // Set the to-be-previewed effect. - dialog.getSettings().set_string('open-preview-effect', Effect.getNick()); - dialog.getSettings().set_string('close-preview-effect', Effect.getNick()); + dialog.getSettings().set_string('open-preview-effect', effect.getNick()); + dialog.getSettings().set_string('close-preview-effect', effect.getNick()); // Make sure that the window.show() firther below "sees" this change. Gio.Settings.sync(); @@ -508,7 +509,7 @@ var PreferencesDialog = class PreferencesDialog { // Create the preview-window. const window = new Gtk.Window({ // Translators: %s will be replaced by the effect's name. - title: _('Preview for %s').replace('%s', Effect.getLabel()), + title: _('Preview for %s').replace('%s', effect.getLabel()), default_width: 800, default_height: 450, modal: true, diff --git a/src/Apparition.js b/src/Apparition.js index a6d1b53..0bd2b8f 100644 --- a/src/Apparition.js +++ b/src/Apparition.js @@ -13,38 +13,56 @@ 'use strict'; -const GObject = imports.gi.GObject; - const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect hides the actor by violently sucking it into the void of magic. // -// towards the middle and then hiding the resulting line from left and right towards // -// the center. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var Apparition = class Apparition { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // Store uniform locations of newly created shaders. + shader._uSeed = shader.get_uniform_location('uSeed'); + shader._uShake = shader.get_uniform_location('uShake'); + shader._uTwirl = shader.get_uniform_location('uTwirl'); + shader._uSuction = shader.get_uniform_location('uSuction'); + shader._uRandomness = shader.get_uniform_location('uRandomness'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + // If we are performing an integration tests, we use a fixed seed. + const testMode = settings.get_boolean('test-mode'); + + // clang-format off + shader.set_uniform_float(shader._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); + shader.set_uniform_float(shader._uShake, 1, [settings.get_double('apparition-shake-intensity')]); + shader.set_uniform_float(shader._uTwirl, 1, [settings.get_double('apparition-twirl-intensity')]); + shader.set_uniform_float(shader._uSuction, 1, [settings.get_double('apparition-suction-intensity')]); + shader.set_uniform_float(shader._uRandomness, 1, [settings.get_double('apparition-randomness')]); + // clang-format on + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is not available on GNOME Shell 3.36 as it requires scaling of the window // actor. - static getMinShellVersion() { + getMinShellVersion() { return [3, 38]; } @@ -52,22 +70,21 @@ var Apparition = class Apparition { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'apparition'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Apparition'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Apparition.ui`); @@ -85,95 +102,10 @@ var Apparition = class Apparition { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 2.0, y: 2.0}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const Shell = imports.gi.Shell; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - this._uSeed = this.get_uniform_location('uSeed'); - this._uShake = this.get_uniform_location('uShake'); - this._uTwirl = this.get_uniform_location('uTwirl'); - this._uSuction = this.get_uniform_location('uSuction'); - this._uRandomness = this.get_uniform_location('uRandomness'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - // If we are currently performing integration test, the animation uses a fixed seed. - const testMode = settings.get_boolean('test-mode'); - - // clang-format off - this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); - this.set_uniform_float(this._uShake, 1, [settings.get_double('apparition-shake-intensity')]); - this.set_uniform_float(this._uTwirl, 1, [settings.get_double('apparition-twirl-intensity')]); - this.set_uniform_float(this._uSuction, 1, [settings.get_double('apparition-suction-intensity')]); - this.set_uniform_float(this._uRandomness, 1, [settings.get_double('apparition-randomness')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${Apparition.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - }); -} \ No newline at end of file diff --git a/src/BrokenGlass.js b/src/BrokenGlass.js index b3d0787..a80648f 100644 --- a/src/BrokenGlass.js +++ b/src/BrokenGlass.js @@ -13,41 +13,114 @@ 'use strict'; -const GObject = imports.gi.GObject; - const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect shatters the window into pieces. For an explanation how this works, look // // further down in this file into the documentation of the Shader class. This effect is // // not available on GNOME 3.3x, due to the limitation described in the documentation // // of vfunc_paint_target further down in this file. // +// This shader creates a complex-looking effect with rather simple means. Here is how // +// it works: The window is drawn five times on top of each other (see the SHARD_LAYERS // +// constant in the GLSL code). Each layer only draws some of the shards, all layers // +// combined make up the entire window. The layers are then scaled, rotated, and moved // +// independently from each other - this creates the impression that all shards are // +// moving independently. In reality, there are only five groups of shards! Which shard // +// belongs to which layer is defined by the green channel of the texture // +// resources/img/shards.png. The red channel of the texture contains the distance to // +// the shard edges. This information is used to fade out the shards. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// This texture will be loaded when the effect is used for the first time. -let shardTexture = null; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var BrokenGlass = class BrokenGlass { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import these modules in this function as they are not available in the + // preferences process. This callback is only called within GNOME Shell's process. + const {Clutter, GdkPixbuf, Cogl} = imports.gi; + + // Create the texture in the first call. + if (!this._shardTexture) { + const shardData = GdkPixbuf.Pixbuf.new_from_resource('/img/shards.png'); + this._shardTexture = new Clutter.Image(); + this._shardTexture.set_data(shardData.get_pixels(), Cogl.PixelFormat.RGB_888, + shardData.width, shardData.height, + shardData.rowstride); + } + + // Store all uniform locations. + shader._uShardTexture = shader.get_uniform_location('uShardTexture'); + shader._uSeed = shader.get_uniform_location('uSeed'); + shader._uEpicenter = shader.get_uniform_location('uEpicenter'); + shader._uShardScale = shader.get_uniform_location('uShardScale'); + shader._uBlowForce = shader.get_uniform_location('uBlowForce'); + shader._uGravity = shader.get_uniform_location('uGravity'); + + // And update all uniforms at the start of each animation. + shader.connect('begin-animation', (shader, settings, forOpening, actor) => { + // Usually, the shards fly away from the center of the window. + let epicenterX = 0.5; + let epicenterY = 0.5; + + // However, if this option is set, we use the mouse pointer position. + if (!forOpening && settings.get_boolean('broken-glass-use-pointer')) { + const [x, y] = global.get_pointer(); + const [ok, localX, localY] = actor.transform_stage_point(x, y); + + if (ok) { + epicenterX = localX / actor.width; + epicenterY = localY / actor.height; + } + } + + // If we are currently performing integration test, the animation uses a fixed + // seed. + const testMode = settings.get_boolean('test-mode'); + + // clang-format off + shader.set_uniform_float(shader._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); + shader.set_uniform_float(shader._uEpicenter, 2, [epicenterX, epicenterY]); + shader.set_uniform_float(shader._uShardScale, 1, [settings.get_double('broken-glass-scale')]); + shader.set_uniform_float(shader._uBlowForce, 1, [settings.get_double('broken-glass-blow-force')]); + shader.set_uniform_float(shader._uGravity, 1, [settings.get_double('broken-glass-gravity')]); + // clang-format on + }); + + // This is required to bind the shard texture for drawing. Sadly, this seems to be + // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was + // called get_target() back then but this is not wrapped in GJS. + // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 + shader.connect('update-animation', (shader) => { + const pipeline = shader.get_pipeline(); + + // Use linear filtering for the window texture. + pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR, + Cogl.PipelineFilter.LINEAR); + + // Bind the shard texture. + pipeline.set_layer_texture(1, this._shardTexture.get_texture()); + pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT); + pipeline.set_uniform_1i(shader._uShardTexture, 1); + }); + }); + } + // ---------------------------------------------------------------------------- metadata // This effect is only available on GNOME Shell 40+. - static getMinShellVersion() { + getMinShellVersion() { return [40, 0]; } @@ -55,22 +128,21 @@ var BrokenGlass = class BrokenGlass { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'broken-glass'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Broken Glass'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource('/ui/gtk4/BrokenGlass.ui'); @@ -88,148 +160,10 @@ var BrokenGlass = class BrokenGlass { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 2.0, y: 2.0}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - shardTexture = null; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi; - - // This shader creates a complex-looking effect with rather simple means. Here is how it - // works: The window is drawn five times on top of each other (see the SHARD_LAYERS - // constant in the GLSL code). Each layer only draws some of the shards, all layers - // combined make up the entire window. The layers are then scaled, rotated, and moved - // independently from each other - this creates the impression that all shards are - // moving independently. In reality, there are only five groups of shards! Which shard - // belongs to which layer is defined by the green channel of the texture - // resources/img/shards.png. The red channel of the texture contains the distance to the - // shard edges. This information is used to fade out the shards. - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - // Load the shards texture. - if (shardTexture == null) { - const shardData = GdkPixbuf.Pixbuf.new_from_resource('/img/shards.png'); - shardTexture = new Clutter.Image(); - shardTexture.set_data(shardData.get_pixels(), Cogl.PixelFormat.RGB_888, - shardData.width, shardData.height, shardData.rowstride); - } - - this._uShardTexture = this.get_uniform_location('uShardTexture'); - this._uSeed = this.get_uniform_location('uSeed'); - this._uEpicenter = this.get_uniform_location('uEpicenter'); - this._uShardScale = this.get_uniform_location('uShardScale'); - this._uBlowForce = this.get_uniform_location('uBlowForce'); - this._uGravity = this.get_uniform_location('uGravity'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - // Usually, the shards fly away from the center of the window. - let epicenterX = 0.5; - let epicenterY = 0.5; - - // However, if this option is set, we use the mouse pointer position. - if (!forOpening && settings.get_boolean('broken-glass-use-pointer')) { - const [x, y] = global.get_pointer(); - const [ok, localX, localY] = actor.transform_stage_point(x, y); - - if (ok) { - epicenterX = localX / actor.width; - epicenterY = localY / actor.height; - } - } - - // If we are currently performing integration test, the animation uses a fixed seed. - const testMode = settings.get_boolean('test-mode'); - - // clang-format off - this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); - this.set_uniform_float(this._uEpicenter, 2, [epicenterX, epicenterY]); - this.set_uniform_float(this._uShardScale, 1, [settings.get_double('broken-glass-scale')]); - this.set_uniform_float(this._uBlowForce, 1, [settings.get_double('broken-glass-blow-force')]); - this.set_uniform_float(this._uGravity, 1, [settings.get_double('broken-glass-gravity')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${BrokenGlass.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - - // This is overridden to bind the shard texture for drawing. Sadly, this seems to be - // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called - // get_target() back then but this is not wrapped in GJS. - // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 - vfunc_paint_target(node, paint_context) { - const pipeline = this.get_pipeline(); - - // Use linear filtering for the window texture. - pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR, - Cogl.PipelineFilter.LINEAR); - - // Bind the shard texture. - pipeline.set_layer_texture(1, shardTexture.get_texture()); - pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT); - pipeline.set_uniform_1i(this._uShardTexture, 1); - - super.vfunc_paint_target(node, paint_context); - } - }); -} \ No newline at end of file diff --git a/src/EnergizeA.js b/src/EnergizeA.js index 40c4d79..697b689 100644 --- a/src/EnergizeA.js +++ b/src/EnergizeA.js @@ -20,28 +20,48 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect looks a bit like the transporter effect from TOS. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var EnergizeA = class EnergizeA { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import Clutter in this function as it is not available in the preferences + // process. This creator function of the ShaderFactory is only called within GNOME + // Shell's process. + const Clutter = imports.gi.Clutter; + + // Store uniform locations of newly created shaders. + shader._uColor = shader.get_uniform_location('uColor'); + shader._uScale = shader.get_uniform_location('uScale'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + const c = Clutter.Color.from_string(settings.get_string('energize-a-color'))[1]; + + // clang-format off + shader.set_uniform_float(shader._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); + shader.set_uniform_float(shader._uScale, 1, [settings.get_double('energize-a-scale')]); + // clang-format on + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. - static getMinShellVersion() { + getMinShellVersion() { return [3, 36]; } @@ -49,22 +69,21 @@ var EnergizeA = class EnergizeA { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'energize-a'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Energize A'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/EnergizeA.ui`); @@ -80,88 +99,10 @@ var EnergizeA = class EnergizeA { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - } -} - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - this._uColor = this.get_uniform_location('uColor'); - this._uScale = this.get_uniform_location('uScale'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - const c = Clutter.Color.from_string(settings.get_string('energize-a-color'))[1]; - - // clang-format off - this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); - this.set_uniform_float(this._uScale, 1, [settings.get_double('energize-a-scale')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${EnergizeA.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - }); } \ No newline at end of file diff --git a/src/EnergizeB.js b/src/EnergizeB.js index 68abf74..0864bc2 100644 --- a/src/EnergizeB.js +++ b/src/EnergizeB.js @@ -20,28 +20,49 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect looks a bit like the transporter effect from TNG. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var EnergizeB = class EnergizeB { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import Clutter in this function as it is not available in the preferences + // process. This creator function of the ShaderFactory is only called within GNOME + // Shell's process. + const Clutter = imports.gi.Clutter; + + // Store uniform locations of newly created shaders. + shader._uColor = shader.get_uniform_location('uColor'); + shader._uScale = shader.get_uniform_location('uScale'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + const c = Clutter.Color.from_string(settings.get_string('energize-b-color'))[1]; + + // clang-format off + shader.set_uniform_float(shader._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); + shader.set_uniform_float(shader._uScale, 1, [settings.get_double('energize-b-scale')]); + // clang-format on + }); + }); + } + + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. - static getMinShellVersion() { + getMinShellVersion() { return [3, 36]; } @@ -49,22 +70,21 @@ var EnergizeB = class EnergizeB { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'energize-b'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Energize B'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/EnergizeB.ui`); @@ -80,88 +100,10 @@ var EnergizeB = class EnergizeB { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - this._uColor = this.get_uniform_location('uColor'); - this._uScale = this.get_uniform_location('uScale'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - const c = Clutter.Color.from_string(settings.get_string('energize-b-color'))[1]; - - // clang-format off - this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); - this.set_uniform_float(this._uScale, 1, [settings.get_double('energize-b-scale')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${EnergizeB.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - }); -} \ No newline at end of file diff --git a/src/Fire.js b/src/Fire.js index 33429e7..cd4f461 100644 --- a/src/Fire.js +++ b/src/Fire.js @@ -13,13 +13,14 @@ 'use strict'; -const {Gio, GObject} = imports.gi; +const {Gio} = imports.gi; const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect is a homage to the good old Compiz days. However, it is implemented // @@ -29,23 +30,58 @@ const utils = Me.imports.src.utils; // there are a couple of moving gradients which fade-in or fade-out the fire effect. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var Fire = class Fire { + + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import Clutter in this function as it is not available in the preferences + // process. This creator function of the ShaderFactory is only called within GNOME + // Shell's process. + const Clutter = imports.gi.Clutter; + + // Store all uniform locations. + shader._uGradient = [ + shader.get_uniform_location('uGradient1'), + shader.get_uniform_location('uGradient2'), + shader.get_uniform_location('uGradient3'), + shader.get_uniform_location('uGradient4'), + shader.get_uniform_location('uGradient5'), + ]; + + shader._u3DNoise = shader.get_uniform_location('u3DNoise'); + shader._uScale = shader.get_uniform_location('uScale'); + shader._uMovementSpeed = shader.get_uniform_location('uMovementSpeed'); + + // And update all uniforms at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + for (let i = 1; i <= 5; i++) { + const c = Clutter.Color.from_string(settings.get_string('fire-color-' + i))[1]; + shader.set_uniform_float( + shader._uGradient[i - 1], 4, + [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]); + } + + // clang-format off + shader.set_uniform_float(shader._u3DNoise, 1, [settings.get_boolean('flame-3d-noise')]); + shader.set_uniform_float(shader._uScale, 1, [settings.get_double('flame-scale')]); + shader.set_uniform_float(shader._uMovementSpeed, 1, [settings.get_double('flame-movement-speed')]); + // clang-format on + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. - static getMinShellVersion() { + getMinShellVersion() { return [3, 36]; } @@ -53,22 +89,21 @@ var Fire = class Fire { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'fire'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Fire'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Fire.ui`); @@ -94,7 +129,7 @@ var Fire = class Fire { }); // Initialize the fire-preset dropdown. - Fire._createFirePresets(dialog); + this._createFirePresets(dialog); // Finally, return the new settings page. return dialog.getBuilder().get_object('fire-prefs'); @@ -102,40 +137,17 @@ var Fire = class Fire { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0}; } - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - } - // ----------------------------------------------------------------------- private stuff // This populates the preset dropdown menu for the fire options. - static _createFirePresets(dialog) { + _createFirePresets(dialog) { dialog.getBuilder().get_object('fire-prefs').connect('realize', (widget) => { const presets = [ { @@ -221,74 +233,3 @@ var Fire = class Fire { }); } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - this._uGradient = [ - this.get_uniform_location('uGradient1'), - this.get_uniform_location('uGradient2'), - this.get_uniform_location('uGradient3'), - this.get_uniform_location('uGradient4'), - this.get_uniform_location('uGradient5'), - ]; - - this._u3DNoise = this.get_uniform_location('u3DNoise'); - this._uScale = this.get_uniform_location('uScale'); - this._uMovementSpeed = this.get_uniform_location('uMovementSpeed'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - - // Load the gradient values from the settings. - for (let i = 1; i <= 5; i++) { - const c = Clutter.Color.from_string(settings.get_string('fire-color-' + i))[1]; - this.set_uniform_float(this._uGradient[i - 1], 4, - [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]); - } - - // clang-format off - this.set_uniform_float(this._u3DNoise, 1, [settings.get_boolean('flame-3d-noise')]); - this.set_uniform_float(this._uScale, 1, [settings.get_double('flame-scale')]); - this.set_uniform_float(this._uMovementSpeed, 1, [settings.get_double('flame-movement-speed')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${Fire.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - }); -} \ No newline at end of file diff --git a/src/Hexagon.js b/src/Hexagon.js index 7eb7a6a..285bbd8 100644 --- a/src/Hexagon.js +++ b/src/Hexagon.js @@ -20,29 +20,66 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect overlays a glowing hexagonal grid over the window. The grid cells then // // gradually shrink until the window is fully dissolved. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var Hexagon = class Hexagon { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import Clutter in this function as it is not available in the preferences + // process. This creator function of the ShaderFactory is only called within GNOME + // Shell's process. + const Clutter = imports.gi.Clutter; + + // Store uniform locations of newly created shaders. + shader._uAdditiveBlending = shader.get_uniform_location('uAdditiveBlending'); + shader._uSeed = shader.get_uniform_location('uSeed'); + shader._uScale = shader.get_uniform_location('uScale'); + shader._uLineWidth = shader.get_uniform_location('uLineWidth'); + shader._uGlowColor = shader.get_uniform_location('uGlowColor'); + shader._uLineColor = shader.get_uniform_location('uLineColor'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + // Get the two configurable colors. They are directly injected into the shader + // code below. + const gc = + Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1]; + const lc = + Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1]; + + // If we are currently performing integration test, the animation uses a fixed + // seed. + const testMode = settings.get_boolean('test-mode'); + + // clang-format off + shader.set_uniform_float(shader._uAdditiveBlending, 1, [settings.get_boolean('hexagon-additive-blending')]); + shader.set_uniform_float(shader._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); + shader.set_uniform_float(shader._uScale, 1, [settings.get_double('hexagon-scale')]); + shader.set_uniform_float(shader._uLineWidth, 1, [settings.get_double('hexagon-line-width')]); + shader.set_uniform_float(shader._uGlowColor, 4, [gc.red / 255, gc.green / 255, gc.blue / 255, gc.alpha / 255]); + shader.set_uniform_float(shader._uLineColor, 4, [lc.red / 255, lc.green / 255, lc.blue / 255, lc.alpha / 255]); + // clang-format on + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. - static getMinShellVersion() { + getMinShellVersion() { return [3, 36]; } @@ -50,22 +87,21 @@ var Hexagon = class Hexagon { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'hexagon'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Hexagon'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Hexagon.ui`); @@ -84,103 +120,10 @@ var Hexagon = class Hexagon { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - this._uAdditiveBlending = this.get_uniform_location('uAdditiveBlending'); - this._uSeed = this.get_uniform_location('uSeed'); - this._uScale = this.get_uniform_location('uScale'); - this._uLineWidth = this.get_uniform_location('uLineWidth'); - this._uGlowColor = this.get_uniform_location('uGlowColor'); - this._uLineColor = this.get_uniform_location('uLineColor'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - - // Get the two configurable colors. They are directly injected into the shader code - // below. - const gc = Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1]; - const lc = Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1]; - - // If we are currently performing integration test, the animation uses a fixed seed. - const testMode = settings.get_boolean('test-mode'); - - // clang-format off - this.set_uniform_float(this._uAdditiveBlending, 1, [settings.get_boolean('hexagon-additive-blending')]); - this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); - this.set_uniform_float(this._uScale, 1, [settings.get_double('hexagon-scale')]); - this.set_uniform_float(this._uLineWidth, 1, [settings.get_double('hexagon-line-width')]); - this.set_uniform_float(this._uGlowColor, 4, [gc.red / 255, gc.green / 255, gc.blue / 255, gc.alpha / 255]); - this.set_uniform_float(this._uLineColor, 4, [lc.red / 255, lc.green / 255, lc.blue / 255, lc.alpha / 255]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${Hexagon.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - }); -} \ No newline at end of file diff --git a/src/Matrix.js b/src/Matrix.js index 97dcbe6..b7bc525 100644 --- a/src/Matrix.js +++ b/src/Matrix.js @@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // The Matrix shader multiplies a grid of random letters with some gradients which are // @@ -29,26 +30,72 @@ const utils = Me.imports.src.utils; // documentation of vfunc_paint_target further down in this file. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// This texture will be loaded when the effect is used for the first time. -let fontTexture = null; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var Matrix = class Matrix { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import these modules in this function as they are not available in the + // preferences process. This callback is only called within GNOME Shell's process. + const {Clutter, GdkPixbuf, Cogl} = imports.gi; + + // Create the texture in the first call. + if (!this._fontTexture) { + const fontData = GdkPixbuf.Pixbuf.new_from_resource('/img/matrixFont.png'); + this._fontTexture = new Clutter.Image(); + this._fontTexture.set_data( + fontData.get_pixels(), + fontData.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888, + fontData.width, fontData.height, fontData.rowstride); + } + + // Store uniform locations of newly created shaders. + shader._uFontTexture = shader.get_uniform_location('uFontTexture'); + shader._uTrailColor = shader.get_uniform_location('uTrailColor'); + shader._uTipColor = shader.get_uniform_location('uTipColor'); + shader._uLetterSize = shader.get_uniform_location('uLetterSize'); + shader._uRandomness = shader.get_uniform_location('uRandomness'); + shader._uOverShoot = shader.get_uniform_location('uOverShoot'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + const c1 = + Clutter.Color.from_string(settings.get_string('matrix-trail-color'))[1]; + const c2 = Clutter.Color.from_string(settings.get_string('matrix-tip-color'))[1]; + + // clang-format off + shader.set_uniform_float(shader._uTrailColor, 3, [c1.red / 255, c1.green / 255, c1.blue / 255]); + shader.set_uniform_float(shader._uTipColor, 3, [c2.red / 255, c2.green / 255, c2.blue / 255]); + shader.set_uniform_float(shader._uLetterSize, 1, [settings.get_int('matrix-scale')]); + shader.set_uniform_float(shader._uRandomness, 1, [settings.get_double('matrix-randomness')]); + shader.set_uniform_float(shader._uOverShoot, 1, [settings.get_double('matrix-overshoot')]); + // clang-format on + }); + + // This is required to bind the font texture for drawing. Sadly, this seems to be + // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was + // called get_target() back then but this is not wrapped in GJS. + // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 + shader.connect('update-animation', (shader) => { + const pipeline = shader.get_pipeline(); + + // Bind the font texture. + pipeline.set_layer_texture(1, this._fontTexture.get_texture()); + pipeline.set_uniform_1i(shader._uFontTexture, 1); + }); + }); + } + // ---------------------------------------------------------------------------- metadata // This effect is only available on GNOME Shell 40+. - static getMinShellVersion() { + getMinShellVersion() { return [40, 0]; } @@ -56,22 +103,21 @@ var Matrix = class Matrix { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'matrix'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Matrix'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource('/ui/gtk4/Matrix.ui'); @@ -90,121 +136,10 @@ var Matrix = class Matrix { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0 + settings.get_double('matrix-overshoot')}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - fontTexture = null; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - // Load the font texture. - if (fontTexture == null) { - const fontData = GdkPixbuf.Pixbuf.new_from_resource('/img/matrixFont.png'); - fontTexture = new Clutter.Image(); - fontTexture.set_data( - fontData.get_pixels(), - fontData.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888, - fontData.width, fontData.height, fontData.rowstride); - } - - this._uFontTexture = this.get_uniform_location('uFontTexture'); - this._uTrailColor = this.get_uniform_location('uTrailColor'); - this._uTipColor = this.get_uniform_location('uTipColor'); - this._uLetterSize = this.get_uniform_location('uLetterSize'); - this._uRandomness = this.get_uniform_location('uRandomness'); - this._uOverShoot = this.get_uniform_location('uOverShoot'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - const c1 = Clutter.Color.from_string(settings.get_string('matrix-trail-color'))[1]; - const c2 = Clutter.Color.from_string(settings.get_string('matrix-tip-color'))[1]; - - // clang-format off - this.set_uniform_float(this._uTrailColor, 3, [c1.red / 255, c1.green / 255, c1.blue / 255]); - this.set_uniform_float(this._uTipColor, 3, [c2.red / 255, c2.green / 255, c2.blue / 255]); - this.set_uniform_float(this._uLetterSize, 1, [settings.get_int('matrix-scale')]); - this.set_uniform_float(this._uRandomness, 1, [settings.get_double('matrix-randomness')]); - this.set_uniform_float(this._uOverShoot, 1, [settings.get_double('matrix-overshoot')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. The technique for this effect was inspired by - // https://www.shadertoy.com/view/ldccW4, however the implementation is quite - // different as the letters drop only once and there is no need for a noise texture. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${Matrix.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - - // This is overridden to bind the font texture for drawing. Sadly, this seems to be - // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called - // get_target() back then but this is not wrapped in GJS. - // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 - vfunc_paint_target(node, paint_context) { - const pipeline = this.get_pipeline(); - pipeline.set_layer_texture(1, fontTexture.get_texture()); - pipeline.set_uniform_1i(this._uFontTexture, 1); - - super.vfunc_paint_target(node, paint_context); - } - }); -} \ No newline at end of file diff --git a/src/Shader.js b/src/Shader.js new file mode 100644 index 0000000..4d53a35 --- /dev/null +++ b/src/Shader.js @@ -0,0 +1,145 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< // +// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ // +// |__/ // +// Copyright (c) 2021 Simon Schneegans // +// Released under the GPLv3 or later. See LICENSE file for details. // +////////////////////////////////////////////////////////////////////////////////////////// + +'use strict'; + +const {Gio, Shell, GObject, Clutter} = imports.gi; +const ByteArray = imports.byteArray; + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = imports.misc.extensionUtils.getCurrentExtension(); +const utils = Me.imports.src.utils; + +////////////////////////////////////////////////////////////////////////////////////////// +// This is the base class for all shaders of Burn-My-Windows. It automagically loads // +// the shader's source code from the resource file resources/shaders/.glsl and // +// ensures that some standard uniforms are always updated. // +// Using Shell.GLSLEffect as a base class has some benefits and some drawbacks. The // +// main benefit when compared to Clutter.ShaderEffect is that setting uniforms of types // +// vec2, vec3 or vec4 is supported via the API (with Clutter.ShaderEffect the GJS // +// binding does not work properly). However, there are two drawbacks: On the one hand, // +// the shader source code is cached statically - this mean if we want to have a // +// different shader, we have to derive a new class. Therefore, each effect has to // +// derive its own class from the class below. This is encapsulated in the // +// ShaderFactory, however it is some really awkward code. The other drawback is the // +// hard-coded use of straight alpha (as opposed to premultiplied). This makes the // +// shaders a bit more complicated than required. // +// // +// The Shader fires two signals: // +// * begin-animation: This is called each time a new animation is started. It can // +// be used to set uniform values which do not change during the // +// animation. // +// * update-animation: This is called at each frame during the animation. It can be // +// used to set uniforms which change during the animation. // +////////////////////////////////////////////////////////////////////////////////////////// + +var Shader = GObject.registerClass( + { + Signals: { + 'begin-animation': + {param_types: [Gio.Settings.$gtype, GObject.TYPE_BOOLEAN, Clutter.Actor.$gtype]}, + 'update-animation': {param_types: [GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE]} + } + }, + class Shader extends Shell.GLSLEffect { // -------------------------------------------- + // The constructor automagically loads the shader's source code (in + // vfunc_build_pipeline()) from the resource file resources/shaders/.glsl + // resolving any #includes in this file. + _init(nick) { + this._nick = nick; + + // This will call vfunc_build_pipeline(). + super._init(); + + // These will be updated during the animation. + this._progress = 0; + this._time = 0; + + // Store standard uniform locations. + this._uForOpening = this.get_uniform_location('uForOpening'); + this._uProgress = this.get_uniform_location('uProgress'); + this._uTime = this.get_uniform_location('uTime'); + this._uSize = this.get_uniform_location('uSize'); + } + + // This is called once each time the shader is used. + beginAnimation(settings, forOpening, actor) { + this.set_uniform_float(this._uForOpening, 1, [forOpening]); + this.set_uniform_float(this._uSize, 2, [actor.width, actor.height]); + + this.emit('begin-animation', settings, forOpening, actor); + } + + // This is called at each frame during the animation. + updateAnimation(progress, time) { + this.set_uniform_float(this._uProgress, 1, [progress]); + this.set_uniform_float(this._uTime, 1, [time]); + + // Store the current time and progress values. The corresponding signal is emitted a + // but later in vfunc_paint_target. + this._progress = progress; + this._time = time; + } + + // This is called by the constructor. This means, it's only called when the + // effect is used for the first time. + vfunc_build_pipeline() { + + // Shell.GLSLEffect requires the declarations and the main source code as separate + // strings. As it's more convenient to store the in one GLSL file, we use a regex + // here to split the source code in two parts. + const code = this._loadGLSLResource(`/shaders/${this._nick}.glsl`); + + // Match anything between the curly brackets of "void main() {...}". + const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); + const match = regex.exec(code); + + const declarations = code.substr(0, match.index); + const main = match[1]; + + this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); + } + + // We use this vfunc to trigger the update as it allows calling this.get_pipeline() in + // the handler. This could still be null if called from the updateAnimation() above. + vfunc_paint_target(...params) { + this.emit('update-animation', this._progress, this._time); + super.vfunc_paint_target(...params); + } + + // --------------------------------------------------------------------- private stuff + + // This loads the file at 'path' contained in the extension's resources to a + // JavaScript string. + _loadStringResource(path) { + const data = Gio.resources_lookup_data(path, 0); + return ByteArray.toString(ByteArray.fromGBytes(data)); + } + + // This loads a GLSL file from the extension's resources to a JavaScript string. Any + // #include statements in this file are replaced with the corresponding file contents. + _loadGLSLResource(path) { + let code = this._loadStringResource(path); + + // This regex matches either #include "..." or #include <...>. The part between the + // brackets is captured in the capture group. + const regex = RegExp('#include ["<](.+)[">]', 'g'); + + code = code.replace(regex, (m, file) => { + return this._loadStringResource('/shaders/' + file); + }); + + // Add a trailing newline. Else the GLSL compiler complains... + return code + '\n'; + } + }); diff --git a/src/ShaderFactory.js b/src/ShaderFactory.js new file mode 100644 index 0000000..2bc132d --- /dev/null +++ b/src/ShaderFactory.js @@ -0,0 +1,95 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< // +// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ // +// |__/ // +// Copyright (c) 2021 Simon Schneegans // +// Released under the GPLv3 or later. See LICENSE file for details. // +////////////////////////////////////////////////////////////////////////////////////////// + +'use strict'; + +const GObject = imports.gi.GObject; + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = imports.misc.extensionUtils.getCurrentExtension(); + +////////////////////////////////////////////////////////////////////////////////////////// +// Each effect of Burn-My-Windows owns an instance of this class. It is used to created // +// shaders whenever a new one is required. It tries to re-use old shaders as much as // +// possible in order to avoid memory leaks. // +////////////////////////////////////////////////////////////////////////////////////////// + +var ShaderFactory = class ShaderFactory { + + // Creates a new ShaderFactory. Requires the nick of the effect and a callback function + // which will be called whenever a new shader is created. + constructor(nick, setupFunc) { + // The _freeShaders array contains previously created shaders which are not currently + // in use. + this._freeShaders = []; + + // The nick of the effect is required when creating new shaders as it's part of the + // GLSL source code file name. + this._nick = nick; + + // Store the setup callback. + this._setupFunc = setupFunc; + } + + // ---------------------------------------------------------------- API for extension.js + + // This is called from extension.js whenever a window is opened or closed. It returns an + // instance of the shader class, trying to reuse previously created shaders. If a new + // shader instance is required, it calls creates a new one and calls the setupFunc + // thereafter. + getShader() { + let shader; + + // If there are currently no free shaders, we have to create a new one. + if (this._freeShaders.length == 0) { + + // Since Shell.GLSLEffect caches the shader source per class (not per instance), we + // have to derive a new shader type for each effect. The type name contains the nick + // of the effect to make it unique. + const typeName = `BurnMyWindowsShader_${this._nick}`; + + // Only try to register the new type once. + if (GObject.type_from_name(typeName) == null) { + const outerThis = this; + GObject.registerClass({GTypeName: typeName}, + class Shader extends Me.imports.src.Shader.Shader { + // This will actually load the GLSL source code from the resources. + _init() { + super._init(outerThis._nick); + } + + // This can be called to mark this instance to be re-usable. + returnToFactory() { + outerThis._freeShaders.push(this); + } + }); + } + + // Now create a niew instance of the newly registered shader type. + // GObject.Object.new is only available with newer versions of GJS. + if (GObject.Object.new) { + shader = GObject.Object.new(GObject.type_from_name(typeName), {}); + } else { + shader = GObject.Object.newv(GObject.type_from_name(typeName), []); + } + + // Call the provided setup callback. + this._setupFunc(shader); + + } else { + shader = this._freeShaders.pop(); + } + + return shader; + } +} diff --git a/src/SnapOfDisintegration.js b/src/SnapOfDisintegration.js index b4500b2..27e1be0 100644 --- a/src/SnapOfDisintegration.js +++ b/src/SnapOfDisintegration.js @@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effects dissolves your windows into a cloud of dust. For this, it uses an // @@ -31,26 +32,74 @@ const utils = Me.imports.src.utils; // documentation of vfunc_paint_target further down in this file. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// This texture will be loaded when the effect is used for the first time. -let dustTexture = null; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var SnapOfDisintegration = class SnapOfDisintegration { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import these modules in this function as they are not available in the + // preferences process. This callback is only called within GNOME Shell's process. + const {Clutter, GdkPixbuf, Cogl} = imports.gi; + + // Create the texture in the first call. + if (!this._dustTexture) { + const dustData = GdkPixbuf.Pixbuf.new_from_resource('/img/dust.png'); + this._dustTexture = new Clutter.Image(); + this._dustTexture.set_data(dustData.get_pixels(), Cogl.PixelFormat.RGB_888, + dustData.width, dustData.height, dustData.rowstride); + } + + // Store uniform locations of newly created shaders. + shader._uDustTexture = shader.get_uniform_location('uDustTexture'); + shader._uDustColor = shader.get_uniform_location('uDustColor'); + shader._uSeed = shader.get_uniform_location('uSeed'); + shader._uDustScale = shader.get_uniform_location('uDustScale'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + // The dust particles will fade to this color over time. + const c = Clutter.Color.from_string(settings.get_string('snap-color'))[1]; + + // If we are currently performing integration test, the animation uses a fixed + // seed. + const testMode = settings.get_boolean('test-mode'); + + // clang-format off + shader.set_uniform_float(shader._uDustColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]); + shader.set_uniform_float(shader._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); + shader.set_uniform_float(shader._uDustScale, 1, [settings.get_double('snap-scale')]); + // clang-format on + }); + + // This is required to bind the dust texture for drawing. Sadly, this seems to be + // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was + // called get_target() back then but this is not wrapped in GJS. + // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 + shader.connect('update-animation', (shader) => { + const pipeline = shader.get_pipeline(); + + // Use linear filtering for the window texture. + pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR, + Cogl.PipelineFilter.LINEAR); + + // Bind the dust texture. + pipeline.set_layer_texture(1, this._dustTexture.get_texture()); + pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT); + pipeline.set_uniform_1i(shader._uDustTexture, 1); + }); + }); + } + // ---------------------------------------------------------------------------- metadata // This effect is only available on GNOME Shell 40+. - static getMinShellVersion() { + getMinShellVersion() { return [40, 0]; } @@ -58,22 +107,21 @@ var SnapOfDisintegration = class SnapOfDisintegration { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'snap'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Snap of Disintegration'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource('/ui/gtk4/SnapOfDisintegration.ui'); @@ -89,119 +137,10 @@ var SnapOfDisintegration = class SnapOfDisintegration { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.2, y: 1.2}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - dustTexture = null; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - // Load the dust texture. - if (dustTexture == null) { - const dustData = GdkPixbuf.Pixbuf.new_from_resource('/img/dust.png'); - dustTexture = new Clutter.Image(); - dustTexture.set_data(dustData.get_pixels(), Cogl.PixelFormat.RGB_888, - dustData.width, dustData.height, dustData.rowstride); - } - - this._uDustTexture = this.get_uniform_location('uDustTexture'); - this._uDustColor = this.get_uniform_location('uDustColor'); - this._uSeed = this.get_uniform_location('uSeed'); - this._uDustScale = this.get_uniform_location('uDustScale'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - // The dust particles will fade to this color over time. - const c = Clutter.Color.from_string(settings.get_string('snap-color'))[1]; - - // If we are currently performing integration test, the animation uses a fixed seed. - const testMode = settings.get_boolean('test-mode'); - - // clang-format off - this.set_uniform_float(this._uDustColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]); - this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); - this.set_uniform_float(this._uDustScale, 1, [settings.get_double('snap-scale')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = - utils.loadGLSLResource(`/shaders/${SnapOfDisintegration.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - - // This is overridden to bind the dust texture for drawing. Sadly, this seems to be - // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called - // get_target() back then but this is not wrapped in GJS. - // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 - vfunc_paint_target(node, paint_context) { - const pipeline = this.get_pipeline(); - pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR, - Cogl.PipelineFilter.LINEAR); - pipeline.set_layer_texture(1, dustTexture.get_texture()); - pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT); - pipeline.set_uniform_1i(this._uDustTexture, 1); - super.vfunc_paint_target(node, paint_context); - } - }); -} \ No newline at end of file diff --git a/src/TRexAttack.js b/src/TRexAttack.js index 2410ce1..579f324 100644 --- a/src/TRexAttack.js +++ b/src/TRexAttack.js @@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect tears your windows apart with a series of violent scratches! // @@ -27,26 +28,76 @@ const utils = Me.imports.src.utils; // documentation of vfunc_paint_target further down in this file. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// This texture will be loaded when the effect is used for the first time. -let clawTexture = null; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var TRexAttack = class TRexAttack { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import these modules in this function as they are not available in the + // preferences process. This callback is only called within GNOME Shell's process. + const {Clutter, GdkPixbuf, Cogl} = imports.gi; + + // Create the texture in the first call. + if (!this._clawTexture) { + const clawData = GdkPixbuf.Pixbuf.new_from_resource('/img/claws.png'); + this._clawTexture = new Clutter.Image(); + this._clawTexture.set_data(clawData.get_pixels(), Cogl.PixelFormat.RGB_888, + clawData.width, clawData.height, clawData.rowstride); + } + + // Store uniform locations of newly created shaders. + shader._uClawTexture = shader.get_uniform_location('uClawTexture'); + shader._uFlashColor = shader.get_uniform_location('uFlashColor'); + shader._uSeed = shader.get_uniform_location('uSeed'); + shader._uClawSize = shader.get_uniform_location('uClawSize'); + shader._uNumClaws = shader.get_uniform_location('uNumClaws'); + shader._uWarpIntensity = shader.get_uniform_location('uWarpIntensity'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + const c = Clutter.Color.from_string(settings.get_string('claw-scratch-color'))[1]; + + // If we are currently performing integration test, the animation uses a fixed + // seed. + const testMode = settings.get_boolean('test-mode'); + + // clang-format off + shader.set_uniform_float(shader._uFlashColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]); + shader.set_uniform_float(shader._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); + shader.set_uniform_float(shader._uClawSize, 1, [settings.get_double('claw-scratch-scale')]); + shader.set_uniform_float(shader._uNumClaws, 1, [settings.get_int('claw-scratch-count')]); + shader.set_uniform_float(shader._uWarpIntensity, 1, [settings.get_double('claw-scratch-warp')]); + // clang-format on + }); + + // This is required to bind the claw texture for drawing. Sadly, this seems to be + // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was + // called get_target() back then but this is not wrapped in GJS. + // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 + shader.connect('update-animation', (shader) => { + const pipeline = shader.get_pipeline(); + + // Use linear filtering for the window texture. + pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR, + Cogl.PipelineFilter.LINEAR); + + // Bind the claw texture. + pipeline.set_layer_texture(1, this._clawTexture.get_texture()); + pipeline.set_uniform_1i(shader._uClawTexture, 1); + }); + }); + } + // ---------------------------------------------------------------------------- metadata // This effect is only available on GNOME Shell 40+. - static getMinShellVersion() { + getMinShellVersion() { return [40, 0]; } @@ -54,22 +105,21 @@ var TRexAttack = class TRexAttack { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'trex'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('T-Rex Attack'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource('/ui/gtk4/TRexAttack.ui'); @@ -87,120 +137,11 @@ var TRexAttack = class TRexAttack { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { const scale = 1.0 + 0.5 * settings.get_double('claw-scratch-warp'); return {x: scale, y: scale}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - clawTexture = null; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - // Load the claw texture. - if (clawTexture == null) { - const clawData = GdkPixbuf.Pixbuf.new_from_resource('/img/claws.png'); - clawTexture = new Clutter.Image(); - clawTexture.set_data(clawData.get_pixels(), Cogl.PixelFormat.RGB_888, - clawData.width, clawData.height, clawData.rowstride); - } - - this._uClawTexture = this.get_uniform_location('uClawTexture'); - this._uFlashColor = this.get_uniform_location('uFlashColor'); - this._uSeed = this.get_uniform_location('uSeed'); - this._uClawSize = this.get_uniform_location('uClawSize'); - this._uNumClaws = this.get_uniform_location('uNumClaws'); - this._uWarpIntensity = this.get_uniform_location('uWarpIntensity'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - const c = Clutter.Color.from_string(settings.get_string('claw-scratch-color'))[1]; - - // If we are currently performing integration test, the animation uses a fixed seed. - const testMode = settings.get_boolean('test-mode'); - - // clang-format off - this.set_uniform_float(this._uFlashColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]); - this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); - this.set_uniform_float(this._uClawSize, 1, [settings.get_double('claw-scratch-scale')]); - this.set_uniform_float(this._uNumClaws, 1, [settings.get_int('claw-scratch-count')]); - this.set_uniform_float(this._uWarpIntensity, 1, [settings.get_double('claw-scratch-warp')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${TRexAttack.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - - // This is overridden to bind the claw texture for drawing. Sadly, this seems to be - // impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called - // get_target() back then but this is not wrapped in GJS. - // https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598 - vfunc_paint_target(node, paint_context) { - const pipeline = this.get_pipeline(); - pipeline.set_layer_texture(1, clawTexture.get_texture()); - pipeline.set_uniform_1i(this._uClawTexture, 1); - - super.vfunc_paint_target(node, paint_context); - } - }); -} \ No newline at end of file diff --git a/src/TVEffect.js b/src/TVEffect.js index db03fba..c0dbebd 100644 --- a/src/TVEffect.js +++ b/src/TVEffect.js @@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect hides the actor by making it first transparent from top and bottom // @@ -27,23 +28,38 @@ const utils = Me.imports.src.utils; // the center. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// The effect class is completely static. It can be used to get some metadata (like the -// effect's name or supported GNOME Shell versions), to initialize the respective page of -// the settings dialog, as well as to create the actual shader for the effect. +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var TVEffect = class TVEffect { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import Clutter in this function as it is not available in the preferences + // process. This creator function of the ShaderFactory is only called within GNOME + // Shell's process. + const Clutter = imports.gi.Clutter; + + // Store uniform locations of newly created shaders. + shader._uColor = shader.get_uniform_location('uColor'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + const c = Clutter.Color.from_string(settings.get_string('tv-effect-color'))[1]; + shader.set_uniform_float(shader._uColor, 3, + [c.red / 255, c.green / 255, c.blue / 255]); + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. - static getMinShellVersion() { + getMinShellVersion() { return [3, 36]; } @@ -51,22 +67,21 @@ var TVEffect = class TVEffect { // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. '*-close-effect'), and its animation time // (e.g. '*-animation-time'). - static getNick() { + getNick() { return 'tv'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('TV Effect'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/TVEffect.ui`); @@ -81,83 +96,10 @@ var TVEffect = class TVEffect { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - this._uColor = this.get_uniform_location('uColor'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - const c = Clutter.Color.from_string(settings.get_string('tv-effect-color'))[1]; - this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${TVEffect.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - }); -} \ No newline at end of file diff --git a/src/Wisps.js b/src/Wisps.js index 65e2d17..42a7ea1 100644 --- a/src/Wisps.js +++ b/src/Wisps.js @@ -20,52 +20,76 @@ const _ = imports.gettext.domain('burn-my-windows').gettext; const ExtensionUtils = imports.misc.extensionUtils; const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; +const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory; ////////////////////////////////////////////////////////////////////////////////////////// // This effect lets your windows be carried to the realm of dreams by some little // // fairies. It's implemented with several overlaid grids of randomly moving points. // ////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect is registered further down in this file. When this -// effect is used for the first time, an instance of this shader class is created. Once -// the effect is finished, the shader will be stored in the freeShaders array and will -// then be reused if a new shader is requested. ShaderClass which will be used whenever -// this effect is used. -let ShaderClass = null; -let freeShaders = []; - -// This will be called in various places where a unique identifier for this effect is -// required. It should match the prefix of the settings keys which store whether the -// effect is enabled currently (e.g. '*-close-effect'), and its animation time -// (e.g. '*-animation-time'). +// The effect class can be used to get some metadata (like the effect's name or supported +// GNOME Shell versions), to initialize the respective page of the settings dialog, as +// well as to create the actual shader for the effect. var Wisps = class Wisps { + // The constructor creates a ShaderFactory which will be used by extension.js to create + // shader instances for this effect. The shaders will be automagically created using the + // GLSL file in resources/shaders/.glsl. The callback will be called for each + // newly created shader instance. + constructor() { + this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => { + // We import Clutter in this function as it is not available in the preferences + // process. This creator function of the ShaderFactory is only called within GNOME + // Shell's process. + const Clutter = imports.gi.Clutter; + + // Store uniform locations of newly created shaders. + shader._uSeed = shader.get_uniform_location('uSeed'); + shader._uColor = shader.get_uniform_location('uColor'); + shader._uScale = shader.get_uniform_location('uScale'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', (shader, settings) => { + const c = Clutter.Color.from_string(settings.get_string('wisps-color'))[1]; + + // If we are currently performing integration test, the animation uses a fixed + // seed. + const testMode = settings.get_boolean('test-mode'); + + // clang-format off + shader.set_uniform_float(shader._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); + shader.set_uniform_float(shader._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); + shader.set_uniform_float(shader._uScale, 1, [settings.get_double('wisps-scale')]); + // clang-format on + }); + }); + } + // ---------------------------------------------------------------------------- metadata // The effect is available on all GNOME Shell versions supported by this extension. - static getMinShellVersion() { + getMinShellVersion() { return [3, 36]; } // This will be called in various places where a unique identifier for this effect is // required. It should match the prefix of the settings keys which store whether the // effect is enabled currently (e.g. the '*-close-effect'). - static getNick() { + getNick() { return 'wisps'; } // This will be shown in the sidebar of the preferences dialog as well as in the // drop-down menus where the user can choose the effect. - static getLabel() { + getLabel() { return _('Wisps'); } // -------------------------------------------------------------------- API for prefs.js // This is called by the preferences dialog. It loads the settings page for this effect, - // binds all properties to the settings and appends the page to the main stack of the - // preferences dialog. - static getPreferences(dialog) { + // and binds all properties to the settings. + getPreferences(dialog) { // Add the settings page to the builder. dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Wisps.ui`); @@ -81,93 +105,10 @@ var Wisps = class Wisps { // ---------------------------------------------------------------- API for extension.js - // This is called from extension.js whenever a window is opened or closed with this - // effect. It returns an instance of the shader class, trying to reuse previously - // created shaders. - static getShader(actor, settings, forOpening) { - let shader; - - if (freeShaders.length == 0) { - shader = new ShaderClass(); - } else { - shader = freeShaders.pop(); - } - - shader.setUniforms(actor, settings, forOpening); - - return shader; - } - // The getActorScale() is called from extension.js to adjust the actor's size during the // animation. This is useful if the effect requires drawing something beyond the usual // bounds of the actor. This only works for GNOME 3.38+. - static getActorScale(settings) { + getActorScale(settings) { return {x: 1.0, y: 1.0}; } - - // This is called from extension.js if the extension is disabled. This should free all - // static resources. - static cleanUp() { - freeShaders = []; - } } - - -////////////////////////////////////////////////////////////////////////////////////////// -// The shader class for this effect will only be registered in GNOME Shell's process // -// (not in the preferences process). It's done this way as Clutter may not be installed // -// on the system and therefore the preferences would crash. // -////////////////////////////////////////////////////////////////////////////////////////// - -if (utils.isInShellProcess()) { - - const {Clutter, Shell} = imports.gi; - - ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect { - // This is called when the effect is used for the first time. This can be used to - // store all required uniform locations. - _init() { - super._init(); - - this._uSeed = this.get_uniform_location('uSeed'); - this._uColor = this.get_uniform_location('uColor'); - this._uScale = this.get_uniform_location('uScale'); - } - - // This is called each time the effect is used. This can be used to retrieve the - // configuration from the settings and update all uniforms accordingly. - setUniforms(actor, settings, forOpening) { - const c = Clutter.Color.from_string(settings.get_string('wisps-color'))[1]; - - // If we are currently performing integration test, the animation uses a fixed seed. - const testMode = settings.get_boolean('test-mode'); - - // clang-format off - this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]); - this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); - this.set_uniform_float(this._uScale, 1, [settings.get_double('wisps-scale')]); - // clang-format on - } - - // This is called by extension.js when the shader is not used anymore. We will store - // this instance of the shader so that it can be re-used in th future. - free() { - freeShaders.push(this); - } - - // This is called by the constructor. This means, it's only called when the effect - // is used for the first time. - vfunc_build_pipeline() { - const code = utils.loadGLSLResource(`/shaders/${Wisps.getNick()}.glsl`); - - // Match anything between the curly brackets of "void main() {...}". - const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}'); - const match = regex.exec(code); - - const declarations = code.substr(0, match.index); - const main = match[1]; - - this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true); - } - }); -} \ No newline at end of file diff --git a/src/utils.js b/src/utils.js index 4f298f8..9f528d8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -13,8 +13,7 @@ 'use strict'; -const {Gtk, Gio} = imports.gi; -const ByteArray = imports.byteArray; +const {Gtk} = imports.gi; // Returns the given argument, except for "alpha", "beta", and "rc". In these cases -3, // -2, and -1 are returned respectively. @@ -66,12 +65,6 @@ function getGTKString() { return isGTK4() ? 'gtk4' : 'gtk3'; } -// This method returns true if called in GNOME Shell's process, false if called in the -// preferences process. -function isInShellProcess() { - return window.global && global.stage; -} - // This method returns true if the current GNOME Shell version matches the given // arguments. function shellVersionIs(major, minor) { @@ -90,28 +83,4 @@ function shellVersionIsAtLeast(major, minor) { } return false; -} - -// This loads the file at 'path' contained in the extension's resources to a JavaScript -// string. -function loadStringResource(path) { - const data = Gio.resources_lookup_data(path, 0); - return ByteArray.toString(ByteArray.fromGBytes(data)); -} - -// This loads a GLSL file from the extension's resources to a JavaScript string. Any -// #include statements in this file are replaced with the corresponding file contents. -function loadGLSLResource(path) { - let code = loadStringResource(path); - - // This regex matches either #include "..." or #include <...>. The part between the - // brackets is captured in the capture group. - const regex = RegExp('#include ["<](.+)[">]', 'g'); - - code = code.replace(regex, (m, file) => { - return loadStringResource('/shaders/' + file); - }); - - // Add a trailing newline. Else the GLSL compiler complains... - return code + '\n'; } \ No newline at end of file diff --git a/tests/generate-references.sh b/tests/generate-references.sh index 954476a..32416d7 100755 --- a/tests/generate-references.sh +++ b/tests/generate-references.sh @@ -110,14 +110,12 @@ capture_effect() { set_setting "open-preview-effect" "${1}" set_setting "close-preview-effect" "${1}" - sleep 2 do_in_pod gnome-extensions prefs "${EXTENSION}" - sleep 2 + sleep 3 capture "tests/references/${1}-open-${SESSION}-${FEDORA_VERSION}.png" send_keystroke "Alt+F4" - sleep 2 + sleep 3 capture "tests/references/${1}-close-${SESSION}-${FEDORA_VERSION}.png" - sleep 2 } # ----------------------------------------------------- wait for the container to start up diff --git a/tests/references/energize-a-close-gnome-wayland-nested-32.png b/tests/references/energize-a-close-gnome-wayland-nested-32.png index fa22732..490c5ab 100644 Binary files a/tests/references/energize-a-close-gnome-wayland-nested-32.png and b/tests/references/energize-a-close-gnome-wayland-nested-32.png differ diff --git a/tests/references/energize-a-close-gnome-wayland-nested-33.png b/tests/references/energize-a-close-gnome-wayland-nested-33.png index 903f978..5b07c26 100644 Binary files a/tests/references/energize-a-close-gnome-wayland-nested-33.png and b/tests/references/energize-a-close-gnome-wayland-nested-33.png differ diff --git a/tests/references/energize-a-close-gnome-wayland-nested-34.png b/tests/references/energize-a-close-gnome-wayland-nested-34.png index 33eed45..6d80d91 100644 Binary files a/tests/references/energize-a-close-gnome-wayland-nested-34.png and b/tests/references/energize-a-close-gnome-wayland-nested-34.png differ diff --git a/tests/references/energize-a-close-gnome-wayland-nested-35.png b/tests/references/energize-a-close-gnome-wayland-nested-35.png index 33eed45..6d80d91 100644 Binary files a/tests/references/energize-a-close-gnome-wayland-nested-35.png and b/tests/references/energize-a-close-gnome-wayland-nested-35.png differ diff --git a/tests/references/energize-a-close-gnome-wayland-nested-36.png b/tests/references/energize-a-close-gnome-wayland-nested-36.png index f9e12d3..32dc974 100644 Binary files a/tests/references/energize-a-close-gnome-wayland-nested-36.png and b/tests/references/energize-a-close-gnome-wayland-nested-36.png differ diff --git a/tests/references/energize-a-close-gnome-xsession-32.png b/tests/references/energize-a-close-gnome-xsession-32.png index fa22732..490c5ab 100644 Binary files a/tests/references/energize-a-close-gnome-xsession-32.png and b/tests/references/energize-a-close-gnome-xsession-32.png differ diff --git a/tests/references/energize-a-close-gnome-xsession-33.png b/tests/references/energize-a-close-gnome-xsession-33.png index 903f978..5b07c26 100644 Binary files a/tests/references/energize-a-close-gnome-xsession-33.png and b/tests/references/energize-a-close-gnome-xsession-33.png differ diff --git a/tests/references/energize-a-close-gnome-xsession-34.png b/tests/references/energize-a-close-gnome-xsession-34.png index 38fed08..f10cd01 100644 Binary files a/tests/references/energize-a-close-gnome-xsession-34.png and b/tests/references/energize-a-close-gnome-xsession-34.png differ diff --git a/tests/references/energize-a-close-gnome-xsession-35.png b/tests/references/energize-a-close-gnome-xsession-35.png index 38fed08..f10cd01 100644 Binary files a/tests/references/energize-a-close-gnome-xsession-35.png and b/tests/references/energize-a-close-gnome-xsession-35.png differ diff --git a/tests/references/energize-a-close-gnome-xsession-36.png b/tests/references/energize-a-close-gnome-xsession-36.png index a1d607d..6b17f98 100644 Binary files a/tests/references/energize-a-close-gnome-xsession-36.png and b/tests/references/energize-a-close-gnome-xsession-36.png differ diff --git a/tests/references/energize-a-open-gnome-wayland-nested-32.png b/tests/references/energize-a-open-gnome-wayland-nested-32.png index 0f285fc..f9c8df6 100644 Binary files a/tests/references/energize-a-open-gnome-wayland-nested-32.png and b/tests/references/energize-a-open-gnome-wayland-nested-32.png differ diff --git a/tests/references/energize-a-open-gnome-wayland-nested-33.png b/tests/references/energize-a-open-gnome-wayland-nested-33.png index ed7e414..cd6589e 100644 Binary files a/tests/references/energize-a-open-gnome-wayland-nested-33.png and b/tests/references/energize-a-open-gnome-wayland-nested-33.png differ diff --git a/tests/references/energize-a-open-gnome-wayland-nested-34.png b/tests/references/energize-a-open-gnome-wayland-nested-34.png index 4abac88..df32f12 100644 Binary files a/tests/references/energize-a-open-gnome-wayland-nested-34.png and b/tests/references/energize-a-open-gnome-wayland-nested-34.png differ diff --git a/tests/references/energize-a-open-gnome-wayland-nested-35.png b/tests/references/energize-a-open-gnome-wayland-nested-35.png index 4abac88..df32f12 100644 Binary files a/tests/references/energize-a-open-gnome-wayland-nested-35.png and b/tests/references/energize-a-open-gnome-wayland-nested-35.png differ diff --git a/tests/references/energize-a-open-gnome-wayland-nested-36.png b/tests/references/energize-a-open-gnome-wayland-nested-36.png index 44cdd32..59ff769 100644 Binary files a/tests/references/energize-a-open-gnome-wayland-nested-36.png and b/tests/references/energize-a-open-gnome-wayland-nested-36.png differ diff --git a/tests/references/energize-a-open-gnome-xsession-32.png b/tests/references/energize-a-open-gnome-xsession-32.png index 0f285fc..f9c8df6 100644 Binary files a/tests/references/energize-a-open-gnome-xsession-32.png and b/tests/references/energize-a-open-gnome-xsession-32.png differ diff --git a/tests/references/energize-a-open-gnome-xsession-33.png b/tests/references/energize-a-open-gnome-xsession-33.png index ed7e414..cd6589e 100644 Binary files a/tests/references/energize-a-open-gnome-xsession-33.png and b/tests/references/energize-a-open-gnome-xsession-33.png differ diff --git a/tests/references/energize-a-open-gnome-xsession-34.png b/tests/references/energize-a-open-gnome-xsession-34.png index 8d7fe30..5a185d5 100644 Binary files a/tests/references/energize-a-open-gnome-xsession-34.png and b/tests/references/energize-a-open-gnome-xsession-34.png differ diff --git a/tests/references/energize-a-open-gnome-xsession-35.png b/tests/references/energize-a-open-gnome-xsession-35.png index 8d7fe30..5a185d5 100644 Binary files a/tests/references/energize-a-open-gnome-xsession-35.png and b/tests/references/energize-a-open-gnome-xsession-35.png differ diff --git a/tests/references/energize-a-open-gnome-xsession-36.png b/tests/references/energize-a-open-gnome-xsession-36.png index f221edc..c26f7a0 100644 Binary files a/tests/references/energize-a-open-gnome-xsession-36.png and b/tests/references/energize-a-open-gnome-xsession-36.png differ diff --git a/tests/references/energize-b-close-gnome-wayland-nested-32.png b/tests/references/energize-b-close-gnome-wayland-nested-32.png index 689ba0d..6d68c1f 100644 Binary files a/tests/references/energize-b-close-gnome-wayland-nested-32.png and b/tests/references/energize-b-close-gnome-wayland-nested-32.png differ diff --git a/tests/references/energize-b-close-gnome-wayland-nested-33.png b/tests/references/energize-b-close-gnome-wayland-nested-33.png index 50a813f..4cc14b0 100644 Binary files a/tests/references/energize-b-close-gnome-wayland-nested-33.png and b/tests/references/energize-b-close-gnome-wayland-nested-33.png differ diff --git a/tests/references/energize-b-close-gnome-wayland-nested-34.png b/tests/references/energize-b-close-gnome-wayland-nested-34.png index b968178..741d521 100644 Binary files a/tests/references/energize-b-close-gnome-wayland-nested-34.png and b/tests/references/energize-b-close-gnome-wayland-nested-34.png differ diff --git a/tests/references/energize-b-close-gnome-wayland-nested-35.png b/tests/references/energize-b-close-gnome-wayland-nested-35.png index b968178..741d521 100644 Binary files a/tests/references/energize-b-close-gnome-wayland-nested-35.png and b/tests/references/energize-b-close-gnome-wayland-nested-35.png differ diff --git a/tests/references/energize-b-close-gnome-wayland-nested-36.png b/tests/references/energize-b-close-gnome-wayland-nested-36.png index acdce33..6919274 100644 Binary files a/tests/references/energize-b-close-gnome-wayland-nested-36.png and b/tests/references/energize-b-close-gnome-wayland-nested-36.png differ diff --git a/tests/references/energize-b-close-gnome-xsession-32.png b/tests/references/energize-b-close-gnome-xsession-32.png index 689ba0d..6d68c1f 100644 Binary files a/tests/references/energize-b-close-gnome-xsession-32.png and b/tests/references/energize-b-close-gnome-xsession-32.png differ diff --git a/tests/references/energize-b-close-gnome-xsession-33.png b/tests/references/energize-b-close-gnome-xsession-33.png index 50a813f..4cc14b0 100644 Binary files a/tests/references/energize-b-close-gnome-xsession-33.png and b/tests/references/energize-b-close-gnome-xsession-33.png differ diff --git a/tests/references/energize-b-close-gnome-xsession-34.png b/tests/references/energize-b-close-gnome-xsession-34.png index f48f0ce..ef0b5ef 100644 Binary files a/tests/references/energize-b-close-gnome-xsession-34.png and b/tests/references/energize-b-close-gnome-xsession-34.png differ diff --git a/tests/references/energize-b-close-gnome-xsession-35.png b/tests/references/energize-b-close-gnome-xsession-35.png index f48f0ce..ef0b5ef 100644 Binary files a/tests/references/energize-b-close-gnome-xsession-35.png and b/tests/references/energize-b-close-gnome-xsession-35.png differ diff --git a/tests/references/energize-b-close-gnome-xsession-36.png b/tests/references/energize-b-close-gnome-xsession-36.png index 3cbd8ad..5c8ea85 100644 Binary files a/tests/references/energize-b-close-gnome-xsession-36.png and b/tests/references/energize-b-close-gnome-xsession-36.png differ diff --git a/tests/references/energize-b-open-gnome-wayland-nested-32.png b/tests/references/energize-b-open-gnome-wayland-nested-32.png index 60403f3..fb10327 100644 Binary files a/tests/references/energize-b-open-gnome-wayland-nested-32.png and b/tests/references/energize-b-open-gnome-wayland-nested-32.png differ diff --git a/tests/references/energize-b-open-gnome-wayland-nested-33.png b/tests/references/energize-b-open-gnome-wayland-nested-33.png index 03ab7f9..23802d0 100644 Binary files a/tests/references/energize-b-open-gnome-wayland-nested-33.png and b/tests/references/energize-b-open-gnome-wayland-nested-33.png differ diff --git a/tests/references/energize-b-open-gnome-wayland-nested-34.png b/tests/references/energize-b-open-gnome-wayland-nested-34.png index fb9579b..d3b62d9 100644 Binary files a/tests/references/energize-b-open-gnome-wayland-nested-34.png and b/tests/references/energize-b-open-gnome-wayland-nested-34.png differ diff --git a/tests/references/energize-b-open-gnome-wayland-nested-35.png b/tests/references/energize-b-open-gnome-wayland-nested-35.png index fb9579b..d3b62d9 100644 Binary files a/tests/references/energize-b-open-gnome-wayland-nested-35.png and b/tests/references/energize-b-open-gnome-wayland-nested-35.png differ diff --git a/tests/references/energize-b-open-gnome-wayland-nested-36.png b/tests/references/energize-b-open-gnome-wayland-nested-36.png index b293a47..dc10d3b 100644 Binary files a/tests/references/energize-b-open-gnome-wayland-nested-36.png and b/tests/references/energize-b-open-gnome-wayland-nested-36.png differ diff --git a/tests/references/energize-b-open-gnome-xsession-32.png b/tests/references/energize-b-open-gnome-xsession-32.png index 60403f3..fb10327 100644 Binary files a/tests/references/energize-b-open-gnome-xsession-32.png and b/tests/references/energize-b-open-gnome-xsession-32.png differ diff --git a/tests/references/energize-b-open-gnome-xsession-33.png b/tests/references/energize-b-open-gnome-xsession-33.png index 03ab7f9..23802d0 100644 Binary files a/tests/references/energize-b-open-gnome-xsession-33.png and b/tests/references/energize-b-open-gnome-xsession-33.png differ diff --git a/tests/references/energize-b-open-gnome-xsession-34.png b/tests/references/energize-b-open-gnome-xsession-34.png index 5521568..efbbdfe 100644 Binary files a/tests/references/energize-b-open-gnome-xsession-34.png and b/tests/references/energize-b-open-gnome-xsession-34.png differ diff --git a/tests/references/energize-b-open-gnome-xsession-35.png b/tests/references/energize-b-open-gnome-xsession-35.png index 5521568..efbbdfe 100644 Binary files a/tests/references/energize-b-open-gnome-xsession-35.png and b/tests/references/energize-b-open-gnome-xsession-35.png differ diff --git a/tests/references/energize-b-open-gnome-xsession-36.png b/tests/references/energize-b-open-gnome-xsession-36.png index ae6caab..991beb2 100644 Binary files a/tests/references/energize-b-open-gnome-xsession-36.png and b/tests/references/energize-b-open-gnome-xsession-36.png differ diff --git a/tests/references/fire-close-gnome-wayland-nested-32.png b/tests/references/fire-close-gnome-wayland-nested-32.png index 4cafe63..8bbece7 100644 Binary files a/tests/references/fire-close-gnome-wayland-nested-32.png and b/tests/references/fire-close-gnome-wayland-nested-32.png differ diff --git a/tests/references/fire-close-gnome-wayland-nested-33.png b/tests/references/fire-close-gnome-wayland-nested-33.png index efc787c..7c816de 100644 Binary files a/tests/references/fire-close-gnome-wayland-nested-33.png and b/tests/references/fire-close-gnome-wayland-nested-33.png differ diff --git a/tests/references/fire-close-gnome-wayland-nested-34.png b/tests/references/fire-close-gnome-wayland-nested-34.png index 902bfbc..7c5b3f2 100644 Binary files a/tests/references/fire-close-gnome-wayland-nested-34.png and b/tests/references/fire-close-gnome-wayland-nested-34.png differ diff --git a/tests/references/fire-close-gnome-wayland-nested-35.png b/tests/references/fire-close-gnome-wayland-nested-35.png index 902bfbc..7c5b3f2 100644 Binary files a/tests/references/fire-close-gnome-wayland-nested-35.png and b/tests/references/fire-close-gnome-wayland-nested-35.png differ diff --git a/tests/references/fire-close-gnome-wayland-nested-36.png b/tests/references/fire-close-gnome-wayland-nested-36.png index 486ddb1..b14d60e 100644 Binary files a/tests/references/fire-close-gnome-wayland-nested-36.png and b/tests/references/fire-close-gnome-wayland-nested-36.png differ diff --git a/tests/references/fire-close-gnome-xsession-32.png b/tests/references/fire-close-gnome-xsession-32.png index 4cafe63..8bbece7 100644 Binary files a/tests/references/fire-close-gnome-xsession-32.png and b/tests/references/fire-close-gnome-xsession-32.png differ diff --git a/tests/references/fire-close-gnome-xsession-33.png b/tests/references/fire-close-gnome-xsession-33.png index efc787c..7c816de 100644 Binary files a/tests/references/fire-close-gnome-xsession-33.png and b/tests/references/fire-close-gnome-xsession-33.png differ diff --git a/tests/references/fire-close-gnome-xsession-34.png b/tests/references/fire-close-gnome-xsession-34.png index e7feaef..da00512 100644 Binary files a/tests/references/fire-close-gnome-xsession-34.png and b/tests/references/fire-close-gnome-xsession-34.png differ diff --git a/tests/references/fire-close-gnome-xsession-35.png b/tests/references/fire-close-gnome-xsession-35.png index e7feaef..da00512 100644 Binary files a/tests/references/fire-close-gnome-xsession-35.png and b/tests/references/fire-close-gnome-xsession-35.png differ diff --git a/tests/references/fire-close-gnome-xsession-36.png b/tests/references/fire-close-gnome-xsession-36.png index 86a3f38..d0e153f 100644 Binary files a/tests/references/fire-close-gnome-xsession-36.png and b/tests/references/fire-close-gnome-xsession-36.png differ diff --git a/tests/references/fire-open-gnome-wayland-nested-32.png b/tests/references/fire-open-gnome-wayland-nested-32.png index 9f082a5..52b3104 100644 Binary files a/tests/references/fire-open-gnome-wayland-nested-32.png and b/tests/references/fire-open-gnome-wayland-nested-32.png differ diff --git a/tests/references/fire-open-gnome-wayland-nested-33.png b/tests/references/fire-open-gnome-wayland-nested-33.png index f0e1556..630dd03 100644 Binary files a/tests/references/fire-open-gnome-wayland-nested-33.png and b/tests/references/fire-open-gnome-wayland-nested-33.png differ diff --git a/tests/references/fire-open-gnome-wayland-nested-34.png b/tests/references/fire-open-gnome-wayland-nested-34.png index 08a2f2b..1b84cfe 100644 Binary files a/tests/references/fire-open-gnome-wayland-nested-34.png and b/tests/references/fire-open-gnome-wayland-nested-34.png differ diff --git a/tests/references/fire-open-gnome-wayland-nested-35.png b/tests/references/fire-open-gnome-wayland-nested-35.png index 08a2f2b..1b84cfe 100644 Binary files a/tests/references/fire-open-gnome-wayland-nested-35.png and b/tests/references/fire-open-gnome-wayland-nested-35.png differ diff --git a/tests/references/fire-open-gnome-wayland-nested-36.png b/tests/references/fire-open-gnome-wayland-nested-36.png index c9bbfaf..432c8c0 100644 Binary files a/tests/references/fire-open-gnome-wayland-nested-36.png and b/tests/references/fire-open-gnome-wayland-nested-36.png differ diff --git a/tests/references/fire-open-gnome-xsession-32.png b/tests/references/fire-open-gnome-xsession-32.png index 9f082a5..52b3104 100644 Binary files a/tests/references/fire-open-gnome-xsession-32.png and b/tests/references/fire-open-gnome-xsession-32.png differ diff --git a/tests/references/fire-open-gnome-xsession-33.png b/tests/references/fire-open-gnome-xsession-33.png index f0e1556..630dd03 100644 Binary files a/tests/references/fire-open-gnome-xsession-33.png and b/tests/references/fire-open-gnome-xsession-33.png differ diff --git a/tests/references/fire-open-gnome-xsession-34.png b/tests/references/fire-open-gnome-xsession-34.png index 5cbdd22..564104b 100644 Binary files a/tests/references/fire-open-gnome-xsession-34.png and b/tests/references/fire-open-gnome-xsession-34.png differ diff --git a/tests/references/fire-open-gnome-xsession-35.png b/tests/references/fire-open-gnome-xsession-35.png index 5cbdd22..564104b 100644 Binary files a/tests/references/fire-open-gnome-xsession-35.png and b/tests/references/fire-open-gnome-xsession-35.png differ diff --git a/tests/references/fire-open-gnome-xsession-36.png b/tests/references/fire-open-gnome-xsession-36.png index a4dd48d..13f1679 100644 Binary files a/tests/references/fire-open-gnome-xsession-36.png and b/tests/references/fire-open-gnome-xsession-36.png differ diff --git a/tests/references/matrix-close-gnome-wayland-nested-34.png b/tests/references/matrix-close-gnome-wayland-nested-34.png index e2ffe68..d47a7b3 100644 Binary files a/tests/references/matrix-close-gnome-wayland-nested-34.png and b/tests/references/matrix-close-gnome-wayland-nested-34.png differ diff --git a/tests/references/matrix-close-gnome-wayland-nested-35.png b/tests/references/matrix-close-gnome-wayland-nested-35.png index 0e62f5b..f1e0901 100644 Binary files a/tests/references/matrix-close-gnome-wayland-nested-35.png and b/tests/references/matrix-close-gnome-wayland-nested-35.png differ diff --git a/tests/references/matrix-close-gnome-wayland-nested-36.png b/tests/references/matrix-close-gnome-wayland-nested-36.png index 865a06e..81dd670 100644 Binary files a/tests/references/matrix-close-gnome-wayland-nested-36.png and b/tests/references/matrix-close-gnome-wayland-nested-36.png differ diff --git a/tests/references/matrix-close-gnome-xsession-34.png b/tests/references/matrix-close-gnome-xsession-34.png index ff673db..f25bb21 100644 Binary files a/tests/references/matrix-close-gnome-xsession-34.png and b/tests/references/matrix-close-gnome-xsession-34.png differ diff --git a/tests/references/matrix-close-gnome-xsession-35.png b/tests/references/matrix-close-gnome-xsession-35.png index 2fda4e1..c9deefd 100644 Binary files a/tests/references/matrix-close-gnome-xsession-35.png and b/tests/references/matrix-close-gnome-xsession-35.png differ diff --git a/tests/references/matrix-close-gnome-xsession-36.png b/tests/references/matrix-close-gnome-xsession-36.png index b9b5cb4..1d01e7f 100644 Binary files a/tests/references/matrix-close-gnome-xsession-36.png and b/tests/references/matrix-close-gnome-xsession-36.png differ diff --git a/tests/references/matrix-open-gnome-wayland-nested-34.png b/tests/references/matrix-open-gnome-wayland-nested-34.png index 29132d2..2fe003b 100644 Binary files a/tests/references/matrix-open-gnome-wayland-nested-34.png and b/tests/references/matrix-open-gnome-wayland-nested-34.png differ diff --git a/tests/references/matrix-open-gnome-wayland-nested-35.png b/tests/references/matrix-open-gnome-wayland-nested-35.png index fafd964..3bd2103 100644 Binary files a/tests/references/matrix-open-gnome-wayland-nested-35.png and b/tests/references/matrix-open-gnome-wayland-nested-35.png differ diff --git a/tests/references/matrix-open-gnome-wayland-nested-36.png b/tests/references/matrix-open-gnome-wayland-nested-36.png index 26c24c0..fce7b37 100644 Binary files a/tests/references/matrix-open-gnome-wayland-nested-36.png and b/tests/references/matrix-open-gnome-wayland-nested-36.png differ diff --git a/tests/references/matrix-open-gnome-xsession-34.png b/tests/references/matrix-open-gnome-xsession-34.png index 14c68f2..2e0fc8a 100644 Binary files a/tests/references/matrix-open-gnome-xsession-34.png and b/tests/references/matrix-open-gnome-xsession-34.png differ diff --git a/tests/references/matrix-open-gnome-xsession-35.png b/tests/references/matrix-open-gnome-xsession-35.png index ee61a6e..08e7ecd 100644 Binary files a/tests/references/matrix-open-gnome-xsession-35.png and b/tests/references/matrix-open-gnome-xsession-35.png differ diff --git a/tests/references/matrix-open-gnome-xsession-36.png b/tests/references/matrix-open-gnome-xsession-36.png index 161bacc..c436851 100644 Binary files a/tests/references/matrix-open-gnome-xsession-36.png and b/tests/references/matrix-open-gnome-xsession-36.png differ diff --git a/tests/references/trex-close-gnome-wayland-nested-34.png b/tests/references/trex-close-gnome-wayland-nested-34.png index 076e2f4..bed4006 100644 Binary files a/tests/references/trex-close-gnome-wayland-nested-34.png and b/tests/references/trex-close-gnome-wayland-nested-34.png differ diff --git a/tests/references/trex-close-gnome-wayland-nested-35.png b/tests/references/trex-close-gnome-wayland-nested-35.png index cdd3ab0..358a732 100644 Binary files a/tests/references/trex-close-gnome-wayland-nested-35.png and b/tests/references/trex-close-gnome-wayland-nested-35.png differ diff --git a/tests/references/trex-close-gnome-wayland-nested-36.png b/tests/references/trex-close-gnome-wayland-nested-36.png index 1d20ca8..2949e4d 100644 Binary files a/tests/references/trex-close-gnome-wayland-nested-36.png and b/tests/references/trex-close-gnome-wayland-nested-36.png differ diff --git a/tests/references/trex-close-gnome-xsession-34.png b/tests/references/trex-close-gnome-xsession-34.png index 29ac6fd..6bb57b8 100644 Binary files a/tests/references/trex-close-gnome-xsession-34.png and b/tests/references/trex-close-gnome-xsession-34.png differ diff --git a/tests/references/trex-close-gnome-xsession-35.png b/tests/references/trex-close-gnome-xsession-35.png index 00bdb8f..632c14d 100644 Binary files a/tests/references/trex-close-gnome-xsession-35.png and b/tests/references/trex-close-gnome-xsession-35.png differ diff --git a/tests/references/trex-close-gnome-xsession-36.png b/tests/references/trex-close-gnome-xsession-36.png index dc393d5..60cdd1e 100644 Binary files a/tests/references/trex-close-gnome-xsession-36.png and b/tests/references/trex-close-gnome-xsession-36.png differ diff --git a/tests/references/trex-open-gnome-wayland-nested-34.png b/tests/references/trex-open-gnome-wayland-nested-34.png index 2cafaef..1e5a00e 100644 Binary files a/tests/references/trex-open-gnome-wayland-nested-34.png and b/tests/references/trex-open-gnome-wayland-nested-34.png differ diff --git a/tests/references/trex-open-gnome-wayland-nested-35.png b/tests/references/trex-open-gnome-wayland-nested-35.png index b2fa139..7af72cf 100644 Binary files a/tests/references/trex-open-gnome-wayland-nested-35.png and b/tests/references/trex-open-gnome-wayland-nested-35.png differ diff --git a/tests/references/trex-open-gnome-wayland-nested-36.png b/tests/references/trex-open-gnome-wayland-nested-36.png index b7c62b4..f4e9368 100644 Binary files a/tests/references/trex-open-gnome-wayland-nested-36.png and b/tests/references/trex-open-gnome-wayland-nested-36.png differ diff --git a/tests/references/trex-open-gnome-xsession-34.png b/tests/references/trex-open-gnome-xsession-34.png index 52f4212..9e9bd83 100644 Binary files a/tests/references/trex-open-gnome-xsession-34.png and b/tests/references/trex-open-gnome-xsession-34.png differ diff --git a/tests/references/trex-open-gnome-xsession-35.png b/tests/references/trex-open-gnome-xsession-35.png index 7190dd2..6623887 100644 Binary files a/tests/references/trex-open-gnome-xsession-35.png and b/tests/references/trex-open-gnome-xsession-35.png differ diff --git a/tests/references/trex-open-gnome-xsession-36.png b/tests/references/trex-open-gnome-xsession-36.png index d32b9f8..a4f1b81 100644 Binary files a/tests/references/trex-open-gnome-xsession-36.png and b/tests/references/trex-open-gnome-xsession-36.png differ diff --git a/tests/references/wisps-close-gnome-wayland-nested-32.png b/tests/references/wisps-close-gnome-wayland-nested-32.png index b752f62..e6a326e 100644 Binary files a/tests/references/wisps-close-gnome-wayland-nested-32.png and b/tests/references/wisps-close-gnome-wayland-nested-32.png differ diff --git a/tests/references/wisps-close-gnome-wayland-nested-33.png b/tests/references/wisps-close-gnome-wayland-nested-33.png index 64377df..bd90da1 100644 Binary files a/tests/references/wisps-close-gnome-wayland-nested-33.png and b/tests/references/wisps-close-gnome-wayland-nested-33.png differ diff --git a/tests/references/wisps-close-gnome-wayland-nested-34.png b/tests/references/wisps-close-gnome-wayland-nested-34.png index e608427..44a4ced 100644 Binary files a/tests/references/wisps-close-gnome-wayland-nested-34.png and b/tests/references/wisps-close-gnome-wayland-nested-34.png differ diff --git a/tests/references/wisps-close-gnome-wayland-nested-35.png b/tests/references/wisps-close-gnome-wayland-nested-35.png index e608427..44a4ced 100644 Binary files a/tests/references/wisps-close-gnome-wayland-nested-35.png and b/tests/references/wisps-close-gnome-wayland-nested-35.png differ diff --git a/tests/references/wisps-close-gnome-wayland-nested-36.png b/tests/references/wisps-close-gnome-wayland-nested-36.png index 66d39bb..4b0ca89 100644 Binary files a/tests/references/wisps-close-gnome-wayland-nested-36.png and b/tests/references/wisps-close-gnome-wayland-nested-36.png differ diff --git a/tests/references/wisps-close-gnome-xsession-32.png b/tests/references/wisps-close-gnome-xsession-32.png index b752f62..e6a326e 100644 Binary files a/tests/references/wisps-close-gnome-xsession-32.png and b/tests/references/wisps-close-gnome-xsession-32.png differ diff --git a/tests/references/wisps-close-gnome-xsession-33.png b/tests/references/wisps-close-gnome-xsession-33.png index 64377df..bd90da1 100644 Binary files a/tests/references/wisps-close-gnome-xsession-33.png and b/tests/references/wisps-close-gnome-xsession-33.png differ diff --git a/tests/references/wisps-close-gnome-xsession-34.png b/tests/references/wisps-close-gnome-xsession-34.png index 787aa72..72239f1 100644 Binary files a/tests/references/wisps-close-gnome-xsession-34.png and b/tests/references/wisps-close-gnome-xsession-34.png differ diff --git a/tests/references/wisps-close-gnome-xsession-35.png b/tests/references/wisps-close-gnome-xsession-35.png index 787aa72..72239f1 100644 Binary files a/tests/references/wisps-close-gnome-xsession-35.png and b/tests/references/wisps-close-gnome-xsession-35.png differ diff --git a/tests/references/wisps-close-gnome-xsession-36.png b/tests/references/wisps-close-gnome-xsession-36.png index 5972449..023342c 100644 Binary files a/tests/references/wisps-close-gnome-xsession-36.png and b/tests/references/wisps-close-gnome-xsession-36.png differ diff --git a/tests/references/wisps-open-gnome-wayland-nested-32.png b/tests/references/wisps-open-gnome-wayland-nested-32.png index ff231bd..ae913c3 100644 Binary files a/tests/references/wisps-open-gnome-wayland-nested-32.png and b/tests/references/wisps-open-gnome-wayland-nested-32.png differ diff --git a/tests/references/wisps-open-gnome-wayland-nested-33.png b/tests/references/wisps-open-gnome-wayland-nested-33.png index 3ed4888..f779c77 100644 Binary files a/tests/references/wisps-open-gnome-wayland-nested-33.png and b/tests/references/wisps-open-gnome-wayland-nested-33.png differ diff --git a/tests/references/wisps-open-gnome-wayland-nested-34.png b/tests/references/wisps-open-gnome-wayland-nested-34.png index 39b55f1..13bf613 100644 Binary files a/tests/references/wisps-open-gnome-wayland-nested-34.png and b/tests/references/wisps-open-gnome-wayland-nested-34.png differ diff --git a/tests/references/wisps-open-gnome-wayland-nested-35.png b/tests/references/wisps-open-gnome-wayland-nested-35.png index 39b55f1..13bf613 100644 Binary files a/tests/references/wisps-open-gnome-wayland-nested-35.png and b/tests/references/wisps-open-gnome-wayland-nested-35.png differ diff --git a/tests/references/wisps-open-gnome-wayland-nested-36.png b/tests/references/wisps-open-gnome-wayland-nested-36.png index 25c36a5..be29e64 100644 Binary files a/tests/references/wisps-open-gnome-wayland-nested-36.png and b/tests/references/wisps-open-gnome-wayland-nested-36.png differ diff --git a/tests/references/wisps-open-gnome-xsession-32.png b/tests/references/wisps-open-gnome-xsession-32.png index ff231bd..ae913c3 100644 Binary files a/tests/references/wisps-open-gnome-xsession-32.png and b/tests/references/wisps-open-gnome-xsession-32.png differ diff --git a/tests/references/wisps-open-gnome-xsession-33.png b/tests/references/wisps-open-gnome-xsession-33.png index 3ed4888..f779c77 100644 Binary files a/tests/references/wisps-open-gnome-xsession-33.png and b/tests/references/wisps-open-gnome-xsession-33.png differ diff --git a/tests/references/wisps-open-gnome-xsession-34.png b/tests/references/wisps-open-gnome-xsession-34.png index 8a79bd0..abb064e 100644 Binary files a/tests/references/wisps-open-gnome-xsession-34.png and b/tests/references/wisps-open-gnome-xsession-34.png differ diff --git a/tests/references/wisps-open-gnome-xsession-35.png b/tests/references/wisps-open-gnome-xsession-35.png index 8a79bd0..abb064e 100644 Binary files a/tests/references/wisps-open-gnome-xsession-35.png and b/tests/references/wisps-open-gnome-xsession-35.png differ diff --git a/tests/references/wisps-open-gnome-xsession-36.png b/tests/references/wisps-open-gnome-xsession-36.png index d253dae..39e56ea 100644 Binary files a/tests/references/wisps-open-gnome-xsession-36.png and b/tests/references/wisps-open-gnome-xsession-36.png differ diff --git a/tests/run-test.sh b/tests/run-test.sh index 09f05ed..bca773f 100755 --- a/tests/run-test.sh +++ b/tests/run-test.sh @@ -141,14 +141,12 @@ test_effect() { set_setting "open-preview-effect" "${1}" set_setting "close-preview-effect" "${1}" - sleep 2 do_in_pod gnome-extensions prefs "${EXTENSION}" - sleep 2 + sleep 3 compare_with_target "tests/references/${1}-open-${SESSION}-${FEDORA_VERSION}.png" "Failed to test ${1} window open effect!" send_keystroke "Alt+F4" - sleep 2 + sleep 3 compare_with_target "tests/references/${1}-close-${SESSION}-${FEDORA_VERSION}.png" "Failed to test ${1} window close effect!" - sleep 2 } # ----------------------------------------------------- wait for the container to start up