Skip to content

Fix radial shader compatibility with GNOME Shell 51 - #318

Closed
gonwe wants to merge 1 commit into
dsheeler:mainfrom
gonwe:gnome-shell-51-support
Closed

gonwe wants to merge 1 commit into
dsheeler:mainfrom
gonwe:gnome-shell-51-support

Conversation

@gonwe

@gonwe gonwe commented Oct 2, 2026

Copy link
Copy Markdown

GNOME Shell 51 no longer provides the Shell.GLSLEffect interfaces used by the radial effect. This ports the effect to Clutter.ShaderEffect, supplies its fragment shader through a Cogl.Snippet, updates uniforms with set_uniform_value(), and adds 51 to the supported shell versions in metadata.

@LavaTiger99

Copy link
Copy Markdown
Contributor

Adding test evidence and an additional GNOME 51 shader compatibility finding. This concerns the failure reported in #317.

Environment: CachyOS, GNOME Shell 51.0, Mutter 51.0, Wayland, after upgrading from GNOME 50.5. The runtime checks below used a separately launched headless GNOME 51 session with isolated settings.

An equivalent local radial shader port fixed the import failure:

TypeError: class heritage (intermediate value).GLSLEffect is not an object or null

The optional src/effects/color_effect.js and src/effects/glitch_effect.js also call set_shader_source(), which is unavailable in GNOME 51's Clutter.ShaderEffect. The file list of this PR currently includes only the radial shader and metadata, so these two effects need a port as well.

The attached patch ports all three effects: static Cogl.Snippet construction via Cogl.Snippet.new(), vfunc_get_static_snippet(), and named radial uniforms. For color/glitch, it separates the existing GLSL declarations from the main() body and supplies the body as snippet replacement code.

Checks passed in the isolated session:

  • Extension enabled.
  • Background shading was made visible.
  • Color and glitch effects were instantiated on visible actors without shader errors.
  • With two temporary GTK windows, the switcher opened, moved next/previous, and activated the selected window without extension errors.

This validates our local patch, not this PR's exact branch. Multiple monitors, physical touchpad navigation, every animation/setting, and older GNOME versions were not tested. No source build/lint run is claimed.

The affected source files matched upstream commit aee3b83769dc870c52ba123c673ad07bd404a3c8. The reference patch below intentionally declares only 51; it is suitable for a 51-specific release or adaptation by the maintainer, not a claim that these shader changes support older GNOME releases.

Migration reference: https://gjs.guide/extensions/upgrading/gnome-shell-51.html

Reference GNOME 51-only source patch
diff --git a/metadata.json b/metadata.json
index 4027178..a4a4e84 100644
--- a/metadata.json
+++ b/metadata.json
@@ -7,7 +7,7 @@
     "gettext-domain": "CoverflowAltTab@palatis.blogspot.com",
     "name": "Coverflow Alt-Tab",
     "settings-schema": "org.gnome.shell.extensions.coverflowalttab",
-    "shell-version": ["50"],
+    "shell-version": ["51"],
     "uuid": "CoverflowAltTab@palatis.blogspot.com",
     "description": "Replacement of Alt-Tab, iterates through windows in a cover-flow manner.",
     "url": "https://github.com/dsheeler/CoverflowAltTab"
diff --git a/src/effects/color_effect.js b/src/effects/color_effect.js
index 993aa31..f9c0803 100644
--- a/src/effects/color_effect.js
+++ b/src/effects/color_effect.js
@@ -3,6 +3,7 @@
 //'use strict';
 
 import Clutter from 'gi://Clutter';
+import Cogl from 'gi://Cogl';
 import Shell from 'gi://Shell';
 import GObject from 'gi://GObject';
 import GLib from 'gi://GLib';
