diff --git a/extension.js b/extension.js
index b61f168..2076973 100644
--- a/extension.js
+++ b/extension.js
@@ -52,6 +52,7 @@ class Extension {
this._ALL_EFFECTS = [
new Me.imports.src.Apparition.Apparition(),
new Me.imports.src.BrokenGlass.BrokenGlass(),
+ new Me.imports.src.Burn.Burn(),
new Me.imports.src.EnergizeA.EnergizeA(),
new Me.imports.src.EnergizeB.EnergizeB(),
new Me.imports.src.Fire.Fire(),
diff --git a/prefs.js b/prefs.js
index bb45daa..f0dff01 100644
--- a/prefs.js
+++ b/prefs.js
@@ -49,6 +49,7 @@ var PreferencesDialog = class PreferencesDialog {
this._ALL_EFFECTS = [
new Me.imports.src.Apparition.Apparition(),
new Me.imports.src.BrokenGlass.BrokenGlass(),
+ new Me.imports.src.Burn.Burn(),
new Me.imports.src.EnergizeA.EnergizeA(),
new Me.imports.src.EnergizeB.EnergizeB(),
new Me.imports.src.Fire.Fire(),
diff --git a/resources/shaders/burn.glsl b/resources/shaders/burn.glsl
new file mode 100644
index 0000000..7287eb1
--- /dev/null
+++ b/resources/shaders/burn.glsl
@@ -0,0 +1,85 @@
+// Inject some common shader snippets. It is only possible to include glsl files from the
+// "common" directory. Also, the files in the "common" directory are not allowed to
+// include any further files.
+#include "common/uniforms.glsl"
+#include "common/noise.glsl"
+#include "common/easing.glsl"
+#include "common/compositing.glsl"
+#include "common/edgeMask.glsl"
+
+uniform vec2 uSeed;
+uniform vec3 uColor;
+uniform float uScale;
+uniform float uWidth;
+uniform float uTurbulence;
+
+vec4 getFireColor(float v) {
+ const float steps[3] = float[](0.0, 0.5, 1.0);
+ vec4 colors[3] = vec4[](vec4(1, 0, 0, 0), vec4(1, 0, 0, 0.5), vec4(1, 1, 0, 1));
+
+ if (v < steps[0]) {
+ return colors[0];
+ }
+
+ for (int i = 0; i < 2; ++i) {
+ if (v <= steps[i + 1]) {
+ return mix(colors[i], colors[i + 1],
+ vec4(v - steps[i]) / (steps[i + 1] - steps[i]));
+ }
+ }
+
+ return colors[2];
+}
+
+void main() {
+ float progress = uProgress / 0.8;
+
+ progress = uForOpening ? 1.0 - progress : progress;
+
+ vec2 uv = 0.01 * cogl_tex_coord_in[0].st * uSize / uScale;
+
+ float lfNoise = simplex2D(cogl_tex_coord_in[0].st + uSeed);
+ lfNoise = mix(lfNoise - 1.0, lfNoise + 1.0, progress);
+
+ float hfNoise = simplex2DFractal(uv + uSeed);
+ hfNoise = mix(hfNoise - 1.0, hfNoise + 1.0, progress);
+
+ float noise = mix(lfNoise, hfNoise, uTurbulence);
+ float fire = noise / (0.03 * uWidth) - progress / (0.03 * uWidth) + progress;
+
+ vec4 windowColor = texture2D(uTexture, cogl_tex_coord_in[0].st);
+
+ // Shell.GLSLEffect uses straight alpha. So we have to convert from premultiplied.
+ if (windowColor.a > 0) {
+ windowColor.rgb /= windowColor.a;
+ }
+
+ cogl_color_out = windowColor;
+
+ if (fire >= 1) {
+ cogl_color_out.a = 0;
+ } else if (fire > 0) {
+ vec4 fire = getFireColor(fire);
+ fire.a *= cogl_color_out.a;
+ cogl_color_out = alphaOver(cogl_color_out, fire);
+ }
+
+ if ((uForOpening && fire < 0) || (!uForOpening && fire > 1)) {
+ float smoke = (uForOpening ? -fire : fire - 1) * uWidth;
+
+ vec2 uv = 0.01 * cogl_tex_coord_in[0].st * uSize / uScale;
+ vec2 smokeShift = vec2(0, smoke * 0.1);
+ smoke = clamp(smoke, 0, 1) * simplex2DFractal(uv + uSeed + smokeShift);
+
+ smoke *= getRelativeEdgeMask(0.3);
+
+ float fadeOutProgress = clamp((uProgress - 0.7) / 0.3, 0, 1);
+ smoke *= 1 - fadeOutProgress;
+
+ cogl_color_out = alphaOver(cogl_color_out, vec4(vec3(0.2), smoke));
+ }
+
+ // if (cogl_tex_coord_in[0].s > 0.5) {
+ // cogl_color_out = vec4(vec3(noise), 1.0);
+ // }
+}
\ No newline at end of file
diff --git a/resources/ui/gtk4/Burn.ui b/resources/ui/gtk4/Burn.ui
new file mode 100644
index 0000000..1dab008
--- /dev/null
+++ b/resources/ui/gtk4/Burn.ui
@@ -0,0 +1,243 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
index f852dad..7df24ef 100644
--- a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
+++ b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
@@ -325,6 +325,52 @@
Use additive blending for the line and the glow.
+
+
+
+
+
+ false
+ Burn Close Effect
+ Use the Burn effect for window closing.
+
+
+
+ false
+ Burn Open Effect
+ Use the Burn effect for window opening.
+
+
+
+ 2000
+ Burn Animation Time
+ The time the Burn effect takes.
+
+
+
+ 1.0
+ Burn Scale
+ The scale of the effect.
+
+
+
+ 1.0
+ Burn Width
+ The width of the effect.
+
+
+
+ 0.15
+ Burn Turbulence
+ The turbulence of the effect.
+
+
+
+ "rgb(255, 200, 50)"
+ Burn Color
+ The color of the effect.
+
+
diff --git a/src/Burn.js b/src/Burn.js
new file mode 100644
index 0000000..2b44bcf
--- /dev/null
+++ b/src/Burn.js
@@ -0,0 +1,120 @@
+//////////////////////////////////////////////////////////////////////////////////////////
+// ) ( //
+// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
+// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
+// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
+// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
+// | '_ \ || | '_| ' \)) | ' \()| || | \ 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 _ = 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 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 Burn = 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) => {
+ // 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._uWidth = shader.get_uniform_location('uWidth');
+ shader._uScale = shader.get_uniform_location('uScale');
+ shader._uTurbulence = shader.get_uniform_location('uTurbulence');
+
+ // 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('burn-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._uWidth, 1, [settings.get_double('burn-width')]);
+ shader.set_uniform_float(shader._uScale, 1, [settings.get_double('burn-scale')]);
+ shader.set_uniform_float(shader._uTurbulence, 1, [settings.get_double('burn-turbulence')]);
+ // clang-format on
+ });
+ });
+ }
+
+ // ---------------------------------------------------------------------------- metadata
+
+ // The effect is available on all GNOME Shell versions supported by this extension.
+ 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. '*-close-effect'), and its animation time
+ // (e.g. '*-animation-time').
+ getNick() {
+ return 'burn';
+ }
+
+ // 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 _('Burn');
+ }
+
+ // -------------------------------------------------------------------- API for prefs.js
+
+ // This is called by the preferences dialog. It loads the settings page for this effect,
+ // and binds all properties to the settings.
+ getPreferences(dialog) {
+
+ // Add the settings page to the builder.
+ dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Burn.ui`);
+
+ // Bind all properties.
+ dialog.bindAdjustment('burn-animation-time');
+ dialog.bindAdjustment('burn-width');
+ dialog.bindAdjustment('burn-scale');
+ dialog.bindAdjustment('burn-turbulence');
+ dialog.bindColorButton('burn-color');
+
+ // Finally, return the new settings page.
+ return dialog.getBuilder().get_object('burn-prefs');
+ }
+
+ // ---------------------------------------------------------------- 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+.
+ getActorScale(settings) {
+ return {x: 1.0, y: 1.0};
+ }
+}
\ No newline at end of file