summaryrefslogtreecommitdiff
path: root/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences
diff options
context:
space:
mode:
authorNippy <nippy@Nippy-Mac-mini.local>2026-10-09 17:23:53 +0200
committerNippy <nippy@Nippy-Mac-mini.local>2026-10-09 17:23:53 +0200
commit9d5be1660d8f74038ebed3d74fdbc809e43787ae (patch)
tree4cb46c3765be87ca4c5219f2f714425d7dfddb56 /raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences
parentdeb4b8a9c407832c35100a643bd7a592589c9679 (diff)
downloadRaveOS-PKGBUILD-9d5be1660d8f74038ebed3d74fdbc809e43787ae.tar.gz
RaveOS-PKGBUILD-9d5be1660d8f74038ebed3d74fdbc809e43787ae.zip
gnome fix
Diffstat (limited to 'raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences')
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js154
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js49
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js68
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js23
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js101
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js187
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline_settings.js136
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js56
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js180
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/shader_uniforms.js61
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js108
11 files changed, 732 insertions, 391 deletions
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js
index 0583db3..8d17b62 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js
@@ -1,106 +1,98 @@
import GObject from 'gi://GObject';
-/// An object to easily manage signals.
+function supports_destroy_signal(object) {
+ const gtype = object?.constructor?.$gtype;
+ return gtype && GObject.signal_lookup('destroy', gtype) !== 0;
+}
+
export const Connections = class Connections {
constructor() {
- this.buffer = [];
+ this.records = new Map();
}
- /// Adds a connection.
- ///
- /// Takes as arguments:
- /// - an actor, which fires the signal
- /// - signal(s) (string or array of strings), which are watched for
- /// - a callback, which is called when the signal is fired
- connect(actor, signals, handler) {
- if (signals instanceof Array) {
- signals.forEach(signal => {
- let id = actor.connect(signal, handler);
- this.process_connection(actor, id);
- });
- } else {
- let id = actor.connect(signals, handler);
- this.process_connection(actor, id);
- }
+ connect(object, signals, handler) {
+ const names = Array.isArray(signals) ? signals : [signals];
+ const record = this.get_record(object);
+ const ids = [];
+ for (const signal of names) {
+ const id = object.connect(signal, handler);
+ record.ids.add(id);
+ ids.push(id);
+ }
+ return Array.isArray(signals) ? ids : ids[0];
}
- /// Process the given actor and id.
- ///
- /// This makes sure that the signal is disconnected when the actor is
- /// destroyed, and that the signal can be managed through other Connections
- /// methods.
- process_connection(actor, id) {
- let infos = {
- actor: actor,
- id: id
- };
+ get_record(object) {
+ let record = this.records.get(object);
+ if (record)
+ return record;
- // remove the signal when the actor is destroyed
- if (
- actor.connect &&
- (
- !(actor instanceof GObject.Object) ||
- GObject.signal_lookup('destroy', actor)
- )
- ) {
- let destroy_id = actor.connect('destroy', () => {
- actor.disconnect(id);
- actor.disconnect(destroy_id);
+ record = {
+ ids: new Set(),
+ destroy_id: null,
+ };
+ this.records.set(object, record);
- let index = this.buffer.indexOf(infos);
- if (index >= 0) {
- this.buffer.splice(index, 1);
- }
+ if (supports_destroy_signal(object))
+ record.destroy_id = object.connect('destroy', () => {
+ this.records.delete(object);
+ record.ids.clear();
+ record.destroy_id = null;
});
- infos.destroy_id = destroy_id;
- }
- this.buffer.push(infos);
+ return record;
}
- /// Disconnects every connection found for an actor.
- disconnect_all_for(actor) {
- // get every connection stored for the actor
- let actor_connections = this.buffer.filter(
- infos => infos.actor === actor
- );
+ disconnect_all_for(object) {
+ const record = this.records.get(object);
+ if (!record)
+ return;
- // remove each of them
- actor_connections.forEach(connection => {
- // disconnect
- try {
- connection.actor.disconnect(connection.id);
- if ('destroy_id' in connection)
- connection.actor.disconnect(connection.destroy_id);
- } catch (e) {
- this._warn(`error removing connection: ${e}; continuing`);
- }
-
- // remove from buffer
- let index = this.buffer.indexOf(connection);
- this.buffer.splice(index, 1);
- });
+ this.records.delete(object);
+ for (const id of record.ids)
+ this.raw_disconnect(object, id);
+ if (record.destroy_id)
+ this.raw_disconnect(object, record.destroy_id);
}
- /// Disconnect every connection for each actor.
disconnect_all() {
- this.buffer.forEach(connection => {
- // disconnect
- try {
- connection.actor.disconnect(connection.id);
- if ('destroy_id' in connection)
- connection.actor.disconnect(connection.destroy_id);
- } catch (e) {
- this._warn(`error removing connection: ${e}; continuing`);
- }
- });
+ for (const object of [...this.records.keys()])
+ this.disconnect_all_for(object);
+ }
- // reset buffer
- this.buffer = [];
+ disconnect(object, id) {
+ const record = this.records.get(object);
+ if (record)
+ record.ids.delete(id);
+
+ this.raw_disconnect(object, id);
+
+ if (record && record.ids.size === 0) {
+ this.records.delete(object);
+ if (record.destroy_id)
+ this.raw_disconnect(object, record.destroy_id);
+ }
+ }
+
+ raw_disconnect(object, id) {
+ if (!id)
+ return;
+
+ try {
+ if (
+ object instanceof GObject.Object
+ && !GObject.signal_handler_is_connected(object, id)
+ )
+ return;
+
+ object.disconnect(id);
+ } catch (error) {
+ this._warn(`error removing connection: ${error}; continuing`);
+ }
}
_warn(str) {
console.warn(`[Blur my Shell > connections] ${str}`);
}
-}; \ No newline at end of file
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js
index 02b2d13..4d71d46 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js
@@ -4,9 +4,12 @@ import Clutter from 'gi://Clutter';
/// A dummy `Pipeline`, for dynamic blur only.
/// Instead of a pipeline id, we take the settings of the component we want to blur.
export const DummyPipeline = class DummyPipeline {
- constructor(effects_manager, settings, actor = null) {
+ constructor(effects_manager, settings, actor = null, options = {}) {
this.effects_manager = effects_manager;
this.settings = settings;
+ this.corner_radius_key = options.corner_radius_key ?? 'corner-radius';
+ this.corner_radius_getter = options.corner_radius_getter ?? (() => this.settings.CORNER_RADIUS);
+ this.opacity_factor = 1;
this.effect = null;
this.attach_effect_to_actor(actor);
}
@@ -41,9 +44,9 @@ export const DummyPipeline = class DummyPipeline {
// build the new effect to be added
this.build_effect({
- unscaled_radius: 2 * this.settings.SIGMA,
- brightness: this.settings.BRIGHTNESS,
- corner_radius: this.settings.CORNER_RADIUS,
+ unscaled_radius: this.get_unscaled_radius(),
+ brightness: this.get_brightness(),
+ unscaled_corner_radius: this.corner_radius_getter(),
});
this.actor_destroy_id = this.actor.connect(
@@ -70,17 +73,40 @@ export const DummyPipeline = class DummyPipeline {
this.effect = this.effects_manager.new_native_dynamic_gaussian_blur_effect(params);
// connect to settings changes, using the true gsettings object
- this._sigma_changed_id = this.settings.settings.connect(
- 'changed::sigma', () => this.effect.unscaled_radius = 2 * this.settings.SIGMA
- );
- this._brightness_changed_id = this.settings.settings.connect(
- 'changed::brightness', () => this.effect.brightness = this.settings.BRIGHTNESS
- );
+ this._sigma_changed_id = this.settings.settings.connect('changed::sigma', () => this.update_effect());
+ this._brightness_changed_id = this.settings.settings.connect('changed::brightness', () => this.update_effect());
this._corner_radius_changed_id = this.settings.settings.connect(
- 'changed::corner-radius', () => this.effect.corner_radius = this.settings.CORNER_RADIUS
+ `changed::${this.corner_radius_key}`,
+ () => this.update_effect()
);
}
+ set_opacity_factor(opacity_factor) {
+ opacity_factor = Math.max(0, Math.min(1, opacity_factor));
+ if (this.opacity_factor === opacity_factor)
+ return;
+
+ this.opacity_factor = opacity_factor;
+ this.update_effect();
+ }
+
+ update_effect() {
+ if (!this.effect)
+ return;
+
+ this.effect.unscaled_radius = this.get_unscaled_radius();
+ this.effect.brightness = this.get_brightness();
+ this.effect.unscaled_corner_radius = this.corner_radius_getter();
+ }
+
+ get_unscaled_radius() {
+ return 2 * this.settings.SIGMA * this.opacity_factor;
+ }
+
+ get_brightness() {
+ return 1 - (1 - this.settings.BRIGHTNESS) * this.opacity_factor;
+ }
+
repaint_effect() {
this.effect?.queue_repaint();
}
@@ -109,7 +135,6 @@ export const DummyPipeline = class DummyPipeline {
/// Note: exposed to public API.
destroy() {
- this.remove_effect();
this.remove_pipeline_from_actor();
}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js
index 947d535..8b99396 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js
@@ -4,7 +4,7 @@ import { get_supported_effects } from '../effects/effects.js';
export const EffectsManager = class EffectsManager {
constructor(connections) {
this.connections = connections;
- this.used = [];
+ this.used = new Set();
this.SUPPORTED_EFFECTS = get_supported_effects();
Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
@@ -15,60 +15,56 @@ export const EffectsManager = class EffectsManager {
this['new_' + effect_name + '_effect'] = function (params) {
let effect;
if (this[effect_name + '_effects'].length > 0) {
- effect = this[effect_name + '_effects'].splice(0, 1)[0];
+ effect = this[effect_name + '_effects'].pop();
effect.set({
...this.SUPPORTED_EFFECTS[effect_name].class.default_params, ...params
});
- } else
+ } else {
effect = new this.SUPPORTED_EFFECTS[effect_name].class({
...this.SUPPORTED_EFFECTS[effect_name].class.default_params, ...params
});
+ this.connect_to_destroy(effect);
+ }
- this.used.push(effect);
- this.connect_to_destroy(effect);
+ this.used.add(effect);
return effect;
};
});
}
connect_to_destroy(effect) {
- effect.old_actor = effect.get_actor();
- if (effect.old_actor)
- effect.old_actor_id = effect.old_actor.connect('destroy', _ => {
- this.remove(effect, true);
- });
-
- this.connections.connect(effect, 'notify::actor', _ => {
- let actor = effect.get_actor();
+ const update_actor = () => {
+ const actor = effect.get_actor();
+ if (actor === effect._bms_actor)
+ return;
- if (effect.old_actor && actor != effect.old_actor)
- effect.old_actor.disconnect(effect.old_actor_id);
+ this.disconnect_actor_destroy(effect);
+ effect._bms_actor = actor;
+ if (actor)
+ effect._bms_actor_destroy_id = actor.connect(
+ 'destroy', () => this.remove(effect, true)
+ );
+ };
- if (actor && actor != effect.old_actor) {
- effect.old_actor_id = actor.connect('destroy', _ => {
- this.remove(effect, true);
- });
- }
- });
+ this.connections.connect(effect, 'notify::actor', update_actor);
+ update_actor();
}
- // IMPORTANT: do never call this in a mutable `this.used.forEach`
- remove(effect, actor_already_destroyed = false) {
- if (!actor_already_destroyed)
- try {
- effect.get_actor()?.remove_effect(effect);
- } catch (e) {
- this._warn(`could not remove the effect, continuing: ${e}`);
- }
- if (effect.old_actor)
- effect.old_actor.disconnect(effect.old_actor_id);
- delete effect.old_actor;
- delete effect.old_actor_id;
+ disconnect_actor_destroy(effect) {
+ if (effect._bms_actor && effect._bms_actor_destroy_id) {
+ effect._bms_actor.disconnect(effect._bms_actor_destroy_id);
+ }
+ effect._bms_actor = null;
+ effect._bms_actor_destroy_id = null;
+ }
- let index = this.used.indexOf(effect);
- if (index >= 0) {
- this.used.splice(index, 1);
+ remove(effect) {
+ const actor = effect.get_actor();
+ if (actor)
+ actor.remove_effect(effect);
+ this.disconnect_actor_destroy(effect);
+ if (this.used.delete(effect)) {
Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
if (effect instanceof this.SUPPORTED_EFFECTS[effect_name].class)
this[effect_name + '_effects'].push(effect);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js
index d821b88..0876d16 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js
@@ -38,6 +38,9 @@ export const KEYS = [
{ type: Type.B, name: "override-background" },
{ type: Type.I, name: "style-panel" },
{ type: Type.B, name: "override-background-dynamically" },
+ { type: Type.I, name: "override-background-dynamically-mode" },
+ { type: Type.B, name: "gradient-panel" },
+ { type: Type.I, name: "gradient-panel-mode" },
]
},
{
@@ -65,6 +68,7 @@ export const KEYS = [
{ type: Type.I, name: "opacity" },
{ type: Type.B, name: "dynamic-opacity" },
{ type: Type.B, name: "blur-on-overview" },
+ { type: Type.B, name: "unblur-when-fullscreen" },
{ type: Type.B, name: "enable-all" },
{ type: Type.AS, name: "whitelist" },
{ type: Type.AS, name: "blacklist" },
@@ -97,6 +101,25 @@ export const KEYS = [
]
},
{
+ component: "popup", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.B, name: "static-blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.I, name: "corner-radius" },
+ { type: Type.I, name: "menu-corner-radius" },
+ { type: Type.I, name: "quick-settings-corner-radius" },
+ { type: Type.I, name: "notification-corner-radius" },
+ { type: Type.I, name: "osd-corner-radius" },
+ { type: Type.I, name: "dialog-corner-radius" },
+ { type: Type.I, name: "osk-corner-radius" },
+ { type: Type.B, name: "override-background" },
+ { type: Type.B, name: "preserve-shell-theme" },
+ { type: Type.I, name: "style-popup" },
+ ]
+ },
+ {
component: "hidetopbar", schemas: [
{ type: Type.B, name: "compatibility" },
]
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js
index 5fbfee4..10a6d26 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js
@@ -1,91 +1,64 @@
import GObject from 'gi://GObject';
import Clutter from 'gi://Clutter';
+const PAINTS_BETWEEN_REPAINTS = 2;
export const PaintSignals = class PaintSignals {
constructor(connections) {
- this.buffer = [];
+ this.entries = new Map();
this.connections = connections;
}
connect(actor, blur_effect) {
- let paint_effect = new EmitPaintSignal();
- let infos = {
- actor: actor,
- paint_effect: paint_effect
- };
- let counter = 0;
+ this.disconnect_all_for_actor(actor);
- actor.add_effect(paint_effect);
- this.connections.connect(paint_effect, 'update-blur', () => {
- try {
- // checking if blur_effect.queue_repaint() has been recently called
- if (counter === 0) {
- counter = 2;
- blur_effect.queue_repaint();
- }
- else counter--;
- } catch (e) { }
- });
+ let paints_to_skip = 0;
+ const paint_effect = new PaintCallbackEffect();
+ paint_effect.set_callback(() => {
+ if (paints_to_skip > 0) {
+ paints_to_skip--;
+ return;
+ }
- // remove the actor from buffer when it is destroyed
- if (
- actor.connect &&
- (
- !(actor instanceof GObject.Object) ||
- GObject.signal_lookup('destroy', actor)
- )
- )
- this.connections.connect(actor, 'destroy', () => {
- const immutable_buffer = [...this.buffer];
- immutable_buffer.forEach(infos => {
- if (infos.actor === actor) {
- // remove from buffer
- let index = this.buffer.indexOf(infos);
- this.buffer.splice(index, 1);
- }
- });
- });
+ paints_to_skip = PAINTS_BETWEEN_REPAINTS;
+ blur_effect.queue_repaint();
+ });
- this.buffer.push(infos);
+ actor.add_effect(paint_effect);
+ const destroy_id = this.connections.connect(actor, 'destroy', () => {
+ paint_effect.set_callback(null);
+ this.entries.delete(actor);
+ });
+ this.entries.set(actor, { paint_effect, destroy_id });
}
disconnect_all_for_actor(actor) {
- const immutable_buffer = [...this.buffer];
- immutable_buffer.forEach(infos => {
- if (infos.actor === actor) {
- this.connections.disconnect_all_for(infos.paint_effect);
- infos.actor.remove_effect(infos.paint_effect);
+ const entry = this.entries.get(actor);
+ if (!entry)
+ return;
- // remove from buffer
- let index = this.buffer.indexOf(infos);
- this.buffer.splice(index, 1);
- }
- });
+ this.entries.delete(actor);
+ entry.paint_effect.set_callback(null);
+ this.connections.disconnect(actor, entry.destroy_id);
+ actor.remove_effect(entry.paint_effect);
}
disconnect_all() {
- this.buffer.forEach(infos => {
- this.connections.disconnect_all_for(infos.paint_effect);
- infos.actor.remove_effect(infos.paint_effect);
- });
-
- this.buffer = [];
+ for (const actor of [...this.entries.keys()])
+ this.disconnect_all_for_actor(actor);
}
};
-export const EmitPaintSignal = GObject.registerClass({
- GTypeName: 'EmitPaintSignal',
- Signals: {
- 'update-blur': {
- param_types: []
- },
- }
-},
- class EmitPaintSignal extends Clutter.Effect {
+const PaintCallbackEffect = GObject.registerClass(
+ { GTypeName: 'BmsPaintCallbackEffect' },
+ class PaintCallbackEffect extends Clutter.Effect {
+ set_callback(callback) {
+ this._callback = callback;
+ }
+
vfunc_paint(node, paint_context, paint_flags) {
- this.emit("update-blur");
+ this._callback();
super.vfunc_paint(node, paint_context, paint_flags);
}
}
-); \ No newline at end of file
+);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js
index 84b1d7a..f52e26f 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js
@@ -1,6 +1,9 @@
import St from 'gi://St';
+import Meta from 'gi://Meta';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as Background from 'resource:///org/gnome/shell/ui/background.js';
+import * as uniforms from './shader_uniforms.js';
+import * as utils from './utils.js';
/// A `Pipeline` object is a handy way to manage the effects attached to an actor. It only manages
/// one actor at a time (so blurring multiple widgets will need multiple `Pipeline`), and is
@@ -18,10 +21,11 @@ import * as Background from 'resource:///org/gnome/shell/ui/background.js';
/// - `'pipeline_id'::effect-'id'-key-updated`, handing the key that was changed and its new value
/// - `'pipeline_id'::effect-'id'-key-added`, handing the key that was added and its value
export const Pipeline = class Pipeline {
- constructor(effects_manager, pipelines_manager, pipeline_id, actor = null) {
+ constructor(effects_manager, pipelines_manager, pipeline_id, actor = null, options = {}) {
this.effects_manager = effects_manager;
this.pipelines_manager = pipelines_manager;
this.effects = [];
+ this.effect_overrides = options.effect_overrides ?? {};
this.set_pipeline_id(pipeline_id);
this.attach_pipeline_to_actor(actor);
}
@@ -41,19 +45,18 @@ export const Pipeline = class Pipeline {
) {
let monitor = Main.layoutManager.monitors[monitor_index];
+ this.remove_pipeline_from_actor();
+
// create the new actor
this.actor = new St.Widget({
name: widget_name,
x: use_absolute_position ? monitor.x : 0,
- y: .5 + (use_absolute_position ? monitor.y : 0), // add 1 to correct z-position
+ y: utils.subpixel_stage_offset() + (use_absolute_position ? monitor.y : 0),
z_position: 1, // seems to fix the multi-monitor glitch
width: monitor.width,
height: monitor.height
});
- // remove the effects, wether or not we attach the pipeline to the actor: if they are fired
- // while the actor has changed, this could go bad
- this.remove_all_effects();
if (this.pipeline_id)
this.attach_pipeline_to_actor(this.actor);
@@ -64,6 +67,17 @@ export const Pipeline = class Pipeline {
});
bg_manager._bms_pipeline = this;
+ // 'controlPosition: false' skips BackgroundManager's default layout pass, which also diables
+ // sibling re-ordering.
+ // Without it, new actors render on top while loading, causing a solid color flash through
+ // on the surface.
+ this.child_added_id = this.actor.connect(
+ 'child-added', (container, child) => {
+ if (child instanceof Meta.BackgroundActor)
+ container.set_child_below_sibling(child, null);
+ }
+ );
+
background_managers.push(bg_manager);
background_group.insert_child_at_index(this.actor, 0);
@@ -104,14 +118,14 @@ export const Pipeline = class Pipeline {
/// Attach a Pipeline object with `pipeline_id` already set to an actor.
attach_pipeline_to_actor(actor) {
- // set the actor
- if (actor)
- this.actor = actor;
- else {
+ if (!actor) {
this.remove_pipeline_from_actor();
return;
}
+ this.disconnect_actor_destroy();
+ this.actor = actor;
+
// attach the pipeline
let pipeline = this.pipelines_manager.pipelines[this.pipeline_id];
if (!pipeline) {
@@ -134,10 +148,23 @@ export const Pipeline = class Pipeline {
remove_pipeline_from_actor() {
this.remove_all_effects();
- if (this.actor && this.actor_destroy_id)
+ this.disconnect_actor_destroy();
+ this.disconnect_child_added();
+ this.actor = null;
+ }
+
+ disconnect_actor_destroy() {
+ if (this.actor && this.actor_destroy_id) {
this.actor.disconnect(this.actor_destroy_id);
+ }
this.actor_destroy_id = null;
- this.actor = null;
+ }
+
+ disconnect_child_added() {
+ if (this.actor && this.child_added_id) {
+ this.actor.disconnect(this.child_added_id);
+ }
+ this.child_added_id = null;
}
/// Update the effects from the given pipeline object, the hard way.
@@ -147,7 +174,9 @@ export const Pipeline = class Pipeline {
// build the new effects to be added
pipeline.effects.forEach(effect => {
- if ('new_' + effect.type + '_effect' in this.effects_manager)
+ if (effect.type === 'pixelize')
+ this.build_pixelize_effect(effect);
+ else if ('new_' + effect.type + '_effect' in this.effects_manager)
this.build_effect(effect);
else
this._warn(`could not add effect to actor, effect "${effect.type}" not found`);
@@ -156,32 +185,143 @@ export const Pipeline = class Pipeline {
// add the effects to the actor
if (this.actor)
- this.effects.forEach(effect => this.actor.add_effect(effect));
+ this.effects.forEach(effect => {
+ this.actor.add_effect(effect);
+ uniforms.mark_dirty(effect);
+ });
else
this._warn(`could not add effect to actor, actor does not exist anymore`);
}
+ build_pixelize_effect(effect_infos) {
+ const effect_params = this.get_effect_params(effect_infos);
+ const effect_overrides = this.get_effect_overrides(effect_infos.type, effect_params);
+ const params = {
+ ...effect_params,
+ ...effect_overrides,
+ };
+
+ const downscale = this.effects_manager.new_downscale_effect({
+ divider: params.factor,
+ downsampling_mode: params.downsampling_mode,
+ opacity_factor: params.opacity_factor,
+ });
+ const upscale = this.effects_manager.new_upscale_effect({
+ factor: params.factor,
+ opacity_factor: params.opacity_factor,
+ });
+
+ this.setup_effect(downscale, effect_infos, effect_params, 'downscale');
+ this.setup_effect(upscale, effect_infos, effect_params, 'upscale');
+ }
+
/// Given an `effect_infos` object containing the effect type, id and params, build an effect
/// and append it to the effects list
build_effect(effect_infos) {
- let effect = this.effects_manager['new_' + effect_infos.type + '_effect'](effect_infos.params);
+ const effect_params = this.get_effect_params(effect_infos);
+ const effect_overrides = this.get_effect_overrides(effect_infos.type, effect_params);
+ let effect = this.effects_manager['new_' + effect_infos.type + '_effect']({
+ ...effect_params,
+ ...effect_overrides,
+ });
+ this.setup_effect(effect, effect_infos, effect_params);
+ }
+
+ setup_effect(effect, effect_infos, effect_params, pixelize_role = null) {
+ effect._bms_effect_type = effect_infos.type;
+ effect._bms_effect_id = effect_infos.id;
+ effect._bms_effect_params = effect_params;
+ effect._bms_pixelize_role = pixelize_role;
this.effects.push(effect);
// connect to settings changes
effect._effect_key_removed_id = this.pipelines_manager.connect(
- this.pipeline_id + '::effect-' + effect_infos.id + '-key-removed',
- (_, key) => effect[key] = effect.constructor.default_params[key]
+ this.pipeline_id + '::effect-' + effect_infos.id + '-key-removed', (_, key) => {
+ const default_value = this.get_effect_default_param(effect, key);
+ effect._bms_effect_params[key] = default_value;
+ if (!this.apply_effect_override(effect, key))
+ this.set_effect_param(effect, key, default_value);
+ }
);
effect._effect_key_updated_id = this.pipelines_manager.connect(
- this.pipeline_id + '::effect-' + effect_infos.id + '-key-updated',
- (_, key, value) => effect[key] = value
+ this.pipeline_id + '::effect-' + effect_infos.id + '-key-updated', (_, key, value) => {
+ effect._bms_effect_params[key] = value;
+ if (!this.apply_effect_override(effect, key))
+ this.set_effect_param(effect, key, value);
+ }
);
effect._effect_key_added_id = this.pipelines_manager.connect(
- this.pipeline_id + '::effect-' + effect_infos.id + '-key-added',
- (_, key, value) => effect[key] = value
+ this.pipeline_id + '::effect-' + effect_infos.id + '-key-added', (_, key, value) => {
+ effect._bms_effect_params[key] = value;
+ if (!this.apply_effect_override(effect, key))
+ this.set_effect_param(effect, key, value);
+ }
);
}
+ get_effect_params(effect_infos) {
+ const effect_class = this.effects_manager.SUPPORTED_EFFECTS[effect_infos.type]?.class;
+ return {
+ ...(effect_class?.default_params ?? {}),
+ ...(effect_infos.params ?? {}),
+ };
+ }
+
+ get_effect_default_param(effect, key) {
+ return this.effects_manager.SUPPORTED_EFFECTS[effect._bms_effect_type]
+ ?.class
+ ?.default_params
+ ?.[key];
+ }
+
+ get_effect_overrides(effect_type, effect_params = {}) {
+ const overrides = this.effect_overrides[effect_type] ?? {};
+ return typeof overrides === 'function' ? overrides(effect_params) : overrides;
+ }
+
+ apply_effect_override(effect, key) {
+ const overrides = this.get_effect_overrides(effect._bms_effect_type, effect._bms_effect_params);
+ if (!(key in overrides))
+ return false;
+
+ this.set_effect_param(effect, key, overrides[key]);
+ return true;
+ }
+
+ apply_effect_overrides(effect_type = null) {
+ this.effects.forEach(effect => {
+ if (effect_type && effect._bms_effect_type !== effect_type)
+ return;
+
+ const overrides = this.get_effect_overrides(effect._bms_effect_type, effect._bms_effect_params);
+ Object.keys(overrides).forEach(key => this.set_effect_param(effect, key, overrides[key]));
+ });
+ }
+
+ set_effect_param(effect, key, value) {
+ if (effect._bms_effect_type !== 'pixelize') {
+ effect[key] = value;
+ return;
+ }
+
+ if (key === 'factor') {
+ if (effect._bms_pixelize_role === 'downscale')
+ effect.divider = value;
+ else if (effect._bms_pixelize_role === 'upscale')
+ effect.factor = value;
+ return;
+ }
+
+ if (key === 'downsampling_mode') {
+ if (effect._bms_pixelize_role === 'downscale')
+ effect.downsampling_mode = value;
+ return;
+ }
+
+ if (key in effect)
+ effect[key] = value;
+ }
+
/// Remove every effect from the actor it is attached to. Please note that they are not
/// destroyed, but rather stored (thanks to the `EffectManager` class) to be reused later.
remove_all_effects() {
@@ -197,6 +337,10 @@ export const Pipeline = class Pipeline {
delete effect._effect_key_removed_id;
delete effect._effect_key_updated_id;
delete effect._effect_key_added_id;
+ delete effect._bms_effect_type;
+ delete effect._bms_effect_id;
+ delete effect._bms_effect_params;
+ delete effect._bms_pixelize_role;
});
this.effects = [];
}
@@ -211,7 +355,6 @@ export const Pipeline = class Pipeline {
/// Resets the `Pipeline` object to a sane state, removing every effect and signal.
/// Note: exposed to public API.
destroy() {
- this.remove_all_effects();
this.remove_connections();
this.remove_pipeline_from_actor();
this.pipeline_id = null;
@@ -220,4 +363,4 @@ export const Pipeline = class Pipeline {
_warn(str) {
console.warn(`[Blur my Shell > pipeline] ${str}`);
}
-}; \ No newline at end of file
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline_settings.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline_settings.js
new file mode 100644
index 0000000..eb5c173
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline_settings.js
@@ -0,0 +1,136 @@
+import GLib from 'gi://GLib';
+
+function is_plain_object(value) {
+ return value !== null
+ && typeof value === 'object'
+ && value.constructor === Object;
+}
+
+function unpack_string(value, label) {
+ const unpacked = value?.deep_unpack();
+ if (typeof unpacked !== 'string')
+ throw new Error(`${label} is not a string`);
+
+ return unpacked;
+}
+
+function unpack_effect(effect_variant) {
+ const effect = effect_variant?.deep_unpack();
+ if (!is_plain_object(effect))
+ throw new Error('effect is not an object');
+ if (!('type' in effect))
+ throw new Error('effect has no type');
+ if (!('id' in effect))
+ throw new Error('effect has no id');
+
+ const params = 'params' in effect ? effect.params.deep_unpack() : {};
+ if (!is_plain_object(params))
+ throw new Error('effect params is not an object');
+
+ return {
+ type: unpack_string(effect.type, 'effect type'),
+ id: unpack_string(effect.id, 'effect id'),
+ params: Object.fromEntries(
+ Object.entries(params).map(([key, value]) => [key, value.deep_unpack()])
+ ),
+ };
+}
+
+export function unpack_pipelines(value) {
+ const unpacked = value.deep_unpack();
+ if (!is_plain_object(unpacked))
+ throw new Error('pipelines is not an object');
+
+ const pipelines = {};
+ for (const [pipeline_id, pipeline] of Object.entries(unpacked)) {
+ if (!is_plain_object(pipeline))
+ throw new Error(`pipeline ${pipeline_id} is not an object`);
+ if (!('name' in pipeline))
+ throw new Error(`pipeline ${pipeline_id} has no name`);
+ if (!('effects' in pipeline))
+ throw new Error(`pipeline ${pipeline_id} has no effects`);
+
+ const effects = pipeline.effects.deep_unpack();
+ if (!Array.isArray(effects))
+ throw new Error(`pipeline ${pipeline_id} effects is not an array`);
+
+ pipelines[pipeline_id] = {
+ name: unpack_string(pipeline.name, `pipeline ${pipeline_id} name`),
+ effects: effects.map(unpack_effect),
+ };
+ }
+
+ return pipelines;
+}
+
+function pack_param(value, label) {
+ if (typeof value === 'boolean')
+ return GLib.Variant.new_boolean(value);
+ if (typeof value === 'number')
+ return Number.isInteger(value)
+ ? GLib.Variant.new_int32(value)
+ : GLib.Variant.new_double(value);
+ if (typeof value === 'string')
+ return GLib.Variant.new_string(value);
+ if (
+ Array.isArray(value)
+ && value.length === 4
+ && value.every(component => typeof component === 'number')
+ )
+ return new GLib.Variant('(dddd)', value);
+
+ throw new Error(`${label} has an unsupported type`);
+}
+
+function pack_effect(effect, label) {
+ if (!is_plain_object(effect))
+ throw new Error(`${label} is not an object`);
+ if (typeof effect.type !== 'string')
+ throw new Error(`${label} type is not a string`);
+ if (typeof effect.id !== 'string')
+ throw new Error(`${label} id is not a string`);
+
+ const params = effect.params ?? {};
+ if (!is_plain_object(params))
+ throw new Error(`${label} params is not an object`);
+
+ const packed_params = Object.fromEntries(
+ Object.entries(params).map(([key, value]) => [
+ key,
+ pack_param(value, `${label} parameter ${key}`),
+ ])
+ );
+
+ return new GLib.Variant('a{sv}', {
+ type: GLib.Variant.new_string(effect.type),
+ id: GLib.Variant.new_string(effect.id),
+ params: new GLib.Variant('a{sv}', packed_params),
+ });
+}
+
+export function pack_pipelines(pipelines) {
+ if (!is_plain_object(pipelines))
+ throw new Error('pipelines is not an object');
+
+ const packed = {};
+ for (const [pipeline_id, pipeline] of Object.entries(pipelines)) {
+ if (!is_plain_object(pipeline))
+ throw new Error(`pipeline ${pipeline_id} is not an object`);
+ if (typeof pipeline.name !== 'string')
+ throw new Error(`pipeline ${pipeline_id} name is not a string`);
+ if (!Array.isArray(pipeline.effects))
+ throw new Error(`pipeline ${pipeline_id} effects is not an array`);
+
+ packed[pipeline_id] = {
+ name: GLib.Variant.new_string(pipeline.name),
+ effects: new GLib.Variant(
+ 'av',
+ pipeline.effects.map((effect, index) =>
+ pack_effect(effect, `pipeline ${pipeline_id} effect ${index}`)
+ )
+ ),
+ };
+ }
+
+ return new GLib.Variant('a{sa{sv}}', packed);
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
index 0fdfe9e..6d9749a 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
@@ -1,5 +1,23 @@
+import GLib from 'gi://GLib';
+
const Signals = imports.signals;
+function new_id(prefix) {
+ return `${prefix}_${GLib.uuid_string_random()}`;
+}
+
+function clone_effect(effect) {
+ return {
+ ...effect,
+ params: Object.fromEntries(
+ Object.entries(effect.params ?? {}).map(([key, value]) => [
+ key,
+ Array.isArray(value) ? [...value] : value,
+ ])
+ ),
+ };
+}
+
/// The `PipelinesManager` object permits to store the list of pipelines and their effects in
/// memory. It is meant to *always* be in sync with the `org.gnome.shell.extensions.blur-my-shell`'s
/// `pipelines` gschema. However, we do not want to re-create every effect each time this schema is
@@ -36,10 +54,8 @@ export class PipelinesManager {
}
create_pipeline(name, effects = []) {
- // select a random id for the pipeline
- let id = "pipeline_" + ("" + Math.random()).slice(2, 16);
- // add a random ID for each effect, to help tracking them
- effects.forEach(effect => effect.id = "effect_" + ("" + Math.random()).slice(2, 16));
+ const id = new_id('pipeline');
+ effects.forEach(effect => effect.id = new_id('effect'));
this.pipelines[id] = { name, effects };
this.settings.PIPELINES = this.pipelines;
@@ -54,8 +70,10 @@ export class PipelinesManager {
return;
}
const pipeline = this.pipelines[id];
- this.create_pipeline(pipeline.name + " - duplicate", [...pipeline.effects]);
- this.settings.PIPELINES = this.pipelines;
+ this.create_pipeline(
+ `${pipeline.name} - duplicate`,
+ pipeline.effects.map(clone_effect)
+ );
}
delete_pipeline(id) {
@@ -89,7 +107,7 @@ export class PipelinesManager {
this._warn(`could not rename pipeline, id ${id} does not exist`);
return;
}
- this.pipelines[id].name = name.slice();
+ this.pipelines[id].name = name;
this.settings.PIPELINES = this.pipelines;
this._emit(id + '::pipeline-renamed', name);
this._emit('pipeline-names-changed');
@@ -98,8 +116,18 @@ export class PipelinesManager {
on_pipeline_update() {
const old_pipelines = this.pipelines;
this.pipelines = this.settings.PIPELINES;
+ const old_ids = Object.keys(old_pipelines);
+ const new_ids = Object.keys(this.pipelines);
+ const list_changed = old_ids.length !== new_ids.length
+ || old_ids.some(id => !(id in this.pipelines));
+ let names_changed = false;
- for (var pipeline_id in old_pipelines) {
+ for (const pipeline_id of new_ids) {
+ if (!(pipeline_id in old_pipelines))
+ this._emit('pipeline-created', pipeline_id, this.pipelines[pipeline_id]);
+ }
+
+ for (const pipeline_id of old_ids) {
// if we find a pipeline that does not exist anymore, signal it
if (!(pipeline_id in this.pipelines)) {
this._emit(pipeline_id + '::pipeline-destroyed');
@@ -109,6 +137,11 @@ export class PipelinesManager {
const old_pipeline = old_pipelines[pipeline_id];
const new_pipeline = this.pipelines[pipeline_id];
+ if (old_pipeline.name !== new_pipeline.name) {
+ this._emit(pipeline_id + '::pipeline-renamed', new_pipeline.name);
+ names_changed = true;
+ }
+
// verify if both pipelines have effects in the same order
// if they have, then check for their parameters
if (
@@ -144,6 +177,11 @@ export class PipelinesManager {
else
this._emit(pipeline_id + '::pipeline-updated', new_pipeline);
}
+
+ if (list_changed)
+ this._emit('pipeline-list-changed');
+ if (names_changed)
+ this._emit('pipeline-names-changed');
}
destroy() {
@@ -165,4 +203,4 @@ export class PipelinesManager {
}
}
-Signals.addSignalMethods(PipelinesManager.prototype); \ No newline at end of file
+Signals.addSignalMethods(PipelinesManager.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js
index 53f8497..337c8c2 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js
@@ -1,4 +1,5 @@
import GLib from 'gi://GLib';
+import { pack_pipelines, unpack_pipelines } from './pipeline_settings.js';
const Signals = imports.signals;
/// An enum non-extensively describing the type of gsettings key.
@@ -122,171 +123,20 @@ export const Settings = class Settings {
case Type.PIPELINES:
Object.defineProperty(component, property_name, {
get() {
- let pips = component_settings.get_value(key.name).deep_unpack();
- Object.keys(pips).forEach(pipeline_id => {
- let pipeline = pips[pipeline_id];
-
- if (!('name' in pipeline)) {
- this._warn('impossible to get pipelines, pipeline has not name, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
- let name = pipeline.name.deep_unpack();
- if (typeof name !== 'string') {
- this._warn('impossible to get pipelines, pipeline name is not a string, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
-
- if (!('effects' in pipeline)) {
- this._warn('impossible to get pipelines, pipeline has not effects, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
- let effects = pipeline.effects.deep_unpack();
- if (!Array.isArray(effects)) {
- this._warn('impossible to get pipelines, pipeline effects is not an array, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
-
- effects = effects.map(effect => effect.deep_unpack());
- effects.forEach(effect => {
- if (!('type' in effect)) {
- this._warn('impossible to get pipelines, effect has not type, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
- let type = effect.type.deep_unpack();
- if (typeof type !== 'string') {
- this._warn('impossible to get pipelines, effect type is not a string, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
-
- if (!('id' in effect)) {
- this._warn('impossible to get pipelines, effect has not id, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
- let id = effect.id.deep_unpack();
- if (typeof id !== 'string') {
- this._warn('impossible to get pipelines, effect id is not a string, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
-
- let params = {};
- if ('params' in effect)
- params = effect.params.deep_unpack();
- if (!(params && typeof params === 'object' && params.constructor === Object)) {
- this._warn('impossible to get pipelines, effect params is not an object, resetting');
- component[property_name + '_reset']();
- return component[property_name];
- }
- Object.keys(params).forEach(param_key => {
- params[param_key] = params[param_key].deep_unpack();
- });
-
- effect.type = type;
- effect.id = id;
- effect.params = params;
- });
- pipeline.name = name;
- pipeline.effects = effects;
- });
- return pips;
+ try {
+ return unpack_pipelines(component_settings.get_value(key.name));
+ } catch (error) {
+ this._warn(`impossible to get pipelines, resetting: ${error.message}`);
+ component_settings.reset(key.name);
+ return unpack_pipelines(component_settings.get_value(key.name));
+ }
},
- set(pips) {
- let pipelines = {};
- Object.keys(pips).forEach(pipeline_id => {
- let pipeline = pips[pipeline_id];
- if (!(pipeline && typeof pipeline === 'object' && pipeline.constructor === Object)) {
- this._warn('impossible to set pipelines, pipeline is not an object');
- return;
- }
-
- if (!('name' in pipeline)) {
- this._warn('impossible to set pipelines, pipeline has no name');
- return;
- }
- if (typeof pipeline.name !== 'string') {
- this._warn('impossible to set pipelines, pipeline name is not a string');
- return;
- }
-
- if (!('effects' in pipeline)) {
- this._warn('impossible to set pipelines, pipeline has no effect');
- return;
- }
- if (!Array.isArray(pipeline.effects)) {
- this._warn('impossible to set pipelines, effects is not an array');
- return;
- }
-
- let gvariant_effects = [];
- pipeline.effects.forEach(effect => {
- if (!(effect instanceof Object)) {
- this._warn('impossible to set pipelines, effect is not an object');
- return;
- }
-
- if (!('type' in effect)) {
- this._warn('impossible to set pipelines, effect has not type');
- return;
- }
- if (typeof effect.type !== 'string') {
- this._warn('impossible to set pipelines, effect type is not a string');
- return;
- }
-
- if (!('id' in effect)) {
- this._warn('impossible to set pipelines, effect has not id');
- return;
- }
- if (typeof effect.id !== 'string') {
- this._warn('impossible to set pipelines, effect id is not a string');
- return;
- }
-
- let params = {};
- if ('params' in effect) {
- params = effect.params;
- }
- let gvariant_params = {};
- Object.keys(params).forEach(param_key => {
- let param = params[param_key];
- if (typeof param === 'boolean')
- gvariant_params[param_key] = GLib.Variant.new_boolean(param);
- else if (typeof param === 'number') {
- if (Number.isInteger(param))
- gvariant_params[param_key] = GLib.Variant.new_int32(param);
- else
- gvariant_params[param_key] = GLib.Variant.new_double(param);
- } else if (typeof param === 'string')
- gvariant_params[param_key] = GLib.Variant.new_string(param);
- else if (Array.isArray(param) && param.length == 4)
- gvariant_params[param_key] = new GLib.Variant("(dddd)", param);
- else
- this._warn('impossible to set pipeline, effect parameter type is unknown');
- });
-
- gvariant_effects.push(
- new GLib.Variant("a{sv}", {
- type: GLib.Variant.new_string(effect.type),
- id: GLib.Variant.new_string(effect.id),
- params: new GLib.Variant("a{sv}", gvariant_params)
- })
- );
- });
-
- pipelines[pipeline_id] = {
- name: GLib.Variant.new_string(pipeline.name),
- effects: new GLib.Variant("av", gvariant_effects)
- };
- });
- let val = new GLib.Variant("a{sa{sv}}", pipelines);
- component_settings.set_value(key.name, val);
+ set(pipelines) {
+ try {
+ component_settings.set_value(key.name, pack_pipelines(pipelines));
+ } catch (error) {
+ this._warn(`impossible to set pipelines: ${error.message}`);
+ }
}
});
break;
@@ -360,4 +210,4 @@ export const Settings = class Settings {
}
};
-Signals.addSignalMethods(Settings.prototype); \ No newline at end of file
+Signals.addSignalMethods(Settings.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/shader_uniforms.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/shader_uniforms.js
new file mode 100644
index 0000000..a82b261
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/shader_uniforms.js
@@ -0,0 +1,61 @@
+import GObject from 'gi://GObject';
+
+const INTEGRAL_UNIFORMS = new Set([
+ 'corners_bottom',
+ 'corners_top',
+ 'divider',
+ 'dir',
+ 'downsampling_mode',
+ 'factor',
+ 'iterations',
+ 'mode',
+ 'operation',
+ 'prefer_closer_pixels',
+ 'straight_corners',
+ 'use_base_pixel',
+]);
+
+export function set_uniform(effect, name, value) {
+ if (!effect._bms_uniforms)
+ effect._bms_uniforms = new Map();
+
+ effect._bms_uniforms.set(name, value);
+ effect._bms_uniforms_dirty = true;
+ effect.queue_repaint();
+}
+
+export function mark_dirty(effect) {
+ if (effect._bms_uniforms)
+ effect._bms_uniforms_dirty = true;
+}
+
+export function upload_uniforms(effect) {
+ if (!effect._bms_uniforms_dirty || !effect._bms_uniforms || !effect.get_actor())
+ return;
+
+ for (const [name, value] of effect._bms_uniforms) {
+ try {
+ effect.set_uniform_value(name, get_shader_value(name, value));
+ } catch (e) {
+ if (!effect._bms_uniform_warning_shown) {
+ effect._bms_uniform_warning_shown = true;
+ console.warn(`[Blur my Shell] shader uniform upload failed for ${effect.constructor.name}`, e);
+ }
+ }
+ }
+
+ effect._bms_uniforms_dirty = false;
+}
+
+function get_shader_value(name, value) {
+ if (typeof value !== 'number')
+ return value;
+
+ if (INTEGRAL_UNIFORMS.has(name))
+ return Math.trunc(value);
+
+ const float_value = new GObject.Value();
+ float_value.init(GObject.TYPE_FLOAT);
+ float_value.set_float(value);
+ return float_value;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js
index 20def0b..b7bcff3 100644
--- a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js
@@ -1,4 +1,5 @@
import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
export const IS_IN_PREFERENCES = typeof global === 'undefined';
@@ -10,12 +11,32 @@ export async function import_in_shell_only(module) {
if (IS_IN_PREFERENCES)
return null;
try {
- return (await import(module)).default;
+ const imported = await import(module);
+ return imported.default ?? imported;
} catch (e) {
return null;
}
}
+const Clutter = await import_in_shell_only('gi://Clutter');
+const Cogl = await import_in_shell_only('gi://Cogl');
+const USES_SHADER_SNIPPET_API =
+ typeof Clutter?.ShaderEffect?.new_with_snippet === 'function';
+
+export function is_usable_blur_module(ns) {
+ if (!ns)
+ return false;
+ try {
+ const { BlurEffect } = ns;
+ if (typeof BlurEffect !== 'function' || !BlurEffect.$gtype)
+ return false;
+ GObject.type_name(BlurEffect.$gtype);
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+
// In use for the effects, to prevent boilerplate code
export function setup_params(outer_this, params) {
// setup each parameter, either with the given or the default value
@@ -39,4 +60,87 @@ export const get_shader_source = (Shell, shader_filename, self_uri) => {
console.warn(`[Blur my Shell > effect] error loading shader from ${shader_path}: ${e}`);
return null;
}
-}; \ No newline at end of file
+};
+
+export function subpixel_stage_offset() {
+ return USES_SHADER_SNIPPET_API ? 0 : 0.5;
+}
+
+export function static_blur_clip_inset() {
+ return USES_SHADER_SNIPPET_API ? 1 : 0;
+}
+
+export function register_shader_effect(meta, effect_class, source) {
+ if (USES_SHADER_SNIPPET_API) {
+ Object.defineProperty(effect_class.prototype, 'vfunc_get_static_snippet', {
+ value() {
+ return create_fragment_shader_snippet(source);
+ },
+ });
+ }
+
+ return GObject.registerClass(meta, effect_class);
+}
+
+function split_fragment_shader(source) {
+ const main_index = source.search(/void\s+main\s*(?:\(\s*(?:void)?\s*\))?\s*\{/);
+ if (main_index < 0)
+ return null;
+
+ const declarations = source.slice(0, main_index).trim();
+ const brace_index = source.indexOf('{', main_index);
+ if (brace_index < 0)
+ return null;
+
+ let depth = 0;
+ for (let i = brace_index; i < source.length; i++) {
+ if (source[i] === '{')
+ depth++;
+ else if (source[i] === '}') {
+ depth--;
+ if (depth === 0) {
+ return {
+ declarations,
+ body: source.slice(brace_index + 1, i).trim(),
+ };
+ }
+ }
+ }
+
+ return null;
+}
+
+function create_fragment_shader_snippet(source) {
+ if (!Cogl || !source)
+ return null;
+
+ const parts = split_fragment_shader(source);
+ if (!parts) {
+ console.warn('[Blur my Shell > effect] could not split shader source');
+ return null;
+ }
+
+ try {
+ const snippet = Cogl.Snippet.new(
+ Cogl.SnippetHook.FRAGMENT,
+ parts.declarations,
+ null
+ );
+ snippet.set_replace(parts.body);
+ return snippet;
+ } catch (e) {
+ console.warn(`[Blur my Shell > effect] could not create shader snippet: ${e}`);
+ return null;
+ }
+}
+
+export function initialize_shader_effect(effect, source) {
+ if (!source || !effect || USES_SHADER_SNIPPET_API)
+ return;
+
+ try {
+ effect.set_shader_source(source);
+ } catch (e) {
+ console.warn(`[Blur my Shell > effect] set_shader_source failed: ${e}`);
+ }
+}