diff --git a/extension.js b/extension.js index 54e9c08..bef19c7 100644 --- a/extension.js +++ b/extension.js @@ -64,6 +64,7 @@ class Extension { new Me.imports.src.effects.Glitch.Glitch(), new Me.imports.src.effects.Hexagon.Hexagon(), new Me.imports.src.effects.Incinerate.Incinerate(), + new Me.imports.src.effects.MagicLamp.MagicLamp(), new Me.imports.src.effects.Matrix.Matrix(), new Me.imports.src.effects.Pixelate.Pixelate(), new Me.imports.src.effects.PixelWheel.PixelWheel(), diff --git a/prefs.js b/prefs.js index d992ae8..0fb1c48 100644 --- a/prefs.js +++ b/prefs.js @@ -55,6 +55,7 @@ var PreferencesDialog = class PreferencesDialog { new Me.imports.src.effects.Glitch.Glitch(), new Me.imports.src.effects.Hexagon.Hexagon(), new Me.imports.src.effects.Incinerate.Incinerate(), + new Me.imports.src.effects.MagicLamp.MagicLamp(), new Me.imports.src.effects.Matrix.Matrix(), new Me.imports.src.effects.Pixelate.Pixelate(), new Me.imports.src.effects.PixelWheel.PixelWheel(), diff --git a/resources/shaders/magic-lamp.frag b/resources/shaders/magic-lamp.frag new file mode 100644 index 0000000..5bef87c --- /dev/null +++ b/resources/shaders/magic-lamp.frag @@ -0,0 +1,44 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< // +// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ // +// |__/ // +////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: Simon Schneegans +// SPDX-License-Identifier: GPL-3.0-or-later + +// The content from common.glsl is automatically prepended to each shader effect. + +uniform vec2 uActorScale; +uniform vec2 uPointerPos; +uniform vec2 uControlPointA; +uniform vec2 uControlPointB; + +void main() { + float progress = uForOpening ? 1.0 - uProgress : uProgress; + + vec2 texcoord = iTexCoord.st; + texcoord = texcoord * uActorScale + 0.5 - uActorScale * 0.5; + + vec4 oColor = getInputColor(texcoord); + oColor.rgb *= vec3(1.0, 0.5, 0.5); + + if (length(texcoord - uPointerPos) < 0.02) { + oColor = vec4(1.0); + } + + if (length(texcoord - uControlPointA) < 0.02) { + oColor = vec4(1.0, 0.0, 0.0, 1.0); + } + + if (length(texcoord - uControlPointB) < 0.02) { + oColor = vec4(0.0, 1.0, 0.0, 1.0); + } + + setOutputColor(oColor); +} \ No newline at end of file diff --git a/resources/ui/adw/magic-lamp.ui b/resources/ui/adw/magic-lamp.ui new file mode 100644 index 0000000..d969c8c --- /dev/null +++ b/resources/ui/adw/magic-lamp.ui @@ -0,0 +1,47 @@ + + + + + + + + + + + Animation Time [ms] + + + center + 1 + 0 + left + 300 + + + 10000 + 100 + 1 + 100 + + + + + + + edit-clear-symbolic + center + Reset to Default Value + + + + + + + + + \ No newline at end of file diff --git a/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml b/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml index 866a721..1eced43 100644 --- a/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml +++ b/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml @@ -318,6 +318,22 @@ SPDX-License-Identifier: CC0-1.0 The amount of shifting. + + + + + + false + Magic Lamp Enable Effect + Use the magic lamp effect. + + + + 1500 + Magic Lamp Animation Time + The time the magic lamp effect takes. + + diff --git a/src/effects/MagicLamp.js b/src/effects/MagicLamp.js new file mode 100644 index 0000000..e1fa0e6 --- /dev/null +++ b/src/effects/MagicLamp.js @@ -0,0 +1,173 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< // +// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ // +// |__/ // +////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: Simon Schneegans +// SPDX-License-Identifier: GPL-3.0-or-later + +'use strict'; + +const {GObject, Graphene} = 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; + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +// 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 MagicLamp = class { + + // 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._uActorScale = shader.get_uniform_location('uActorScale'); + shader._uPointerPos = shader.get_uniform_location('uPointerPos'); + shader._uControlPointA = shader.get_uniform_location('uControlPointA'); + shader._uControlPointB = shader.get_uniform_location('uControlPointB'); + + // Write all uniform values at the start of each animation. + shader.connect('begin-animation', + (shader, settings, forOpening, testMode, actor) => { + shader.set_uniform_float(shader._uActorScale, 2, [ + 2.0 * Math.max(1.0, global.stage.width / actor.width), + 2.0 * Math.max(1.0, global.stage.height / actor.height) + ]); + + this._actor = actor; + }); + + shader.connect('update-animation', (shader) => { + const convertCoords = coords => { + const [ok, localX, localY] = + this._actor.transform_stage_point(coords[0], coords[1]); + + const scale = [ + 2.0 * Math.max(1.0, global.stage.width / this._actor.width), + 2.0 * Math.max(1.0, global.stage.height / this._actor.height) + ]; + + return [ + ok, + localX / this._actor.width * scale[0] + 0.5 - scale[0] * 0.5, + localY / this._actor.height * scale[1] + 0.5 - scale[1] * 0.5, + ]; + }; + const pointer = global.get_pointer(); + + { + const [ok, x, y] = convertCoords(pointer); + + if (ok) { + shader.set_uniform_float(shader._uPointerPos, 2, [x, y]); + } + } + + { + const center = this._actor.apply_transform_to_point(new Graphene.Point3D( + {x: this._actor.width / 2, y: this._actor.height / 2, z: 0})); + const controlPointA = + [pointer[0] * 0.33 + 0.66 * center.x, pointer[1] * 0.33 + 0.66 * center.y]; + + if (!this._uControlPointA) { + this._uControlPointA = controlPointA; + } else { + this._uControlPointA = [ + this._uControlPointA[0] * 0.95 + controlPointA[0] * 0.05, + this._uControlPointA[1] * 0.95 + controlPointA[1] * 0.05 + ]; + } + + const [ok, x, y] = convertCoords(this._uControlPointA); + + shader.set_uniform_float(shader._uControlPointA, 2, [x, y]); + } + + { + const center = this._actor.apply_transform_to_point(new Graphene.Point3D( + {x: this._actor.width / 2, y: this._actor.height / 2, z: 0})); + const controlPointB = + [pointer[0] * 0.66 + 0.33 * center.x, pointer[1] * 0.66 + 0.33 * center.y]; + + if (!this._uControlPointB) { + this._uControlPointB = controlPointB; + } else { + this._uControlPointB = [ + this._uControlPointB[0] * 0.9 + controlPointB[0] * 0.1, + this._uControlPointB[1] * 0.9 + controlPointB[1] * 0.1 + ]; + } + + const [ok, x, y] = convertCoords(this._uControlPointB); + + shader.set_uniform_float(shader._uControlPointB, 2, [x, y]); + } + }); + }); + } + + // ---------------------------------------------------------------------------- metadata + + // The effect is not available on GNOME Shell 3.36 as it requires scaling of the window + // actor. + getMinShellVersion() { + return [3, 38]; + } + + // 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. '*-enable-effect'), and its animation time + // (e.g. '*-animation-time'). + getNick() { + return 'magic-lamp'; + } + + // 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. + getLabel() { + return _('Magic Lamp'); + } + + // -------------------------------------------------------------------- API for prefs.js + + // This is called by the preferences dialog whenever a new effect profile is loaded. It + // binds all user interface elements to the respective settings keys of the profile. + bindPreferences(dialog) { + dialog.bindAdjustment('magic-lamp-animation-time'); + // dialog.bindAdjustment('doom-horizontal-scale'); + // dialog.bindAdjustment('doom-vertical-scale'); + // dialog.bindAdjustment('doom-pixel-size'); + } + + // ---------------------------------------------------------------- API for extension.js + + // 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+. + // For this effect, we scale the actor vertically so that it covers the entire screen. + // This ensures that the melted window will not be cut off. + getActorScale(settings, forOpening, actor) { + // return {x: 1.0, y: 1.0}; + return { + x: 2.0 * Math.max(1.0, global.stage.width / actor.width), + y: 2.0 * Math.max(1.0, global.stage.height / actor.height) + }; + } +}