From 2e5a7d93476c0819b5a23f5740e4c843eaedc73a Mon Sep 17 00:00:00 2001 From: Nippy Date: Mon, 1 Jun 2026 18:57:29 +0200 Subject: Raveos gnome update --- .../src/Shader.js | 231 +++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/src/Shader.js (limited to 'raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/src/Shader.js') diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/src/Shader.js b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/src/Shader.js new file mode 100644 index 0000000..b7a6377 --- /dev/null +++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/src/Shader.js @@ -0,0 +1,231 @@ +////////////////////////////////////////////////////////////////////////////////////////// +// ) ( // +// ( /( ( ( ) ( ( ( ( )\ ) ( ( // +// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( // +// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ // +// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) // +// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< // +// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ // +// |__/ // +////////////////////////////////////////////////////////////////////////////////////////// + +// SPDX-FileCopyrightText: Simon Schneegans +// SPDX-License-Identifier: GPL-3.0-or-later + +'use strict'; + +import Gio from 'gi://Gio'; +import Shell from 'gi://Shell'; +import Cogl from 'gi://Cogl'; +import GObject from 'gi://GObject'; +import Clutter from 'gi://Clutter'; +import Meta from 'gi://Meta'; + +import * as utils from './utils.js'; + +////////////////////////////////////////////////////////////////////////////////////////// +// 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 means 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 three 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. // +// * end-animation: This is called when the animation is stopped. This can be // +// used to clean up any resources. +////////////////////////////////////////////////////////////////////////////////////////// + +export var Shader = GObject.registerClass({ + Signals: { + 'begin-animation': { + param_types: [ + Gio.Settings.$gtype, GObject.TYPE_BOOLEAN, GObject.TYPE_BOOLEAN, + Clutter.Actor.$gtype + ] + }, + 'update-animation': {param_types: [GObject.TYPE_DOUBLE]}, + 'end-animation': {} + } +}, + + 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._uIsFullscreen = this.get_uniform_location('uIsFullscreen'); + this._uProgress = this.get_uniform_location('uProgress'); + this._uDuration = this.get_uniform_location('uDuration'); + this._uSize = this.get_uniform_location('uSize'); + this._uPadding = this.get_uniform_location('uPadding'); + + // Create a timeline to drive the animation. + this._timeline = new Clutter.Timeline(); + + // Call updateAnimation() once a frame. + this._timeline.connect('new-frame', (t) => { + if (this._testMode) { + this.updateAnimation(0.5); + } else { + this.updateAnimation(t.get_progress()); + } + }); + + // Clean up if the animation finished or was interrupted. + this._timeline.connect('stopped', (t, finished) => { + this.endAnimation(); + }); + } + + // This is called once each time the shader is used. + beginAnimation(settings, forOpening, testMode, duration, actor) { + if (this._timeline.is_playing()) { + this._timeline.stop(); + } + + // On GNOME 3.36 this method was not yet available. + if (this._timeline.set_actor) { + this._timeline.set_actor(actor); + } + + this._timeline.set_duration(duration); + this._timeline.start(); + + // Make sure that no fullscreen window is drawn over our animations. Since GNOME 48 + // this is a "global" method. + if (Meta.disable_unredirect_for_display) { + Meta.disable_unredirect_for_display(global.display); + } else { + global.compositor.disable_unredirect(); + } + + global.begin_work(); + + // Reset progress value. + this._progress = 0; + this._testMode = testMode; + + // This is not necessarily symmetric, but I haven't figured out a way to + // get the actual values... + const padding = (actor.width - actor.meta_window.get_frame_rect().width) / 2; + let isFullscreen = actor.meta_window.fullscreen; + + // is_maximized has been added in GNOME 49. + if (actor.meta_window.is_maximized) { + isFullscreen |= actor.meta_window.is_maximized(); + } else { + isFullscreen |= actor.meta_window.get_maximized() === Meta.MaximizeFlags.BOTH; + } + + this.set_uniform_float(this._uPadding, 1, [padding]); + this.set_uniform_float(this._uForOpening, 1, [forOpening]); + this.set_uniform_float(this._uIsFullscreen, 1, [isFullscreen]); + this.set_uniform_float(this._uDuration, 1, [duration * 0.001]); + this.set_uniform_float(this._uSize, 2, [actor.width, actor.height]); + + this.emit('begin-animation', settings, forOpening, testMode, actor); + } + + // This is called at each frame during the animation. + updateAnimation(progress) { + // Store the current progress value. The corresponding signal is emitted each frame + // in vfunc_paint_target. We do not emit it here, as the pipeline which may be used + // by handlers must not have been created yet. + this._progress = progress; + + this.queue_repaint(); + } + + // This will stop any running animation and emit the end-animation signal. + endAnimation() { + // This will call endAnimation() again, so we can return for now. + if (this._timeline.is_playing()) { + this._timeline.stop(); + return; + } + + // Restore unredirecting behavior for fullscreen windows. Since GNOME 48 this is a + // "global" method. + if (Meta.disable_unredirect_for_display) { + Meta.enable_unredirect_for_display(global.display); + } else { + global.compositor.enable_unredirect(); + } + global.end_work(); + + this.emit('end-animation'); + } + + // 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._loadShaderResource(`/shaders/${this._nick}.frag`); + + // 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( + Cogl.SnippetHook ? Cogl.SnippetHook.FRAGMENT : 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); + + // Starting with GNOME 44.2, the alpha channel is not written to by default. We need + // to undo this. It is a pity that we have to do this here, as it is not really + // required to be done each frame. But it's the only place where we can do it. + // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2650 + this.get_pipeline().set_blend( + 'RGBA = ADD (SRC_COLOR * (SRC_COLOR[A]), DST_COLOR * (1-SRC_COLOR[A]))'); + + this.set_uniform_float(this._uProgress, 1, [this._progress]); + super.vfunc_paint_target(...params); + } + + // --------------------------------------------------------------------- private stuff + + // This loads a GLSL file from the extension's resources to a JavaScript string. The + // code from "common.glsl" is prepended automatically. + _loadShaderResource(path) { + let common = utils.getStringResource('/shaders/common.glsl'); + let code = utils.getStringResource(path); + + // Add a trailing newline. Else the GLSL compiler complains... + return common + '\n' + code + '\n'; + } +}); -- cgit v1.3