summaryrefslogtreecommitdiff
path: root/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences
diff options
context:
space:
mode:
authorAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
committerAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
commit70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch)
treeab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences
parent5d94c0a7d44a2255b81815a52a7056a94a39842d (diff)
downloadRaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz
RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip
Replaced file structures for themes in the correct format
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.js106
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js112
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js90
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js190
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js91
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js223
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js168
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js363
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js40
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js38
10 files changed, 1421 insertions, 0 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
new file mode 100644
index 0000000..0583db3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js
@@ -0,0 +1,106 @@
+import GObject from 'gi://GObject';
+
+/// An object to easily manage signals.
+export const Connections = class Connections {
+ constructor() {
+ this.buffer = [];
+ }
+
+ /// 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);
+ }
+
+ }
+
+ /// 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
+ };
+
+ // 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);
+
+ let index = this.buffer.indexOf(infos);
+ if (index >= 0) {
+ this.buffer.splice(index, 1);
+ }
+ });
+ infos.destroy_id = destroy_id;
+ }
+
+ this.buffer.push(infos);
+ }
+
+ /// 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
+ );
+
+ // 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);
+ });
+ }
+
+ /// 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`);
+ }
+ });
+
+ // reset buffer
+ this.buffer = [];
+ }
+
+ _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
new file mode 100644
index 0000000..544d68c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js
@@ -0,0 +1,112 @@
+import St from 'gi://St';
+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) {
+ this.effects_manager = effects_manager;
+ this.settings = settings;
+ this.effect = null;
+ this.attach_effect_to_actor(actor);
+ }
+
+ create_background_with_effect(
+ background_group,
+ widget_name
+ ) {
+ // create the new actor
+ this.actor = new St.Widget({ name: widget_name });
+
+ this.attach_effect_to_actor(this.actor);
+
+ // a dummy `BackgroundManager`, just to access the pipeline easily
+ let bg_manager = new Clutter.Actor;
+ bg_manager.backgroundActor = this.actor;
+ bg_manager._bms_pipeline = this;
+
+ background_group.insert_child_at_index(this.actor, 0);
+
+ return [this.actor, bg_manager];
+ };
+
+ attach_effect_to_actor(actor) {
+ // set the actor
+ if (actor)
+ this.actor = actor;
+ else {
+ this.remove_pipeline_from_actor();
+ return;
+ }
+
+ // build the new effect to be added
+ this.build_effect({
+ unscaled_radius: 2 * this.settings.SIGMA,
+ brightness: this.settings.BRIGHTNESS,
+ });
+
+ this.actor_destroy_id = this.actor.connect(
+ "destroy", () => this.remove_pipeline_from_actor()
+ );
+
+ // add the effect to the actor
+ if (this.actor)
+ this.actor.add_effect(this.effect);
+ else
+ this._warn(`could not add effect to actor, actor does not exist anymore`);
+ }
+
+ remove_pipeline_from_actor() {
+ this.remove_effect();
+ if (this.actor && this.actor_destroy_id)
+ this.actor.disconnect(this.actor_destroy_id);
+ this.actor_destroy_id = null;
+ this.actor = null;
+ }
+
+ build_effect(params) {
+ // create the effect
+ 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
+ );
+ }
+
+ repaint_effect() {
+ this.effect?.queue_repaint();
+ }
+
+ /// 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_effect() {
+ if (this.effect)
+ this.effects_manager.remove(this.effect);
+ this.effect = null;
+
+ if (this._sigma_changed_id)
+ this.settings.settings.disconnect(this._sigma_changed_id);
+ if (this._brightness_changed_id)
+ this.settings.settings.disconnect(this._brightness_changed_id);
+ delete this._sigma_changed_id;
+ delete this._brightness_changed_id;
+ }
+
+ /// Do nothing for this dummy pipeline.
+ /// Note: exposed to public API.
+ change_pipeline_to() { return; }
+
+ /// Note: exposed to public API.
+ destroy() {
+ this.remove_effect();
+ this.remove_pipeline_from_actor();
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > dummy pip] ${str}`);
+ }
+}; \ No newline at end of file
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
new file mode 100644
index 0000000..947d535
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js
@@ -0,0 +1,90 @@
+import { get_supported_effects } from '../effects/effects.js';
+
+/// An object to manage effects (by not destroying them all the time)
+export const EffectsManager = class EffectsManager {
+ constructor(connections) {
+ this.connections = connections;
+ this.used = [];
+ this.SUPPORTED_EFFECTS = get_supported_effects();
+
+ Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
+ // init the arrays containing each unused effect
+ this[effect_name + '_effects'] = [];
+
+ // init the functions for each effect
+ 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.set({
+ ...this.SUPPORTED_EFFECTS[effect_name].class.default_params, ...params
+ });
+ } else
+ effect = new this.SUPPORTED_EFFECTS[effect_name].class({
+ ...this.SUPPORTED_EFFECTS[effect_name].class.default_params, ...params
+ });
+
+ this.used.push(effect);
+ this.connect_to_destroy(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();
+
+ if (effect.old_actor && actor != effect.old_actor)
+ effect.old_actor.disconnect(effect.old_actor_id);
+
+ if (actor && actor != effect.old_actor) {
+ effect.old_actor_id = actor.connect('destroy', _ => {
+ this.remove(effect, true);
+ });
+ }
+ });
+ }
+
+ // 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;
+
+ let index = this.used.indexOf(effect);
+ if (index >= 0) {
+ this.used.splice(index, 1);
+
+ Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
+ if (effect instanceof this.SUPPORTED_EFFECTS[effect_name].class)
+ this[effect_name + '_effects'].push(effect);
+ });
+ }
+ }
+
+ destroy_all() {
+ const immutable_used_list = [...this.used];
+ immutable_used_list.forEach(effect => this.remove(effect));
+ Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
+ this[effect_name + '_effects'].splice(0, this[effect_name + '_effects'].length);
+ });
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > effects mng] ${str}`);
+ }
+};
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
new file mode 100644
index 0000000..fcde72b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js
@@ -0,0 +1,190 @@
+import { Type } from './settings.js';
+
+// This lists the preferences keys
+export const KEYS = [
+ {
+ component: "general", schemas: [
+ { type: Type.PIPELINES, name: "pipelines" },
+ { type: Type.I, name: "hacks-level" },
+ { type: Type.B, name: "debug" },
+ ]
+ },
+ {
+ component: "overview", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "style-components" },
+ ]
+ },
+ {
+ component: "appfolder", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.I, name: "style-dialogs" },
+ ]
+ },
+ {
+ component: "panel", 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.B, name: "unblur-in-overview" },
+ { type: Type.B, name: "force-light-text" },
+ { type: Type.B, name: "override-background" },
+ { type: Type.I, name: "style-panel" },
+ { type: Type.B, name: "override-background-dynamically" },
+ ]
+ },
+ {
+ component: "dash-to-dock", 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.B, name: "unblur-in-overview" },
+ { type: Type.B, name: "override-background" },
+ { type: Type.I, name: "style-dash-to-dock" },
+ ]
+ },
+ {
+ component: "applications", 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: "opacity" },
+ { type: Type.B, name: "dynamic-opacity" },
+ { type: Type.B, name: "blur-on-overview" },
+ { type: Type.B, name: "enable-all" },
+ { type: Type.AS, name: "whitelist" },
+ { type: Type.AS, name: "blacklist" },
+ ]
+ },
+ {
+ component: "lockscreen", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ ]
+ },
+ {
+ component: "window-list", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ ]
+ },
+ {
+ component: "coverflow-alt-tab", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ ]
+ },
+ {
+ component: "screenshot", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ ]
+ },
+ {
+ component: "hidetopbar", schemas: [
+ { type: Type.B, name: "compatibility" },
+ ]
+ },
+ {
+ component: "dash-to-panel", schemas: [
+ { type: Type.B, name: "blur-original-panel" },
+ ]
+ },
+];
+
+
+// This lists the deprecated preferences keys
+export const DEPRECATED_KEYS = [
+ {
+ component: "general", schemas: [
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ { type: Type.B, name: "color-and-noise" },
+ ]
+ },
+ {
+ component: "overview", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "appfolder", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "panel", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "dash-to-dock", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ { type: Type.I, name: "corner-radius" },
+ ]
+ },
+ {
+ component: "applications", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "lockscreen", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "window-list", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "screenshot", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+]; \ No newline at end of file
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
new file mode 100644
index 0000000..5fbfee4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js
@@ -0,0 +1,91 @@
+import GObject from 'gi://GObject';
+import Clutter from 'gi://Clutter';
+
+
+export const PaintSignals = class PaintSignals {
+ constructor(connections) {
+ this.buffer = [];
+ this.connections = connections;
+ }
+
+ connect(actor, blur_effect) {
+ let paint_effect = new EmitPaintSignal();
+ let infos = {
+ actor: actor,
+ paint_effect: paint_effect
+ };
+ let counter = 0;
+
+ 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) { }
+ });
+
+ // 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);
+ }
+ });
+ });
+
+ this.buffer.push(infos);
+ }
+
+ 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);
+
+ // remove from buffer
+ let index = this.buffer.indexOf(infos);
+ this.buffer.splice(index, 1);
+ }
+ });
+ }
+
+ disconnect_all() {
+ this.buffer.forEach(infos => {
+ this.connections.disconnect_all_for(infos.paint_effect);
+ infos.actor.remove_effect(infos.paint_effect);
+ });
+
+ this.buffer = [];
+ }
+};
+
+export const EmitPaintSignal = GObject.registerClass({
+ GTypeName: 'EmitPaintSignal',
+ Signals: {
+ 'update-blur': {
+ param_types: []
+ },
+ }
+},
+ class EmitPaintSignal extends Clutter.Effect {
+ vfunc_paint(node, paint_context, paint_flags) {
+ this.emit("update-blur");
+ 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
new file mode 100644
index 0000000..84b1d7a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js
@@ -0,0 +1,223 @@
+import St from 'gi://St';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Background from 'resource:///org/gnome/shell/ui/background.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
+/// linked to a `pipeline_id` that has been (hopefully) defined in the settings.
+///
+/// It communicates with the settings through the `PipelinesManager` object, and receives different
+/// signals (with `pipeline_id` being an unique string):
+/// - `'pipeline_id'::pipeline-updated`, handing a new pipeline descriptor object, when the pipeline
+/// has been changed enough that it needs to rebuild the effects configuration
+/// - `'pipeline_id'::pipeline-destroyed`, when the pipeline has been destroyed; thus making the
+/// `Pipeline` change its id to `pipeline_default`
+///
+/// And each effect, with an unique `id`, is connected to the `PipelinesManager` for the signals:
+/// - `'pipeline_id'::effect-'id'-key-removed`, handing the key that was removed
+/// - `'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) {
+ this.effects_manager = effects_manager;
+ this.pipelines_manager = pipelines_manager;
+ this.effects = [];
+ this.set_pipeline_id(pipeline_id);
+ this.attach_pipeline_to_actor(actor);
+ }
+
+ /// Create a background linked to the monitor with index `monitor_index`, with a
+ /// `BackgroundManager` that is appended to the list `background_managers`. The background actor
+ /// will be given the name `widget_name` and inserted into the given `background_group`.
+ /// If `use_absolute_position` is false, then the position used is at (0,0); useful when the
+ /// positioning is relative.
+ /// Note: exposed to public API.
+ create_background_with_effects(
+ monitor_index,
+ background_managers,
+ background_group,
+ widget_name,
+ use_absolute_position = true
+ ) {
+ let monitor = Main.layoutManager.monitors[monitor_index];
+
+ // 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
+ 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);
+
+ let bg_manager = new Background.BackgroundManager({
+ container: this.actor,
+ monitorIndex: monitor_index,
+ controlPosition: false,
+ });
+ bg_manager._bms_pipeline = this;
+
+ background_managers.push(bg_manager);
+ background_group.insert_child_at_index(this.actor, 0);
+
+ return this.actor;
+ };
+
+ /// Set the pipeline id, correctly connecting the `Pipeline` object to listen the pipelines
+ /// manager for pipeline-wide changes. This does not update the effects in consequence, call
+ /// `change_pipeline_to` instead if you want to reconstruct the effects too.
+ set_pipeline_id(pipeline_id) {
+ // disconnect ancient signals
+ this.remove_connections();
+
+ // change the id
+ this.pipeline_id = pipeline_id;
+
+ // connect to settings changes
+ this._pipeline_changed_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::pipeline-updated',
+ (_, new_pipeline) => this.update_effects_from_pipeline(new_pipeline)
+ );
+ this._pipeline_destroyed_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::pipeline-destroyed',
+ _ => this.change_pipeline_to("pipeline_default")
+ );
+ }
+
+ /// Disconnect the signals for the pipeline changes. Please note that the signals related to the
+ /// effects are stored with them and removed with `remove_all_effects`.
+ remove_connections() {
+ if (this._pipeline_changed_id)
+ this.pipelines_manager.disconnect(this._pipeline_changed_id);
+ if (this._pipeline_destroyed_id)
+ this.pipelines_manager.disconnect(this._pipeline_destroyed_id);
+ this._pipeline_changed_id = null;
+ this._pipeline_destroyed_id = null;
+ }
+
+ /// 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 {
+ this.remove_pipeline_from_actor();
+ return;
+ }
+
+ // attach the pipeline
+ let pipeline = this.pipelines_manager.pipelines[this.pipeline_id];
+ if (!pipeline) {
+ this._warn(`could not attach pipeline to actor, pipeline "${this.pipeline_id}" not found`);
+ // do not recurse...
+ if ("pipeline_default" in this.pipelines_manager.pipelines) {
+ this.set_pipeline_id("pipeline_default");
+ pipeline = this.pipelines_manager.pipelines["pipeline_default"];
+ } else
+ return;
+ }
+
+ this.actor_destroy_id = this.actor.connect(
+ "destroy", () => this.remove_pipeline_from_actor()
+ );
+
+ // update the effects
+ this.update_effects_from_pipeline(pipeline);
+ }
+
+ remove_pipeline_from_actor() {
+ this.remove_all_effects();
+ if (this.actor && this.actor_destroy_id)
+ this.actor.disconnect(this.actor_destroy_id);
+ this.actor_destroy_id = null;
+ this.actor = null;
+ }
+
+ /// Update the effects from the given pipeline object, the hard way.
+ update_effects_from_pipeline(pipeline) {
+ // remove all effects
+ this.remove_all_effects();
+
+ // build the new effects to be added
+ pipeline.effects.forEach(effect => {
+ 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`);
+ });
+ this.effects.reverse();
+
+ // add the effects to the actor
+ if (this.actor)
+ this.effects.forEach(effect => this.actor.add_effect(effect));
+ else
+ this._warn(`could not add effect to actor, actor does not exist anymore`);
+ }
+
+ /// 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);
+ 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]
+ );
+ effect._effect_key_updated_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::effect-' + effect_infos.id + '-key-updated',
+ (_, key, value) => 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
+ );
+ }
+
+ /// 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() {
+ this.effects.forEach(effect => {
+ this.effects_manager.remove(effect);
+ [
+ effect._effect_key_removed_id,
+ effect._effect_key_updated_id,
+ effect._effect_key_added_id
+ ].forEach(
+ id => { if (id) this.pipelines_manager.disconnect(id); }
+ );
+ delete effect._effect_key_removed_id;
+ delete effect._effect_key_updated_id;
+ delete effect._effect_key_added_id;
+ });
+ this.effects = [];
+ }
+
+ /// Change the pipeline id, and update the effects according to this change.
+ /// Note: exposed to public API.
+ change_pipeline_to(pipeline_id) {
+ this.set_pipeline_id(pipeline_id);
+ this.attach_pipeline_to_actor(this.actor);
+ }
+
+ /// 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;
+ }
+
+ _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/pipelines_manager.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
new file mode 100644
index 0000000..0fdfe9e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
@@ -0,0 +1,168 @@
+const Signals = imports.signals;
+
+/// 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
+/// changed, so the pipelines manager handles it, and dispatches the updates with targeted signals.
+///
+/// It is only connected to ONE signal (the pipelines schema being changed), and emits numerous
+/// which are connected to by both the different `Pipeline` objects in the extension, and by the
+/// different pages of the extension preferences.
+/// It emits three different types of signals:
+///
+/// - general changes to the pipelines list, connected to by the extension preferences:
+/// - `pipeline-list-changed`, when the list of pipelines has changed (by creation or deletion)
+/// - `pipeline-names-changed`, when the name of a pipeline is changed
+///
+/// - signals that are targeted towards a given pipeline, with `pipeline_id` being its unique id:
+/// - `'pipeline_id'::pipeline-updated`, handing a new pipeline descriptor object, when the
+/// pipeline has been changed quite a bit (added/destroyed/reordered the effects)
+/// - `'pipeline_id'::pipeline-destroyed`, when the pipeline has been destroyed
+/// - `'pipeline_id'::pipeline-renamed`, handing the new name, when the pipeline has been
+/// renamed, which is only important for the preferences
+///
+/// - signals that are targeted towards a given effect, with `effect_id` being its unique id, and
+/// `pipeline_id` the unique id of the pipeline it is attached to:
+/// - `'pipeline_id'::effect-'effect_id'-key-removed`, handing the key that was removed
+/// - `'pipeline_id'::effect-'effect_id'-key-updated`, handing the key that was changed and its
+/// new value
+/// - `'pipeline_id'::effect-'effect_id'-key-added`, handing the key that was added and its
+/// value
+export class PipelinesManager {
+ constructor(settings) {
+ this.settings = settings;
+ this.pipelines = this.settings.PIPELINES;
+ this.settings.PIPELINES_changed(_ => this.on_pipeline_update());
+ }
+
+ 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));
+
+ this.pipelines[id] = { name, effects };
+ this.settings.PIPELINES = this.pipelines;
+ this._emit('pipeline-created', id, this.pipelines[id]);
+ this._emit('pipeline-list-changed');
+ return id;
+ }
+
+ duplicate_pipeline(id) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not duplicate pipeline, id ${id} does not exist`);
+ return;
+ }
+ const pipeline = this.pipelines[id];
+ this.create_pipeline(pipeline.name + " - duplicate", [...pipeline.effects]);
+ this.settings.PIPELINES = this.pipelines;
+ }
+
+ delete_pipeline(id) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not delete pipeline, id ${id} does not exist`);
+ return;
+ }
+ if (id == "pipeline_default") {
+ this._warn(`could not delete pipeline "pipeline_default" as it is immutable`);
+ return;
+ }
+ delete this.pipelines[id];
+ this.settings.PIPELINES = this.pipelines;
+ this._emit(id + '::pipeline-destroyed');
+ this._emit('pipeline-list-changed');
+ }
+
+ update_pipeline_effects(id, effects, emit_update_signal = true) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not update pipeline effects, id ${id} does not exist`);
+ return;
+ }
+ this.pipelines[id].effects = [...effects];
+ this.settings.PIPELINES = this.pipelines;
+ if (emit_update_signal)
+ this._emit(id + '::pipeline-updated');
+ }
+
+ rename_pipeline(id, name) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not rename pipeline, id ${id} does not exist`);
+ return;
+ }
+ this.pipelines[id].name = name.slice();
+ this.settings.PIPELINES = this.pipelines;
+ this._emit(id + '::pipeline-renamed', name);
+ this._emit('pipeline-names-changed');
+ }
+
+ on_pipeline_update() {
+ const old_pipelines = this.pipelines;
+ this.pipelines = this.settings.PIPELINES;
+
+ for (var pipeline_id in old_pipelines) {
+ // if we find a pipeline that does not exist anymore, signal it
+ if (!(pipeline_id in this.pipelines)) {
+ this._emit(pipeline_id + '::pipeline-destroyed');
+ continue;
+ }
+
+ const old_pipeline = old_pipelines[pipeline_id];
+ const new_pipeline = this.pipelines[pipeline_id];
+
+ // verify if both pipelines have effects in the same order
+ // if they have, then check for their parameters
+ if (
+ old_pipeline.effects.length == new_pipeline.effects.length &&
+ old_pipeline.effects.every((effect, i) => effect.id === new_pipeline.effects[i].id)
+ ) {
+ for (let i = 0; i < old_pipeline.effects.length; i++) {
+ const old_effect = old_pipeline.effects[i];
+ const new_effect = new_pipeline.effects[i];
+ const id = old_effect.id;
+ for (let key in old_effect.params) {
+ // if a key was removed, we emit to tell the effect to use the default value
+ if (!(key in new_effect.params))
+ this._emit(
+ pipeline_id + '::effect-' + id + '-key-removed', key
+ );
+ // if a key was updated, we emit to tell the effect to change its value
+ else if (old_effect.params[key] != new_effect.params[key])
+ this._emit(
+ pipeline_id + '::effect-' + id + '-key-updated', key, new_effect.params[key]
+ );
+ }
+ for (let key in new_effect.params) {
+ // if a key was added, we emit to tell the effect the key and its value
+ if (!(key in old_effect.params))
+ this._emit(
+ pipeline_id + '::effect-' + id + '-key-added', key, new_effect.params[key]
+ );
+ }
+ }
+ }
+ // if either the order has changed, or there are new effects, then rebuild it
+ else
+ this._emit(pipeline_id + '::pipeline-updated', new_pipeline);
+ }
+ }
+
+ destroy() {
+ this.settings.PIPELINES_disconnect();
+ }
+
+ _emit(signal, ...args) {
+ this.emit(signal, ...args);
+ this._log(`signal: '${signal}', arguments: ${args}`);
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > pipelines] ${str}`);
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > pipelines] ${str}`);
+ }
+}
+
+Signals.addSignalMethods(PipelinesManager.prototype); \ No newline at end of file
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
new file mode 100644
index 0000000..53f8497
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js
@@ -0,0 +1,363 @@
+import GLib from 'gi://GLib';
+const Signals = imports.signals;
+
+/// An enum non-extensively describing the type of gsettings key.
+export const Type = {
+ B: 'Boolean',
+ I: 'Integer',
+ D: 'Double',
+ S: 'String',
+ C: 'Color',
+ AS: 'StringArray',
+ PIPELINES: 'Pipelines'
+};
+
+/// An object to get and manage the gsettings preferences.
+///
+/// Should be initialized with an array of keys, for example:
+///
+/// let settings = new Settings([
+/// { type: Type.I, name: "panel-corner-radius" },
+/// { type: Type.B, name: "debug" }
+/// ]);
+///
+/// Each {type, name} object represents a gsettings key, which must be created
+/// in the gschemas.xml file of the extension.
+export const Settings = class Settings {
+ constructor(keys, settings) {
+ this.settings = settings;
+ this.keys = keys;
+
+ this.keys.forEach(bundle => {
+ let component = this;
+ let component_settings = settings;
+ if (bundle.component !== "general") {
+ let bundle_component = bundle.component.replaceAll('-', '_');
+ this[bundle_component] = {
+ settings: this.settings.get_child(bundle.component)
+ };
+ component = this[bundle_component];
+ component_settings = settings.get_child(bundle.component);
+ }
+
+
+ bundle.schemas.forEach(key => {
+ let property_name = this.get_property_name(key.name);
+
+ switch (key.type) {
+ case Type.B:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_boolean(key.name);
+ },
+ set(v) {
+ component_settings.set_boolean(key.name, v);
+ }
+ });
+ break;
+
+ case Type.I:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_int(key.name);
+ },
+ set(v) {
+ component_settings.set_int(key.name, v);
+ }
+ });
+ break;
+
+ case Type.D:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_double(key.name);
+ },
+ set(v) {
+ component_settings.set_double(key.name, v);
+ }
+ });
+ break;
+
+ case Type.S:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_string(key.name);
+ },
+ set(v) {
+ component_settings.set_string(key.name, v);
+ }
+ });
+ break;
+
+ case Type.C:
+ Object.defineProperty(component, property_name, {
+ // returns the array [red, blue, green, alpha] with
+ // values between 0 and 1
+ get() {
+ let val = component_settings.get_value(key.name);
+ return val.deep_unpack();
+ },
+ // takes an array [red, blue, green, alpha] with
+ // values between 0 and 1
+ set(v) {
+ let val = new GLib.Variant("(dddd)", v);
+ component_settings.set_value(key.name, val);
+ }
+ });
+ break;
+
+ case Type.AS:
+ Object.defineProperty(component, property_name, {
+ get() {
+ let val = component_settings.get_value(key.name);
+ return val.deep_unpack();
+ },
+ set(v) {
+ let val = new GLib.Variant("as", v);
+ component_settings.set_value(key.name, val);
+ }
+ });
+ break;
+
+ 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;
+ },
+ 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);
+ }
+ });
+ break;
+ }
+
+
+ component[property_name + '_reset'] = function () {
+ return component_settings.reset(key.name);
+ };
+
+ component[property_name + '_signal_ids'] = [];
+ component[property_name + '_changed'] = function (cb) {
+ component[property_name + '_signal_ids'].push(
+ component_settings.connect('changed::' + key.name, cb)
+ );
+ };
+
+ component[property_name + '_disconnect'] = function () {
+ component[property_name + '_signal_ids'].forEach(
+ id => component_settings.disconnect(id)
+ );
+ component[property_name + '_signal_ids'] = [];
+ };
+ });
+ });
+ };
+
+ /// Reset the preferences.
+ reset() {
+ this.keys.forEach(bundle => {
+ let component = this;
+ if (bundle.component !== "general") {
+ let bundle_component = bundle.component.replaceAll('-', '_');
+ component = this[bundle_component];
+ }
+
+ bundle.schemas.forEach(key => {
+ let property_name = this.get_property_name(key.name);
+ component[property_name + '_reset']();
+ });
+ });
+
+ this.emit('reset', true);
+ }
+
+ /// From the gschema name, returns the name of the associated property on
+ /// the Settings object.
+ get_property_name(name) {
+ return name.replaceAll('-', '_').toUpperCase();
+ }
+
+ /// Remove all connections managed by the Settings object, i.e. created with
+ /// `settings.PROPERTY_changed(callback)`.
+ disconnect_all_settings() {
+ this.keys.forEach(bundle => {
+ let component = this;
+ if (bundle.component !== "general") {
+ let bundle_component = bundle.component.replaceAll('-', '_');
+ component = this[bundle_component];
+ }
+
+ bundle.schemas.forEach(key => {
+ let property_name = this.get_property_name(key.name);
+ component[property_name + '_disconnect']();
+ });
+ });
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > settings] ${str}`);
+ }
+};
+
+Signals.addSignalMethods(Settings.prototype); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js
new file mode 100644
index 0000000..2affbc1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js
@@ -0,0 +1,40 @@
+import { Settings } from './settings.js';
+import { KEYS, DEPRECATED_KEYS } from './keys.js';
+
+const CURRENT_SETTINGS_VERSION = 2;
+
+export function update_from_old_settings(gsettings) {
+ const preferences = new Settings(KEYS, gsettings);
+ const deprecated_preferences = new Settings(DEPRECATED_KEYS, gsettings);
+ const old_version = preferences.settings.get_int('settings-version');
+
+ if (old_version < CURRENT_SETTINGS_VERSION) {
+ // set artifacts hacks to be 1 at most, as it should be suitable now that most big bugs have
+ // been resolved (and especially because hack levels to 2 now means disabling clipped
+ // redraws entirely, which is very much not what we want for users that update)
+ if (preferences.HACKS_LEVEL > 1)
+ preferences.HACKS_LEVEL = 1;
+
+ // enable dash-to-dock blurring, as most disabled it due to the lack of rounded corners; set
+ // it to static blur by default too and with transparent background
+ preferences.dash_to_dock.BLUR = true;
+ preferences.dash_to_dock.STATIC_BLUR = true;
+ preferences.dash_to_dock.STYLE_DASH_TO_DOCK = 0;
+
+ // 'customize' has been removed: we merge the current used settings
+ ['appfolder', 'panel', 'dash_to_dock', 'applications', 'window_list'].forEach(
+ component_name => {
+ const deprecated_component = deprecated_preferences[component_name];
+ const new_component = preferences[component_name];
+ if (!deprecated_component.CUSTOMIZE) {
+ new_component.SIGMA = deprecated_preferences.SIGMA;
+ new_component.BRIGHTNESS = deprecated_preferences.BRIGHTNESS;
+ }
+ });
+
+ // remove old preferences in order not to clutter the gsettings
+ deprecated_preferences.reset();
+ }
+
+ preferences.settings.set_int('settings-version', CURRENT_SETTINGS_VERSION);
+} \ No newline at end of file
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
new file mode 100644
index 0000000..0277e0e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js
@@ -0,0 +1,38 @@
+import GLib from 'gi://GLib';
+
+export const IS_IN_PREFERENCES = typeof global === 'undefined';
+
+// Taken from https://github.com/Schneegans/Burn-My-Windows/blob/main/src/utils.js
+// This method can be used to import a module in the GNOME Shell process only. This
+// is useful if you want to use a module in extension.js, but not in the preferences
+// process. This method returns null if it is called in the preferences process.
+export async function import_in_shell_only(module) {
+ if (IS_IN_PREFERENCES)
+ return null;
+ return (await import(module)).default;
+}
+
+// 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
+ for (const params_name in outer_this.constructor.default_params) {
+ outer_this["_" + params_name] = null;
+ outer_this[params_name] = params_name in params ?
+ params[params_name] :
+ outer_this.constructor.default_params[params_name];
+ }
+};
+
+export const get_shader_source = (Shell, shader_filename, self_uri) => {
+ if (!Shell)
+ return;
+ const shader_path = GLib.filename_from_uri(
+ GLib.uri_resolve_relative(self_uri, shader_filename, GLib.UriFlags.NONE)
+ )[0];
+ try {
+ return Shell.get_file_contents_utf8_sync(shader_path);
+ } catch (e) {
+ console.warn(`[Blur my Shell > effect] error loading shader from ${shader_path}: ${e}`);
+ return null;
+ }
+}; \ No newline at end of file