@@ -73,11 +74,6 @@ export const ColorEffect = new GObject.registerClass({
         this._blue = null;
         this._blend = null;
 
-        // set shader source
-        this._source = get_shader_source();
-
-        if (this._source)
-            this.set_shader_source(this._source);
 
         // set shader color
         if (_color)
@@ -157,6 +153,20 @@ export const ColorEffect = new GObject.registerClass({
     }
 
 
+    vfunc_get_static_snippet() {
+        const source = get_shader_source();
+        if (!source)
+            throw new Error('Unable to load Coverflow shader');
+        const main = /void\s+main\s*\(\s*\)\s*\{/.exec(source);
+        if (!main)
+            throw new Error('Coverflow shader has no main function');
+        const declarations = source.slice(0, main.index);
+        const body = source.slice(main.index + main[0].length, source.lastIndexOf('}'));
+        const snippet = Cogl.Snippet.new(Cogl.SnippetHook.FRAGMENT, declarations, null);
+        snippet.set_replace(body);
+        return snippet;
+    }
+
     vfunc_paint_target(paint_node = null, paint_context = null) {
         this.set_uniform_value("tex", 0);
 
diff --git a/src/effects/glitch_effect.js b/src/effects/glitch_effect.js
index 6d297a6..c221a70 100644
--- a/src/effects/glitch_effect.js
+++ b/src/effects/glitch_effect.js
@@ -3,6 +3,7 @@
 //'use strict';
 
 import Clutter from 'gi://Clutter';
+import Cogl from 'gi://Cogl';
 import Shell from 'gi://Shell';
 import GObject from 'gi://GObject';
 import GLib from 'gi://GLib';
@@ -34,15 +35,24 @@ export const GlitchEffect = new GObject.registerClass({
     constructor(params) {
         super(params);
         this._timeOffset = Math.random() * 1000000;
-        // set shader source
-        this._source = get_shader_source();
-
-        if (this._source)
-            this.set_shader_source(this._source);
 
         this.set_enabled(true);
     }
 
+    vfunc_get_static_snippet() {
+        const source = get_shader_source();
+        if (!source)
+            throw new Error('Unable to load Coverflow shader');
+        const main = /void\s+main\s*\(\s*\)\s*\{/.exec(source);
+        if (!main)
+            throw new Error('Coverflow shader has no main function');
+        const declarations = source.slice(0, main.index);
+        const body = source.slice(main.index + main[0].length, source.lastIndexOf('}'));
+        const snippet = Cogl.Snippet.new(Cogl.SnippetHook.FRAGMENT, declarations, null);
+        snippet.set_replace(body);
+        return snippet;
+    }
+
     vfunc_paint_target(paint_node = null, paint_context = null) {
         const time = this._timeOffset + GLib.get_monotonic_time() / GLib.USEC_PER_SEC;
         this.set_uniform_value("time", time);
diff --git a/src/effects/radialShader.js b/src/effects/radialShader.js
index 47f01e4..4d6a821 100644
--- a/src/effects/radialShader.js
+++ b/src/effects/radialShader.js
@@ -1,4 +1,4 @@
-import Shell from 'gi://Shell'
+import Clutter from 'gi://Clutter'
 import GObject from 'gi://GObject'
 import Cogl from 'gi://Cogl'
 
@@ -29,22 +29,21 @@ export const MyRadialShaderEffect = GObject.registerClass({
             GObject.ParamFlags.READWRITE,
             0, 1, 0),
     },
-}, class MyRadialShaderEffect extends Shell.GLSLEffect {
+}, class MyRadialShaderEffect extends Clutter.ShaderEffect {
     constructor(params) {
         super(params);
         this._brightness = undefined;
         this._sharpness = undefined;
 
-        this._brightnessLocation = this.get_uniform_location('brightness');
-        this._sharpnessLocation = this.get_uniform_location('vignette_sharpness');
 
         this.brightness = 1.0;
         this.sharpness = 0.0;
     }
 
-    vfunc_build_pipeline() {
-        this.add_glsl_snippet(Cogl.SnippetHook.FRAGMENT,
-            VIGNETTE_DECLARATIONS, VIGNETTE_CODE, true);
+    vfunc_get_static_snippet() {
+        const snippet = Cogl.Snippet.new(Cogl.SnippetHook.FRAGMENT, VIGNETTE_DECLARATIONS, null);
+        snippet.set_replace(VIGNETTE_CODE);
+        return snippet;
     }
 
     get brightness() {
@@ -55,7 +54,7 @@ export const MyRadialShaderEffect = GObject.registerClass({
         if (this._brightness === v)
             return;
         this._brightness = v;
-        this.set_uniform_float(this._brightnessLocation,
+        this.set_uniform_float('brightness',
             1, [this._brightness]);
         this.notify('brightness');
     }
@@ -68,7 +67,7 @@ export const MyRadialShaderEffect = GObject.registerClass({
         if (this._sharpness === v)
             return;
         this._sharpness = v;
-        this.set_uniform_float(this._sharpnessLocation,
+        this.set_uniform_float('vignette_sharpness',
             1, [this._sharpness]);
         this.notify('sharpness');
     }

@camiloeferrera

Copy link
Copy Markdown
Contributor

Opened #319 with a corrected version of this port after reproducing both API issues on a final GNOME 51.0 system (CachyOS, Wayland):

  • new Cogl.Snippet(...) throws Argument to the constructor of Snippet should be a plain JS object with properties to set — and produces a corrupt snippet that can crash the compositor.
  • set_uniform_value('brightness', <number>) throws Expected type gfloat for Argument 'value' but got type 'number'.

#319 uses Cogl.Snippet.new(hook, declarations, null) and named set_uniform_float(name, 1, [v]), and also ports the color/glitch effects discussed here (#318's file list only covers the radial shader). Thanks for the original port — it pointed the way.

@dsheeler

dsheeler commented Oct 8, 2026

Copy link
Copy Markdown
Owner

@gonwe, thanks so much for the pr. I am going to close this one and merge #319. Thanks @LavaTiger99 and @camiloeferrera for furthering the effort!

@dsheeler dsheeler closed this Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants