From c92a2d4fb720f91a850856892d29b7e498ca7931 Mon Sep 17 00:00:00 2001 From: Simon Schneegans Date: Mon, 19 Dec 2022 20:15:23 +0100 Subject: [PATCH] :tada: Add initial portal effect --- extension.js | 1 + prefs.js | 1 + resources/shaders/portal.frag | 121 ++++++++++++ resources/ui/adw/Portal.ui | 73 +++++++ resources/ui/gtk3/Portal.ui | 184 ++++++++++++++++++ resources/ui/gtk4/Portal.ui | 114 +++++++++++ ...ell.extensions.burn-my-windows.gschema.xml | 37 +++- src/Portal.js | 106 ++++++++++ 8 files changed, 634 insertions(+), 3 deletions(-) create mode 100644 resources/shaders/portal.frag create mode 100644 resources/ui/adw/Portal.ui create mode 100644 resources/ui/gtk3/Portal.ui create mode 100644 resources/ui/gtk4/Portal.ui create mode 100644 src/Portal.js diff --git a/extension.js b/extension.js index 2f33920..c8c0b5d 100644 --- a/extension.js +++ b/extension.js @@ -64,6 +64,7 @@ class Extension { new Me.imports.src.Pixelate.Pixelate(), new Me.imports.src.PixelWheel.PixelWheel(), new Me.imports.src.PixelWipe.PixelWipe(), + new Me.imports.src.Portal.Portal(), new Me.imports.src.SnapOfDisintegration.SnapOfDisintegration(), new Me.imports.src.TRexAttack.TRexAttack(), new Me.imports.src.TVEffect.TVEffect(), diff --git a/prefs.js b/prefs.js index 7f8c9cf..0cf131a 100644 --- a/prefs.js +++ b/prefs.js @@ -60,6 +60,7 @@ var PreferencesDialog = class PreferencesDialog { new Me.imports.src.Pixelate.Pixelate(), new Me.imports.src.PixelWheel.PixelWheel(), new Me.imports.src.PixelWipe.PixelWipe(), + new Me.imports.src.Portal.Portal(), new Me.imports.src.SnapOfDisintegration.SnapOfDisintegration(), new Me.imports.src.TRexAttack.TRexAttack(), new Me.imports.src.TVEffect.TVEffect(), diff --git a/resources/shaders/portal.frag b/resources/shaders/portal.frag new file mode 100644 index 0000000..981ee78 --- /dev/null +++ b/resources/shaders/portal.frag @@ -0,0 +1,121 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ 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 vec3 uColor; + +const float PORTAL_OPEN_TIME = 0.3; +const float PORTAL_CLOSE_TIME = 0.4; +const float PORTAL_ROTATION_SPEED = 0.5; +const float PORTAL_WARPING = 2.0; + +const float WINDOW_ANIMATION_TIME = 0.4; +const float WINDOW_SCALE = 0.3; +const float WINDOW_SQUISH = 1.0; +const float WINDOW_TILT = -1.0; +const float WINDOW_SHIFT = 0.0; + +const float GLOW_EDGE_WIDTH = 0.02; + +vec4 getPortalColor() { + + // We simply inverse the progress for opening windows. + float progress = uForOpening ? 1.0 - uProgress : uProgress; + + // Put coordinate origin to the center of the window and make ensure a 1:1 aspect ratio. + vec2 coords = (iTexCoord.st - vec2(0.5)) * 2.0 * uSize / vec2(min(uSize.x, uSize.y)); + + // Add some margin for the elastic overshooting. + coords *= 1.5; + + float scale = 1.0; + if (uProgress < PORTAL_OPEN_TIME) { + scale = easeOutBack(uProgress / PORTAL_OPEN_TIME); + } else if (uProgress > 1.0 - PORTAL_CLOSE_TIME) { + scale = easeOutBack(1.0 - (uProgress - 1.0 + PORTAL_CLOSE_TIME) / PORTAL_CLOSE_TIME); + } + + coords /= max(scale, 0.01); + + // Apply some whirling. + // The math for the whirling is inspired by this post: + float rotation = PORTAL_ROTATION_SPEED * uProgress; + float warping = PORTAL_WARPING * (1.0 + 0.5 * uProgress); + coords = whirl(coords, warping, rotation); + + float xDisplace = simplex2D(coords * 2.0) - 0.5; + float yDisplace = simplex2D(coords * 2.0 + vec2(12.0, 123.0)) - 0.5; + coords += vec2(xDisplace, yDisplace) * 0.1; + + float dist = length(coords); + vec3 backgroundColor = mix(0.2 * uColor, 0.8 * uColor, clamp(pow(dist, 5.0), 0.0, 1.0)); + float alpha = dist > 1.0 ? 0.0 : 1.0; + vec4 color = vec4(backgroundColor, alpha); + + float glowingEdge = + pow(clamp((1.0 - abs(dist - 1.0)) + GLOW_EDGE_WIDTH, 0.0, 1.0), 100.0); + color = alphaOver(color, vec4(vec3(1.0), glowingEdge)); + + float bands = simplex2D(coords * 4.0) > 0.7 ? alpha : 0.0; + color = alphaOver(color, vec4(uColor, bands)); + + float stars = pow((simplex3D(vec3(coords * 5.0, uProgress * uDuration))) + 0.2, 50.0); + color.rgb += vec3(stars); + + color.a *= pow(clamp(scale, 0.0, 1.0), 2.0); + + return clamp(color, 0.0, 1.0); +} + +vec4 getWindowColor() { + float progress = (uForOpening ? (1.0 - uProgress) : uProgress) / WINDOW_ANIMATION_TIME; + + progress = easeInBack(clamp(progress, 0.0, 1.0)); + + // Put texture coordinate origin to center of window. + vec2 coords = iTexCoord.st * 2.0 - 1.0; + + // Scale image texture with progress. + coords /= mix(1.0, WINDOW_SCALE, progress); + + // Squish image texture vertically. + coords.y /= mix(1.0, (1.0 - 0.2 * WINDOW_SQUISH), progress); + + // 'Tilt' image texture around x-axis. + coords.x /= mix(1.0, 1.0 - 0.1 * WINDOW_TILT * coords.y, progress); + + // Move image texture vertically. + coords.y += WINDOW_SHIFT * progress; + + // Move texture coordinate center to corner again. + coords = coords * 0.5 + 0.5; + + vec4 oColor = getInputColor(coords); + + // Dissolve window. + oColor.a *= clamp((1.0 - progress) * 3.0, 0.0, 1.0); + + return oColor; +} + +void main() { + vec4 portal = getPortalColor(); + vec4 window = getWindowColor(); + + setOutputColor(alphaOver(portal, window)); + + // setOutputColor(portal); + // setOutputColor(window); +} \ No newline at end of file diff --git a/resources/ui/adw/Portal.ui b/resources/ui/adw/Portal.ui new file mode 100644 index 0000000..2fcb82f --- /dev/null +++ b/resources/ui/adw/Portal.ui @@ -0,0 +1,73 @@ + + + + + + + + + + + Animation Time [ms] + + + center + 1 + 0 + left + 300 + + + 5000 + 100 + 1 + 100 + + + + + + + edit-clear-symbolic + center + Reset to Default Value + + + + + + + + + Color + portal-color + + + center + 250 + 0 + 1 + + + + + edit-clear-symbolic + center + Reset to Default Value + + + + + + + + + \ No newline at end of file diff --git a/resources/ui/gtk3/Portal.ui b/resources/ui/gtk3/Portal.ui new file mode 100644 index 0000000..65cf67c --- /dev/null +++ b/resources/ui/gtk3/Portal.ui @@ -0,0 +1,184 @@ + + + + + + + + 5000 + 100 + 1 + 100 + + + + vertical + + + + + + none + + + + + 10 + 10 + 10 + 10 + 0 + + + 8 + + + Animation Time [ms] + 0 + start + center + 1 + + + + + end + center + 1 + 0 + left + 300 + + portal-animation-time + + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + 10 + 10 + 10 + 10 + 0 + + + 8 + + + Scale + 0 + start + center + 1 + + + + + end + center + 1 + 2 + left + 300 + portal-scale + + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + 10 + 10 + 10 + 10 + 0 + + + + + Color + 0 + start + center + 1 + + + + + 0 + 1 + + + + + + + + edit-clear-symbolic + 1 + + + Reset to Default Value + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/ui/gtk4/Portal.ui b/resources/ui/gtk4/Portal.ui new file mode 100644 index 0000000..2b1e540 --- /dev/null +++ b/resources/ui/gtk4/Portal.ui @@ -0,0 +1,114 @@ + + + + + + + + vertical + + + + + + none + 1 + + + + + 0 + + + + + Animation Time [ms] + 0 + start + center + 1 + + + + + end + center + 1 + 0 + left + 300 + + + 5000 + 100 + 1 + 100 + + + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + 0 + + + + + Color + 0 + start + center + 1 + + + + + 0 + 1 + + + + + edit-clear-symbolic + Reset to Default Value + + + + + + + + + + + + + + + + + \ 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 6af42eb..5b1097d 100644 --- a/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml +++ b/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml @@ -2,12 +2,14 @@ - + @@ -40,7 +42,8 @@ SPDX-License-Identifier: CC0-1.0 false Test Mode - If set to true, all animations will show only one still frame to allow for continuous integration tests. + If set to true, all animations will show only one still frame to allow for + continuous integration tests. @@ -715,6 +718,34 @@ SPDX-License-Identifier: CC0-1.0 Use the pointer location for the broken glass effect. + + + + + + false + Portal Close Effect + Use the Portal effect for window closing. + + + + false + Portal Open Effect + Use the Portal effect for window opening. + + + + 1000 + Portal Animation Time + The time the Portal effect takes. + + + + "rgb(155, 255, 50)" + Portal Color + The color of the effect. + + diff --git a/src/Portal.js b/src/Portal.js new file mode 100644 index 0000000..b9476ee --- /dev/null +++ b/src/Portal.js @@ -0,0 +1,106 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< // +// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ // +// |__/ // +////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: Simon Schneegans +// SPDX-License-Identifier: GPL-3.0-or-later + +'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; + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +// 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 Portal = 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._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('portal-color'))[1]; + + // clang-format off + shader.set_uniform_float(shader._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]); + // 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 'portal'; + } + + // 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 _('Portal'); + } + + // -------------------------------------------------------------------- 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.getUIDir()}/Portal.ui`); + + // Bind all properties. + dialog.bindAdjustment('portal-animation-time'); + dialog.bindColorButton('portal-color'); + + // Finally, return the new settings page. + return dialog.getBuilder().get_object('portal-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}; + } +}