diff --git a/README.md b/README.md
index 08737e3..0a560a2 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,7 @@ Effect | Preview
**Hexagon** With glowing lines and hexagon-shaped tiles, this effect looks very sci-fi. |
**Incinerate** A less snappy but definitely more fancy take on the fire effect. |
**Matrix** Turn your windows into a shower of green letters! The color is actually configurable.|
+**Paint Brush** Paint or erase your windows with a thick paint brush. |
**Pixelate** Pixelate your windows and randomly hide pixels until the entire window is gone.|
**Pixel Wheel** This simple effect pixelates the window and hides the pixels in a wheel-like fashion.|
**Pixel Wipe** This effect pixelates the window and hides the pixels radially, starting from the pointer position.|
@@ -71,6 +72,7 @@ Below is a table which summarizes the current availability for all effects.
Hexagon
✅
✅
✅
✅
Incinerate
✅
✅
✅
✅
Matrix
✅
+
Paint Brush
✅
Pixelate
✅
✅
✅
✅
Pixel Wheel
✅
✅
✅
✅
Pixel Wipe
✅
✅
✅
✅
diff --git a/assets/README.md b/assets/README.md
index b71ffb7..745d80a 100644
--- a/assets/README.md
+++ b/assets/README.md
@@ -1,7 +1,20 @@
-# Notes for `claws.blend`
+# `claws.blend`
This is the source file for `resources/img/claws.png`. To generate the image, the animation in `claws.blend` is rendered to 256 PNG files and then combined using this command:
```bash
convert *.png -average claws.png
-```
\ No newline at end of file
+```
+
+# `brush.blend`
+
+This is the source file for `resources/img/brush.png`. To generate the image, the four dynamic paint image sequences in `brush.blend` are baked to 256 PNG files each. They are then combined using these commands:
+
+```bash
+convert brush1/*.png -average brush1.png
+convert brush2/*.png -average brush2.png
+convert brush3/*.png -average brush3.png
+convert brush4/*.png -average brush4.png
+
+convert brush1.png brush2.png brush3.png brush4.png -channel RGBA -combine brush.png
+```
diff --git a/assets/brush.blend b/assets/brush.blend
new file mode 100644
index 0000000..0c11fea
Binary files /dev/null and b/assets/brush.blend differ
diff --git a/docs/changelog.md b/docs/changelog.md
index d5a7f3e..762730d 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -5,6 +5,14 @@ SPDX-License-Identifier: CC-BY-4.0
# Changelog of the Burn-My-Windows Extension
+## [Burn My Windows 35](https://github.com/schneegans/Burn-My-Windows/releases/tag/v35)
+
+**Release Date:** TBD
+
+#### New Features
+
+- **New Effect: Paint-Brush.** This effect paints or erases your windows with a thick paint brush.
+
## [Burn My Windows 34](https://github.com/schneegans/Burn-My-Windows/releases/tag/v34)
**Release Date:** 2023-09-06
diff --git a/docs/pics/paint-brush.gif b/docs/pics/paint-brush.gif
new file mode 100644
index 0000000..6422c82
Binary files /dev/null and b/docs/pics/paint-brush.gif differ
diff --git a/extension.js b/extension.js
index 2f3fc96..21d496e 100644
--- a/extension.js
+++ b/extension.js
@@ -65,6 +65,7 @@ class Extension {
new Me.imports.src.effects.Hexagon.Hexagon(),
new Me.imports.src.effects.Incinerate.Incinerate(),
new Me.imports.src.effects.Matrix.Matrix(),
+ new Me.imports.src.effects.PaintBrush.PaintBrush(),
new Me.imports.src.effects.Pixelate.Pixelate(),
new Me.imports.src.effects.PixelWheel.PixelWheel(),
new Me.imports.src.effects.PixelWipe.PixelWipe(),
diff --git a/po/burn-my-windows.pot b/po/burn-my-windows.pot
index 8007d96..5de1032 100644
--- a/po/burn-my-windows.pot
+++ b/po/burn-my-windows.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: burn-my-windows\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-02-21 16:46+0100\n"
+"POT-Creation-Date: 2023-07-02 21:54+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -17,77 +17,77 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: src/ProfileManager.js:168 resources/ui/gtk4/prefs.ui:220
+#: src/ProfileManager.js:175 resources/ui/gtk4/prefs.ui:220
#: resources/ui/gtk3/prefs.ui:265 resources/ui/adw/prefs.ui:103
msgid "Opening Windows"
msgstr ""
-#: src/ProfileManager.js:169 resources/ui/gtk4/prefs.ui:221
+#: src/ProfileManager.js:176 resources/ui/gtk4/prefs.ui:221
#: resources/ui/gtk3/prefs.ui:266 resources/ui/adw/prefs.ui:104
msgid "Closing Windows"
msgstr ""
-#: src/ProfileManager.js:170 resources/ui/gtk4/prefs.ui:249
+#: src/ProfileManager.js:177 resources/ui/gtk4/prefs.ui:249
#: resources/ui/gtk3/prefs.ui:294 resources/ui/adw/prefs.ui:118
msgid "Normal Windows"
msgstr ""
-#: src/ProfileManager.js:171 resources/ui/gtk4/prefs.ui:250
+#: src/ProfileManager.js:178 resources/ui/gtk4/prefs.ui:250
#: resources/ui/gtk3/prefs.ui:295 resources/ui/adw/prefs.ui:119
msgid "Dialog Windows"
msgstr ""
-#: src/ProfileManager.js:172 resources/ui/gtk4/prefs.ui:278
+#: src/ProfileManager.js:179 resources/ui/gtk4/prefs.ui:278
#: resources/ui/gtk3/prefs.ui:323 resources/ui/adw/prefs.ui:133
msgid "Default Color Scheme"
msgstr ""
-#: src/ProfileManager.js:173 resources/ui/gtk4/prefs.ui:279
+#: src/ProfileManager.js:180 resources/ui/gtk4/prefs.ui:279
#: resources/ui/gtk3/prefs.ui:324 resources/ui/adw/prefs.ui:134
msgid "Dark Color Scheme"
msgstr ""
-#: src/ProfileManager.js:174 resources/ui/gtk4/prefs.ui:307
+#: src/ProfileManager.js:181 resources/ui/gtk4/prefs.ui:307
#: resources/ui/gtk3/prefs.ui:352 resources/ui/adw/prefs.ui:148
msgid "On Battery"
msgstr ""
-#: src/ProfileManager.js:175 resources/ui/gtk4/prefs.ui:308
+#: src/ProfileManager.js:182 resources/ui/gtk4/prefs.ui:308
#: resources/ui/gtk3/prefs.ui:353 resources/ui/adw/prefs.ui:149
msgid "Plugged In"
msgstr ""
-#: src/ProfileManager.js:176 resources/ui/gtk4/prefs.ui:336
+#: src/ProfileManager.js:183 resources/ui/gtk4/prefs.ui:336
#: resources/ui/gtk3/prefs.ui:381 resources/ui/adw/prefs.ui:163
msgid "Power-Saver Mode"
msgstr ""
-#: src/ProfileManager.js:177 resources/ui/gtk4/prefs.ui:337
+#: src/ProfileManager.js:184 resources/ui/gtk4/prefs.ui:337
#: resources/ui/gtk3/prefs.ui:382 resources/ui/adw/prefs.ui:164
msgid "Balanced Mode"
msgstr ""
-#: src/ProfileManager.js:178 resources/ui/gtk4/prefs.ui:338
+#: src/ProfileManager.js:185 resources/ui/gtk4/prefs.ui:338
#: resources/ui/gtk3/prefs.ui:383 resources/ui/adw/prefs.ui:165
msgid "Performance Mode"
msgstr ""
-#: src/ProfileManager.js:179 resources/ui/gtk4/prefs.ui:339
+#: src/ProfileManager.js:186 resources/ui/gtk4/prefs.ui:339
#: resources/ui/gtk3/prefs.ui:384 resources/ui/adw/prefs.ui:166
msgid "Power Saver or Balanced"
msgstr ""
-#: src/ProfileManager.js:180 resources/ui/gtk4/prefs.ui:340
+#: src/ProfileManager.js:187 resources/ui/gtk4/prefs.ui:340
#: resources/ui/gtk3/prefs.ui:385 resources/ui/adw/prefs.ui:167
msgid "Balanced or Performance"
msgstr ""
-#: src/ProfileManager.js:185 resources/ui/gtk4/prefs.ui:387
+#: src/ProfileManager.js:192 resources/ui/gtk4/prefs.ui:387
#: resources/ui/gtk3/prefs.ui:432 resources/ui/adw/prefs.ui:183
msgid "High Priority"
msgstr ""
-#: src/ProfileManager.js:191
+#: src/ProfileManager.js:198
msgid "Standard Profile"
msgstr ""
@@ -155,6 +155,10 @@ msgstr ""
msgid "Matrix"
msgstr ""
+#: src/effects/PaintBrush.js:102
+msgid "Paint Brush"
+msgstr ""
+
#: src/effects/Pixelate.js:74
msgid "Pixelate"
msgstr ""
@@ -191,60 +195,60 @@ msgstr ""
msgid "Wisps"
msgstr ""
-#: prefs.js:247
+#: prefs.js:256
msgid "Preview this effect"
msgstr ""
-#: prefs.js:364 resources/ui/gtk4/prefs.ui:50 resources/ui/gtk3/prefs.ui:86
+#: prefs.js:375 resources/ui/gtk4/prefs.ui:50 resources/ui/gtk3/prefs.ui:86
msgid "Burn-My-Windows has been updated!"
msgstr ""
-#: prefs.js:365 resources/ui/common/menus.ui:17 resources/ui/gtk4/prefs.ui:56
+#: prefs.js:376 resources/ui/common/menus.ui:17 resources/ui/gtk4/prefs.ui:56
#: resources/ui/gtk3/prefs.ui:96
msgid "View Changelog"
msgstr ""
-#: prefs.js:496 prefs.js:528
+#: prefs.js:557 prefs.js:589
msgid "Gold Sponsors"
msgstr ""
-#: prefs.js:500 prefs.js:532
+#: prefs.js:561 prefs.js:593
msgid "Silver Sponsors"
msgstr ""
-#: prefs.js:504 prefs.js:536
+#: prefs.js:565 prefs.js:597
msgid "Bronze Sponsors"
msgstr ""
-#: prefs.js:508 prefs.js:540
+#: prefs.js:569 prefs.js:601
msgid "Past Sponsors"
msgstr ""
-#: prefs.js:585
+#: prefs.js:646
msgid "Delete this Profile?"
msgstr ""
-#: prefs.js:587
+#: prefs.js:647
msgid ""
"The current effect profile with all its effect settings will be permanently "
"lost."
msgstr ""
-#: prefs.js:592
+#: prefs.js:650
msgid "Cancel"
msgstr ""
-#: prefs.js:593
+#: prefs.js:655
msgid "Delete"
msgstr ""
#. Translators: %s will be replaced by the effect's name.
-#: prefs.js:923
+#: prefs.js:966
#, javascript-format
msgid "Preview for %s"
msgstr ""
-#: prefs.js:945
+#: prefs.js:988
msgid "Close this Window to Preview the Effect!"
msgstr ""
@@ -272,35 +276,36 @@ msgstr ""
msgid "Donate"
msgstr ""
-#: resources/ui/common/menus.ui:52
+#: resources/ui/common/menus.ui:56
msgid "About Burn-My-Windows"
msgstr ""
-#: resources/ui/common/menus.ui:63
+#: resources/ui/common/menus.ui:67
msgid "Add New Profile..."
msgstr ""
#: resources/ui/gtk4/broken-glass.ui:22 resources/ui/gtk4/tv-glitch.ui:23
#: resources/ui/gtk4/glitch.ui:23 resources/ui/gtk4/matrix.ui:23
-#: resources/ui/gtk4/doom.ui:22 resources/ui/gtk4/trex.ui:23
-#: resources/ui/gtk4/wisps.ui:23 resources/ui/gtk4/energize-b.ui:23
-#: resources/ui/gtk4/pixel-wheel.ui:23 resources/ui/gtk4/glide.ui:23
-#: resources/ui/gtk4/apparition.ui:22 resources/ui/gtk4/portal.ui:23
-#: resources/ui/gtk4/incinerate.ui:23 resources/ui/gtk4/hexagon.ui:23
-#: resources/ui/gtk4/fire.ui:46 resources/ui/gtk4/pixel-wipe.ui:23
-#: resources/ui/gtk4/snap.ui:23 resources/ui/gtk4/energize-a.ui:23
-#: resources/ui/gtk4/pixelate.ui:23 resources/ui/gtk4/tv.ui:23
-#: resources/ui/gtk3/tv-glitch.ui:52 resources/ui/gtk3/glitch.ui:52
-#: resources/ui/gtk3/doom.ui:52 resources/ui/gtk3/wisps.ui:38
-#: resources/ui/gtk3/energize-b.ui:38 resources/ui/gtk3/pixel-wheel.ui:45
-#: resources/ui/gtk3/glide.ui:59 resources/ui/gtk3/apparition.ui:59
-#: resources/ui/gtk3/portal.ui:52 resources/ui/gtk3/incinerate.ui:45
-#: resources/ui/gtk3/hexagon.ui:45 resources/ui/gtk3/fire.ui:84
-#: resources/ui/gtk3/pixel-wipe.ui:45 resources/ui/gtk3/energize-a.ui:38
-#: resources/ui/gtk3/pixelate.ui:45 resources/ui/gtk3/tv.ui:31
-#: resources/ui/adw/broken-glass.ui:15 resources/ui/adw/tv-glitch.ui:14
-#: resources/ui/adw/glitch.ui:14 resources/ui/adw/matrix.ui:14
-#: resources/ui/adw/doom.ui:14 resources/ui/adw/trex.ui:14
+#: resources/ui/gtk4/doom.ui:22 resources/ui/gtk4/paint-brush.ui:23
+#: resources/ui/gtk4/trex.ui:23 resources/ui/gtk4/wisps.ui:23
+#: resources/ui/gtk4/energize-b.ui:23 resources/ui/gtk4/pixel-wheel.ui:23
+#: resources/ui/gtk4/glide.ui:23 resources/ui/gtk4/apparition.ui:22
+#: resources/ui/gtk4/portal.ui:23 resources/ui/gtk4/incinerate.ui:23
+#: resources/ui/gtk4/hexagon.ui:23 resources/ui/gtk4/fire.ui:46
+#: resources/ui/gtk4/pixel-wipe.ui:23 resources/ui/gtk4/snap.ui:23
+#: resources/ui/gtk4/energize-a.ui:23 resources/ui/gtk4/pixelate.ui:23
+#: resources/ui/gtk4/tv.ui:23 resources/ui/gtk3/tv-glitch.ui:52
+#: resources/ui/gtk3/glitch.ui:52 resources/ui/gtk3/doom.ui:52
+#: resources/ui/gtk3/wisps.ui:38 resources/ui/gtk3/energize-b.ui:38
+#: resources/ui/gtk3/pixel-wheel.ui:45 resources/ui/gtk3/glide.ui:59
+#: resources/ui/gtk3/apparition.ui:59 resources/ui/gtk3/portal.ui:52
+#: resources/ui/gtk3/incinerate.ui:45 resources/ui/gtk3/hexagon.ui:45
+#: resources/ui/gtk3/fire.ui:84 resources/ui/gtk3/pixel-wipe.ui:45
+#: resources/ui/gtk3/energize-a.ui:38 resources/ui/gtk3/pixelate.ui:45
+#: resources/ui/gtk3/tv.ui:31 resources/ui/adw/broken-glass.ui:15
+#: resources/ui/adw/tv-glitch.ui:14 resources/ui/adw/glitch.ui:14
+#: resources/ui/adw/matrix.ui:14 resources/ui/adw/doom.ui:14
+#: resources/ui/adw/paint-brush.ui:14 resources/ui/adw/trex.ui:14
#: resources/ui/adw/wisps.ui:14 resources/ui/adw/energize-b.ui:15
#: resources/ui/adw/pixel-wheel.ui:14 resources/ui/adw/glide.ui:14
#: resources/ui/adw/apparition.ui:15 resources/ui/adw/portal.ui:14
@@ -323,7 +328,8 @@ msgstr ""
#: resources/ui/gtk4/matrix.ui:189 resources/ui/gtk4/matrix.ui:226
#: resources/ui/gtk4/matrix.ui:263 resources/ui/gtk4/doom.ui:50
#: resources/ui/gtk4/doom.ui:96 resources/ui/gtk4/doom.ui:142
-#: resources/ui/gtk4/doom.ui:188 resources/ui/gtk4/trex.ui:51
+#: resources/ui/gtk4/doom.ui:188 resources/ui/gtk4/paint-brush.ui:51
+#: resources/ui/gtk4/paint-brush.ui:97 resources/ui/gtk4/trex.ui:51
#: resources/ui/gtk4/trex.ui:97 resources/ui/gtk4/trex.ui:143
#: resources/ui/gtk4/trex.ui:189 resources/ui/gtk4/trex.ui:226
#: resources/ui/gtk4/wisps.ui:51 resources/ui/gtk4/wisps.ui:97
@@ -395,7 +401,8 @@ msgstr ""
#: resources/ui/adw/matrix.ui:135 resources/ui/adw/matrix.ui:160
#: resources/ui/adw/matrix.ui:185 resources/ui/adw/doom.ui:36
#: resources/ui/adw/doom.ui:69 resources/ui/adw/doom.ui:102
-#: resources/ui/adw/doom.ui:135 resources/ui/adw/trex.ui:36
+#: resources/ui/adw/doom.ui:135 resources/ui/adw/paint-brush.ui:36
+#: resources/ui/adw/paint-brush.ui:69 resources/ui/adw/trex.ui:36
#: resources/ui/adw/trex.ui:69 resources/ui/adw/trex.ui:102
#: resources/ui/adw/trex.ui:135 resources/ui/adw/trex.ui:160
#: resources/ui/adw/wisps.ui:36 resources/ui/adw/wisps.ui:69
@@ -531,6 +538,10 @@ msgstr ""
msgid "Pixel Size"
msgstr ""
+#: resources/ui/gtk4/paint-brush.ui:69 resources/ui/adw/paint-brush.ui:47
+msgid "Brush Size"
+msgstr ""
+
#: resources/ui/gtk4/trex.ui:69 resources/ui/adw/trex.ui:47
msgid "Claw Scratch Scale"
msgstr ""
diff --git a/prefs.js b/prefs.js
index a1dc3a9..c751fc1 100644
--- a/prefs.js
+++ b/prefs.js
@@ -56,6 +56,7 @@ var PreferencesDialog = class PreferencesDialog {
new Me.imports.src.effects.Hexagon.Hexagon(),
new Me.imports.src.effects.Incinerate.Incinerate(),
new Me.imports.src.effects.Matrix.Matrix(),
+ new Me.imports.src.effects.PaintBrush.PaintBrush(),
new Me.imports.src.effects.Pixelate.Pixelate(),
new Me.imports.src.effects.PixelWheel.PixelWheel(),
new Me.imports.src.effects.PixelWipe.PixelWipe(),
diff --git a/resources/img/brush.png b/resources/img/brush.png
new file mode 100644
index 0000000..90bb750
Binary files /dev/null and b/resources/img/brush.png differ
diff --git a/resources/shaders/paint-brush.frag b/resources/shaders/paint-brush.frag
new file mode 100644
index 0000000..d5875f8
--- /dev/null
+++ b/resources/shaders/paint-brush.frag
@@ -0,0 +1,62 @@
+//////////////////////////////////////////////////////////////////////////////////////////
+// ) ( //
+// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
+// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
+// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
+// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
+// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
+// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
+// |__/ //
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// SPDX-FileCopyrightText: Simon Schneegans
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// The content from common.glsl is automatically prepended to each shader effect.
+
+// See assets/README.md for how this texture was created.
+uniform sampler2D uBrushTexture;
+uniform float uBrushSize;
+
+const float SHADOW_WIDTH = 0.03;
+const float SHADOW_STEPS = 10.0;
+
+// Returns 1.0 if the window texture at the given texCoord should be painted.
+float getMask(vec2 texCoord, float progress) {
+ float brush = texture2D(uBrushTexture, texCoord)[int(uBrushSize - 1.0)];
+ if (uForOpening) {
+ return step(brush, progress);
+ }
+
+ return step(progress, brush);
+}
+
+void main() {
+
+ vec4 oColor = getInputColor(iTexCoord.st);
+
+ // This will be 0.0 in the masked-out areas.
+ float mask = getMask(iTexCoord.st, uProgress);
+
+ // Add a simple drop shadow below the remaining parts.
+ if (mask == 0.0) {
+
+ for (float i = 0.0; i < SHADOW_STEPS; ++i) {
+ vec2 shadowTexCoord = iTexCoord.st - vec2(0.0, i * SHADOW_WIDTH / SHADOW_STEPS);
+ float shadowMask = getMask(shadowTexCoord, uProgress);
+ float shadowAlpha = (1.0 - i / SHADOW_STEPS) * shadowMask * getInputColor(shadowTexCoord).a;
+
+ if (shadowAlpha > 0.0) {
+ oColor = vec4(vec3(0.0), shadowAlpha * 0.2);
+ break;
+ }
+
+ // If we arrived at the end of the shadow, make sure to set the alpha to 0.
+ if (i == SHADOW_STEPS - 1.0) {
+ oColor.a = 0.0;
+ }
+ }
+ }
+
+ setOutputColor(oColor);
+}
diff --git a/resources/ui/adw/paint-brush.ui b/resources/ui/adw/paint-brush.ui
new file mode 100644
index 0000000..50ee7f2
--- /dev/null
+++ b/resources/ui/adw/paint-brush.ui
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/ui/gtk4/paint-brush.ui b/resources/ui/gtk4/paint-brush.ui
new file mode 100644
index 0000000..206f457
--- /dev/null
+++ b/resources/ui/gtk4/paint-brush.ui
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+ none
+
+
+
+ 0
+
+
+
+
+ Animation Time [ms]
+ 0
+ start
+ center
+ 1
+
+
+
+
+ end
+ center
+ 1
+ 0
+ left
+ 300
+
+
+ 10000
+ 100
+ 1
+ 100
+
+
+
+
+
+
+ edit-clear-symbolic
+ Reset to Default Value
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+ Brush Size
+ 0
+ start
+ center
+ 1
+
+
+
+
+ end
+ center
+ 1
+ 0
+ left
+ 300
+
+
+ 4
+ 1
+ 1
+ 1
+
+
+
+
+
+
+ edit-clear-symbolic
+ Reset to Default Value
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml b/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml
index 866a721..f7d6b8a 100644
--- a/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml
+++ b/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml
@@ -386,6 +386,28 @@ SPDX-License-Identifier: CC0-1.0
The color of the TV effect.
+
+
+
+
+
+ false
+ Paint Brush Enable Effect
+ Use the paint-brush effect.
+
+
+
+ 1000
+ Paint Brush Animation Time
+ The time the paint-brush effect takes.
+
+
+
+ 3
+ Paint Brush Size
+ Affects the number of paint strokes.
+
+
diff --git a/src/effects/PaintBrush.js b/src/effects/PaintBrush.js
new file mode 100644
index 0000000..f818e9d
--- /dev/null
+++ b/src/effects/PaintBrush.js
@@ -0,0 +1,122 @@
+//////////////////////////////////////////////////////////////////////////////////////////
+// ) ( //
+// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
+// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
+// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
+// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
+// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
+// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
+// |__/ //
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// SPDX-FileCopyrightText: Simon Schneegans
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+'use strict';
+
+const GObject = imports.gi.GObject;
+
+const _ = imports.gettext.domain('burn-my-windows').gettext;
+
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = imports.misc.extensionUtils.getCurrentExtension();
+const utils = Me.imports.src.utils;
+const ShaderFactory = Me.imports.src.ShaderFactory.ShaderFactory;
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This effect paints your windows with a thick paint brush. //
+// This effect is not available on GNOME 3.3x, due to the limitation described in the //
+// documentation of vfunc_paint_target further down in this file. //
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// The effect class 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 PaintBrush = class {
+
+ // The constructor creates a ShaderFactory which will be used by extension.js to create
+ // shader instances for this effect. The shaders will be automagically created using the
+ // GLSL file in resources/shaders/.glsl. The callback will be called for each
+ // newly created shader instance.
+ constructor() {
+ this.shaderFactory = new ShaderFactory(this.getNick(), (shader) => {
+ // We import these modules in this function as they are not available in the
+ // preferences process. This callback is only called within GNOME Shell's process.
+ const {Clutter, GdkPixbuf, Cogl} = imports.gi;
+
+ // Create the texture in the first call.
+ if (!this._brushTexture) {
+ const brushData = GdkPixbuf.Pixbuf.new_from_resource('/img/brush.png');
+ this._brushTexture = new Clutter.Image();
+ this._brushTexture.set_data(brushData.get_pixels(),
+ Cogl.PixelFormat.RGBA_8888_PRE, brushData.width,
+ brushData.height, brushData.rowstride);
+ }
+
+ // Store uniform locations of newly created shaders.
+ shader._uBrushTexture = shader.get_uniform_location('uBrushTexture');
+ shader._uBrushSize = shader.get_uniform_location('uBrushSize');
+
+ // Write all uniform values at the start of each animation.
+ shader.connect('begin-animation', (shader, settings, forOpening, testMode) => {
+ shader.set_uniform_float(shader._uBrushSize, 1,
+ [settings.get_int('paint-brush-size')]);
+ });
+
+ // This is required to bind the brush 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
+ shader.connect('update-animation', (shader) => {
+ const pipeline = shader.get_pipeline();
+
+ // Use linear filtering for the window texture.
+ pipeline.set_layer_filters(0, Cogl.PipelineFilter.LINEAR,
+ Cogl.PipelineFilter.LINEAR);
+
+ // Bind the brush texture.
+ pipeline.set_layer_texture(1, this._brushTexture.get_texture());
+ pipeline.set_uniform_1i(shader._uBrushTexture, 1);
+ });
+ });
+ }
+
+ // ---------------------------------------------------------------------------- metadata
+
+ // This effect is only available on GNOME Shell 40+.
+ getMinShellVersion() {
+ return [40, 0];
+ }
+
+ // 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. '*-enable-effect'), and its animation time
+ // (e.g. '*-animation-time').
+ getNick() {
+ return 'paint-brush';
+ }
+
+ // 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.
+ getLabel() {
+ return _('Paint Brush');
+ }
+
+ // -------------------------------------------------------------------- API for prefs.js
+
+ // This is called by the preferences dialog whenever a new effect profile is loaded. It
+ // binds all user interface elements to the respective settings keys of the profile.
+ bindPreferences(dialog) {
+ dialog.bindAdjustment('paint-brush-animation-time');
+ dialog.bindAdjustment('paint-brush-size');
+ }
+
+ // ---------------------------------------------------------------- API for extension.js
+
+ // 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+.
+ getActorScale(settings) {
+ return {x: 1.0, y: 1.0};
+ }
+}