pipelines_manager.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. const Signals = imports.signals;
  2. /// The `PipelinesManager` object permits to store the list of pipelines and their effects in
  3. /// memory. It is meant to *always* be in sync with the `org.gnome.shell.extensions.blur-my-shell`'s
  4. /// `pipelines` gschema. However, we do not want to re-create every effect each time this schema is
  5. /// changed, so the pipelines manager handles it, and dispatches the updates with targeted signals.
  6. ///
  7. /// It is only connected to ONE signal (the pipelines schema being changed), and emits numerous
  8. /// which are connected to by both the different `Pipeline` objects in the extension, and by the
  9. /// different pages of the extension preferences.
  10. /// It emits three different types of signals:
  11. ///
  12. /// - general changes to the pipelines list, connected to by the extension preferences:
  13. /// - `pipeline-list-changed`, when the list of pipelines has changed (by creation or deletion)
  14. /// - `pipeline-names-changed`, when the name of a pipeline is changed
  15. ///
  16. /// - signals that are targeted towards a given pipeline, with `pipeline_id` being its unique id:
  17. /// - `'pipeline_id'::pipeline-updated`, handing a new pipeline descriptor object, when the
  18. /// pipeline has been changed quite a bit (added/destroyed/reordered the effects)
  19. /// - `'pipeline_id'::pipeline-destroyed`, when the pipeline has been destroyed
  20. /// - `'pipeline_id'::pipeline-renamed`, handing the new name, when the pipeline has been
  21. /// renamed, which is only important for the preferences
  22. ///
  23. /// - signals that are targeted towards a given effect, with `effect_id` being its unique id, and
  24. /// `pipeline_id` the unique id of the pipeline it is attached to:
  25. /// - `'pipeline_id'::effect-'effect_id'-key-removed`, handing the key that was removed
  26. /// - `'pipeline_id'::effect-'effect_id'-key-updated`, handing the key that was changed and its
  27. /// new value
  28. /// - `'pipeline_id'::effect-'effect_id'-key-added`, handing the key that was added and its
  29. /// value
  30. export class PipelinesManager {
  31. constructor(settings) {
  32. this.settings = settings;
  33. this.pipelines = this.settings.PIPELINES;
  34. this.settings.PIPELINES_changed(_ => this.on_pipeline_update());
  35. }
  36. create_pipeline(name, effects = []) {
  37. // select a random id for the pipeline
  38. let id = "pipeline_" + ("" + Math.random()).slice(2, 16);
  39. // add a random ID for each effect, to help tracking them
  40. effects.forEach(effect => effect.id = "effect_" + ("" + Math.random()).slice(2, 16));
  41. this.pipelines[id] = { name, effects };
  42. this.settings.PIPELINES = this.pipelines;
  43. this._emit('pipeline-created', id, this.pipelines[id]);
  44. this._emit('pipeline-list-changed');
  45. return id;
  46. }
  47. duplicate_pipeline(id) {
  48. if (!(id in this.pipelines)) {
  49. this._warn(`could not duplicate pipeline, id ${id} does not exist`);
  50. return;
  51. }
  52. const pipeline = this.pipelines[id];
  53. this.create_pipeline(pipeline.name + " - duplicate", [...pipeline.effects]);
  54. this.settings.PIPELINES = this.pipelines;
  55. }
  56. delete_pipeline(id) {
  57. if (!(id in this.pipelines)) {
  58. this._warn(`could not delete pipeline, id ${id} does not exist`);
  59. return;
  60. }
  61. if (id == "pipeline_default") {
  62. this._warn(`could not delete pipeline "pipeline_default" as it is immutable`);
  63. return;
  64. }
  65. delete this.pipelines[id];
  66. this.settings.PIPELINES = this.pipelines;
  67. this._emit(id + '::pipeline-destroyed');
  68. this._emit('pipeline-list-changed');
  69. }
  70. update_pipeline_effects(id, effects, emit_update_signal = true) {
  71. if (!(id in this.pipelines)) {
  72. this._warn(`could not update pipeline effects, id ${id} does not exist`);
  73. return;
  74. }
  75. this.pipelines[id].effects = [...effects];
  76. this.settings.PIPELINES = this.pipelines;
  77. if (emit_update_signal)
  78. this._emit(id + '::pipeline-updated');
  79. }
  80. rename_pipeline(id, name) {
  81. if (!(id in this.pipelines)) {
  82. this._warn(`could not rename pipeline, id ${id} does not exist`);
  83. return;
  84. }
  85. this.pipelines[id].name = name.slice();
  86. this.settings.PIPELINES = this.pipelines;
  87. this._emit(id + '::pipeline-renamed', name);
  88. this._emit('pipeline-names-changed');
  89. }
  90. on_pipeline_update() {
  91. const old_pipelines = this.pipelines;
  92. this.pipelines = this.settings.PIPELINES;
  93. for (var pipeline_id in old_pipelines) {
  94. // if we find a pipeline that does not exist anymore, signal it
  95. if (!(pipeline_id in this.pipelines)) {
  96. this._emit(pipeline_id + '::pipeline-destroyed');
  97. continue;
  98. }
  99. const old_pipeline = old_pipelines[pipeline_id];
  100. const new_pipeline = this.pipelines[pipeline_id];
  101. // verify if both pipelines have effects in the same order
  102. // if they have, then check for their parameters
  103. if (
  104. old_pipeline.effects.length == new_pipeline.effects.length &&
  105. old_pipeline.effects.every((effect, i) => effect.id === new_pipeline.effects[i].id)
  106. ) {
  107. for (let i = 0; i < old_pipeline.effects.length; i++) {
  108. const old_effect = old_pipeline.effects[i];
  109. const new_effect = new_pipeline.effects[i];
  110. const id = old_effect.id;
  111. for (let key in old_effect.params) {
  112. // if a key was removed, we emit to tell the effect to use the default value
  113. if (!(key in new_effect.params))
  114. this._emit(
  115. pipeline_id + '::effect-' + id + '-key-removed', key
  116. );
  117. // if a key was updated, we emit to tell the effect to change its value
  118. else if (old_effect.params[key] != new_effect.params[key])
  119. this._emit(
  120. pipeline_id + '::effect-' + id + '-key-updated', key, new_effect.params[key]
  121. );
  122. }
  123. for (let key in new_effect.params) {
  124. // if a key was added, we emit to tell the effect the key and its value
  125. if (!(key in old_effect.params))
  126. this._emit(
  127. pipeline_id + '::effect-' + id + '-key-added', key, new_effect.params[key]
  128. );
  129. }
  130. }
  131. }
  132. // if either the order has changed, or there are new effects, then rebuild it
  133. else
  134. this._emit(pipeline_id + '::pipeline-updated', new_pipeline);
  135. }
  136. }
  137. destroy() {
  138. this.settings.PIPELINES_disconnect();
  139. }
  140. _emit(signal, ...args) {
  141. this.emit(signal, ...args);
  142. this._log(`signal: '${signal}', arguments: ${args}`);
  143. }
  144. _log(str) {
  145. if (this.settings.DEBUG)
  146. console.log(`[Blur my Shell > pipelines] ${str}`);
  147. }
  148. _warn(str) {
  149. console.warn(`[Blur my Shell > pipelines] ${str}`);
  150. }
  151. }
  152. Signals.addSignalMethods(PipelinesManager.prototype);