diff --git a/extension.js b/extension.js index 47f09e9..4396104 100644 --- a/extension.js +++ b/extension.js @@ -85,20 +85,26 @@ class Extension { // the WorkspacesView. const extensionThis = this; - // Do not attempt to close windows twice. Due to the animation in the overview, the - // close button can be clicked twice which normally would lead to a crash. + // This class is only available in GNOME Shell 3.38+. if (WindowPreview) { this._origDeleteAll = WindowPreview.prototype._deleteAll; + // The _deleteAll is called when the user clicks the X in the overview. WindowPreview.prototype._deleteAll = function() { + // Do not attempt to close windows twice. Due to the animation in the overview, + // the close button can be clicked twice which normally would lead to a crash. if (!this._closeRequested) { + // When the user clicks the X in the overview, the window is not deleted + // immediately. However, as soon as the window is really deleted, we need to + // adjust the transition of its clone. this.metaWindow.connect('unmanaged', () => { const transitionConfig = extensionThis._effect.getCloseTransition( this.window_container, extensionThis._settings); extensionThis._tweakTransitions(this.window_container, transitionConfig); }); + // Call the original method. extensionThis._origDeleteAll.apply(this); // This is required, else WindowPreview's _restack() which is called by the diff --git a/src/BrokenGlass.js b/src/BrokenGlass.js index 8a7a61d..48b6c22 100644 --- a/src/BrokenGlass.js +++ b/src/BrokenGlass.js @@ -20,9 +20,10 @@ const Me = imports.misc.extensionUtils.getCurrentExtension(); const utils = Me.imports.src.utils; ////////////////////////////////////////////////////////////////////////////////////////// -// T... // -// 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 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. // ////////////////////////////////////////////////////////////////////////////////////////// // The shader class for this effect is registered further down in this file. @@ -88,7 +89,7 @@ var BrokenGlass = class BrokenGlass { // This is also called from extension.js. It is used to tweak the ongoing transition of // the actor - usually windows are faded to transparency and scaled down slightly by // GNOME Shell. For this effect, windows are set to twice their original size, so that - // we have some space to draw the dust. We also set the animation mode to "Linear". + // we have some space to draw the shards. We also set the animation mode to "Linear". static getCloseTransition(actor, settings) { return { 'opacity': {to: 255, mode: 1}, @@ -110,21 +111,32 @@ if (utils.isInShellProcess()) { const {Clutter, GdkPixbuf, Cogl} = imports.gi; const shaderSnippets = Me.imports.src.shaderSnippets; + // 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. Shader = GObject.registerClass({}, class Shader extends Clutter.ShaderEffect { _init(actor, settings) { super._init({shader_type: Clutter.ShaderType.FRAGMENT_SHADER}); - // Load the claw texture. As the shader is re-created for each window animation, + // Load the shards texture. As the shader is re-created for each window animation, // this texture is also re-created each time. This could be improved in the future! - const dustData = GdkPixbuf.Pixbuf.new_from_resource('/img/shards.png'); - this._dustTexture = new Clutter.Image(); - this._dustTexture.set_data( - dustData.get_pixels(), Cogl.PixelFormat.RGB_888, dustData.width, - dustData.height, dustData.rowstride); + 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); + // 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 (settings.get_boolean('broken-glass-use-pointer')) { const [x, y] = global.get_pointer(); const [ok, localX, localY] = actor.transform_stage_point(x, y); @@ -137,48 +149,61 @@ if (utils.isInShellProcess()) { // Inject some common shader snippets. ${shaderSnippets.standardUniforms()} - uniform sampler2D uDustTexture; + uniform sampler2D uShardTexture; const vec2 SEED = vec2(${Math.random()}, ${Math.random()}); const float SHARD_SCALE = ${settings.get_double('broken-glass-scale')}; const float SHARD_LAYERS = 5; const float GRAVITY = ${settings.get_double('broken-glass-gravity')}; const float BLOW_FORCE = ${settings.get_double('broken-glass-blow-force')}; - const float CANVAS_SCALE = 2.0; + const float ACTOR_SCALE = 2.0; + const float PADDING = ACTOR_SCALE / 2.0 - 0.5; + const vec2 EPICENTER = vec2(${epicenterX}, ${epicenterY}); void main() { cogl_color_out = vec4(0, 0, 0, 0); + // Draw the individual shard layers. for (float i=0; i 0 ? 1: 0; + + // Dissolve the shard by using distance information from the red channel. + float dissolve = (shardMap.x - pow(uProgress+0.1, 2)) > 0 ? 1: 0; cogl_color_out = mix(cogl_color_out, windowColor, dissolve); } } @@ -186,17 +211,22 @@ if (utils.isInShellProcess()) { `); }; - // This is overridden to bind the dust texture for drawing. Sadly, this seems to be + // 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); - pipeline.set_layer_texture(1, this._dustTexture.get_texture()); + + // Bind the shard texture. + pipeline.set_layer_texture(1, this._shardTexture.get_texture()); pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT); - this.set_uniform_value('uDustTexture', 1); + this.set_uniform_value('uShardTexture', 1); + super.vfunc_paint_target(node, paint_context); } });