♻️ Add base classes for the effects and shaders
This commit is contained in:
@@ -128,7 +128,7 @@ var SimpleFade = class SimpleFade {
|
|||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is available on all GNOME Shell versions supported by this extension.
|
// The effect is available on all GNOME Shell versions supported by this extension.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 36];
|
return [3, 36];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,13 +136,13 @@ var SimpleFade = class SimpleFade {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'simple-fade';
|
return 'simple-fade';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Simple Fade Effect');
|
return _('Simple Fade Effect');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ var SimpleFade = class SimpleFade {
|
|||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// binds all properties to the settings and appends the page to the main stack of the
|
||||||
// preferences dialog.
|
// preferences dialog.
|
||||||
static getPreferences(dialog) {
|
getPreferences(dialog) {
|
||||||
// Empty for now... Code is added here later in the tutorial!
|
// Empty for now... Code is added here later in the tutorial!
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ var SimpleFade = class SimpleFade {
|
|||||||
// This is called from extension.js whenever a window is opened or closed with this
|
// This is called from extension.js whenever a window is opened or closed with this
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
// effect. It returns an instance of the shader class, trying to reuse previously
|
||||||
// created shaders.
|
// created shaders.
|
||||||
static getShader(actor, settings, forOpening) {
|
getShader(actor, settings, forOpening) {
|
||||||
let shader;
|
let shader;
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
if (freeShaders.length == 0) {
|
||||||
@@ -170,7 +170,7 @@ var SimpleFade = class SimpleFade {
|
|||||||
shader = freeShaders.pop();
|
shader = freeShaders.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
shader.updateAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
return shader;
|
return shader;
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ var SimpleFade = class SimpleFade {
|
|||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
// animation. This is useful if the effect requires drawing something beyond the usual
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
// bounds of the actor. This only works for GNOME 3.38+.
|
||||||
static getActorScale(settings) {
|
getActorScale(settings) {
|
||||||
return {x: 1.0, y: 1.0};
|
return {x: 1.0, y: 1.0};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,9 +203,9 @@ if (utils.isInShellProcess()) {
|
|||||||
this._uFadeWidth = this.get_uniform_location('uFadeWidth');
|
this._uFadeWidth = this.get_uniform_location('uFadeWidth');
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
// This is called each time the shader is used. This can be used to retrieve the
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
// configuration from the settings and update all uniforms accordingly.
|
||||||
setUniforms(actor, settings, forOpening) {
|
updateAnimation(actor, settings, forOpening) {
|
||||||
this.set_uniform_float(this._uForOpening, 1, [forOpening]);
|
this.set_uniform_float(this._uForOpening, 1, [forOpening]);
|
||||||
this.set_uniform_float(this._uFadeWidth, 1, [settings.get_double('simple-fade-width')]);
|
this.set_uniform_float(this._uFadeWidth, 1, [settings.get_double('simple-fade-width')]);
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-29
@@ -33,17 +33,17 @@ const utils = Me.imports.src.utils;
|
|||||||
|
|
||||||
// New effects must be registered here and in prefs.js.
|
// New effects must be registered here and in prefs.js.
|
||||||
const ALL_EFFECTS = [
|
const ALL_EFFECTS = [
|
||||||
Me.imports.src.Apparition.Apparition,
|
new Me.imports.src.Apparition.Apparition(),
|
||||||
Me.imports.src.BrokenGlass.BrokenGlass,
|
new Me.imports.src.BrokenGlass.BrokenGlass(),
|
||||||
Me.imports.src.EnergizeA.EnergizeA,
|
new Me.imports.src.EnergizeA.EnergizeA(),
|
||||||
Me.imports.src.EnergizeB.EnergizeB,
|
new Me.imports.src.EnergizeB.EnergizeB(),
|
||||||
Me.imports.src.Fire.Fire,
|
new Me.imports.src.Fire.Fire(),
|
||||||
Me.imports.src.Hexagon.Hexagon,
|
new Me.imports.src.Hexagon.Hexagon(),
|
||||||
Me.imports.src.Matrix.Matrix,
|
new Me.imports.src.Matrix.Matrix(),
|
||||||
Me.imports.src.SnapOfDisintegration.SnapOfDisintegration,
|
new Me.imports.src.SnapOfDisintegration.SnapOfDisintegration(),
|
||||||
Me.imports.src.TRexAttack.TRexAttack,
|
new Me.imports.src.TRexAttack.TRexAttack(),
|
||||||
Me.imports.src.TVEffect.TVEffect,
|
new Me.imports.src.TVEffect.TVEffect(),
|
||||||
Me.imports.src.Wisps.Wisps,
|
new Me.imports.src.Wisps.Wisps(),
|
||||||
];
|
];
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -222,7 +222,7 @@ class Extension {
|
|||||||
const shader = actor.get_effect('burn-my-windows-effect');
|
const shader = actor.get_effect('burn-my-windows-effect');
|
||||||
if (shader) {
|
if (shader) {
|
||||||
actor.remove_effect(shader);
|
actor.remove_effect(shader);
|
||||||
shader.free();
|
shader.endAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +306,7 @@ class Extension {
|
|||||||
disable() {
|
disable() {
|
||||||
|
|
||||||
// Free all effect resources.
|
// Free all effect resources.
|
||||||
ALL_EFFECTS.forEach(Effect => Effect.cleanUp());
|
ALL_EFFECTS.forEach(effect => effect.cleanUp());
|
||||||
|
|
||||||
// Unregister our resources.
|
// Unregister our resources.
|
||||||
Gio.resources_unregister(this._resources);
|
Gio.resources_unregister(this._resources);
|
||||||
@@ -371,7 +371,7 @@ class Extension {
|
|||||||
const oldShader = actor.get_effect('burn-my-windows-effect');
|
const oldShader = actor.get_effect('burn-my-windows-effect');
|
||||||
if (oldShader) {
|
if (oldShader) {
|
||||||
actor.remove_effect(oldShader);
|
actor.remove_effect(oldShader);
|
||||||
oldShader.free();
|
oldShader.endAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ choose an effect
|
// ------------------------------------------------------------------ choose an effect
|
||||||
@@ -381,9 +381,7 @@ class Extension {
|
|||||||
|
|
||||||
// First we check if an effect is to be previewed.
|
// First we check if an effect is to be previewed.
|
||||||
if (previewNick != '') {
|
if (previewNick != '') {
|
||||||
effect = ALL_EFFECTS.find(Effect => {
|
effect = ALL_EFFECTS.find(effect => effect.getNick() == previewNick);
|
||||||
return Effect.getNick() == previewNick;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only preview the effect once.
|
// Only preview the effect once.
|
||||||
this._settings.set_string(action + '-preview-effect', '');
|
this._settings.set_string(action + '-preview-effect', '');
|
||||||
@@ -393,8 +391,8 @@ class Extension {
|
|||||||
else {
|
else {
|
||||||
|
|
||||||
// Therefore, we first create a list of all currently enabled effects.
|
// Therefore, we first create a list of all currently enabled effects.
|
||||||
const enabled = ALL_EFFECTS.filter(Effect => {
|
const enabled = ALL_EFFECTS.filter(effect => {
|
||||||
return this._settings.get_boolean(`${Effect.getNick()}-${action}-effect`);
|
return this._settings.get_boolean(`${effect.getNick()}-${action}-effect`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// And then choose a random effect.
|
// And then choose a random effect.
|
||||||
@@ -490,17 +488,12 @@ class Extension {
|
|||||||
|
|
||||||
actor.add_effect_with_name('burn-my-windows-effect', shader);
|
actor.add_effect_with_name('burn-my-windows-effect', shader);
|
||||||
|
|
||||||
|
shader.beginAnimation(actor, this._settings, forOpening);
|
||||||
|
|
||||||
// Update uniforms at each frame.
|
// Update uniforms at each frame.
|
||||||
transition.connect('new-frame', (t) => {
|
transition.connect('new-frame', (t) => {
|
||||||
shader.set_uniform_float(shader.get_uniform_location('uForOpening'), 1,
|
shader.updateAnimation(testMode ? 0.5 : t.get_progress(),
|
||||||
[forOpening]);
|
testMode ? duration / 2 : 0.001 * t.get_elapsed_time());
|
||||||
shader.set_uniform_float(shader.get_uniform_location('uProgress'), 1,
|
|
||||||
[testMode ? 0.5 : t.get_progress()]);
|
|
||||||
shader.set_uniform_float(
|
|
||||||
shader.get_uniform_location('uTime'), 1,
|
|
||||||
[testMode ? duration / 2 : 0.001 * t.get_elapsed_time()]);
|
|
||||||
shader.set_uniform_float(shader.get_uniform_location('uSize'), 2,
|
|
||||||
[actor.width, actor.height]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove the effect if the animation finished or was interrupted.
|
// Remove the effect if the animation finished or was interrupted.
|
||||||
@@ -509,7 +502,7 @@ class Extension {
|
|||||||
const oldShader = actor.get_effect('burn-my-windows-effect');
|
const oldShader = actor.get_effect('burn-my-windows-effect');
|
||||||
if (oldShader) {
|
if (oldShader) {
|
||||||
actor.remove_effect(oldShader);
|
actor.remove_effect(oldShader);
|
||||||
oldShader.free();
|
oldShader.endAnimation();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,17 +32,17 @@ const utils = Me.imports.src.utils;
|
|||||||
|
|
||||||
// New effects must be registered here and in extension.js.
|
// New effects must be registered here and in extension.js.
|
||||||
const ALL_EFFECTS = [
|
const ALL_EFFECTS = [
|
||||||
Me.imports.src.Apparition.Apparition,
|
new Me.imports.src.Apparition.Apparition(),
|
||||||
Me.imports.src.BrokenGlass.BrokenGlass,
|
new Me.imports.src.BrokenGlass.BrokenGlass(),
|
||||||
Me.imports.src.EnergizeA.EnergizeA,
|
new Me.imports.src.EnergizeA.EnergizeA(),
|
||||||
Me.imports.src.EnergizeB.EnergizeB,
|
new Me.imports.src.EnergizeB.EnergizeB(),
|
||||||
Me.imports.src.Fire.Fire,
|
new Me.imports.src.Fire.Fire(),
|
||||||
Me.imports.src.Hexagon.Hexagon,
|
new Me.imports.src.Hexagon.Hexagon(),
|
||||||
Me.imports.src.Matrix.Matrix,
|
new Me.imports.src.Matrix.Matrix(),
|
||||||
Me.imports.src.SnapOfDisintegration.SnapOfDisintegration,
|
new Me.imports.src.SnapOfDisintegration.SnapOfDisintegration(),
|
||||||
Me.imports.src.TRexAttack.TRexAttack,
|
new Me.imports.src.TRexAttack.TRexAttack(),
|
||||||
Me.imports.src.TVEffect.TVEffect,
|
new Me.imports.src.TVEffect.TVEffect(),
|
||||||
Me.imports.src.Wisps.Wisps,
|
new Me.imports.src.Wisps.Wisps(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// This template widget class is defined at the bottom of this file.
|
// This template widget class is defined at the bottom of this file.
|
||||||
@@ -113,24 +113,24 @@ var PreferencesDialog = class PreferencesDialog {
|
|||||||
const group = new Adw.PreferencesGroup({title: _('Effect Options')});
|
const group = new Adw.PreferencesGroup({title: _('Effect Options')});
|
||||||
this.gtkBoxAppend(this._widget, group);
|
this.gtkBoxAppend(this._widget, group);
|
||||||
|
|
||||||
ALL_EFFECTS.forEach(Effect => {
|
ALL_EFFECTS.forEach(effect => {
|
||||||
const [minMajor, minMinor] = Effect.getMinShellVersion();
|
const [minMajor, minMinor] = effect.getMinShellVersion();
|
||||||
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
||||||
|
|
||||||
const row = new Adw.ActionRow({title: Effect.getLabel(), activatable: true});
|
const row = new Adw.ActionRow({title: effect.getLabel(), activatable: true});
|
||||||
row.add_suffix(new Gtk.Image({icon_name: 'go-next-symbolic'}));
|
row.add_suffix(new Gtk.Image({icon_name: 'go-next-symbolic'}));
|
||||||
|
|
||||||
// Open a subpage with the effect's settings.
|
// Open a subpage with the effect's settings.
|
||||||
row.connect('activated', () => {
|
row.connect('activated', () => {
|
||||||
const page = new BurnMyWindowsEffectPage(Effect, this);
|
const page = new BurnMyWindowsEffectPage(effect, this);
|
||||||
page.valign = Gtk.Align.CENTER;
|
page.valign = Gtk.Align.CENTER;
|
||||||
page.margin_top = 10;
|
page.margin_top = 10;
|
||||||
page.margin_bottom = 10;
|
page.margin_bottom = 10;
|
||||||
page.margin_start = 10;
|
page.margin_start = 10;
|
||||||
page.margin_end = 10;
|
page.margin_end = 10;
|
||||||
|
|
||||||
// Add the Effect's preferences (if any).
|
// Add the effect's preferences (if any).
|
||||||
const preferences = Effect.getPreferences(this);
|
const preferences = effect.getPreferences(this);
|
||||||
if (preferences) {
|
if (preferences) {
|
||||||
this.gtkBoxAppend(page, preferences);
|
this.gtkBoxAppend(page, preferences);
|
||||||
}
|
}
|
||||||
@@ -186,23 +186,23 @@ var PreferencesDialog = class PreferencesDialog {
|
|||||||
this.gtkBoxAppend(this._widget, stack);
|
this.gtkBoxAppend(this._widget, stack);
|
||||||
|
|
||||||
// Add all other effect pages.
|
// Add all other effect pages.
|
||||||
ALL_EFFECTS.forEach(Effect => {
|
ALL_EFFECTS.forEach(effect => {
|
||||||
const [minMajor, minMinor] = Effect.getMinShellVersion();
|
const [minMajor, minMinor] = effect.getMinShellVersion();
|
||||||
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
||||||
|
|
||||||
const page = new BurnMyWindowsEffectPage(Effect, this);
|
const page = new BurnMyWindowsEffectPage(effect, this);
|
||||||
page.margin_start = 60;
|
page.margin_start = 60;
|
||||||
page.margin_end = 60;
|
page.margin_end = 60;
|
||||||
page.margin_top = 60;
|
page.margin_top = 60;
|
||||||
page.margin_bottom = 60;
|
page.margin_bottom = 60;
|
||||||
|
|
||||||
// Add the Effect's preferences (if any).
|
// Add the effect's preferences (if any).
|
||||||
const preferences = Effect.getPreferences(this);
|
const preferences = effect.getPreferences(this);
|
||||||
if (preferences) {
|
if (preferences) {
|
||||||
this.gtkBoxAppend(page, preferences);
|
this.gtkBoxAppend(page, preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.add_titled(page, Effect.getNick(), Effect.getLabel());
|
stack.add_titled(page, effect.getNick(), effect.getLabel());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -302,11 +302,11 @@ var PreferencesDialog = class PreferencesDialog {
|
|||||||
const group = Gio.SimpleActionGroup.new();
|
const group = Gio.SimpleActionGroup.new();
|
||||||
window.insert_action_group('open-effects', group);
|
window.insert_action_group('open-effects', group);
|
||||||
|
|
||||||
ALL_EFFECTS.forEach(Effect => {
|
ALL_EFFECTS.forEach(effect => {
|
||||||
const [minMajor, minMinor] = Effect.getMinShellVersion();
|
const [minMajor, minMinor] = effect.getMinShellVersion();
|
||||||
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
||||||
const nick = Effect.getNick();
|
const nick = effect.getNick();
|
||||||
const label = Effect.getLabel();
|
const label = effect.getLabel();
|
||||||
const actionName = nick + '-open-effect';
|
const actionName = nick + '-open-effect';
|
||||||
const fullName = 'open-effects.' + actionName;
|
const fullName = 'open-effects.' + actionName;
|
||||||
|
|
||||||
@@ -324,11 +324,11 @@ var PreferencesDialog = class PreferencesDialog {
|
|||||||
const group = Gio.SimpleActionGroup.new();
|
const group = Gio.SimpleActionGroup.new();
|
||||||
window.insert_action_group('close-effects', group);
|
window.insert_action_group('close-effects', group);
|
||||||
|
|
||||||
ALL_EFFECTS.forEach(Effect => {
|
ALL_EFFECTS.forEach(effect => {
|
||||||
const [minMajor, minMinor] = Effect.getMinShellVersion();
|
const [minMajor, minMinor] = effect.getMinShellVersion();
|
||||||
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
|
||||||
const nick = Effect.getNick();
|
const nick = effect.getNick();
|
||||||
const label = Effect.getLabel();
|
const label = effect.getLabel();
|
||||||
const actionName = nick + '-close-effect';
|
const actionName = nick + '-close-effect';
|
||||||
const fullName = 'close-effects.' + actionName;
|
const fullName = 'close-effects.' + actionName;
|
||||||
|
|
||||||
@@ -490,17 +490,17 @@ var PreferencesDialog = class PreferencesDialog {
|
|||||||
InternalChildren: ['label', 'button'],
|
InternalChildren: ['label', 'button'],
|
||||||
},
|
},
|
||||||
class BurnMyWindowsEffectPage extends Gtk.Box { // ------------------------------
|
class BurnMyWindowsEffectPage extends Gtk.Box { // ------------------------------
|
||||||
_init(Effect, dialog) {
|
_init(effect, dialog) {
|
||||||
super._init();
|
super._init();
|
||||||
|
|
||||||
// Set the effect's name as label.
|
// Set the effect's name as label.
|
||||||
this._label.label = Effect.getLabel();
|
this._label.label = effect.getLabel();
|
||||||
|
|
||||||
// Open the preview window once the preview button is clicked.
|
// Open the preview window once the preview button is clicked.
|
||||||
this._button.connect('clicked', () => {
|
this._button.connect('clicked', () => {
|
||||||
// Set the to-be-previewed effect.
|
// Set the to-be-previewed effect.
|
||||||
dialog.getSettings().set_string('open-preview-effect', Effect.getNick());
|
dialog.getSettings().set_string('open-preview-effect', effect.getNick());
|
||||||
dialog.getSettings().set_string('close-preview-effect', Effect.getNick());
|
dialog.getSettings().set_string('close-preview-effect', effect.getNick());
|
||||||
|
|
||||||
// Make sure that the window.show() firther below "sees" this change.
|
// Make sure that the window.show() firther below "sees" this change.
|
||||||
Gio.Settings.sync();
|
Gio.Settings.sync();
|
||||||
@@ -508,7 +508,7 @@ var PreferencesDialog = class PreferencesDialog {
|
|||||||
// Create the preview-window.
|
// Create the preview-window.
|
||||||
const window = new Gtk.Window({
|
const window = new Gtk.Window({
|
||||||
// Translators: %s will be replaced by the effect's name.
|
// Translators: %s will be replaced by the effect's name.
|
||||||
title: _('Preview for %s').replace('%s', Effect.getLabel()),
|
title: _('Preview for %s').replace('%s', effect.getLabel()),
|
||||||
default_width: 800,
|
default_width: 800,
|
||||||
default_height: 450,
|
default_height: 450,
|
||||||
modal: true,
|
modal: true,
|
||||||
|
|||||||
+56
-102
@@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect hides the actor by violently sucking it into the void of magic. //
|
// This effect hides the actor by violently sucking it into the void of magic. //
|
||||||
@@ -27,24 +28,16 @@ const utils = Me.imports.src.utils;
|
|||||||
// the center. //
|
// the center. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var Apparition = class Apparition extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var Apparition = class Apparition {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is not available on GNOME Shell 3.36 as it requires scaling of the window
|
// The effect is not available on GNOME Shell 3.36 as it requires scaling of the window
|
||||||
// actor.
|
// actor.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 38];
|
return [3, 38];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,22 +45,21 @@ var Apparition = class Apparition {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'apparition';
|
return 'apparition';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Apparition');
|
return _('Apparition');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Apparition.ui`);
|
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Apparition.ui`);
|
||||||
@@ -85,95 +77,57 @@ var Apparition = class Apparition {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
|
||||||
// created shaders.
|
|
||||||
static getShader(actor, settings, forOpening) {
|
|
||||||
let shader;
|
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
|
||||||
shader = new ShaderClass();
|
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
// animation. This is useful if the effect requires drawing something beyond the usual
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
// bounds of the actor. This only works for GNOME 3.38+.
|
||||||
static getActorScale(settings) {
|
getActorScale(settings) {
|
||||||
return {x: 2.0, y: 2.0};
|
return {x: 2.0, y: 2.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
// static resources.
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
static cleanUp() {
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
freeShaders = [];
|
// not be available.
|
||||||
|
createShader() {
|
||||||
|
|
||||||
|
// Only register the shader class when this method is called for the first time.
|
||||||
|
if (!this._ShaderClass) {
|
||||||
|
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uSeed = this.get_uniform_location('uSeed');
|
||||||
|
this._uShake = this.get_uniform_location('uShake');
|
||||||
|
this._uTwirl = this.get_uniform_location('uTwirl');
|
||||||
|
this._uSuction = this.get_uniform_location('uSuction');
|
||||||
|
this._uRandomness = this.get_uniform_location('uRandomness');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
// If we are currently performing integration test, the animation uses a fixed
|
||||||
|
// seed.
|
||||||
|
const testMode = settings.get_boolean('test-mode');
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
||||||
|
this.set_uniform_float(this._uShake, 1, [settings.get_double('apparition-shake-intensity')]);
|
||||||
|
this.set_uniform_float(this._uTwirl, 1, [settings.get_double('apparition-twirl-intensity')]);
|
||||||
|
this.set_uniform_float(this._uSuction, 1, [settings.get_double('apparition-suction-intensity')]);
|
||||||
|
this.set_uniform_float(this._uRandomness, 1, [settings.get_double('apparition-randomness')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const Shell = imports.gi.Shell;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
this._uSeed = this.get_uniform_location('uSeed');
|
|
||||||
this._uShake = this.get_uniform_location('uShake');
|
|
||||||
this._uTwirl = this.get_uniform_location('uTwirl');
|
|
||||||
this._uSuction = this.get_uniform_location('uSuction');
|
|
||||||
this._uRandomness = this.get_uniform_location('uRandomness');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
// If we are currently performing integration test, the animation uses a fixed seed.
|
|
||||||
const testMode = settings.get_boolean('test-mode');
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
|
||||||
this.set_uniform_float(this._uShake, 1, [settings.get_double('apparition-shake-intensity')]);
|
|
||||||
this.set_uniform_float(this._uTwirl, 1, [settings.get_double('apparition-twirl-intensity')]);
|
|
||||||
this.set_uniform_float(this._uSuction, 1, [settings.get_double('apparition-suction-intensity')]);
|
|
||||||
this.set_uniform_float(this._uRandomness, 1, [settings.get_double('apparition-randomness')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${Apparition.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+112
-156
@@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect shatters the window into pieces. For an explanation how this works, look //
|
// This effect shatters the window into pieces. For an explanation how this works, look //
|
||||||
@@ -28,26 +29,15 @@ const utils = Me.imports.src.utils;
|
|||||||
// of vfunc_paint_target further down in this file. //
|
// of vfunc_paint_target further down in this file. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var BrokenGlass = class BrokenGlass extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// This texture will be loaded when the effect is used for the first time.
|
|
||||||
let shardTexture = null;
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var BrokenGlass = class BrokenGlass {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// This effect is only available on GNOME Shell 40+.
|
// This effect is only available on GNOME Shell 40+.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [40, 0];
|
return [40, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,22 +45,21 @@ var BrokenGlass = class BrokenGlass {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'broken-glass';
|
return 'broken-glass';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Broken Glass');
|
return _('Broken Glass');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource('/ui/gtk4/BrokenGlass.ui');
|
dialog.getBuilder().add_from_resource('/ui/gtk4/BrokenGlass.ui');
|
||||||
@@ -88,148 +77,115 @@ var BrokenGlass = class BrokenGlass {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
|
||||||
// created shaders.
|
|
||||||
static getShader(actor, settings, forOpening) {
|
|
||||||
let shader;
|
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
|
||||||
shader = new ShaderClass();
|
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
// animation. This is useful if the effect requires drawing something beyond the usual
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
// bounds of the actor. This only works for GNOME 3.38+.
|
||||||
static getActorScale(settings) {
|
getActorScale(settings) {
|
||||||
return {x: 2.0, y: 2.0};
|
return {x: 2.0, y: 2.0};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
// This is called from extension.js if the extension is disabled. This should free all
|
||||||
// static resources.
|
// static resources.
|
||||||
static cleanUp() {
|
cleanUp() {
|
||||||
freeShaders = [];
|
super.cleanUp();
|
||||||
shardTexture = null;
|
this._shardTexture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
|
// not be available.
|
||||||
|
createShader() {
|
||||||
|
|
||||||
|
// Only register the shader class when this method is called for the first time.
|
||||||
|
if (!this._ShaderClass) {
|
||||||
|
|
||||||
|
const {Clutter, GdkPixbuf, Cogl} = imports.gi;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
const shardData = GdkPixbuf.Pixbuf.new_from_resource('/img/shards.png');
|
||||||
|
this._shardTexture = new Clutter.Image();
|
||||||
|
this._shardTexture.set_data(shardData.get_pixels(), Cogl.PixelFormat.RGB_888,
|
||||||
|
shardData.width, shardData.height, shardData.rowstride);
|
||||||
|
|
||||||
|
// This shader creates a complex-looking effect with rather simple means. Here is
|
||||||
|
// how it works: The window is drawn five times on top of each other (see the
|
||||||
|
// SHARD_LAYERS constant in the GLSL code). Each layer only draws some of the
|
||||||
|
// shards, all layers combined make up the entire window. The layers are then
|
||||||
|
// scaled, rotated, and moved independently from each other - this creates the
|
||||||
|
// impression that all shards are moving independently. In reality, there are only
|
||||||
|
// five groups of shards! Which shard belongs to which layer is defined by the green
|
||||||
|
// channel of the texture resources/img/shards.png. The red channel of the texture
|
||||||
|
// contains the distance to the shard edges. This information is used to fade out
|
||||||
|
// the shards.
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uShardTexture = this.get_uniform_location('uShardTexture');
|
||||||
|
this._uSeed = this.get_uniform_location('uSeed');
|
||||||
|
this._uEpicenter = this.get_uniform_location('uEpicenter');
|
||||||
|
this._uShardScale = this.get_uniform_location('uShardScale');
|
||||||
|
this._uBlowForce = this.get_uniform_location('uBlowForce');
|
||||||
|
this._uGravity = this.get_uniform_location('uGravity');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
// Usually, the shards fly away from the center of the window.
|
||||||
|
let epicenterX = 0.5;
|
||||||
|
let epicenterY = 0.5;
|
||||||
|
|
||||||
|
// However, if this option is set, we use the mouse pointer position.
|
||||||
|
if (!forOpening && settings.get_boolean('broken-glass-use-pointer')) {
|
||||||
|
const [x, y] = global.get_pointer();
|
||||||
|
const [ok, localX, localY] = actor.transform_stage_point(x, y);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
epicenterX = localX / actor.width;
|
||||||
|
epicenterY = localY / actor.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are currently performing integration test, the animation uses a fixed
|
||||||
|
// seed.
|
||||||
|
const testMode = settings.get_boolean('test-mode');
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
||||||
|
this.set_uniform_float(this._uEpicenter, 2, [epicenterX, epicenterY]);
|
||||||
|
this.set_uniform_float(this._uShardScale, 1, [settings.get_double('broken-glass-scale')]);
|
||||||
|
this.set_uniform_float(this._uBlowForce, 1, [settings.get_double('broken-glass-blow-force')]);
|
||||||
|
this.set_uniform_float(this._uGravity, 1, [settings.get_double('broken-glass-gravity')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is overridden to bind the shard texture for drawing. Sadly, this seems to
|
||||||
|
// be impossible under GNOME 3.3x as this.get_pipeline() is not available. It was
|
||||||
|
// called get_target() back then but this is not wrapped in GJS.
|
||||||
|
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
||||||
|
vfunc_paint_target(node, paint_context) {
|
||||||
|
const pipeline = this.get_pipeline();
|
||||||
|
|
||||||
|
// Use linear filtering for the window texture.
|
||||||
|
pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR,
|
||||||
|
Cogl.PipelineFilter.LINEAR);
|
||||||
|
|
||||||
|
// Bind the shard texture.
|
||||||
|
pipeline.set_layer_texture(1, this._effect._shardTexture.get_texture());
|
||||||
|
pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT);
|
||||||
|
pipeline.set_uniform_1i(this._uShardTexture, 1);
|
||||||
|
|
||||||
|
super.vfunc_paint_target(node, paint_context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi;
|
|
||||||
|
|
||||||
// This shader creates a complex-looking effect with rather simple means. Here is how it
|
|
||||||
// works: The window is drawn five times on top of each other (see the SHARD_LAYERS
|
|
||||||
// constant in the GLSL code). Each layer only draws some of the shards, all layers
|
|
||||||
// combined make up the entire window. The layers are then scaled, rotated, and moved
|
|
||||||
// independently from each other - this creates the impression that all shards are
|
|
||||||
// moving independently. In reality, there are only five groups of shards! Which shard
|
|
||||||
// belongs to which layer is defined by the green channel of the texture
|
|
||||||
// resources/img/shards.png. The red channel of the texture contains the distance to the
|
|
||||||
// shard edges. This information is used to fade out the shards.
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
// Load the shards texture.
|
|
||||||
if (shardTexture == null) {
|
|
||||||
const shardData = GdkPixbuf.Pixbuf.new_from_resource('/img/shards.png');
|
|
||||||
shardTexture = new Clutter.Image();
|
|
||||||
shardTexture.set_data(shardData.get_pixels(), Cogl.PixelFormat.RGB_888,
|
|
||||||
shardData.width, shardData.height, shardData.rowstride);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._uShardTexture = this.get_uniform_location('uShardTexture');
|
|
||||||
this._uSeed = this.get_uniform_location('uSeed');
|
|
||||||
this._uEpicenter = this.get_uniform_location('uEpicenter');
|
|
||||||
this._uShardScale = this.get_uniform_location('uShardScale');
|
|
||||||
this._uBlowForce = this.get_uniform_location('uBlowForce');
|
|
||||||
this._uGravity = this.get_uniform_location('uGravity');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
// Usually, the shards fly away from the center of the window.
|
|
||||||
let epicenterX = 0.5;
|
|
||||||
let epicenterY = 0.5;
|
|
||||||
|
|
||||||
// However, if this option is set, we use the mouse pointer position.
|
|
||||||
if (!forOpening && settings.get_boolean('broken-glass-use-pointer')) {
|
|
||||||
const [x, y] = global.get_pointer();
|
|
||||||
const [ok, localX, localY] = actor.transform_stage_point(x, y);
|
|
||||||
|
|
||||||
if (ok) {
|
|
||||||
epicenterX = localX / actor.width;
|
|
||||||
epicenterY = localY / actor.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are currently performing integration test, the animation uses a fixed seed.
|
|
||||||
const testMode = settings.get_boolean('test-mode');
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
|
||||||
this.set_uniform_float(this._uEpicenter, 2, [epicenterX, epicenterY]);
|
|
||||||
this.set_uniform_float(this._uShardScale, 1, [settings.get_double('broken-glass-scale')]);
|
|
||||||
this.set_uniform_float(this._uBlowForce, 1, [settings.get_double('broken-glass-blow-force')]);
|
|
||||||
this.set_uniform_float(this._uGravity, 1, [settings.get_double('broken-glass-gravity')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${BrokenGlass.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is overridden to bind the shard texture for drawing. Sadly, this seems to be
|
|
||||||
// impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called
|
|
||||||
// get_target() back then but this is not wrapped in GJS.
|
|
||||||
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
|
||||||
vfunc_paint_target(node, paint_context) {
|
|
||||||
const pipeline = this.get_pipeline();
|
|
||||||
|
|
||||||
// Use linear filtering for the window texture.
|
|
||||||
pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR,
|
|
||||||
Cogl.PipelineFilter.LINEAR);
|
|
||||||
|
|
||||||
// Bind the shard texture.
|
|
||||||
pipeline.set_layer_texture(1, shardTexture.get_texture());
|
|
||||||
pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT);
|
|
||||||
pipeline.set_uniform_1i(this._uShardTexture, 1);
|
|
||||||
|
|
||||||
super.vfunc_paint_target(node, paint_context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ) ( //
|
||||||
|
// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
|
||||||
|
// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
|
||||||
|
// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
|
||||||
|
// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
|
||||||
|
// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
|
||||||
|
// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
|
||||||
|
// |__/ //
|
||||||
|
// Copyright (c) 2021 Simon Schneegans //
|
||||||
|
// Released under the GPLv3 or later. See LICENSE file for details. //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This is the base class for all effects of Burn-My-Windows. It provides the logic //
|
||||||
|
// required for creating shader instances and reusing them as much as possible. //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var Effect = class Effect {
|
||||||
|
|
||||||
|
// The _freeShaders array contains previously created shaders which are not currently in
|
||||||
|
// use.
|
||||||
|
constructor() {
|
||||||
|
this._freeShaders = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
|
// This is called from extension.js whenever a window is opened or closed with this
|
||||||
|
// effect. It returns an instance of the shader class, trying to reuse previously
|
||||||
|
// created shaders. If a new shader instance is required, it calls this.createShader().
|
||||||
|
// This method must be defined be the derived class!
|
||||||
|
getShader(actor, settings, forOpening) {
|
||||||
|
let shader;
|
||||||
|
|
||||||
|
if (this._freeShaders.length == 0) {
|
||||||
|
shader = this.createShader(this);
|
||||||
|
} else {
|
||||||
|
shader = this._freeShaders.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
shader.updateAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called from extension.js whenever a shader previously retrieved with
|
||||||
|
// getShader() is not used anymore.
|
||||||
|
freeShader(shader) {
|
||||||
|
this._freeShaders.push(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------- "virtual" methods - feel free to override them!
|
||||||
|
|
||||||
|
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
||||||
|
// animation. Override this, if your effect requires drawing something beyond the usual
|
||||||
|
// bounds of the actor. This only works for GNOME 3.38+.
|
||||||
|
getActorScale(settings) {
|
||||||
|
return {x: 1.0, y: 1.0};
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called from extension.js if the extension is disabled. This should free all
|
||||||
|
// static resources. So if you have to delete some textures for example, you should
|
||||||
|
// override this.
|
||||||
|
cleanUp() {
|
||||||
|
freeShaders = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
-98
@@ -20,28 +20,21 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect looks a bit like the transporter effect from TOS. //
|
// This effect looks a bit like the transporter effect from TOS. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var EnergizeA = class EnergizeA extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var EnergizeA = class EnergizeA {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is available on all GNOME Shell versions supported by this extension.
|
// The effect is available on all GNOME Shell versions supported by this extension.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 36];
|
return [3, 36];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,22 +42,21 @@ var EnergizeA = class EnergizeA {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'energize-a';
|
return 'energize-a';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Energize A');
|
return _('Energize A');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/EnergizeA.ui`);
|
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/EnergizeA.ui`);
|
||||||
@@ -80,88 +72,43 @@ var EnergizeA = class EnergizeA {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
// created shaders.
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
static getShader(actor, settings, forOpening) {
|
// not be available.
|
||||||
let shader;
|
createShader() {
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
// Only register the shader class when this method is called for the first time.
|
||||||
shader = new ShaderClass();
|
if (!this._ShaderClass) {
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uColor = this.get_uniform_location('uColor');
|
||||||
|
this._uScale = this.get_uniform_location('uScale');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
const c = Clutter.Color.from_string(settings.get_string('energize-a-color'))[1];
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]);
|
||||||
|
this.set_uniform_float(this._uScale, 1, [settings.get_double('energize-a-scale')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
return shader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
|
||||||
static getActorScale(settings) {
|
|
||||||
return {x: 1.0, y: 1.0};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
|
||||||
// static resources.
|
|
||||||
static cleanUp() {
|
|
||||||
freeShaders = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
this._uColor = this.get_uniform_location('uColor');
|
|
||||||
this._uScale = this.get_uniform_location('uScale');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
const c = Clutter.Color.from_string(settings.get_string('energize-a-color'))[1];
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]);
|
|
||||||
this.set_uniform_float(this._uScale, 1, [settings.get_double('energize-a-scale')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${EnergizeA.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
+45
-98
@@ -20,28 +20,21 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect looks a bit like the transporter effect from TNG. //
|
// This effect looks a bit like the transporter effect from TNG. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var EnergizeB = class EnergizeB extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var EnergizeB = class EnergizeB {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is available on all GNOME Shell versions supported by this extension.
|
// The effect is available on all GNOME Shell versions supported by this extension.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 36];
|
return [3, 36];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,22 +42,21 @@ var EnergizeB = class EnergizeB {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'energize-b';
|
return 'energize-b';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Energize B');
|
return _('Energize B');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/EnergizeB.ui`);
|
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/EnergizeB.ui`);
|
||||||
@@ -80,88 +72,43 @@ var EnergizeB = class EnergizeB {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
// created shaders.
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
static getShader(actor, settings, forOpening) {
|
// not be available.
|
||||||
let shader;
|
createShader() {
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
// Only register the shader class when this method is called for the first time.
|
||||||
shader = new ShaderClass();
|
if (!this._ShaderClass) {
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uColor = this.get_uniform_location('uColor');
|
||||||
|
this._uScale = this.get_uniform_location('uScale');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
const c = Clutter.Color.from_string(settings.get_string('energize-b-color'))[1];
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]);
|
||||||
|
this.set_uniform_float(this._uScale, 1, [settings.get_double('energize-b-scale')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
|
||||||
static getActorScale(settings) {
|
|
||||||
return {x: 1.0, y: 1.0};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
|
||||||
// static resources.
|
|
||||||
static cleanUp() {
|
|
||||||
freeShaders = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
this._uColor = this.get_uniform_location('uColor');
|
|
||||||
this._uScale = this.get_uniform_location('uScale');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
const c = Clutter.Color.from_string(settings.get_string('energize-b-color'))[1];
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]);
|
|
||||||
this.set_uniform_float(this._uScale, 1, [settings.get_double('energize-b-scale')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${EnergizeB.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+64
-116
@@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect is a homage to the good old Compiz days. However, it is implemented //
|
// This effect is a homage to the good old Compiz days. However, it is implemented //
|
||||||
@@ -29,23 +30,15 @@ const utils = Me.imports.src.utils;
|
|||||||
// there are a couple of moving gradients which fade-in or fade-out the fire effect. //
|
// there are a couple of moving gradients which fade-in or fade-out the fire effect. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var Fire = class Fire extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var Fire = class Fire {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is available on all GNOME Shell versions supported by this extension.
|
// The effect is available on all GNOME Shell versions supported by this extension.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 36];
|
return [3, 36];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,22 +46,21 @@ var Fire = class Fire {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'fire';
|
return 'fire';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Fire');
|
return _('Fire');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Fire.ui`);
|
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Fire.ui`);
|
||||||
@@ -94,7 +86,7 @@ var Fire = class Fire {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Initialize the fire-preset dropdown.
|
// Initialize the fire-preset dropdown.
|
||||||
Fire._createFirePresets(dialog);
|
this._createFirePresets(dialog);
|
||||||
|
|
||||||
// Finally, return the new settings page.
|
// Finally, return the new settings page.
|
||||||
return dialog.getBuilder().get_object('fire-prefs');
|
return dialog.getBuilder().get_object('fire-prefs');
|
||||||
@@ -102,40 +94,67 @@ var Fire = class Fire {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
// created shaders.
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
static getShader(actor, settings, forOpening) {
|
// not be available.
|
||||||
let shader;
|
createShader() {
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
// Only register the shader class when this method is called for the first time.
|
||||||
shader = new ShaderClass();
|
if (!this._ShaderClass) {
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uGradient = [
|
||||||
|
this.get_uniform_location('uGradient1'),
|
||||||
|
this.get_uniform_location('uGradient2'),
|
||||||
|
this.get_uniform_location('uGradient3'),
|
||||||
|
this.get_uniform_location('uGradient4'),
|
||||||
|
this.get_uniform_location('uGradient5'),
|
||||||
|
];
|
||||||
|
|
||||||
|
this._u3DNoise = this.get_uniform_location('u3DNoise');
|
||||||
|
this._uScale = this.get_uniform_location('uScale');
|
||||||
|
this._uMovementSpeed = this.get_uniform_location('uMovementSpeed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
// Load the gradient values from the settings.
|
||||||
|
for (let i = 1; i <= 5; i++) {
|
||||||
|
const c =
|
||||||
|
Clutter.Color.from_string(settings.get_string('fire-color-' + i))[1];
|
||||||
|
this.set_uniform_float(
|
||||||
|
this._uGradient[i - 1], 4,
|
||||||
|
[c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._u3DNoise, 1, [settings.get_boolean('flame-3d-noise')]);
|
||||||
|
this.set_uniform_float(this._uScale, 1, [settings.get_double('flame-scale')]);
|
||||||
|
this.set_uniform_float(this._uMovementSpeed, 1, [settings.get_double('flame-movement-speed')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
|
||||||
static getActorScale(settings) {
|
|
||||||
return {x: 1.0, y: 1.0};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
|
||||||
// static resources.
|
|
||||||
static cleanUp() {
|
|
||||||
freeShaders = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------- private stuff
|
// ----------------------------------------------------------------------- private stuff
|
||||||
|
|
||||||
// This populates the preset dropdown menu for the fire options.
|
// This populates the preset dropdown menu for the fire options.
|
||||||
static _createFirePresets(dialog) {
|
_createFirePresets(dialog) {
|
||||||
dialog.getBuilder().get_object('fire-prefs').connect('realize', (widget) => {
|
dialog.getBuilder().get_object('fire-prefs').connect('realize', (widget) => {
|
||||||
const presets = [
|
const presets = [
|
||||||
{
|
{
|
||||||
@@ -221,74 +240,3 @@ var Fire = class Fire {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
this._uGradient = [
|
|
||||||
this.get_uniform_location('uGradient1'),
|
|
||||||
this.get_uniform_location('uGradient2'),
|
|
||||||
this.get_uniform_location('uGradient3'),
|
|
||||||
this.get_uniform_location('uGradient4'),
|
|
||||||
this.get_uniform_location('uGradient5'),
|
|
||||||
];
|
|
||||||
|
|
||||||
this._u3DNoise = this.get_uniform_location('u3DNoise');
|
|
||||||
this._uScale = this.get_uniform_location('uScale');
|
|
||||||
this._uMovementSpeed = this.get_uniform_location('uMovementSpeed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
|
|
||||||
// Load the gradient values from the settings.
|
|
||||||
for (let i = 1; i <= 5; i++) {
|
|
||||||
const c = Clutter.Color.from_string(settings.get_string('fire-color-' + i))[1];
|
|
||||||
this.set_uniform_float(this._uGradient[i - 1], 4,
|
|
||||||
[c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._u3DNoise, 1, [settings.get_boolean('flame-3d-noise')]);
|
|
||||||
this.set_uniform_float(this._uScale, 1, [settings.get_double('flame-scale')]);
|
|
||||||
this.set_uniform_float(this._uMovementSpeed, 1, [settings.get_double('flame-movement-speed')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${Fire.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+62
-113
@@ -20,29 +20,22 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect overlays a glowing hexagonal grid over the window. The grid cells then //
|
// This effect overlays a glowing hexagonal grid over the window. The grid cells then //
|
||||||
// gradually shrink until the window is fully dissolved. //
|
// gradually shrink until the window is fully dissolved. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var Hexagon = class Hexagon extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var Hexagon = class Hexagon {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is available on all GNOME Shell versions supported by this extension.
|
// The effect is available on all GNOME Shell versions supported by this extension.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 36];
|
return [3, 36];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,22 +43,21 @@ var Hexagon = class Hexagon {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'hexagon';
|
return 'hexagon';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Hexagon');
|
return _('Hexagon');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Hexagon.ui`);
|
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Hexagon.ui`);
|
||||||
@@ -84,103 +76,60 @@ var Hexagon = class Hexagon {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
// created shaders.
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
static getShader(actor, settings, forOpening) {
|
// not be available.
|
||||||
let shader;
|
createShader() {
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
// Only register the shader class when this method is called for the first time.
|
||||||
shader = new ShaderClass();
|
if (!this._ShaderClass) {
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uAdditiveBlending = this.get_uniform_location('uAdditiveBlending');
|
||||||
|
this._uSeed = this.get_uniform_location('uSeed');
|
||||||
|
this._uScale = this.get_uniform_location('uScale');
|
||||||
|
this._uLineWidth = this.get_uniform_location('uLineWidth');
|
||||||
|
this._uGlowColor = this.get_uniform_location('uGlowColor');
|
||||||
|
this._uLineColor = this.get_uniform_location('uLineColor');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
// Get the two configurable colors. They are directly injected into the shader
|
||||||
|
// code below.
|
||||||
|
const gc =
|
||||||
|
Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1];
|
||||||
|
const lc =
|
||||||
|
Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1];
|
||||||
|
|
||||||
|
// If we are currently performing integration test, the animation uses a fixed
|
||||||
|
// seed.
|
||||||
|
const testMode = settings.get_boolean('test-mode');
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uAdditiveBlending, 1, [settings.get_boolean('hexagon-additive-blending')]);
|
||||||
|
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
||||||
|
this.set_uniform_float(this._uScale, 1, [settings.get_double('hexagon-scale')]);
|
||||||
|
this.set_uniform_float(this._uLineWidth, 1, [settings.get_double('hexagon-line-width')]);
|
||||||
|
this.set_uniform_float(this._uGlowColor, 4, [gc.red / 255, gc.green / 255, gc.blue / 255, gc.alpha / 255]);
|
||||||
|
this.set_uniform_float(this._uLineColor, 4, [lc.red / 255, lc.green / 255, lc.blue / 255, lc.alpha / 255]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
|
||||||
static getActorScale(settings) {
|
|
||||||
return {x: 1.0, y: 1.0};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
|
||||||
// static resources.
|
|
||||||
static cleanUp() {
|
|
||||||
freeShaders = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
this._uAdditiveBlending = this.get_uniform_location('uAdditiveBlending');
|
|
||||||
this._uSeed = this.get_uniform_location('uSeed');
|
|
||||||
this._uScale = this.get_uniform_location('uScale');
|
|
||||||
this._uLineWidth = this.get_uniform_location('uLineWidth');
|
|
||||||
this._uGlowColor = this.get_uniform_location('uGlowColor');
|
|
||||||
this._uLineColor = this.get_uniform_location('uLineColor');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
|
|
||||||
// Get the two configurable colors. They are directly injected into the shader code
|
|
||||||
// below.
|
|
||||||
const gc = Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1];
|
|
||||||
const lc = Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1];
|
|
||||||
|
|
||||||
// If we are currently performing integration test, the animation uses a fixed seed.
|
|
||||||
const testMode = settings.get_boolean('test-mode');
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uAdditiveBlending, 1, [settings.get_boolean('hexagon-additive-blending')]);
|
|
||||||
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
|
||||||
this.set_uniform_float(this._uScale, 1, [settings.get_double('hexagon-scale')]);
|
|
||||||
this.set_uniform_float(this._uLineWidth, 1, [settings.get_double('hexagon-line-width')]);
|
|
||||||
this.set_uniform_float(this._uGlowColor, 4, [gc.red / 255, gc.green / 255, gc.blue / 255, gc.alpha / 255]);
|
|
||||||
this.set_uniform_float(this._uLineColor, 4, [lc.red / 255, lc.green / 255, lc.blue / 255, lc.alpha / 255]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${Hexagon.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+83
-129
@@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// The Matrix shader multiplies a grid of random letters with some gradients which are //
|
// The Matrix shader multiplies a grid of random letters with some gradients which are //
|
||||||
@@ -29,26 +30,15 @@ const utils = Me.imports.src.utils;
|
|||||||
// documentation of vfunc_paint_target further down in this file. //
|
// documentation of vfunc_paint_target further down in this file. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var Matrix = class Matrix extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// This texture will be loaded when the effect is used for the first time.
|
|
||||||
let fontTexture = null;
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var Matrix = class Matrix {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// This effect is only available on GNOME Shell 40+.
|
// This effect is only available on GNOME Shell 40+.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [40, 0];
|
return [40, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,22 +46,21 @@ var Matrix = class Matrix {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'matrix';
|
return 'matrix';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Matrix');
|
return _('Matrix');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource('/ui/gtk4/Matrix.ui');
|
dialog.getBuilder().add_from_resource('/ui/gtk4/Matrix.ui');
|
||||||
@@ -90,121 +79,86 @@ var Matrix = class Matrix {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
|
||||||
// created shaders.
|
|
||||||
static getShader(actor, settings, forOpening) {
|
|
||||||
let shader;
|
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
|
||||||
shader = new ShaderClass();
|
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
// animation. This is useful if the effect requires drawing something beyond the usual
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
// bounds of the actor. This only works for GNOME 3.38+.
|
||||||
static getActorScale(settings) {
|
getActorScale(settings) {
|
||||||
return {x: 1.0, y: 1.0 + settings.get_double('matrix-overshoot')};
|
return {x: 1.0, y: 1.0 + settings.get_double('matrix-overshoot')};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
// This is called from extension.js if the extension is disabled. This should free all
|
||||||
// static resources.
|
// static resources.
|
||||||
static cleanUp() {
|
cleanUp() {
|
||||||
freeShaders = [];
|
super.cleanUp();
|
||||||
fontTexture = null;
|
this._fontTexture = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
|
// not be available.
|
||||||
|
createShader() {
|
||||||
|
|
||||||
|
// Only register the shader class when this method is called for the first time.
|
||||||
|
if (!this._ShaderClass) {
|
||||||
|
|
||||||
|
const {Clutter, GdkPixbuf, Cogl} = imports.gi;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
const fontData = GdkPixbuf.Pixbuf.new_from_resource('/img/matrixFont.png');
|
||||||
|
this._fontTexture = new Clutter.Image();
|
||||||
|
this._fontTexture.set_data(
|
||||||
|
fontData.get_pixels(),
|
||||||
|
fontData.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888,
|
||||||
|
fontData.width, fontData.height, fontData.rowstride);
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uFontTexture = this.get_uniform_location('uFontTexture');
|
||||||
|
this._uTrailColor = this.get_uniform_location('uTrailColor');
|
||||||
|
this._uTipColor = this.get_uniform_location('uTipColor');
|
||||||
|
this._uLetterSize = this.get_uniform_location('uLetterSize');
|
||||||
|
this._uRandomness = this.get_uniform_location('uRandomness');
|
||||||
|
this._uOverShoot = this.get_uniform_location('uOverShoot');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
const c1 =
|
||||||
|
Clutter.Color.from_string(settings.get_string('matrix-trail-color'))[1];
|
||||||
|
const c2 =
|
||||||
|
Clutter.Color.from_string(settings.get_string('matrix-tip-color'))[1];
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uTrailColor, 3, [c1.red / 255, c1.green / 255, c1.blue / 255]);
|
||||||
|
this.set_uniform_float(this._uTipColor, 3, [c2.red / 255, c2.green / 255, c2.blue / 255]);
|
||||||
|
this.set_uniform_float(this._uLetterSize, 1, [settings.get_int('matrix-scale')]);
|
||||||
|
this.set_uniform_float(this._uRandomness, 1, [settings.get_double('matrix-randomness')]);
|
||||||
|
this.set_uniform_float(this._uOverShoot, 1, [settings.get_double('matrix-overshoot')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is overridden to bind the font texture for drawing. Sadly, this seems to
|
||||||
|
// be impossible under GNOME 3.3x as this.get_pipeline() is not available. It was
|
||||||
|
// called get_target() back then but this is not wrapped in GJS.
|
||||||
|
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
||||||
|
vfunc_paint_target(node, paint_context) {
|
||||||
|
const pipeline = this.get_pipeline();
|
||||||
|
pipeline.set_layer_texture(1, this._effect._fontTexture.get_texture());
|
||||||
|
pipeline.set_uniform_1i(this._uFontTexture, 1);
|
||||||
|
|
||||||
|
super.vfunc_paint_target(node, paint_context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
// Load the font texture.
|
|
||||||
if (fontTexture == null) {
|
|
||||||
const fontData = GdkPixbuf.Pixbuf.new_from_resource('/img/matrixFont.png');
|
|
||||||
fontTexture = new Clutter.Image();
|
|
||||||
fontTexture.set_data(
|
|
||||||
fontData.get_pixels(),
|
|
||||||
fontData.has_alpha ? Cogl.PixelFormat.RGBA_8888 : Cogl.PixelFormat.RGB_888,
|
|
||||||
fontData.width, fontData.height, fontData.rowstride);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._uFontTexture = this.get_uniform_location('uFontTexture');
|
|
||||||
this._uTrailColor = this.get_uniform_location('uTrailColor');
|
|
||||||
this._uTipColor = this.get_uniform_location('uTipColor');
|
|
||||||
this._uLetterSize = this.get_uniform_location('uLetterSize');
|
|
||||||
this._uRandomness = this.get_uniform_location('uRandomness');
|
|
||||||
this._uOverShoot = this.get_uniform_location('uOverShoot');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
const c1 = Clutter.Color.from_string(settings.get_string('matrix-trail-color'))[1];
|
|
||||||
const c2 = Clutter.Color.from_string(settings.get_string('matrix-tip-color'))[1];
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uTrailColor, 3, [c1.red / 255, c1.green / 255, c1.blue / 255]);
|
|
||||||
this.set_uniform_float(this._uTipColor, 3, [c2.red / 255, c2.green / 255, c2.blue / 255]);
|
|
||||||
this.set_uniform_float(this._uLetterSize, 1, [settings.get_int('matrix-scale')]);
|
|
||||||
this.set_uniform_float(this._uRandomness, 1, [settings.get_double('matrix-randomness')]);
|
|
||||||
this.set_uniform_float(this._uOverShoot, 1, [settings.get_double('matrix-overshoot')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time. The technique for this effect was inspired by
|
|
||||||
// https://www.shadertoy.com/view/ldccW4, however the implementation is quite
|
|
||||||
// different as the letters drop only once and there is no need for a noise texture.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${Matrix.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is overridden to bind the font texture for drawing. Sadly, this seems to be
|
|
||||||
// impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called
|
|
||||||
// get_target() back then but this is not wrapped in GJS.
|
|
||||||
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
|
||||||
vfunc_paint_target(node, paint_context) {
|
|
||||||
const pipeline = this.get_pipeline();
|
|
||||||
pipeline.set_layer_texture(1, fontTexture.get_texture());
|
|
||||||
pipeline.set_uniform_1i(this._uFontTexture, 1);
|
|
||||||
|
|
||||||
super.vfunc_paint_target(node, paint_context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// ) ( //
|
||||||
|
// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
|
||||||
|
// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
|
||||||
|
// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
|
||||||
|
// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
|
||||||
|
// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
|
||||||
|
// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
|
||||||
|
// |__/ //
|
||||||
|
// Copyright (c) 2021 Simon Schneegans //
|
||||||
|
// Released under the GPLv3 or later. See LICENSE file for details. //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {Gio, Shell, GObject} = imports.gi;
|
||||||
|
const ByteArray = imports.byteArray;
|
||||||
|
|
||||||
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
|
const utils = Me.imports.src.utils;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This is the base class for all shaders of Burn-My-Windows. It automagically loads //
|
||||||
|
// the shader's source code from the resource file resources/shaders/<nick>.glsl and //
|
||||||
|
// ensures that some standard uniforms are always updated. //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var Shader = GObject.registerClass({}, class Shader extends Shell.GLSLEffect {
|
||||||
|
// The constructor is used to store all required uniform locations. Make sure to chain
|
||||||
|
// up to this base constructor before trying to access the uniform locations!
|
||||||
|
_init(effect) {
|
||||||
|
this._effect = effect;
|
||||||
|
|
||||||
|
// This will call vfunc_build_pipeline().
|
||||||
|
super._init();
|
||||||
|
|
||||||
|
this._uForOpening = this.get_uniform_location('uForOpening');
|
||||||
|
this._uProgress = this.get_uniform_location('uProgress');
|
||||||
|
this._uTime = this.get_uniform_location('uTime');
|
||||||
|
this._uSize = this.get_uniform_location('uSize');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called each time the shader is used. This can be used to retrieve the
|
||||||
|
// configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
this.set_uniform_float(this._uForOpening, 1, [forOpening]);
|
||||||
|
this.set_uniform_float(this._uSize, 2, [actor.width, actor.height]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called at each frame during the animation. This can be used to update
|
||||||
|
// uniforms which need to change each frame.
|
||||||
|
updateAnimation(progress, time) {
|
||||||
|
this.set_uniform_float(this._uProgress, 1, [progress]);
|
||||||
|
this.set_uniform_float(this._uTime, 1, [time]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called by extension.js when the shader is not used anymore. We will
|
||||||
|
// store this instance of the shader so that it can be re-used in th future.
|
||||||
|
endAnimation() {
|
||||||
|
this._effect.freeShader(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called by the constructor. This means, it's only called when the
|
||||||
|
// effect is used for the first time.
|
||||||
|
vfunc_build_pipeline() {
|
||||||
|
const code = this._loadGLSLResource(`/shaders/${this._effect.getNick()}.glsl`);
|
||||||
|
|
||||||
|
// Match anything between the curly brackets of "void main() {...}".
|
||||||
|
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
||||||
|
const match = regex.exec(code);
|
||||||
|
|
||||||
|
const declarations = code.substr(0, match.index);
|
||||||
|
const main = match[1];
|
||||||
|
|
||||||
|
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------- private stuff
|
||||||
|
|
||||||
|
// This loads the file at 'path' contained in the extension's resources to a JavaScript
|
||||||
|
// string.
|
||||||
|
_loadStringResource(path) {
|
||||||
|
const data = Gio.resources_lookup_data(path, 0);
|
||||||
|
return ByteArray.toString(ByteArray.fromGBytes(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This loads a GLSL file from the extension's resources to a JavaScript string. Any
|
||||||
|
// #include statements in this file are replaced with the corresponding file contents.
|
||||||
|
_loadGLSLResource(path) {
|
||||||
|
let code = this._loadStringResource(path);
|
||||||
|
|
||||||
|
// This regex matches either #include "..." or #include <...>. The part between the
|
||||||
|
// brackets is captured in the capture group.
|
||||||
|
const regex = RegExp('#include ["<](.+)[">]', 'g');
|
||||||
|
|
||||||
|
code = code.replace(regex, (m, file) => {
|
||||||
|
return this._loadStringResource('/shaders/' + file);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a trailing newline. Else the GLSL compiler complains...
|
||||||
|
return code + '\n';
|
||||||
|
}
|
||||||
|
});
|
||||||
+76
-120
@@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effects dissolves your windows into a cloud of dust. For this, it uses an //
|
// This effects dissolves your windows into a cloud of dust. For this, it uses an //
|
||||||
@@ -31,26 +32,15 @@ const utils = Me.imports.src.utils;
|
|||||||
// documentation of vfunc_paint_target further down in this file. //
|
// documentation of vfunc_paint_target further down in this file. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var SnapOfDisintegration = class SnapOfDisintegration extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// This texture will be loaded when the effect is used for the first time.
|
|
||||||
let dustTexture = null;
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var SnapOfDisintegration = class SnapOfDisintegration {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// This effect is only available on GNOME Shell 40+.
|
// This effect is only available on GNOME Shell 40+.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [40, 0];
|
return [40, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,22 +48,21 @@ var SnapOfDisintegration = class SnapOfDisintegration {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'snap';
|
return 'snap';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Snap of Disintegration');
|
return _('Snap of Disintegration');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource('/ui/gtk4/SnapOfDisintegration.ui');
|
dialog.getBuilder().add_from_resource('/ui/gtk4/SnapOfDisintegration.ui');
|
||||||
@@ -89,119 +78,86 @@ var SnapOfDisintegration = class SnapOfDisintegration {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
|
||||||
// created shaders.
|
|
||||||
static getShader(actor, settings, forOpening) {
|
|
||||||
let shader;
|
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
|
||||||
shader = new ShaderClass();
|
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
// animation. This is useful if the effect requires drawing something beyond the usual
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
// bounds of the actor. This only works for GNOME 3.38+.
|
||||||
static getActorScale(settings) {
|
getActorScale(settings) {
|
||||||
return {x: 1.2, y: 1.2};
|
return {x: 1.2, y: 1.2};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
// This is called from extension.js if the extension is disabled. This should free all
|
||||||
// static resources.
|
// static resources.
|
||||||
static cleanUp() {
|
cleanUp() {
|
||||||
freeShaders = [];
|
super.cleanUp();
|
||||||
dustTexture = null;
|
this._dustTexture = null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
|
// not be available.
|
||||||
|
createShader() {
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
// Only register the shader class when this method is called for the first time.
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
if (!this._ShaderClass) {
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
const {Clutter, GdkPixbuf, Cogl} = imports.gi;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
// Load the dust texture.
|
// Load the dust texture.
|
||||||
if (dustTexture == null) {
|
const dustData = GdkPixbuf.Pixbuf.new_from_resource('/img/dust.png');
|
||||||
const dustData = GdkPixbuf.Pixbuf.new_from_resource('/img/dust.png');
|
this._dustTexture = new Clutter.Image();
|
||||||
dustTexture = new Clutter.Image();
|
this._dustTexture.set_data(dustData.get_pixels(), Cogl.PixelFormat.RGB_888,
|
||||||
dustTexture.set_data(dustData.get_pixels(), Cogl.PixelFormat.RGB_888,
|
dustData.width, dustData.height, dustData.rowstride);
|
||||||
dustData.width, dustData.height, dustData.rowstride);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._uDustTexture = this.get_uniform_location('uDustTexture');
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
this._uDustColor = this.get_uniform_location('uDustColor');
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
this._uSeed = this.get_uniform_location('uSeed');
|
_init(effect) {
|
||||||
this._uDustScale = this.get_uniform_location('uDustScale');
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uDustTexture = this.get_uniform_location('uDustTexture');
|
||||||
|
this._uDustColor = this.get_uniform_location('uDustColor');
|
||||||
|
this._uSeed = this.get_uniform_location('uSeed');
|
||||||
|
this._uDustScale = this.get_uniform_location('uDustScale');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
// The dust particles will fade to this color over time.
|
||||||
|
const c = Clutter.Color.from_string(settings.get_string('snap-color'))[1];
|
||||||
|
|
||||||
|
// If we are currently performing integration test, the animation uses a fixed
|
||||||
|
// seed.
|
||||||
|
const testMode = settings.get_boolean('test-mode');
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uDustColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]);
|
||||||
|
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
||||||
|
this.set_uniform_float(this._uDustScale, 1, [settings.get_double('snap-scale')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is overridden to bind the dust texture for drawing. Sadly, this seems to
|
||||||
|
// be
|
||||||
|
// impossible under GNOME 3.3x as this.get_pipeline() is not available. It was
|
||||||
|
// called get_target() back then but this is not wrapped in GJS.
|
||||||
|
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
||||||
|
vfunc_paint_target(node, paint_context) {
|
||||||
|
const pipeline = this.get_pipeline();
|
||||||
|
pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR,
|
||||||
|
Cogl.PipelineFilter.LINEAR);
|
||||||
|
pipeline.set_layer_texture(1, this._effect._dustTexture.get_texture());
|
||||||
|
pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT);
|
||||||
|
pipeline.set_uniform_1i(this._uDustTexture, 1);
|
||||||
|
super.vfunc_paint_target(node, paint_context);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
// Finally, return a new instance of the shader class.
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
return new this._ShaderClass(this);
|
||||||
setUniforms(actor, settings, forOpening) {
|
}
|
||||||
// The dust particles will fade to this color over time.
|
}
|
||||||
const c = Clutter.Color.from_string(settings.get_string('snap-color'))[1];
|
|
||||||
|
|
||||||
// If we are currently performing integration test, the animation uses a fixed seed.
|
|
||||||
const testMode = settings.get_boolean('test-mode');
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uDustColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]);
|
|
||||||
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
|
||||||
this.set_uniform_float(this._uDustScale, 1, [settings.get_double('snap-scale')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code =
|
|
||||||
utils.loadGLSLResource(`/shaders/${SnapOfDisintegration.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is overridden to bind the dust texture for drawing. Sadly, this seems to be
|
|
||||||
// impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called
|
|
||||||
// get_target() back then but this is not wrapped in GJS.
|
|
||||||
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
|
||||||
vfunc_paint_target(node, paint_context) {
|
|
||||||
const pipeline = this.get_pipeline();
|
|
||||||
pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR,
|
|
||||||
Cogl.PipelineFilter.LINEAR);
|
|
||||||
pipeline.set_layer_texture(1, dustTexture.get_texture());
|
|
||||||
pipeline.set_layer_wrap_mode(1, Cogl.PipelineWrapMode.REPEAT);
|
|
||||||
pipeline.set_uniform_1i(this._uDustTexture, 1);
|
|
||||||
super.vfunc_paint_target(node, paint_context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
+78
-120
@@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect tears your windows apart with a series of violent scratches! //
|
// This effect tears your windows apart with a series of violent scratches! //
|
||||||
@@ -27,26 +28,15 @@ const utils = Me.imports.src.utils;
|
|||||||
// documentation of vfunc_paint_target further down in this file. //
|
// documentation of vfunc_paint_target further down in this file. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var TRexAttack = class TRexAttack extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// This texture will be loaded when the effect is used for the first time.
|
|
||||||
let clawTexture = null;
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var TRexAttack = class TRexAttack {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// This effect is only available on GNOME Shell 40+.
|
// This effect is only available on GNOME Shell 40+.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [40, 0];
|
return [40, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,22 +44,21 @@ var TRexAttack = class TRexAttack {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'trex';
|
return 'trex';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('T-Rex Attack');
|
return _('T-Rex Attack');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource('/ui/gtk4/TRexAttack.ui');
|
dialog.getBuilder().add_from_resource('/ui/gtk4/TRexAttack.ui');
|
||||||
@@ -87,120 +76,89 @@ var TRexAttack = class TRexAttack {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
|
||||||
// created shaders.
|
|
||||||
static getShader(actor, settings, forOpening) {
|
|
||||||
let shader;
|
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
|
||||||
shader = new ShaderClass();
|
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
// animation. This is useful if the effect requires drawing something beyond the usual
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
// bounds of the actor. This only works for GNOME 3.38+.
|
||||||
static getActorScale(settings) {
|
getActorScale(settings) {
|
||||||
const scale = 1.0 + 0.5 * settings.get_double('claw-scratch-warp');
|
const scale = 1.0 + 0.5 * settings.get_double('claw-scratch-warp');
|
||||||
return {x: scale, y: scale};
|
return {x: scale, y: scale};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
// This is called from extension.js if the extension is disabled. This should free all
|
||||||
// static resources.
|
// static resources.
|
||||||
static cleanUp() {
|
cleanUp() {
|
||||||
freeShaders = [];
|
super.cleanUp();
|
||||||
clawTexture = null;
|
this._clawTexture = null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
|
// not be available.
|
||||||
|
createShader() {
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
// Only register the shader class when this method is called for the first time.
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
if (!this._ShaderClass) {
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
const {Clutter, GdkPixbuf, Cogl} = imports.gi;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
const {Clutter, GdkPixbuf, Cogl, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
// Load the claw texture.
|
// Load the claw texture.
|
||||||
if (clawTexture == null) {
|
const clawData = GdkPixbuf.Pixbuf.new_from_resource('/img/claws.png');
|
||||||
const clawData = GdkPixbuf.Pixbuf.new_from_resource('/img/claws.png');
|
this._clawTexture = new Clutter.Image();
|
||||||
clawTexture = new Clutter.Image();
|
this._clawTexture.set_data(clawData.get_pixels(), Cogl.PixelFormat.RGB_888,
|
||||||
clawTexture.set_data(clawData.get_pixels(), Cogl.PixelFormat.RGB_888,
|
clawData.width, clawData.height, clawData.rowstride);
|
||||||
clawData.width, clawData.height, clawData.rowstride);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._uClawTexture = this.get_uniform_location('uClawTexture');
|
|
||||||
this._uFlashColor = this.get_uniform_location('uFlashColor');
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
this._uSeed = this.get_uniform_location('uSeed');
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
this._uClawSize = this.get_uniform_location('uClawSize');
|
_init(effect) {
|
||||||
this._uNumClaws = this.get_uniform_location('uNumClaws');
|
super._init(effect);
|
||||||
this._uWarpIntensity = this.get_uniform_location('uWarpIntensity');
|
|
||||||
|
this._uClawTexture = this.get_uniform_location('uClawTexture');
|
||||||
|
this._uFlashColor = this.get_uniform_location('uFlashColor');
|
||||||
|
this._uSeed = this.get_uniform_location('uSeed');
|
||||||
|
this._uClawSize = this.get_uniform_location('uClawSize');
|
||||||
|
this._uNumClaws = this.get_uniform_location('uNumClaws');
|
||||||
|
this._uWarpIntensity = this.get_uniform_location('uWarpIntensity');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
const c =
|
||||||
|
Clutter.Color.from_string(settings.get_string('claw-scratch-color'))[1];
|
||||||
|
|
||||||
|
// If we are currently performing integration test, the animation uses a fixed
|
||||||
|
// seed.
|
||||||
|
const testMode = settings.get_boolean('test-mode');
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uFlashColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]);
|
||||||
|
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
||||||
|
this.set_uniform_float(this._uClawSize, 1, [settings.get_double('claw-scratch-scale')]);
|
||||||
|
this.set_uniform_float(this._uNumClaws, 1, [settings.get_int('claw-scratch-count')]);
|
||||||
|
this.set_uniform_float(this._uWarpIntensity, 1, [settings.get_double('claw-scratch-warp')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is overridden to bind the claw texture for drawing. Sadly, this seems to
|
||||||
|
// be impossible under GNOME 3.3x as this.get_pipeline() is not available. It was
|
||||||
|
// called get_target() back then but this is not wrapped in GJS.
|
||||||
|
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
||||||
|
vfunc_paint_target(node, paint_context) {
|
||||||
|
const pipeline = this.get_pipeline();
|
||||||
|
pipeline.set_layer_texture(1, this._effect._clawTexture.get_texture());
|
||||||
|
pipeline.set_uniform_1i(this._uClawTexture, 1);
|
||||||
|
|
||||||
|
super.vfunc_paint_target(node, paint_context);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
// Finally, return a new instance of the shader class.
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
return new this._ShaderClass(this);
|
||||||
setUniforms(actor, settings, forOpening) {
|
}
|
||||||
const c = Clutter.Color.from_string(settings.get_string('claw-scratch-color'))[1];
|
}
|
||||||
|
|
||||||
// If we are currently performing integration test, the animation uses a fixed seed.
|
|
||||||
const testMode = settings.get_boolean('test-mode');
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uFlashColor, 4, [c.red / 255, c.green / 255, c.blue / 255, c.alpha / 255]);
|
|
||||||
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
|
||||||
this.set_uniform_float(this._uClawSize, 1, [settings.get_double('claw-scratch-scale')]);
|
|
||||||
this.set_uniform_float(this._uNumClaws, 1, [settings.get_int('claw-scratch-count')]);
|
|
||||||
this.set_uniform_float(this._uWarpIntensity, 1, [settings.get_double('claw-scratch-warp')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${TRexAttack.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is overridden to bind the claw texture for drawing. Sadly, this seems to be
|
|
||||||
// impossible under GNOME 3.3x as this.get_pipeline() is not available. It was called
|
|
||||||
// get_target() back then but this is not wrapped in GJS.
|
|
||||||
// https://gitlab.gnome.org/GNOME/mutter/-/blob/gnome-3-36/clutter/clutter/clutter-offscreen-effect.c#L598
|
|
||||||
vfunc_paint_target(node, paint_context) {
|
|
||||||
const pipeline = this.get_pipeline();
|
|
||||||
pipeline.set_layer_texture(1, clawTexture.get_texture());
|
|
||||||
pipeline.set_uniform_1i(this._uClawTexture, 1);
|
|
||||||
|
|
||||||
super.vfunc_paint_target(node, paint_context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
+41
-93
@@ -20,6 +20,7 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect hides the actor by making it first transparent from top and bottom //
|
// This effect hides the actor by making it first transparent from top and bottom //
|
||||||
@@ -27,23 +28,15 @@ const utils = Me.imports.src.utils;
|
|||||||
// the center. //
|
// the center. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var TVEffect = class TVEffect extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// The effect class is completely static. It can be used to get some metadata (like the
|
|
||||||
// effect's name or supported GNOME Shell versions), to initialize the respective page of
|
|
||||||
// the settings dialog, as well as to create the actual shader for the effect.
|
|
||||||
var TVEffect = class TVEffect {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is available on all GNOME Shell versions supported by this extension.
|
// The effect is available on all GNOME Shell versions supported by this extension.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 36];
|
return [3, 36];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,22 +44,21 @@ var TVEffect = class TVEffect {
|
|||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
||||||
// (e.g. '*-animation-time').
|
// (e.g. '*-animation-time').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'tv';
|
return 'tv';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('TV Effect');
|
return _('TV Effect');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/TVEffect.ui`);
|
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/TVEffect.ui`);
|
||||||
@@ -81,83 +73,39 @@ var TVEffect = class TVEffect {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
// created shaders.
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
static getShader(actor, settings, forOpening) {
|
// not be available.
|
||||||
let shader;
|
createShader() {
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
// Only register the shader class when this method is called for the first time.
|
||||||
shader = new ShaderClass();
|
if (!this._ShaderClass) {
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uColor = this.get_uniform_location('uColor');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
const c = Clutter.Color.from_string(settings.get_string('tv-effect-color'))[1];
|
||||||
|
this.set_uniform_float(this._uColor, 3,
|
||||||
|
[c.red / 255, c.green / 255, c.blue / 255]);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
|
||||||
static getActorScale(settings) {
|
|
||||||
return {x: 1.0, y: 1.0};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
|
||||||
// static resources.
|
|
||||||
static cleanUp() {
|
|
||||||
freeShaders = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
this._uColor = this.get_uniform_location('uColor');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
const c = Clutter.Color.from_string(settings.get_string('tv-effect-color'))[1];
|
|
||||||
this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${TVEffect.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+51
-104
@@ -20,52 +20,43 @@ const _ = imports.gettext.domain('burn-my-windows').gettext;
|
|||||||
const ExtensionUtils = imports.misc.extensionUtils;
|
const ExtensionUtils = imports.misc.extensionUtils;
|
||||||
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
const Me = imports.misc.extensionUtils.getCurrentExtension();
|
||||||
const utils = Me.imports.src.utils;
|
const utils = Me.imports.src.utils;
|
||||||
|
const Effect = Me.imports.src.Effect.Effect;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// This effect lets your windows be carried to the realm of dreams by some little //
|
// This effect lets your windows be carried to the realm of dreams by some little //
|
||||||
// fairies. It's implemented with several overlaid grids of randomly moving points. //
|
// fairies. It's implemented with several overlaid grids of randomly moving points. //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The shader class for this effect is registered further down in this file. When this
|
// The effect class can be used to get some metadata (like the effect's name or supported
|
||||||
// effect is used for the first time, an instance of this shader class is created. Once
|
// GNOME Shell versions), to initialize the respective page of the settings dialog, as
|
||||||
// the effect is finished, the shader will be stored in the freeShaders array and will
|
// well as to create the actual shader for the effect.
|
||||||
// then be reused if a new shader is requested. ShaderClass which will be used whenever
|
var Wisps = class Wisps extends Effect {
|
||||||
// this effect is used.
|
|
||||||
let ShaderClass = null;
|
|
||||||
let freeShaders = [];
|
|
||||||
|
|
||||||
// This will be called in various places where a unique identifier for this effect is
|
|
||||||
// required. It should match the prefix of the settings keys which store whether the
|
|
||||||
// effect is enabled currently (e.g. '*-close-effect'), and its animation time
|
|
||||||
// (e.g. '*-animation-time').
|
|
||||||
var Wisps = class Wisps {
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------- metadata
|
// ---------------------------------------------------------------------------- metadata
|
||||||
|
|
||||||
// The effect is available on all GNOME Shell versions supported by this extension.
|
// The effect is available on all GNOME Shell versions supported by this extension.
|
||||||
static getMinShellVersion() {
|
getMinShellVersion() {
|
||||||
return [3, 36];
|
return [3, 36];
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be called in various places where a unique identifier for this effect is
|
// This will be called in various places where a unique identifier for this effect is
|
||||||
// required. It should match the prefix of the settings keys which store whether the
|
// required. It should match the prefix of the settings keys which store whether the
|
||||||
// effect is enabled currently (e.g. the '*-close-effect').
|
// effect is enabled currently (e.g. the '*-close-effect').
|
||||||
static getNick() {
|
getNick() {
|
||||||
return 'wisps';
|
return 'wisps';
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be shown in the sidebar of the preferences dialog as well as in the
|
// This will be shown in the sidebar of the preferences dialog as well as in the
|
||||||
// drop-down menus where the user can choose the effect.
|
// drop-down menus where the user can choose the effect.
|
||||||
static getLabel() {
|
getLabel() {
|
||||||
return _('Wisps');
|
return _('Wisps');
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------- API for prefs.js
|
// -------------------------------------------------------------------- API for prefs.js
|
||||||
|
|
||||||
// This is called by the preferences dialog. It loads the settings page for this effect,
|
// This is called by the preferences dialog. It loads the settings page for this effect,
|
||||||
// binds all properties to the settings and appends the page to the main stack of the
|
// and binds all properties to the settings.
|
||||||
// preferences dialog.
|
getPreferences(dialog) {
|
||||||
static getPreferences(dialog) {
|
|
||||||
|
|
||||||
// Add the settings page to the builder.
|
// Add the settings page to the builder.
|
||||||
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Wisps.ui`);
|
dialog.getBuilder().add_from_resource(`/ui/${utils.getGTKString()}/Wisps.ui`);
|
||||||
@@ -81,93 +72,49 @@ var Wisps = class Wisps {
|
|||||||
|
|
||||||
// ---------------------------------------------------------------- API for extension.js
|
// ---------------------------------------------------------------- API for extension.js
|
||||||
|
|
||||||
// This is called from extension.js whenever a window is opened or closed with this
|
// This is called by the effect's base class whenever a new shader is required. Since
|
||||||
// effect. It returns an instance of the shader class, trying to reuse previously
|
// this shader depends on classes by GNOME Shell, we register it locally in this method
|
||||||
// created shaders.
|
// as this file is also included from the preferences dialog where those classes would
|
||||||
static getShader(actor, settings, forOpening) {
|
// not be available.
|
||||||
let shader;
|
createShader() {
|
||||||
|
|
||||||
if (freeShaders.length == 0) {
|
// Only register the shader class when this method is called for the first time.
|
||||||
shader = new ShaderClass();
|
if (!this._ShaderClass) {
|
||||||
} else {
|
|
||||||
shader = freeShaders.pop();
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const Shader = Me.imports.src.Shader.Shader;
|
||||||
|
|
||||||
|
this._ShaderClass = GObject.registerClass({}, class ShaderClass extends Shader {
|
||||||
|
// We use the constructor of the shader to store all required uniform locations.
|
||||||
|
_init(effect) {
|
||||||
|
super._init(effect);
|
||||||
|
|
||||||
|
this._uSeed = this.get_uniform_location('uSeed');
|
||||||
|
this._uColor = this.get_uniform_location('uColor');
|
||||||
|
this._uScale = this.get_uniform_location('uScale');
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called once each time the shader is used. This can be used to retrieve
|
||||||
|
// the configuration from the settings and update all uniforms accordingly.
|
||||||
|
beginAnimation(actor, settings, forOpening) {
|
||||||
|
super.beginAnimation(actor, settings, forOpening);
|
||||||
|
|
||||||
|
const c = Clutter.Color.from_string(settings.get_string('wisps-color'))[1];
|
||||||
|
|
||||||
|
// If we are currently performing integration test, the animation uses a fixed
|
||||||
|
// seed.
|
||||||
|
const testMode = settings.get_boolean('test-mode');
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
||||||
|
this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]);
|
||||||
|
this.set_uniform_float(this._uScale, 1, [settings.get_double('wisps-scale')]);
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.setUniforms(actor, settings, forOpening);
|
// Finally, return a new instance of the shader class.
|
||||||
|
return new this._ShaderClass(this);
|
||||||
return shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The getActorScale() is called from extension.js to adjust the actor's size during the
|
|
||||||
// animation. This is useful if the effect requires drawing something beyond the usual
|
|
||||||
// bounds of the actor. This only works for GNOME 3.38+.
|
|
||||||
static getActorScale(settings) {
|
|
||||||
return {x: 1.0, y: 1.0};
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from extension.js if the extension is disabled. This should free all
|
|
||||||
// static resources.
|
|
||||||
static cleanUp() {
|
|
||||||
freeShaders = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The shader class for this effect will only be registered in GNOME Shell's process //
|
|
||||||
// (not in the preferences process). It's done this way as Clutter may not be installed //
|
|
||||||
// on the system and therefore the preferences would crash. //
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
if (utils.isInShellProcess()) {
|
|
||||||
|
|
||||||
const {Clutter, Shell} = imports.gi;
|
|
||||||
|
|
||||||
ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
|
|
||||||
// This is called when the effect is used for the first time. This can be used to
|
|
||||||
// store all required uniform locations.
|
|
||||||
_init() {
|
|
||||||
super._init();
|
|
||||||
|
|
||||||
this._uSeed = this.get_uniform_location('uSeed');
|
|
||||||
this._uColor = this.get_uniform_location('uColor');
|
|
||||||
this._uScale = this.get_uniform_location('uScale');
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called each time the effect is used. This can be used to retrieve the
|
|
||||||
// configuration from the settings and update all uniforms accordingly.
|
|
||||||
setUniforms(actor, settings, forOpening) {
|
|
||||||
const c = Clutter.Color.from_string(settings.get_string('wisps-color'))[1];
|
|
||||||
|
|
||||||
// If we are currently performing integration test, the animation uses a fixed seed.
|
|
||||||
const testMode = settings.get_boolean('test-mode');
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
this.set_uniform_float(this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
|
|
||||||
this.set_uniform_float(this._uColor, 3, [c.red / 255, c.green / 255, c.blue / 255]);
|
|
||||||
this.set_uniform_float(this._uScale, 1, [settings.get_double('wisps-scale')]);
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by extension.js when the shader is not used anymore. We will store
|
|
||||||
// this instance of the shader so that it can be re-used in th future.
|
|
||||||
free() {
|
|
||||||
freeShaders.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called by the constructor. This means, it's only called when the effect
|
|
||||||
// is used for the first time.
|
|
||||||
vfunc_build_pipeline() {
|
|
||||||
const code = utils.loadGLSLResource(`/shaders/${Wisps.getNick()}.glsl`);
|
|
||||||
|
|
||||||
// Match anything between the curly brackets of "void main() {...}".
|
|
||||||
const regex = RegExp('void main *\\(\\) *\\{([\\S\\s]+)\\}');
|
|
||||||
const match = regex.exec(code);
|
|
||||||
|
|
||||||
const declarations = code.substr(0, match.index);
|
|
||||||
const main = match[1];
|
|
||||||
|
|
||||||
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, main, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+1
-26
@@ -13,8 +13,7 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const {Gtk, Gio} = imports.gi;
|
const {Gtk} = imports.gi;
|
||||||
const ByteArray = imports.byteArray;
|
|
||||||
|
|
||||||
// Returns the given argument, except for "alpha", "beta", and "rc". In these cases -3,
|
// Returns the given argument, except for "alpha", "beta", and "rc". In these cases -3,
|
||||||
// -2, and -1 are returned respectively.
|
// -2, and -1 are returned respectively.
|
||||||
@@ -90,28 +89,4 @@ function shellVersionIsAtLeast(major, minor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// This loads the file at 'path' contained in the extension's resources to a JavaScript
|
|
||||||
// string.
|
|
||||||
function loadStringResource(path) {
|
|
||||||
const data = Gio.resources_lookup_data(path, 0);
|
|
||||||
return ByteArray.toString(ByteArray.fromGBytes(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This loads a GLSL file from the extension's resources to a JavaScript string. Any
|
|
||||||
// #include statements in this file are replaced with the corresponding file contents.
|
|
||||||
function loadGLSLResource(path) {
|
|
||||||
let code = loadStringResource(path);
|
|
||||||
|
|
||||||
// This regex matches either #include "..." or #include <...>. The part between the
|
|
||||||
// brackets is captured in the capture group.
|
|
||||||
const regex = RegExp('#include ["<](.+)[">]', 'g');
|
|
||||||
|
|
||||||
code = code.replace(regex, (m, file) => {
|
|
||||||
return loadStringResource('/shaders/' + file);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add a trailing newline. Else the GLSL compiler complains...
|
|
||||||
return code + '\n';
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user