summaryrefslogtreecommitdiff
path: root/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
blob: 0fdfe9ea346390c15cc464247830f70bf605d6cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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);