Browse Source

[gnome] Update extensions for v46

Colin Powell 1 year ago
parent
commit
7421cdba8f
100 changed files with 9637 additions and 2213 deletions
  1. 34 0
      gnome/.commands.json
  2. 13 14
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/blur.js
  3. 3 2
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/carousel.js
  4. 11 3
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/extension.js
  5. BIN
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/locale/pt_BR/LC_MESSAGES/BingWallpaper.mo
  6. 3 2
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/metadata.json
  7. 181 132
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/prefs.js
  8. BIN
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/schemas/gschemas.compiled
  9. 1 1
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/schemas/org.gnome.shell.extensions.bingwallpaper.gschema.xml
  10. 0 1451
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/ui/Settings4.ui
  11. 2 2
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/ui/carousel4.ui
  12. 438 0
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/ui/prefsadw.ui
  13. 20 12
      gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/utils.js
  14. 1493 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/Options_UI.glade
  15. 86 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/convenience.js
  16. 40 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/display_module.js
  17. 1034 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/extension.js
  18. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/Icon_Info.png
  19. 45 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/Icon_Performance.svg
  20. 45 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/Icon_Quality.svg
  21. 99 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_default.svg
  22. 99 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_defaultSel.svg
  23. 99 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_recording.svg
  24. 99 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_recordingSel.svg
  25. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/ca/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  26. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/cs/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  27. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/de/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  28. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/es/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  29. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/fr/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  30. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/it/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  31. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/ja/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  32. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/pt_BR/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  33. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/ru/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  34. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/uk/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  35. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/vi/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  36. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/zh/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo
  37. 13 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/metadata.json
  38. 7 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/prefs.css
  39. 1184 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/prefs.js
  40. BIN
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/schemas/gschemas.compiled
  41. 200 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/schemas/org.gnome.shell.extensions.easyscreencast.gschema.xml
  42. 534 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/selection.js
  43. 192 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/settings.js
  44. 36 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/stylesheet.css
  45. 259 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/timer.js
  46. 321 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilaudio.js
  47. 266 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilexecmd.js
  48. 737 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilgsp.js
  49. 147 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilnotify.js
  50. 216 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilrecorder.js
  51. 315 0
      gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilwebcam.js
  52. 122 33
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/extension.js
  53. 54 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/helpers/subprocess.js
  54. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg
  55. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg
  56. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg
  57. 15 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg
  58. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg
  59. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg
  60. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg
  61. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg
  62. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg
  63. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg
  64. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg
  65. 1 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg
  66. 8 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg
  67. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg
  68. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg
  69. 15 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg
  70. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg
  71. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg
  72. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/network-symbolic.svg
  73. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg
  74. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg
  75. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/system-symbolic.svg
  76. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg
  77. 0 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg
  78. BIN
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo
  79. BIN
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo
  80. BIN
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo
  81. 3 3
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/menuItem.js
  82. 6 2
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/metadata.json
  83. 4 3
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/prefs.js
  84. 453 362
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/prefs.ui
  85. BIN
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/schemas/gschemas.compiled
  86. 15 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml
  87. 252 0
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/sensors.js
  88. 18 1
      gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/values.js
  89. 4 2
      gnome/.local/share/gnome-shell/extensions/auto-activities@CleoMenezesJr.github.io/metadata.json
  90. 0 28
      gnome/.local/share/gnome-shell/extensions/block-caribou-36@lxylxy123456.ercli.dev/extension.js
  91. 0 15
      gnome/.local/share/gnome-shell/extensions/block-caribou-36@lxylxy123456.ercli.dev/metadata.json
  92. 4 4
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/appfolders.js
  93. 13 11
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/applications.js
  94. 315 101
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/dash_to_dock.js
  95. 7 2
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/lockscreen.js
  96. 11 9
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/overview.js
  97. 24 8
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/panel.js
  98. 4 4
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/screenshot.js
  99. 6 6
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/window_list.js
  100. 2 0
      gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/conveniences/keys.js

+ 34 - 0
gnome/.commands.json

@@ -0,0 +1,34 @@
+[
+    {
+        "title": "Record",
+        "command": "record",
+        "icon": "utilities-terminal"
+    },
+    {
+        "title": "File Manager 3",
+        "command": "nautilus",
+        "icon": "folder"
+    },
+    {
+        "type": "separator"
+    },
+    {
+        "title": "Web Browser",
+        "command": "firefox",
+        "icon": "web-browser"
+    },
+    {
+        "type": "separator"
+    },
+    {
+        "title": "SSH Connections",
+        "type": "submenu",
+        "submenu": [
+            {
+                "title": "Connect to Server (SSH)",
+                "command": "gnome-terminal -- bash -c 'ssh root@10.144.1.2 -p 8022'",
+                "icon": "utilities-terminal"
+            }
+        ]
+    }
+]

+ 13 - 14
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/blur.js

@@ -37,7 +37,7 @@ function log(msg) {
 }
 
 // we patch UnlockDialog._updateBackgroundEffects()
-function _updateBackgroundEffects_BWP(monitorIndex) {
+export function _updateBackgroundEffects_BWP(monitorIndex) {
     // GNOME shell 3.36.4 and above
     log("_updateBackgroundEffects_BWP() called for shell >= 3.36.4");
     const themeContext = St.ThemeContext.get_for_stage(global.stage);
@@ -68,18 +68,26 @@ function _updateBackgroundEffects_BWP(monitorIndex) {
 
 // we patch both UnlockDialog._showClock() and UnlockDialog._showPrompt() to let us 
 // adjustable blur in a Windows-like way (this ensures login prompt is readable)
-function _showClock_BWP() {
+export function _showClock_BWP() {
     promptActive = false;
     this._showClock_GNOME(); // pass to default GNOME function
     this._updateBackgroundEffects();
 }
 
-function _showPrompt_BWP() {
+export function _showPrompt_BWP() {
     promptActive = true;
     this._showPrompt_GNOME(); // pass to default GNOME function
     this._updateBackgroundEffects();
 }
 
+export function _clampValue(value) {
+       // valid values are 0 to 100
+    if (value > 100)
+        value = 100;
+    if (value < 0 )
+        value = 0;
+    return value;
+}
 export default class Blur {
     constructor() {
         this.enabled = false;
@@ -87,24 +95,15 @@ export default class Blur {
     }
 
     set_blur_strength(value) {
-        BWP_BLUR_SIGMA = this._clampValue(value);
+        BWP_BLUR_SIGMA = _clampValue(value);
         log("lockscreen blur strength set to "+BWP_BLUR_SIGMA);
     }
 
     set_blur_brightness(value) {
-        BWP_BLUR_BRIGHTNESS = this._clampValue(value);
+        BWP_BLUR_BRIGHTNESS = _clampValue(value);
         log("lockscreen brightness set to " + BWP_BLUR_BRIGHTNESS);
     }
 
-    // valid values are 0 to 100
-    _clampValue(value) {
-        if (value > 100)
-            value = 100;
-        if (value < 0 )
-            value = 0;
-        return value;
-    }
-
     _switch(enabled) {
         if (enabled && !this.enabled) {
             this._enable();

+ 3 - 2
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/carousel.js

@@ -107,8 +107,8 @@ export default class Carousel {
         deleteButton.connect('clicked', (widget) => {
             this.log('Delete requested for '+filename);
             Utils.deleteImage(filename);
-            //Utils.cleanupImageList(this.settings); // hide image instead
-            Utils.hideImage(this.settings, [image]);
+            Utils.setImageHiddenStatus(this.settings, image.urlbase, true);
+            Utils.cleanupImageList(this.settings); // hide image instead
             widget.get_parent().get_parent().set_visible(false); // bit of a hack
             if (this.callbackfunc)
                 this.callbackfunc();
@@ -169,6 +169,7 @@ export default class Carousel {
             this.flowBox.remove(widget.get_parent());
             this.flowBox.set_max_children_per_line(2);
             this._create_gallery();
+            
         });
 
         let item = buildable.get_object('flowBoxPlaceholder');

+ 11 - 3
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/extension.js

@@ -561,6 +561,7 @@ class BingWallpaperIndicator extends Button {
 
     _randomModeChanged() {
         let randomEnabled = this._settings.get_boolean('random-mode-enabled');
+        Utils.validate_interval(this._settings);
         [this.toggleShuffleOnlyFaves, this.toggleShuffleOnlyUHD /*, this.toggleShuffleOnlyUnhidden*/]
             .forEach( x => {
                 x.setSensitive(randomEnabled);
@@ -588,8 +589,14 @@ class BingWallpaperIndicator extends Button {
     _trashImage() {
         log('trash image '+this.imageURL+' status was '+this.hidden_status);
         this.hidden_status = !this.hidden_status;
-        Utils.setImageHiddenStatus(this._settings, [this.imageURL], this.hidden_status);
-        this._setTrashIcon(this.hidden_status?ICON_UNTRASH_BUTTON:ICON_TRASH_BUTTON);
+        Utils.setImageHiddenStatus(this._settings, this.imageURL, this.hidden_status);
+        this._setTrashIcon(this.hidden_status?this.ICON_UNTRASH_BUTTON:this.ICON_TRASH_BUTTON);
+        if (this._settings.get_boolean('trash-deletes-images')) {
+            log('image to be deleted: '+this.filename);
+            Utils.deleteImage(this.filename);
+            Utils.validate_imagename(this._settings);
+        }
+        
     }
 
     _setFavouriteIcon(icon_name) {
@@ -715,7 +722,7 @@ class BingWallpaperIndicator extends Button {
         if (seconds == null) {
             let diff = -Math.floor(GLib.DateTime.new_now_local().difference(this.shuffledue)/1000000);
             log('shuffle ('+this.shuffledue.format_iso8601()+') diff = '+diff);
-            if (diff > 0) {
+            if (diff > 30) { // on occasions the above will be 1 second
                 seconds = diff; // if not specified, we should maintain the existing shuffle timeout (i.e. we just restored from saved state)
             }
             else if (this._settings.get_string('random-interval-mode') != 'custom') {
@@ -842,6 +849,7 @@ class BingWallpaperIndicator extends Button {
         // special values, 'current' is most recent (default mode), 'random' picks one at random, anything else should be filename
         
         if (force_shuffle) {
+            log('forcing shuffle of image')
             image = this._shuffleImage();
             if (this._settings.get_boolean('random-mode-enabled'))
                 this._restartShuffleTimeout();

BIN
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/locale/pt_BR/LC_MESSAGES/BingWallpaper.mo


+ 3 - 2
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/metadata.json

@@ -5,9 +5,10 @@
   "name": "Bing Wallpaper",
   "settings-schema": "org.gnome.shell.extensions.bingwallpaper",
   "shell-version": [
-    "45"
+    "45",
+    "46"
   ],
   "url": "https://github.com/neffo/bing-wallpaper-gnome-extension",
   "uuid": "BingWallpaper@ineffable-gmail.com",
-  "version": 48
+  "version": 49
 }

+ 181 - 132
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/prefs.js

@@ -21,19 +21,25 @@ const BingImageURL = Utils.BingImageURL;
 
 var DESKTOP_SCHEMA = 'org.gnome.desktop.background';
 
-var PREFS_DEFAULT_WIDTH = 950;
-var PREFS_DEFAULT_HEIGHT = 950;
+// this is pretty wide because of the size of the gallery
+var PREFS_DEFAULT_WIDTH = 750;
+var PREFS_DEFAULT_HEIGHT = 750;
 
 export default class BingWallpaperExtensionPreferences extends ExtensionPreferences {
     fillPreferencesWindow(window) {
         // formally globals
         let settings = this.getSettings(Utils.BING_SCHEMA);
-        let desktop_settings = this.getSettings(Utils.DESKTOP_SCHEMA);
+        //let desktop_settings = this.getSettings(Utils.DESKTOP_SCHEMA);
 
         window.set_default_size(PREFS_DEFAULT_WIDTH, PREFS_DEFAULT_HEIGHT);
 
-        let icon_image = null;
+        /*let icon_image = null;*/
         let provider = new Gtk.CssProvider();
+        provider.load_from_path(this.dir.get_path() + '/ui/prefs.css');
+        Gtk.StyleContext.add_provider_for_display(
+            Gdk.Display.get_default(),
+            provider,
+            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
 
         let carousel = null;
         let httpSession = null;
@@ -42,55 +48,137 @@ export default class BingWallpaperExtensionPreferences extends ExtensionPreferen
             if (settings.get_boolean('debug-logging'))
                 console.log("BingWallpaper extension: " + msg); // disable to keep the noise down in journal
         }
-
-        // Prepare labels and controls
+        
         let buildable = new Gtk.Builder();
         // GTK4 removes some properties, and builder breaks when it sees them
-        buildable.add_from_file( this.dir.get_path() + '/ui/Settings4.ui' );
-        provider.load_from_path(this.dir.get_path() + '/ui/prefs.css');
-        Gtk.StyleContext.add_provider_for_display(
-            Gdk.Display.get_default(),
-            provider,
-            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+        buildable.add_from_file( this.dir.get_path() + '/ui/prefsadw.ui' );
+
+        // adw or gtk objects we'll attach to below
+        const settings_page = buildable.get_object('settings_page');
+        const hideSwitch = buildable.get_object('hideSwitch');
+        const notifySwitch = buildable.get_object('notifySwitch');
+        const iconEntry = buildable.get_object('iconEntry');
+        const bgSwitch = buildable.get_object('bgSwitch');
+        const shuffleSwitch = buildable.get_object('shuffleSwitch');
+        const shuffleInterval = buildable.get_object('shuffleInterval'); 
+        const folderRow = buildable.get_object('folderRow');
+        const lockscreen_page = buildable.get_object('lockscreen_page');
+        const overrideSwitch = buildable.get_object('overrideSwitch');
+        const blurPresets = buildable.get_object('blurPresets');
+        const strengthEntry = buildable.get_object('strengthEntry');
+        const brightnessEntry = buildable.get_object('brightnessEntry');
+        const blurAdjustment = buildable.get_object('blurAdjustment');
+        const brightnessAdjustment = buildable.get_object('brightnessAdjustment');
+        const resolutionEntry = buildable.get_object('resolutionEntry');
+        const debugSwitch = buildable.get_object('debug_switch');
+        const revertSwitch = buildable.get_object('revert_switch');
+        const trash_purge_switch = buildable.get_object('trash_purge_switch');
+        const always_export_switch = buildable.get_object('always_export_switch');
+        const gallery_page = buildable.get_object('gallery_page');
+        const carouselFlowBox = buildable.get_object('carouselFlowBox');
+        const randomIntervalEntry = buildable.get_object('entry_random_interval');
+        const debug_page = buildable.get_object('debug_page');
+        const json_actionrow = buildable.get_object('json_actionrow');
+        const about_page = buildable.get_object('about_page');
+        const version_button = buildable.get_object('version_button');
+        const change_log = buildable.get_object('change_log');
+
+        window.add(settings_page);
+        window.add(lockscreen_page);
+        window.add(gallery_page);       
+        window.add(debug_page);
+        window.add(about_page);
+
+        iconEntry.set_value(1+Utils.icon_list.indexOf(settings.get_string('icon-name')));
+
+        // shuffle intervals
+        const shuffleIntervals  = new Gtk.StringList;
+        Utils.randomIntervals.forEach((x) => {
+            shuffleIntervals.append(_(x.title));
+        });
+       
+        shuffleInterval.set_model(shuffleIntervals);
+        shuffleInterval.set_selected(Utils.randomIntervals.map( e => e.value).indexOf(settings.get_string('random-interval-mode')));
+
+        // add wallpaper folder open and change buttons
+        const openBtn = new Gtk.Button( {
+            child: new Adw.ButtonContent({
+                        icon_name: 'folder-pictures-symbolic',
+                        label: _('Open folder'),
+                    },),
+            valign: Gtk.Align.CENTER, 
+            halign: Gtk.Align.CENTER,
+        });
+        const changeBtn = new Gtk.Button( {
+            child: new Adw.ButtonContent({
+                        icon_name: 'folder-download-symbolic',
+                        label: _('Change folder'),
+                    },),
+            valign: Gtk.Align.CENTER,
+            halign: Gtk.Align.CENTER,
+        });
+
+        folderRow.add_suffix(openBtn);
+        folderRow.add_suffix(changeBtn);   
+
+        blurAdjustment.set_value(settings.get_int('lockscreen-blur-strength'));
+        brightnessAdjustment.set_value(settings.get_int('lockscreen-blur-brightness'));
+        
+        const defaultBtn = new Gtk.Button( {
+            child: new Adw.ButtonContent({
+                        icon_name: 'emblem-default-symbolic',
+                        label: _('Default'),
+                    },),
+            valign: Gtk.Align.CENTER, 
+            halign: Gtk.Align.CENTER,
+        });
+        const noBlurBtn = new Gtk.Button( {
+            child: new Adw.ButtonContent({
+                        icon_name: 'emblem-default-symbolic',
+                        label: _('No blur, slight dim'),
+                    },),
+            valign: Gtk.Align.CENTER, 
+            halign: Gtk.Align.CENTER,
+        });
+        const slightBlurBtn = new Gtk.Button( {
+            child: new Adw.ButtonContent({
+                        icon_name: 'emblem-default-symbolic',
+                        label: _('Slight blur & dim'),
+                    },),
+            valign: Gtk.Align.CENTER, 
+            halign: Gtk.Align.CENTER,
+        });
 
-        let box = buildable.get_object('prefs_widget');
-
-        // fix size of prefs window in GNOME shell 40+ (but super racy, so is unreliable)
-
-        buildable.get_object('extension_version').set_text(this.metadata.version.toString());
-        buildable.get_object('extension_name').set_text(this.metadata.name.toString());
-
-        // assign variables to UI objects we've loaded
-        let hideSwitch = buildable.get_object('hide');
-        let iconEntry = buildable.get_object('icon');
-        let notifySwitch = buildable.get_object('notify');
-        let bgSwitch = buildable.get_object('background');
-        let styleEntry = buildable.get_object('background_style');
-        let fileChooserBtn = buildable.get_object('download_folder');
-        let fileChooser = buildable.get_object('file_chooser'); // this should only exist on Gtk4
-        let folderOpenBtn = buildable.get_object('button_open_download_folder');
-        let marketEntry = buildable.get_object('market');
-        let resolutionEntry = buildable.get_object('resolution');
-        let historyEntry = buildable.get_object('history');
-        icon_image = buildable.get_object('icon_image');
-        let overrideSwitch = buildable.get_object('lockscreen_override');
-        let strengthEntry = buildable.get_object('entry_strength');
-        let brightnessEntry = buildable.get_object('entry_brightness');
-        let debugSwitch = buildable.get_object('debug_switch');
-        let revertSwitch = buildable.get_object('revert_switch');
-        let unsafeSwitch = buildable.get_object('unsafe_switch');
-        let randomIntervalEntry = buildable.get_object('entry_random_interval');
-        let change_log = buildable.get_object('change_log');
-        let buttonGDMdefault = buildable.get_object('button_default_gnome');
-        let buttonnoblur = buildable.get_object('button_no_blur');
-        let buttonslightblur = buildable.get_object('button_slight_blur');
-        let buttonImportData = buildable.get_object('button_json_import');
-        let buttonExportData = buildable.get_object('button_json_export');
-        let switchAlwaysExport = buildable.get_object('always_export_switch');
-        let switchEnableShuffle = buildable.get_object('shuffle_enabled_switch');
-        let entryShuffleMode = buildable.get_object('shuffle_mode_combo');
-        let carouselFlowBox = (Gtk.get_major_version() == 4) ? buildable.get_object('carouselFlowBox'): null;
+        // add to presets row
+        blurPresets.add_suffix(defaultBtn);
+        blurPresets.add_suffix(noBlurBtn);
+        blurPresets.add_suffix(slightBlurBtn);
+        
+        randomIntervalEntry.set_value(settings.get_int('random-interval'));
+
+        // these buttons either export or import saved JSON data
+        const buttonImportData = new Gtk.Button( {
+            child: new Adw.ButtonContent({
+                        icon_name: 'document-send-symbolic',
+                        label: _('Import'),
+                    },),
+            valign: Gtk.Align.CENTER, 
+            halign: Gtk.Align.CENTER,
+        });
+        const buttonExportData = new Gtk.Button( {
+            child: new Adw.ButtonContent({
+                        icon_name: 'document-save-symbolic',
+                        label: _('Export'),
+                    },),
+            valign: Gtk.Align.CENTER, 
+            halign: Gtk.Align.CENTER,
+        });
+        
+        json_actionrow.add_suffix(buttonImportData);
+        json_actionrow.add_suffix(buttonExportData);
 
+        version_button.set_label(this.metadata.version.toString());      
+       
         try {
             httpSession = new Soup.Session();
             httpSession.user_agent = 'User-Agent: Mozilla/5.0 (X11; GNOME Shell/' + Config.PACKAGE_VERSION + '; Linux x86_64; +https://github.com/neffo/bing-wallpaper-gnome-extension ) BingWallpaper Gnome Extension/' + this.metadata.version;
@@ -98,43 +186,42 @@ export default class BingWallpaperExtensionPreferences extends ExtensionPreferen
         catch (e) {
             log("Error creating httpSession: " + e);
         }
-
+        const icon_image = buildable.get_object('icon_image');
+        
         // check that these are valid (can be edited through dconf-editor)
         Utils.validate_resolution(settings);
         Utils.validate_icon(settings, this.path, icon_image);
+        Utils.validate_interval(settings);
 
         // Indicator & notifications
         settings.bind('hide', hideSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
         settings.bind('notify', notifySwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
-
-        // add markets to dropdown list (aka a GtkComboText)
-        Utils.icon_list.forEach((iconname, index) => {
-            iconEntry.append(iconname, iconname);
-        });
-
-        // user selectable indicator icons
-        settings.bind('icon-name', iconEntry, 'active_id', Gio.SettingsBindFlags.DEFAULT);
         settings.connect('changed::icon-name', () => {
             Utils.validate_icon(settings, this.path, icon_image);
+            iconEntry.set_value(1 + Utils.icon_list.indexOf(settings.get_string('icon-name')));
+        });
+               
+        iconEntry.connect('output', () => {
+            settings.set_string('icon-name', Utils.icon_list[iconEntry.get_value()-1]);
         });
-        iconEntry.set_active_id(settings.get_string('icon-name'));
 
         // connect switches to settings changes
         settings.bind('set-background', bgSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
         settings.bind('debug-logging', debugSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
         settings.bind('revert-to-current-image', revertSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
-        settings.bind('override-unsafe-wayland', unsafeSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
+        //settings.bind('override-unsafe-wayland', unsafeSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
         settings.bind('random-interval', randomIntervalEntry, 'value', Gio.SettingsBindFlags.DEFAULT);
-        settings.bind('always-export-bing-json', switchAlwaysExport, 'active', Gio.SettingsBindFlags.DEFAULT);
+        settings.bind('trash-deletes-images', trash_purge_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
+        settings.bind('always-export-bing-json', always_export_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
 
         // button opens Nautilus at our image folder
-        folderOpenBtn.connect('clicked', (widget) => {
+        openBtn.connect('clicked', (widget) => {
             Utils.openImageFolder(settings);
         });
-
+        
         // we populate the tab (gtk4+, gnome 40+), this was previously a button to open a new window in gtk3
         carousel = new Carousel(settings, null, null, carouselFlowBox, this.dir.get_path()); // auto load carousel
-
+        
         // this is intended for migrating image folders between computers (or even sharing) or backups
         // we export the Bing JSON data to the image directory, so this folder becomes portable
         buttonImportData.connect('clicked', () => {
@@ -144,104 +231,66 @@ export default class BingWallpaperExtensionPreferences extends ExtensionPreferen
             Utils.exportBingJSON(settings);
         });
 
-        //download folder
-        fileChooserBtn.set_label(Utils.getWallpaperDir(settings));
-
-        fileChooserBtn.connect('clicked', (widget) => {
-            let parent = widget.get_root();
-            let curWallpaperDir = Gio.File.new_for_path(Utils.getWallpaperDir(settings));
-            fileChooser.set_current_folder(curWallpaperDir.get_parent());
-            fileChooser.set_action(Gtk.FileChooserAction.SELECT_FOLDER);
-            fileChooser.set_transient_for(parent);
-            fileChooser.set_accept_label(_('Select folder'));
-            fileChooser.show();
-        });
-
-        fileChooser.connect('response', (widget, response) => {
-            if (response !== Gtk.ResponseType.ACCEPT) {
-                return;
-            }
-            let fileURI = fileChooser.get_file().get_path().replace('file://', '');
-            fileChooserBtn.set_label(fileURI);
-            Utils.moveImagesToNewFolder(settings, Utils.getWallpaperDir(settings), fileURI);
-            Utils.setWallpaperDir(settings, fileURI);
+        // change wallpaper button
+        const dirChooser = new Gtk.FileDialog( {
+            accept_label: "Select",
+            modal: true,
+            title: _("Select wallpaper download folder"),
         });
 
-        // in Gtk 4 instead we use a DropDown, but we need to treat it a bit special
-        let market_grid = buildable.get_object('market_grid');
-        marketEntry = Gtk.DropDown.new_from_strings(Utils.marketName);
-        marketEntry.set_selected(Utils.markets.indexOf(settings.get_string('market')));
-        market_grid.attach(marketEntry, 1, 0, 1, 2);
-        marketEntry.connect('notify::selected-item', () => {
-            let id = marketEntry.get_selected();
-            settings.set_string('market', Utils.markets[id]);
-        });
-
-        settings.connect('changed::market', () => {
-            marketEntry.set_selected(Utils.markets.indexOf(settings.get_string('market')));
-        });
+        changeBtn.connect('clicked', (widget) => {
+            dirChooser.set_initial_folder(Gio.File.new_for_path(Utils.getWallpaperDir(settings)));
+            dirChooser.select_folder(window, null, (self, res) => {
+                let new_path = self.select_folder_finish(res).get_uri().replace('file://', '');
+                log(new_path);
+                Utils.moveImagesToNewFolder(settings, Utils.getWallpaperDir(settings), new_path);
+                Utils.setWallpaperDir(settings, new_path);
+            });
 
-        settings.connect('changed::download-folder', () => {
-            fileChooserBtn.set_label(Utils.getWallpaperDir(settings));
         });
 
-
         // Resolution
+        const resolutionModel = new Gtk.StringList();
         Utils.resolutions.forEach((res) => { // add res to dropdown list (aka a GtkComboText)
-            resolutionEntry.append(res, res);
+            resolutionModel.append(res);
         });
+        resolutionEntry.set_model(resolutionModel);
         
-        settings.bind('resolution', resolutionEntry, 'active_id', Gio.SettingsBindFlags.DEFAULT);
+        settings.connect('changed::resolution', () => {
+            resolutionEntry.set_selected(Utils.resolutions.map( e => e.value).indexOf(settings.get_string('resolution')));
+        });
 
         settings.connect('changed::resolution', () => {
             Utils.validate_resolution(settings);
         });
 
         // shuffle modes
-        settings.bind('random-mode-enabled', switchEnableShuffle, 'active', Gio.SettingsBindFlags.DEFAULT);
-        Utils.randomIntervals.forEach((x) => {
-            entryShuffleMode.append(x.value, _(x.title));
-        });
-        settings.bind('random-interval-mode', entryShuffleMode, 'active_id', Gio.SettingsBindFlags.DEFAULT);
+        settings.bind('random-mode-enabled', shuffleSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
+        /*settings.bind('random-interval-mode', entryShuffleMode, 'active_id', Gio.SettingsBindFlags.DEFAULT);*/
 
-        // selected image can no longer be changed through a dropdown (didn't scale)
-        settings.bind('selected-image', historyEntry, 'label', Gio.SettingsBindFlags.DEFAULT);
-        settings.connect('changed::selected-image', () => {
-            Utils.validate_imagename(settings);
-        });
-        
-        // background styles (e.g. zoom or span)
-        Utils.backgroundStyle.forEach((style) => {
-            styleEntry.append(style, style);
+        settings.connect('changed::random-interval-mode', () => {
+            shuffleInterval.set_selected(Utils.randomIntervals.map( e => e.value).indexOf(settings.get_string('random-interval-mode')));
         });
-        desktop_settings.bind('picture-options', styleEntry, 'active_id', Gio.SettingsBindFlags.DEFAULT);
-    
+            
         // GDM3 lockscreen blur override
         settings.bind('override-lockscreen-blur', overrideSwitch, 'active', Gio.SettingsBindFlags.DEFAULT);
         settings.bind('lockscreen-blur-strength', strengthEntry, 'value', Gio.SettingsBindFlags.DEFAULT);
         settings.bind('lockscreen-blur-brightness', brightnessEntry, 'value', Gio.SettingsBindFlags.DEFAULT);
 
         // add a couple of preset buttons
-        buttonGDMdefault.connect('clicked', (widget) => {
+        defaultBtn.connect('clicked', (widget) => {
             Utils.set_blur_preset(settings, Utils.PRESET_GNOME_DEFAULT);
         });
-        buttonnoblur.connect('clicked', (widget) => {
+        noBlurBtn.connect('clicked', (widget) => {
             Utils.set_blur_preset(settings, Utils.PRESET_NO_BLUR);
         });
-        buttonslightblur.connect('clicked', (widget) => {
+        slightBlurBtn.connect('clicked', (widget) => {
             Utils.set_blur_preset(settings, Utils.PRESET_SLIGHT_BLUR);
         });
-
-        // fetch
+        
+        // fetch change log (on about page)
+        
         if (httpSession)
             Utils.fetch_change_log(this.metadata.version.toString(), change_log, httpSession);
-
-        const page = new Adw.PreferencesPage();
-        const group = new Adw.PreferencesGroup();
-
-        group.add(box);
-        page.add(group);
-
-        window.add(page);
     }
 }

BIN
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/schemas/gschemas.compiled


+ 1 - 1
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/schemas/org.gnome.shell.extensions.bingwallpaper.gschema.xml

@@ -211,7 +211,7 @@
     </key>
 
     <key name="trash-deletes-images" type="b">
-      <default>false</default>
+      <default>true</default>
       <summary>Trash deletes images or just marks as bad</summary>
     </key>
     

+ 0 - 1451
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/ui/Settings4.ui

@@ -1,1451 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.22.2 
-
-Copyright (C) 2017-2021
-
-This file is part of Bing Wallpaper extension preferences.
-
-This extension is free software: you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This extension is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License
-along with this extension.  If not, see <http://www.gnu.org/licenses/>.
-
-Bing Wallpaper GNOME extension by: Michael Carroll
-
--->
-<interface domain="BingWallpaper">
-  <requires lib="gtk" version="4.0"/>
-  <object class="GtkAdjustment" id="adjustment_blur">
-    <property name="upper">50</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment_brightness">
-    <property name="upper">100</property>
-    <property name="step_increment">5</property>
-    <property name="page_increment">10</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment_days">
-    <property name="upper">7</property>
-    <property name="step_increment">1</property>
-    <property name="page_increment">2</property>
-  </object>
-  <object class="GtkAdjustment" id="adjustment_random_interval">
-    <property name="lower">300</property>
-    <property name="upper">604800</property>
-    <property name="step_increment">300</property>
-    <property name="page_increment">3600</property>
-  </object>
-  <object class="GtkFileChooserNative" id="file_chooser">
-    <property name="title" translatable="yes">Bing Wallpaper pictures folder</property>
-    <property name="select-multiple">0</property>
-    <property name="action">select-folder</property>
-    <property name="modal">1</property>
-    <!-- <signal name="response" handler="_onFileChooserResponse" swapped="no"/> -->
-  </object>
-  <object class="GtkEntryBuffer" id="searchBuffer">
-    <property name="max-length">120</property>
-  </object>
-  <object class="GtkNotebook" id="prefs_widget">
-    <property name="can_focus">0</property>
-    <property name="scrollable">0</property>
-    <property name="vexpand">0</property>
-    <property name="height_request">500</property>
-    <child>
-      <object class="GtkNotebookPage">
-        <property name="child">
-          <object class="GtkBox" id="settings_box">
-            <property name="can_focus">0</property>
-            <property name="margin-start">12</property>
-            <property name="margin-end">12</property>
-            <property name="margin_top">12</property>
-            <property name="margin_bottom">12</property>
-            <property name="orientation">vertical</property>
-            <property name="spacing">12</property>
-            <property name="vexpand">0</property>
-            <child>
-              <object class="GtkFrame" id="gui_frame">
-                <property name="can_focus">0</property>
-                <property name="child">
-                  <object class="GtkListBox" id="gui_listbox">
-                    <property name="can_focus">0</property>
-                    <property name="selection_mode">none</property>
-                    <child>
-                      <object class="GtkListBoxRow" id="hide_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="hide_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="hide_label">
-                                <property name="can_focus">0</property>
-                                <property name="label" translatable="yes">Hide the indicator</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSwitch" id="hide">
-                                <property name="halign">end</property>
-                                <property name="hexpand">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="icon_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="icon_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="icon_label">
-                                <property name="can_focus">0</property>
-                                <property name="label" translatable="yes">Indicator icon</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                  <property name="row-span">2</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkComboBoxText" id="icon">
-                                <property name="can_focus">0</property>
-                                <property name="halign">end</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">1</property>
-                                  <property name="row-span">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkImage" id="icon_image">
-                                <property name="width_request">32</property>
-                                <property name="height_request">32</property>
-                                <property name="can_focus">0</property>
-                                <property name="halign">end</property>
-                                <property name="hexpand">1</property>
-                                <property name="file">../icons/bing-symbolic.svg</property>
-                                <property name="css_classes">icon_image_black_bg</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                  <property name="row-span">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="notify_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="notify_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="notify_label">
-                                <property name="can_focus">0</property>
-                                <property name="label" translatable="yes">Enable desktop notifications</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSwitch" id="notify">
-                                <property name="halign">end</property>
-                                <property name="hexpand">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                  </object>
-                </property>
-                <child type="label_item">
-                  <placeholder/>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkFrame" id="pictures_frame">
-                <property name="can_focus">0</property>
-                <property name="child">
-                  <object class="GtkListBox" id="pictures_listbox">
-                    <property name="can_focus">0</property>
-                    <property name="selection_mode">none</property>
-                    <child>
-                      <object class="GtkListBoxRow" id="background_listboxrow">
-                        <property name="child">
-                          <object class="GtkGrid" id="background_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <child>
-                              <object class="GtkLabel" id="background_label">
-                                <property name="can_focus">0</property>
-                                <property name="label" translatable="yes">Set background image</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSwitch" id="background">
-                                <property name="halign">end</property>
-                                <property name="hexpand">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="style_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="style_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="style_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Background style option</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkComboBoxText" id="background_style">
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                  <property name="row-span">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="download_folder_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="download_folder_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="download_folder_label">
-                                <property name="can_focus">0</property>
-                                <property name="label" translatable="yes">Download folder</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkButton" id="download_folder">
-                                <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="halign">end</property>
-                                <property name="hexpand">false</property>
-                                <property name="label" translatable="yes">Bing Wallpaper pictures folder</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkButton" id="button_open_download_folder">
-                                <property name="label" translatable="yes">Open folder</property>
-                                <property name="visible">True</property>
-                                <property name="can_focus">True</property>
-                                <layout>
-                                  <property name="column">2</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                  </object>
-                </property>
-                <child type="label_item">
-                  <placeholder/>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkFrame" id="history_frame">
-                <property name="can_focus">0</property>
-                <property name="child">
-                  <object class="GtkListBox" id="history_listbox">
-                    <property name="can_focus">0</property>
-                    <property name="selection_mode">none</property>
-                    <child>
-                      <object class="GtkListBoxRow" id="history_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="history_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="history_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Selected image</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="history">
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                  <property name="row-span">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="history_shuffle_enabled_boxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="shuffle_enabled_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="shuffle_enabled_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Shuffle enabled</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSwitch" id="shuffle_enabled_switch">
-                                <property name="halign">end</property>
-                                <property name="hexpand">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="history_shufflemodeboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="shuffle_mode_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="shuffle_mode_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Shuffle mode</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkComboBoxText" id="shuffle_mode_combo">
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                  <property name="row-span">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                  </object>
-                </property>
-                <child type="label_item">
-                  <placeholder/>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkFrame" id="market_frame">
-                <property name="can_focus">0</property>
-                <property name="child">
-                  <object class="GtkListBox" id="market_listbox">
-                    <property name="can_focus">0</property>
-                    <property name="selection_mode">none</property>
-                    <child>
-                      <object class="GtkListBoxRow" id="market_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="market_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="market_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Bing locale</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="market_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">0</property>
-                                <property name="label" translatable="no"></property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                  </object>
-                </property>
-                <child type="label_item">
-                  <placeholder/>
-                </child>
-              </object>
-            </child>          
-          </object>
-        </property>
-        <property name="tab">
-          <object class="GtkLabel" id="settings_label">
-            <property name="can_focus">0</property>
-            <property name="label" translatable="yes">Settings</property>
-          </object>
-        </property>
-      </object>
-    </child>
-    <child>
-      <object class="GtkNotebookPage">
-        <property name="position">1</property>
-        <property name="child">
-          <object class="GtkBox" id="lockscreen_box">
-            <property name="can_focus">0</property>
-            <property name="margin-start">12</property>
-            <property name="margin-end">12</property>
-            <property name="margin_top">12</property>
-            <property name="margin_bottom">12</property>
-            <property name="orientation">vertical</property>
-            <property name="spacing">12</property>
-            <child>
-              <object class="GtkFrame" id="lockscreen_frame">
-                <property name="vexpand">0</property>
-                <property name="can_focus">0</property>
-                <property name="child">
-                  <object class="GtkListBox" id="lockscreen_listbox">
-                    <property name="can_focus">0</property>
-                    <property name="selection_mode">none</property>
-                    <child>
-                      <object class="GtkListBoxRow" id="lockscreen_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="lockscreen__grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkSwitch" id="lockscreen_override">
-                                <property name="halign">end</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="override_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Dynamically switches blur on GDM3 lock screen</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="lockscreen_override_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Enable dynamic lockscreen blur</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="blur_intensity_listboxrow">
-                        <property name="child">
-                          <object class="GtkGrid" id="lockscreen__grid1">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="strength_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Blur can improve readability</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="lockscreen_override_label3">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Background blur intensity</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSpinButton" id="entry_strength">
-                                <property name="adjustment">adjustment_blur</property>
-                                <property name="numeric">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="blur_brightness_listboxrow">
-                        <property name="child">
-                          <object class="GtkGrid" id="lockscreen__grid2">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="override_description2">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Can improve contrast of login prompt</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="lockscreen_override_label1">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Background brightness</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSpinButton" id="entry_brightness">
-                                <property name="adjustment">adjustment_brightness</property>
-                                <property name="numeric">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="blur_presets_listboxrow">
-                        <property name="child">
-                          <object class="GtkGrid" id="preview_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="lockscreen_presets">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Presets</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkButton" id="button_no_blur">
-                                <property name="label" translatable="yes">No blur, slight dim</property>
-                                <property name="receives_default">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">3</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkButton" id="button_default_gnome">
-                                <property name="label" translatable="yes">GNOME default</property>
-                                <property name="receives_default">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">2</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkButton" id="button_slight_blur">
-                                <property name="label" translatable="yes">Slight blur, slight dim</property>
-                                <property name="receives_default">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">4</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                  </object>
-                </property>
-                <child type="label_item">
-                  <placeholder/>
-                </child>
-              </object>
-            </child>
-          </object>
-        </property>
-        <property name="tab">
-          <object class="GtkLabel" id="lockscreen_label">
-            <property name="can_focus">0</property>
-            <property name="label" translatable="yes">Lock Screen</property>
-          </object>
-        </property>
-      </object>
-    </child>
-    <child>
-      <object class="GtkNotebookPage">
-        <property name="position">2</property>
-        <property name="child">
-          <object class="GtkBox" id="debug_box">
-            <property name="can_focus">0</property>
-            <property name="margin-start">12</property>
-            <property name="margin-end">12</property>
-            <property name="margin_top">12</property>
-            <property name="margin_bottom">12</property>
-            <property name="orientation">vertical</property>
-            <property name="spacing">12</property>
-            <child>
-              <object class="GtkFrame" id="debug_frame">
-                <property name="vexpand">0</property>
-                <property name="can_focus">0</property>
-                <property name="child">
-                  <object class="GtkListBox" id="debug_listbox">
-                    <property name="can_focus">0</property>
-                    <property name="selection_mode">none</property>
-                    <child>
-                      <object class="GtkListBoxRow" id="debug_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="debug_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkSwitch" id="debug_switch">
-                                <property name="halign">end</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="debug_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Enable logging to system journal</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="debug_switch_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Debug logging</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="revert_listboxrow">
-                        <property name="child">
-                          <object class="GtkGrid" id="revert_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="revert_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Switch to new images when available (unless on random mode)</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="revert_override_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Always show new images</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSwitch" id="revert_switch">
-                                <property name="halign">end</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="unsafe_listboxrow">
-                        <property name="child">
-                          <object class="GtkGrid" id="unsafe_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="unsafe_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Some newer features may be unstable on Wayland</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="unsafe_override_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Enable all features on Wayland</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSwitch" id="unsafe_switch">
-                                <property name="halign">end</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="resolution_listboxrow">
-                        <property name="can_focus">0</property>
-                        <property name="child">
-                          <object class="GtkGrid" id="resolution_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="resolution_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Screen resolution</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="resolution_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="label" translatable="yes">Override automatic resolution selection</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkComboBoxText" id="resolution">
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                  <property name="row-span">2</property>
-                                </layout>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="random_interval_listboxrow">
-                        <property name="child">
-                          <object class="GtkGrid" id="lrandom_interval_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="random_interval_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Manually adjust random interval (seconds)</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="random_interval_label">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Random interval</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSpinButton" id="entry_random_interval">
-                                <property name="adjustment">adjustment_random_interval</property>
-                                <property name="numeric">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="json_import_tool">
-                        <property name="child">
-                          <object class="GtkGrid" id="json_import_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="import_title">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Import Bing Wallpaper data</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="import_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Import previously exported JSON data from wallpaper directory</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>                            
-                            <child>
-                              <object class="GtkButton" id="button_json_import">
-                                <property name="label" translatable="yes">Import</property>
-                                <property name="receives_default">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="json_export_tool">
-                        <property name="child">
-                          <object class="GtkGrid" id="json_export_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="export_title">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Export Bing Wallpaper data</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="export_description">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Export JSON data to wallpaper dir for backup or data migration</property>
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>                                                
-                            <child>
-                              <object class="GtkButton" id="button_json_export">
-                                <property name="label" translatable="yes">Export</property>
-                                <property name="receives_default">1</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow" id="always_export_row">
-                        <property name="child">
-                          <object class="GtkGrid" id="always_export_grid">
-                            <property name="can_focus">0</property>
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin_top">12</property>
-                            <property name="margin_bottom">12</property>
-                            <property name="column_spacing">32</property>
-                            <child>
-                              <object class="GtkLabel" id="always_export_title">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Always export Bing data</property>
-                                <property name="wrap">1</property>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">0</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="always_export_descripton">
-                                <property name="can_focus">0</property>
-                                <property name="halign">start</property>
-                                <property name="hexpand">1</property>
-                                <property name="label" translatable="yes">Export Bing JSON whenever data changes</property>
-                                
-                                <style>
-                                  <class name="dim-label"/>
-                                </style>
-                                <layout>
-                                  <property name="column">0</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkSwitch" id="always_export_switch">
-                                <property name="halign">end</property>
-                                <layout>
-                                  <property name="column">1</property>
-                                  <property name="row">1</property>
-                                </layout>
-                              </object>
-                            </child>
-                            <child>
-                              <placeholder/>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                  </object>
-                </property>
-                <child type="label_item">
-                  <placeholder/>
-                </child>
-              </object>
-            </child>
-          </object>
-        </property>
-        <property name="tab">
-          <object class="GtkLabel" id="debug_label">
-            <property name="can_focus">0</property>
-            <property name="label" translatable="yes">Debug options</property>
-          </object>
-        </property>
-      </object>
-    </child>
-    <child>
-      <object class="GtkNotebookPage" id="galleryPage">
-        <property name="position">3</property>
-        <property name="child">
-          <object class="GtkBox" id="outerGrid">
-            <property name="homogeneous">0</property>            
-            <property name="orientation">vertical</property>
-            <!--<child>
-              <object class="GtkEntry" id="searchBar">
-                <property name="hexpand">True</property>
-                <property name="placeholder-text">Search for images by title or description</property>
-                <property name="buffer">searchBuffer</property>
-                <property name="editable">true</property>
-                <property name="width-chars">80</property>
-
-              </object>
-            </child>-->
-            <child>
-              <object class="GtkViewport" id="carouselViewPort">
-                <property name="can-focus">0</property>
-                <property name="height_request">500</property>
-                <property name="hexpand">0</property>
-                <property name="vexpand">0</property>
-                <property name="child">
-                  <object class="GtkFlowBox" id="carouselFlowBox">
-                    <property name="can-focus">0</property>
-                    <property name="halign">center</property>
-                    <property name="homogeneous">1</property>
-                    <property name="max-children-per-line">2</property>
-                    <property name="min-children-per-line">2</property>
-                    <property name="row-spacing">2</property>
-                    <property name="column-spacing">2</property>
-                  </object>
-                </property>
-              </object>
-            </child>
-          </object>
-        </property>
-        <property name="tab">
-          <object class="GtkLabel" id="Gallery Label">
-            <property name="can_focus">0</property>
-            <property name="label" translatable="yes">Gallery</property>
-          </object>
-        </property>
-      </object>
-    </child>
-    <child>
-      <object class="GtkNotebookPage">
-        <property name="position">4</property>
-        <property name="child">
-          <object class="GtkBox" id="about_box">
-            <property name="can_focus">0</property>
-            <!-- <property name="margin_top">12</property> -->
-            <!-- <property name="margin_bottom">12</property> -->
-            <property name="hexpand">1</property>
-            <property name="vexpand">0</property>
-            <property name="orientation">vertical</property>
-            <property name="spacing">5</property>
-            <!-- <child>
-              <object class="GtkImage" id="logo">
-                <property name="can_focus">0</property>
-                <property name="file">../icons/bing-symbolic.svg</property>
-              </object>
-            </child> -->
-            <child>
-              <object class="GtkLabel" id="extension_name">
-                <property name="can_focus">0</property>
-                <property name="label">Bing Wallpaper</property>
-                <attributes>
-                  <attribute name="weight" value="bold"></attribute>
-                </attributes>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel" id="extension_description">
-                <property name="can_focus">0</property>
-                <property name="label" translatable="yes">New wallpaper images everyday from Bing</property>
-                <property name="justify">center</property>
-                <property name="wrap">1</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkBox" id="version_box">
-                <property name="can_focus">0</property>
-                <property name="halign">center</property>
-                <!-- <property name="margin_bottom">12</property> -->
-                <child>
-                  <object class="GtkLabel" id="extension_version_label">    
-                    <property name="can_focus">0</property>
-                    <property name="halign">end</property>
-                    <property name="label" translatable="yes">GNOME shell extension version </property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="extension_version">
-                    <property name="can_focus">0</property>
-                    <property name="halign">start</property>
-                    <property name="label">...</property>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLinkButton" id="homepage_link">
-                <property name="label" translatable="no">https://github.com/neffo/bing-wallpaper-gnome-extension</property>
-                <property name="can_focus">0</property>
-                <property name="receives_default">1</property>
-                <property name="halign">center</property>
-                <property name="uri">https://github.com/neffo/bing-wallpaper-gnome-extension</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkBox" id="maintainer_box">
-                <property name="can_focus">0</property>
-                <property name="halign">center</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">5</property>
-                <child>
-                  <object class="GtkLabel" id="maintainer_label">
-                    <property name="can_focus">0</property>
-                    <property name="label" translatable="yes">Maintained by Michael Carroll</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="email_label">
-                    <property name="can_focus">0</property>
-                    <property name="label">ineffable@gmail.com</property>
-                    <property name="use_markup">1</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel">
-                    <property name="can_focus">0</property>
-                    <property name="margin-start">36</property>
-                    <property name="label" translatable="yes">Show your support to the author on &lt;a href=&quot;https://flattr.com/@neffo&quot;&gt;Flattr&lt;/a&gt; or &lt;a href=&quot;https://github.com/sponsors/neffo&quot;&gt;Github Sponsors&lt;/a&gt;.</property>
-                    <property name="use_markup">1</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel" id="change_log">
-                    <property name="can_focus">0</property>
-                    <property name="margin-start">36</property>
-                    <!-- <property name="margin-end">12</property> -->
-                    <property name="hexpand">1</property>
-                    <!-- <property name="vexpand">1</property> -->
-                    <property name="label" translatable="yes">Changes since last version</property>
-                    <property name="wrap">1</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkLabel">
-                    <property name="can_focus">0</property>
-                    <property name="margin-start">36</property>
-                    <property name="label" translatable="yes">Based on NASA APOD GNOME shell extension by Elia Argentieri</property>
-                    <property name="justify">center</property>
-                    <property name="wrap">1</property>
-                    <attributes>
-                      <attribute name="style" value="italic"></attribute>
-                    </attributes>
-                  </object>
-                </child>
-              </object>
-            </child>
-            <child>
-              <object class="GtkLabel" id="GNU_label">
-                <property name="vexpand">0</property>
-                <property name="can_focus">0</property>
-                <property name="label" translatable="yes">&lt;span size=&quot;small&quot;&gt;This program comes with ABSOLUTELY NO WARRANTY.
-See the &lt;a href=&quot;https://www.gnu.org/licenses/gpl-3.0.html&quot;&gt;GNU General Public License, version 3 or later&lt;/a&gt; for details.&lt;/span&gt;</property>
-                <property name="use_markup">1</property>
-                <property name="justify">center</property>
-                <property name="wrap">1</property>
-              </object>
-            </child>
-          </object>
-        </property>
-        <property name="tab">
-          <object class="GtkLabel" id="about_label">
-            <property name="can_focus">0</property>
-            <property name="label" translatable="yes">About</property>
-          </object>
-        </property>
-      </object>
-    </child>
-  </object>
-</interface>

+ 2 - 2
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/ui/carousel4.ui

@@ -56,7 +56,7 @@ Author: Michael Carroll
         <child>
           <object class="GtkPicture" id="galleryImage">
             <property name="width-request">320</property>
-            <property name="height-request">180</property>
+            <property name="height-request">120</property>
             <property name="can-focus">0</property>
             <property name="hexpand">1</property>
             <property name="vexpand">1</property>
@@ -186,7 +186,7 @@ Author: Michael Carroll
             <property name="height-request">120</property>
             <property name="can-focus">0</property>
             <property name="icon-name">preferences-desktop-wallpaper-symbolic</property>
-            <property name="icon_size">3</property>
+            <property name="icon_size">2</property>
             <layout>
               <property name="column">0</property>
               <property name="row">0</property>

+ 438 - 0
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/ui/prefsadw.ui

@@ -0,0 +1,438 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+Copyright (C) 2017-2024
+
+This file is part of Bing Wallpaper extension preferences.
+
+This extension is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This extension is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this extension.    If not, see <http://www.gnu.org/licenses/>.
+
+Bing Wallpaper GNOME extension by: Michael Carroll
+-->
+<interface domain="BingWallpaper">
+    <requires lib="gtk" version="4.0"/>
+    <requires lib="libadwaita" version="1.0"/>
+    <object class="AdwPreferencesPage" id="settings_page">
+        <property name="icon-name">emblem-photos-symbolic</property>
+        <property name="title" translatable="yes">Settings</property>
+        <child>
+            <object class="AdwPreferencesGroup" id="ui_group">
+                <property name="title" translatable="yes">Indicator</property>
+                <child>
+                    <object class="AdwSwitchRow" id="hideSwitch">
+                        <property name="title" translatable="yes">Hide Indicator</property>
+                        <property name="subtitle" translatable="yes">Whether to hide the panel indicator</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSwitchRow" id="notifySwitch">
+                        <property name="title" translatable="yes">Desktop notifications</property>
+                        <property name="subtitle" translatable="yes">Enable notifications on new images</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSpinRow" id="iconEntry">
+                        <property name="title" translatable="yes">Indicator icon</property>
+                        <property name="subtitle" translatable="yes">Select from alternate tray icons</property>
+                        <property name="adjustment">
+                            <object class="GtkAdjustment" id="iconEntryAdjustment">
+                                <property name="lower">1</property>
+                                <property name="upper">5</property>
+                                <property name="step_increment">1</property>
+                            </object>
+                        </property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwActionRow" id="icon_image_row">
+                        <!--
+                        <property name="title" translatable="yes">Download folder</property>
+                        <property name="subtitle" translatable="yes">Open or change wallpaper downloads folder</property>
+                        -->
+                        <child>
+                            <object class="GtkImage" id="icon_image">
+                                <property name="width_request">64</property>
+                                <property name="height_request">64</property>
+                                <property name="can_focus">0</property>
+                                <property name="halign">end</property>
+                                <property name="hexpand">1</property>
+                                <property name="file">../icons/bing-symbolic.svg</property>
+                                <property name="css_classes">icon_image_black_bg</property>
+                            </object>
+                        </child>
+                    </object>
+                </child>
+            </object>
+        </child>
+        <child>
+            <object class="AdwPreferencesGroup" id="wp_group">
+                <property name="title" translatable="yes">Wallpaper</property>
+                <child>
+                    <object class="AdwSwitchRow" id="bgSwitch">
+                        <property name="title" translatable="yes">Set wallpaper</property>
+                        <property name="subtitle" translatable="yes">Whether to set wallpaper automatically</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSwitchRow" id="shuffleSwitch">
+                        <property name="title" translatable="yes">Shuffle wallpaper</property>
+                        <property name="subtitle" translatable="yes">Randomly select wallpaper from collection</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwComboRow" id="shuffleInterval">
+                        <property name="title" translatable="yes">Shuffle interval</property>
+                        <property name="subtitle" translatable="yes">How frequently to shuffle wallpapers</property>
+                    </object>
+                </child>
+            </object>
+        </child>
+        <child>
+            <object class="AdwPreferencesGroup" id="dl_group">
+                <property name="title" translatable="yes">Downloads</property>
+                <child>
+                    <object class="AdwActionRow" id="folderRow">
+                        <property name="title" translatable="yes">Download folder</property>
+                        <property name="subtitle" translatable="yes">Open or change wallpaper downloads folder</property>
+                    </object>
+                </child>
+            </object>
+        </child>
+    </object>
+    <object class="AdwPreferencesPage" id="lockscreen_page">
+        <property name="icon-name">applications-system-symbolic</property>
+        <property name="title" translatable="yes">Lock screen</property>
+        <child>
+            <object class="AdwPreferencesGroup" id="ls_group">
+                <property name="title" translatable="yes">Lockscreen blur</property>
+                <child>
+                    <object class="AdwSwitchRow" id="overrideSwitch">
+                        <property name="title" translatable="yes">Dynamic lockscreen blur</property>
+                        <property name="subtitle" translatable="yes">Whether to enable dynamic blur mode on lock screen</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSpinRow" id="strengthEntry">
+                        <property name="title" translatable="yes">Blur strength</property>
+                        <property name="subtitle" translatable="yes">Blur strength when login prompt is not visible</property>
+                        <property name="adjustment">
+                            <object class="GtkAdjustment" id="blurAdjustment">
+                                <property name="lower">0</property>
+                                <property name="upper">50</property>
+                                <property name="page_increment">10</property>
+                                <property name="step_increment">1</property>
+                            </object>
+                        </property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSpinRow" id="brightnessEntry">
+                        <property name="title" translatable="yes">Wallpaper brightness</property>
+                        <property name="subtitle" translatable="yes">Dim wallpaper when login prompt is not visible</property>
+                        <property name="adjustment">
+                            <object class="GtkAdjustment" id="brightnessAdjustment">
+                                <property name="lower">0</property>
+                                <property name="upper">100</property>
+                                <property name="page_increment">10</property>
+                                <property name="step_increment">1</property>
+                            </object>
+                        </property>
+                    </object>
+                </child>
+            </object>
+        </child>
+        <child>
+            <object class="AdwPreferencesGroup" id="ps_group">
+                <!-- <property name="title" translatable="yes">Presets</property> -->
+                <child>
+                    <object class="AdwActionRow" id="blurPresets">
+                        <property name="title" translatable="yes">Presets</property>
+                    </object>
+                </child>
+            </object>
+        </child>
+    </object>
+    <object class="AdwPreferencesPage" id="gallery_page">
+        <property name="icon-name">document-open-recent-symbolic</property>
+        <property name="title" translatable="yes">Gallery</property>
+        <child>
+            <object class="GtkScrolledWindow" id="carouselViewPort">
+            <property name="can-focus">0</property>
+            <property name="height_request">500</property>
+            <property name="hexpand">0</property>
+            <property name="vexpand">0</property>
+            <property name="child">
+              <object class="GtkBox">
+                <child>
+                    <object class="GtkFlowBox" id="carouselFlowBox">
+                        <property name="can-focus">0</property>
+                        <property name="halign">center</property>
+                        <property name="homogeneous">1</property>
+                        <property name="max-children-per-line">2</property>
+                        <property name="min-children-per-line">2</property>
+                        <property name="row-spacing">2</property>
+                        <property name="column-spacing">2</property>
+                    </object>
+                </child>
+              </object>
+            </property>
+            </object>
+        </child>
+    </object>
+    <object class="AdwPreferencesPage" id="debug_page">
+        <property name="icon-name">preferences-other-symbolic</property>
+        <property name="title" translatable="yes">Debug</property>
+        <child>
+            <object class="AdwPreferencesGroup" id="db_group">
+                <property name="title" translatable="yes">Debug options</property>
+                <child>
+                    <object class="AdwSwitchRow" id="debug_switch">
+                        <property name="title" translatable="yes">Debug logging</property>
+                        <property name="subtitle" translatable="yes">Enable logging to system journal</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSwitchRow" id="revert_switch">
+                        <property name="title" translatable="yes">Always show new images</property>
+                        <property name="subtitle" translatable="yes">Switch to new images when available (unless on random mode)</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSwitchRow" id="trash_purge_switch">
+                        <property name="title" translatable="yes">Purge on trash</property>
+                        <property name="subtitle" translatable="yes">Trashing an image will remove it from database</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwComboRow" id="resolutionEntry">
+                        <property name="title" translatable="yes">Screen resolution</property>
+                        <property name="subtitle" translatable="yes">Override automatic resolution selection</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwSpinRow" id="entry_random_interval">
+                        <property name="title" translatable="yes">Random interval</property>
+                        <property name="subtitle" translatable="yes">Custom shuffle interval when enabled</property>
+                        <property name="adjustment">
+                            <object class="GtkAdjustment" id="adjustment_random_interval">
+                                <property name="lower">300</property>
+                                <property name="upper">604800</property>
+                                <property name="page_increment">300</property>
+                                <property name="step_increment">3600</property>
+                            </object>
+                        </property>
+                    </object>
+                </child>
+            </object>
+        </child>
+        <child>
+            <object class="AdwPreferencesGroup" id="js_group">
+                <!-- <property name="title" translatable="yes">Presets</property> -->
+                <child>
+                    <object class="AdwSwitchRow" id="always_export_switch">
+                        <property name="title" translatable="yes">Always export Bing data</property>
+                        <property name="subtitle" translatable="yes">Export Bing JSON when image data changes</property>
+                    </object>
+                </child>
+                <child>
+                    <object class="AdwActionRow" id="json_actionrow">
+                        <property name="title" translatable="yes">Bing JSON data</property>
+                        <property name="subtitle" translatable="yes">Custom shuffle interval when enabled</property>
+                    </object>
+                </child>
+            </object>
+        </child>
+    </object>
+    <object class="AdwPreferencesPage" id="about_page">
+        <property name="icon-name">user-info-symbolic</property>
+        <property name="title" translatable="yes">About</property>
+        <child>
+          <object class="AdwPreferencesGroup" id="about_group">
+            <!-- <property name="orientation">vertical</property>-->
+            <child>
+                <object class="GtkImage" id="app_icon_image">
+                <property name="pixel-size">128</property>
+                <property name="accessible-role">presentation</property>
+                <style>
+                    <class name="icon-dropshadow"/>
+                </style>
+                </object>
+            </child>
+            <child>
+                <object class="GtkLabel" id="app_name_label">
+                <property name="wrap">True</property>
+                <property name="justify">center</property>
+                <property name="label" translatable="yes">Bing Wallpaper</property>
+                <style>
+                    <class name="title-1"/>
+                </style>
+                </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="extension_description">
+                <property name="can_focus">0</property>
+                <property name="label" translatable="yes">New wallpaper images everyday from Bing</property>
+                <property name="justify">center</property>
+                <property name="wrap">1</property>
+              </object>
+            </child>
+            
+            <child>
+                <object class="GtkLabel" id="developer_name_label">
+                <property name="wrap">True</property>
+                <property name="justify">center</property>
+                <property name="label" translatable="yes">Maintained by Michael Carroll</property>
+                </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="AdwPreferencesGroup" id="change_log_group">
+            <child>
+              <object class="AdwActionRow" id="details_row">
+                <property name="title" translatable="yes">Version</property>
+                <child>
+                    <object class="GtkButton" id="version_button">
+                    <property name="halign">center</property>
+                    <property name="can-shrink">True</property>
+                    <style>
+                        <class name="app-version"/>
+                    </style>
+                    </object>
+                </child>
+              </object>
+            </child>
+            <child>
+            <object class="AdwExpanderRow" id="expander_row">
+                <property name="title" translatable="yes">Release notes</property>
+                <child>
+                    <object class="GtkLabel" id="change_log">
+                        <property name="wrap">True</property>
+                        <property name="justify">left</property>
+                    </object>
+                </child>
+            </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="AdwPreferencesGroup" id="about_details_group">
+            <child>
+              <object class="GtkBox">
+                <property name="orientation">vertical</property>
+                <child>
+                    <object class="AdwPreferencesGroup" id="details_group">
+                    
+                    <child>
+                        <object class="AdwActionRow" id="extension_page_row">
+                            <property name="title" translatable="yes">GNOME extensions page</property>
+                            <property name="use-underline">True</property>
+                            <property name="activatable">True</property>
+                            <property name="activatable-widget">extension_page_linkbutton</property>
+                            <child>
+                                <object class="GtkLinkButton" id="extension_page_linkbutton">
+                                    <property name="uri">https://extensions.gnome.org/extension/1262/bing-wallpaper-changer/</property>
+                                </object>
+                            </child>
+                            <child>
+                                <object class="GtkImage">
+                                    <property name="icon-name">adw-external-link-symbolic</property>
+                                    <property name="accessible-role">presentation</property>
+                                </object>
+                            </child>
+                        </object>
+                    </child>
+                    <child>
+                        <object class="AdwActionRow" id="source_code_row">
+                            <property name="title" translatable="yes">Source code</property>
+                            <property name="use-underline">True</property>
+                            <property name="activatable">True</property>
+                            <property name="activatable-widget">source_code_linkbutton</property>
+                            <child>
+                                <object class="GtkLinkButton" id="source_code_linkbutton">
+                                    <property name="uri">https://github.com/neffo/bing-wallpaper-gnome-extension</property>
+                                </object>
+                            </child>
+                            <child>
+                                <object class="GtkImage">
+                                    <property name="icon-name">adw-external-link-symbolic</property>
+                                    <property name="accessible-role">presentation</property>
+                                </object>
+                            </child>
+                        </object>
+                    </child>
+                    <child>
+                        <object class="AdwActionRow" id="bug_report_row">
+                            <property name="title" translatable="yes">Report an issue</property>
+                            <property name="use-underline">True</property>
+                            <property name="activatable">True</property>
+                            <property name="activatable-widget">bug_report_linkbutton</property>
+                            <child>
+                                <object class="GtkLinkButton" id="bug_report_linkbutton">
+                                    <property name="uri">https://github.com/neffo/bing-wallpaper-gnome-extension/issues</property>
+                                </object>
+                            </child>
+                            <child>
+                                <object class="GtkImage">
+                                    <property name="icon-name">adw-external-link-symbolic</property>
+                                    <property name="accessible-role">presentation</property>
+                                </object>
+                            </child>
+                        </object>
+                    </child>
+                    <child>
+                        <object class="AdwActionRow" id="contributors_row">
+                            <property name="title" translatable="yes">Contributors</property>
+                            <property name="use-underline">True</property>
+                            <property name="activatable">True</property>
+                            <property name="activatable-widget">contributors_linkbutton</property>
+                            <child>
+                                <object class="GtkLinkButton" id="contributors_linkbutton">
+                                    <property name="uri">https://github.com/neffo/bing-wallpaper-gnome-extension/graphs/contributors</property>
+                                </object>
+                            </child>
+                            <child>
+                                <object class="GtkImage">
+                                    <property name="icon-name">adw-external-link-symbolic</property>
+                                    <property name="accessible-role">presentation</property>
+                                </object>
+                            </child>
+                        </object>
+                    </child>
+                    <child>
+                        <object class="AdwExpanderRow" id="license_expander">
+                            <property name="title" translatable="yes">License</property>
+                            <child>
+                                <object class="GtkLabel" id="license">
+                                    <property name="wrap">True</property>
+                                    <property name="justify">left</property>
+                                    <property name="label">This extension is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.</property>
+                                </object>
+                            </child>
+                        </object>
+                    </child>
+                    </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+    </object>
+    <object class="GtkFileDialog" id="folderDialog">
+        <property name="accept-label" translatable="yes">Change folder</property>
+        <property name="modal">true</property>
+        <property name="title" translatable="yes">Select new wallpaper download folder</property>
+    </object>
+</interface>
+
+<!-- <signal name="clicked" handler="open_wp_folder" /> -->

+ 20 - 12
gnome/.local/share/gnome-shell/extensions/BingWallpaper@ineffable-gmail.com/utils.js

@@ -12,9 +12,9 @@ import GLib from 'gi://GLib';
 import Soup from 'gi://Soup';
 import GdkPixbuf from 'gi://GdkPixbuf';
 
-export var PRESET_GNOME_DEFAULT = { blur: 60, dim: 55 }; // as at GNOME 40
-export var PRESET_NO_BLUR = { blur: 0, dim: 60 };
-export var PRESET_SLIGHT_BLUR = { blur: 2, dim: 60 };
+export var PRESET_GNOME_DEFAULT = { blur: 45, dim: 65 }; // as at GNOME 40
+export var PRESET_NO_BLUR = { blur: 0, dim: 65 };
+export var PRESET_SLIGHT_BLUR = { blur: 2, dim: 30 };
 
 export var BING_SCHEMA = 'org.gnome.shell.extensions.bingwallpaper';
 export var DESKTOP_SCHEMA = 'org.gnome.desktop.background';
@@ -52,7 +52,8 @@ export var backgroundStyle = ['none', 'wallpaper', 'centered', 'scaled', 'stretc
 
 export var randomIntervals = [ {value: 'hourly', title: ('on the hour')},
                         {value: 'daily', title: ('every day at midnight')},
-                        {value: 'weekly', title: ('every Sunday at midnight')} ];
+                        {value: 'weekly', title: ('Sunday at midnight')},
+                        { value: 'custom', title: ('User defined interval')} ];
 
 export var BingImageURL = 'https://www.bing.com/HPImageArchive.aspx';
 export var BingParams = { format: 'js', idx: '0' , n: '8' , mbl: '1' , mkt: '' } ;
@@ -67,7 +68,7 @@ export function validate_icon(settings, extension_path, icon_image = null) {
     // if called from prefs
     if (icon_image) { 
         BingLog('set icon to: ' + extension_path + '/icons/' + icon_name + '.svg');
-        let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(extension_path + '/icons/' + icon_name + '.svg', 32, 32);
+        let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(extension_path + '/icons/' + icon_name + '.svg', 64, 64);
         icon_image.set_from_pixbuf(pixbuf);
     }
 }
@@ -78,6 +79,12 @@ export function validate_resolution(settings) {
         settings.reset('resolution');
 }
 
+export function validate_interval(settings) {
+    let index = randomIntervals.map( e => e.value).indexOf(settings.get_string('random-interval-mode'));
+    if (index == -1) // if not a valid interval
+        settings.reset('random-interval-mode');
+}
+
 // FIXME: needs work
 export function validate_imagename(settings) {
     let filename = settings.get_string('selected-image');
@@ -188,16 +195,15 @@ export function setImageList(settings, imageList) {
     }
 }
 
-export function setImageHiddenStatus(settings, hide_image_list, hide_status) {
+export function setImageHiddenStatus(settings, hide_image, hide_status) {
     // get current image list
     let image_list = getImageList(settings);
+    log ('image count = '+image_list.length+', hide_image = '+hide_image);
     image_list.forEach( (x, i) => {
-        hide_image_list.forEach(u => {
-            if (u.includes(x.urlbase)) {
-                // mark as hidden
-                x.hidden = hide_status;
-            }
-        });
+        if (hide_image.includes(x.urlbase)) {
+            // mark as hidden
+            x.hidden = hide_status;
+        }
     });
     // export image list back to settings
     setImageList(settings, image_list);
@@ -294,6 +300,8 @@ export function getImageByIndex(imageList, index) {
 }
 
 export function cleanupImageList(settings) {
+    if (settings.get_boolean('trash-deletes-images') == false)
+        return;
     let curList = imageListSortByDate(getImageList(settings));
     let cutOff = GLib.DateTime.new_now_utc().add_days(-8); // 8 days ago
     let newList = [];

+ 1493 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/Options_UI.glade

@@ -0,0 +1,1493 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <object class="GtkBox" id="Main_Container">
+    <property name="margin-bottom">5</property>
+    <child>
+      <object class="GtkStackSidebar" id="sts_SideBar">
+        <property name="halign">center</property>
+        <property name="stack">stk_Main</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkStack" id="stk_Main">
+        <property name="transition-duration">800</property>
+        <property name="transition-type">slide-left-right</property>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">page0</property>
+            <property name="title" translatable="yes">Options</property>
+            <property name="child">
+              <object class="GtkFrame" id="frame1">
+                <property name="css-classes">borderless</property>
+                <property name="child">
+                  <object class="GtkGrid" id="grid1">
+                    <property name="css-classes">options-grid</property>
+                    <property name="row-spacing">18</property>
+                    <property name="column-spacing">15</property>
+                    <child>
+                      <object class="GtkLabel" id="label13">
+                        <property name="halign">end</property>
+                        <property name="margin-top">4</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Recording status indicators</property>
+                        <property name="justify">right</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">0</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label24">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Show alerts and notifications</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">1</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="swt_ShowNotifyAlert">
+                        <property name="focusable">1</property>
+                        <property name="halign">start</property>
+                        <property name="hexpand">1</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">1</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label25">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Show a border around the area being recorded</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">2</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="swt_ShowAreaRec">
+                        <property name="focusable">1</property>
+                        <property name="halign">start</property>
+                        <property name="hexpand">1</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">2</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label10">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Enable keyboard shortcut</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">3</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkViewport" id="viewport_KeyShortcut">
+                        <property name="child">
+                          <object class="GtkTreeView" id="treeview_KeyShortcut">
+                            <property name="focusable">1</property>
+                            <property name="model">liststore_KeyShortcut</property>
+                            <property name="headers-visible">0</property>
+                            <property name="search-column">0</property>
+                            <child internal-child="selection">
+                              <object class="GtkTreeSelection"/>
+                            </child>
+                          </object>
+                        </property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">4</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkComboBoxText" id="cbt_StatusIndicatorsRec">
+                        <items>
+                          <item id="0" translatable="yes">Both  [ESC + Default]</item>
+                          <item id="1" translatable="yes">ESC only</item>
+                          <item id="2" translatable="yes">Default only</item>
+                          <item id="3" translatable="yes">Not any</item>
+                        </items>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">0</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label18">
+                        <property name="tooltip-text" translatable="yes">These words will be replaced
+ _fpath = the absolute path of the screencast video file.
+_dirpath = the absolute path of destination folder for the screencast video file.
+_fname = the name of the screencast video file.</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Command post-recording</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">9</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="txe_postcmd">
+                        <property name="focusable">1</property>
+                        <property name="tooltip-text" translatable="yes">These words will be replaced
+ _fpath = the absolute path of the screencast video file.
+_dirpath = the absolute path of destination folder for the screencast video file.
+_fname = the name of the screencast video file.</property>
+                        <property name="hexpand">1</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">9</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label14">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Execute command after recording</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">8</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="swt_executepostcmd">
+                        <property name="focusable">1</property>
+                        <property name="halign">start</property>
+                        <property name="hexpand">0</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">8</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="swt_DrawCursorRec">
+                        <property name="focusable">1</property>
+                        <property name="halign">start</property>
+                        <property name="hexpand">1</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">5</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="textentry1">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Draw cursor on screencast</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">5</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="height-request">1</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Active shortcut</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">4</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="swt_KeyShortcut">
+                        <property name="focusable">1</property>
+                        <property name="halign">start</property>
+                        <property name="hexpand">1</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">3</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label19">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Execute command before recording</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">6</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSwitch" id="swt_executeprecmd">
+                        <property name="focusable">1</property>
+                        <property name="halign">start</property>
+                        <property name="hexpand">0</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">6</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label26">
+                        <property name="tooltip-text" translatable="yes">These words will be replaced
+_dirpath = the absolute path of destination folder for the screencast video file.</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Command pre-recording</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">7</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="txe_precmd">
+                        <property name="focusable">1</property>
+                        <property name="tooltip-text" translatable="yes">These words will be replaced
+_dirpath = the absolute path of destination folder for the screencast video file.</property>
+                        <property name="hexpand">1</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">7</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                </property>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">page1</property>
+            <property name="title" translatable="yes">Quality</property>
+            <property name="child">
+              <object class="GtkFrame" id="frame2">
+                <property name="css-classes">borderless</property>
+                <property name="child">
+                  <object class="GtkBox" id="box1">
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkGrid" id="grid3">
+                        <property name="row-spacing">18</property>
+                        <property name="column-spacing">15</property>
+                        <child>
+                          <object class="GtkLabel" id="textentry26">
+                            <property name="halign">end</property>
+                            <property name="hexpand">1</property>
+                            <property name="label" translatable="yes">Custom GStreamer Pipeline</property>
+                            <layout>
+                              <property name="column">0</property>
+                              <property name="row">0</property>
+                            </layout>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkSwitch" id="swt_EnableCustomGSP">
+                            <property name="focusable">1</property>
+                            <property name="halign">start</property>
+                            <property name="hexpand">1</property>
+                            <layout>
+                              <property name="column">1</property>
+                              <property name="row">0</property>
+                            </layout>
+                          </object>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparator" id="separator1"/>
+                    </child>
+                    <child>
+                      <object class="GtkStack" id="stk_Quality">
+                        <property name="vexpand">1</property>
+                        <property name="transition-duration">800</property>
+                        <property name="transition-type">slide-up-down</property>
+                        <child>
+                          <object class="GtkStackPage">
+                            <property name="name">pg_Preset</property>
+                            <property name="title" translatable="yes">PagePreset</property>
+                            <property name="child">
+                              <object class="GtkBox" id="box9">
+                                <property name="valign">start</property>
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkBox" id="box3">
+                                    <property name="valign">center</property>
+                                    <property name="css-classes">border-box</property>
+                                    <child>
+                                      <object class="GtkImage" id="img_Performance">
+                                        <property name="halign">start</property>
+                                        <property name="hexpand">true</property>
+                                        <property name="pixel-size">100</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkImage" id="img_Quality">
+                                        <property name="halign">end</property>
+                                        <property name="hexpand">true</property>
+                                        <property name="pixel-size">100</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkScale" id="scl_Quality">
+                                    <property name="valign">center</property>
+                                    <property name="vexpand">1</property>
+                                    <property name="focusable">1</property>
+                                    <property name="margin-bottom">18</property>
+                                    <property name="fill-level">5</property>
+                                    <property name="digits">0</property>
+                                    <property name="has-origin">0</property>
+                                    <property name="value-pos">bottom</property>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="lbl_GSP_Description">
+                                    <property name="halign">center</property>
+                                    <property name="vexpand">1</property>
+                                    <property name="label" translatable="yes">No GSP description
+</property>
+                                    <property name="wrap">1</property>
+                                    <property name="wrap-mode">word-char</property>
+                                    <property name="ellipsize">end</property>
+                                  </object>
+                                </child>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkStackPage">
+                            <property name="name">pg_Custom</property>
+                            <property name="title" translatable="yes">PageCustom</property>
+                            <property name="child">
+                              <object class="GtkBox" id="box10">
+                                <property name="orientation">vertical</property>
+                                <child>
+                                  <object class="GtkGrid" id="grid66">
+                                    <property name="valign">center</property>
+                                    <property name="row-spacing">18</property>
+                                    <property name="column-spacing">15</property>
+                                    <child>
+                                      <object class="GtkLabel" id="label7">
+                                        <property name="halign">end</property>
+                                        <property name="hexpand">1</property>
+                                        <property name="label" translatable="yes">Frames Per Second</property>
+                                        <layout>
+                                          <property name="column">0</property>
+                                          <property name="row">0</property>
+                                        </layout>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkSpinButton" id="spb_FrameRateRec">
+                                        <property name="focusable">1</property>
+                                        <property name="halign">start</property>
+                                        <property name="hexpand">1</property>
+                                        <property name="text" translatable="yes">0</property>
+                                        <property name="climb-rate">1</property>
+                                        <layout>
+                                          <property name="column">1</property>
+                                          <property name="row">0</property>
+                                        </layout>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkFrame" id="frame104">
+                                    <property name="vexpand">1</property>
+                                    <property name="margin-bottom">10</property>
+                                    <property name="label-xalign">0.10000000149011612</property>
+                                    <child>
+                                      <object class="GtkScrolledWindow" id="scrolledwindow2">
+                                        <property name="focusable">1</property>
+                                        <property name="child">
+                                          <object class="GtkTextView" id="txe_CommandStringRec">
+                                            <property name="focusable">1</property>
+                                            <property name="margin-top">5</property>
+                                            <property name="margin-bottom">5</property>
+                                            <property name="hexpand">1</property>
+                                            <property name="vexpand">1</property>
+                                            <property name="wrap-mode">word-char</property>
+                                            <property name="accepts-tab">0</property>
+                                          </object>
+                                        </property>
+                                      </object>
+                                    </child>
+                                    <child type="label">
+                                      <object class="GtkLabel" id="label266">
+                                        <property name="label" translatable="yes">GStreamer Pipeline</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkBox" id="box46">
+                                    <property name="valign">center</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <object class="GtkLabel" id="label146">
+                                        <property name="halign">start</property>
+                                        <property name="label" translatable="yes">The extension does NOT handle the webcam and audio when you use a custom gstreamer pipeline.
+</property>
+                                        <property name="wrap">1</property>
+                                        <attributes>
+                                          <attribute name="weight" value="bold"></attribute>
+                                          <attribute name="foreground" value="#efef29292929"></attribute>
+                                        </attributes>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkBox" id="box8">
+                                        <child>
+                                          <object class="GtkLinkButton" id="linkbutton1">
+                                            <property name="hexpand">1</property>
+                                            <property name="label" translatable="yes">Official doc</property>
+                                            <property name="focusable">1</property>
+                                            <property name="receives-default">1</property>
+                                            <property name="uri">http://gstreamer.freedesktop.org/documentation/plugins.html</property>
+                                            <property name="visited">1</property>
+                                          </object>
+                                        </child>
+                                        <child>
+                                          <object class="GtkLinkButton" id="linkbutton2">
+                                            <property name="hexpand">1</property>
+                                            <property name="label" translatable="yes">Wiki #1</property>
+                                            <property name="focusable">1</property>
+                                            <property name="receives-default">1</property>
+                                            <property name="uri">http://processors.wiki.ti.com/index.php/Example_GStreamer_Pipelines</property>
+                                          </object>
+                                        </child>
+                                        <child>
+                                          <object class="GtkLinkButton" id="linkbutton3">
+                                            <property name="hexpand">1</property>
+                                            <property name="label" translatable="yes">Wiki #2</property>
+                                            <property name="focusable">1</property>
+                                            <property name="receives-default">1</property>
+                                            <property name="uri">http://wiki.oz9aec.net/index.php/Gstreamer_Cheat_Sheet</property>
+                                          </object>
+                                        </child>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">page2</property>
+            <property name="title" translatable="yes">WebCam</property>
+            <property name="child">
+              <object class="GtkFrame" id="frame3">
+                <property name="css-classes">borderless</property>
+                <property name="child">
+                  <object class="GtkBox" id="box5">
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkLabel" id="lbl_Webcam">
+                        <property name="valign">center</property>
+                        <property name="label" translatable="yes">No webcam device selected</property>
+                        <property name="justify">center</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"></attribute>
+                          <attribute name="stretch" value="semi-expanded"></attribute>
+                        </attributes>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="lbl_WebcamCaps">
+                        <property name="valign">center</property>
+                        <property name="halign">center</property>
+                        <property name="hexpand">0</property>
+                        <property name="label" translatable="yes">-</property>
+                        <property name="justify">center</property>
+                        <property name="ellipsize">end</property>
+                        <property name="single-line-mode">1</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparator" id="separator3"/>
+                    </child>
+                    <child>
+                      <object class="GtkStackSwitcher" id="sts_Webcam">
+                        <property name="halign">center</property>
+                        <property name="hexpand">1</property>
+                        <property name="stack">stk_Webcam</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkStack" id="stk_Webcam">
+                        <property name="hexpand">1</property>
+                        <property name="vexpand">1</property>
+                        <property name="transition-duration">800</property>
+                        <property name="transition-type">slide-up-down</property>
+                        <child>
+                          <object class="GtkStackPage">
+                            <property name="name">pg_quality_webcam</property>
+                            <property name="title" translatable="yes">Quality</property>
+                            <property name="child">
+                              <object class="GtkScrolledWindow" id="scrolledwindow1">
+                                <property name="focusable">1</property>
+                                <property name="has-frame">True</property>
+                                <property name="child">
+                                  <object class="GtkTreeView" id="treeview_QualityWebam">
+                                    <property name="focusable">1</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="vexpand">1</property>
+                                    <property name="model">liststore_QualityWebCam</property>
+                                    <property name="search-column">1</property>
+                                    <property name="enable-grid-lines">horizontal</property>
+                                    <child internal-child="selection">
+                                      <object class="GtkTreeSelection"/>
+                                    </child>
+                                  </object>
+                                </property>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkStackPage">
+                            <property name="name">pg_size_webcam</property>
+                            <property name="title" translatable="yes">Size</property>
+                            <property name="child">
+                              <object class="GtkGrid" id="grid5">
+                                <property name="row-spacing">18</property>
+                                <property name="column-spacing">15</property>
+                                <child>
+                                  <object class="GtkLabel" id="label2">
+                                    <property name="halign">end</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="label" translatable="yes">Type of unit of measure</property>
+                                    <layout>
+                                      <property name="column">0</property>
+                                      <property name="row">0</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label3">
+                                    <property name="halign">end</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="label" translatable="yes">Width</property>
+                                    <layout>
+                                      <property name="column">0</property>
+                                      <property name="row">1</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label16">
+                                    <property name="halign">end</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="label" translatable="yes">Height</property>
+                                    <layout>
+                                      <property name="column">0</property>
+                                      <property name="row">2</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="spb_WebCamWidth">
+                                    <property name="focusable">1</property>
+                                    <property name="halign">start</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="numeric">1</property>
+                                    <layout>
+                                      <property name="column">1</property>
+                                      <property name="row">1</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="spb_WebCamHeight">
+                                    <property name="focusable">1</property>
+                                    <property name="halign">start</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="numeric">1</property>
+                                    <layout>
+                                      <property name="column">1</property>
+                                      <property name="row">2</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkComboBoxText" id="cbt_WebCamUnitMeasure">
+                                    <property name="halign">start</property>
+                                    <property name="hexpand">1</property>
+                                    <items>
+                                      <item id="0" translatable="yes">Percentage</item>
+                                      <item id="1" translatable="yes">Pixel</item>
+                                    </items>
+                                    <layout>
+                                      <property name="column">1</property>
+                                      <property name="row">0</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkStackPage">
+                            <property name="name">pg_position_webcam</property>
+                            <property name="title" translatable="yes">Position</property>
+                            <property name="child">
+                              <object class="GtkGrid" id="grid4">
+                                <property name="row-spacing">18</property>
+                                <property name="column-spacing">15</property>
+                                <child>
+                                  <object class="GtkLabel" id="label20">
+                                    <property name="halign">end</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="label" translatable="yes">Put the webcam in the corner</property>
+                                    <layout>
+                                      <property name="column">0</property>
+                                      <property name="row">0</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkComboBoxText" id="cbt_WebCamCorner">
+                                    <property name="halign">start</property>
+                                    <property name="hexpand">1</property>
+                                    <items>
+                                      <item id="0" translatable="yes">Right-Bottom</item>
+                                      <item id="1" translatable="yes">Left-Bottom</item>
+                                      <item id="2" translatable="yes">Right-Top</item>
+                                      <item id="3" translatable="yes">Left-Top</item>
+                                    </items>
+                                    <layout>
+                                      <property name="column">1</property>
+                                      <property name="row">0</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label21">
+                                    <property name="halign">end</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="label" translatable="yes">Margin X</property>
+                                    <layout>
+                                      <property name="column">0</property>
+                                      <property name="row">1</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="spb_WebCamMarginX">
+                                    <property name="focusable">1</property>
+                                    <property name="halign">start</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="numeric">1</property>
+                                    <layout>
+                                      <property name="column">1</property>
+                                      <property name="row">1</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label22">
+                                    <property name="halign">end</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="label" translatable="yes">Margin Y</property>
+                                    <layout>
+                                      <property name="column">0</property>
+                                      <property name="row">2</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="spb_WebCamMarginY">
+                                    <property name="focusable">1</property>
+                                    <property name="halign">start</property>
+                                    <property name="hexpand">0</property>
+                                    <property name="numeric">1</property>
+                                    <layout>
+                                      <property name="column">1</property>
+                                      <property name="row">2</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkLabel" id="label23">
+                                    <property name="halign">end</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="label" translatable="yes">Alpha channel</property>
+                                    <layout>
+                                      <property name="column">0</property>
+                                      <property name="row">3</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="spb_WebCamAlpha">
+                                    <property name="focusable">1</property>
+                                    <property name="halign">start</property>
+                                    <property name="hexpand">1</property>
+                                    <property name="text" translatable="yes">0,00</property>
+                                    <property name="climb-rate">0.05</property>
+                                    <property name="digits">2</property>
+                                    <property name="numeric">1</property>
+                                    <layout>
+                                      <property name="column">1</property>
+                                      <property name="row">3</property>
+                                    </layout>
+                                  </object>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                                <child>
+                                  <placeholder/>
+                                </child>
+                              </object>
+                            </property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">page3</property>
+            <property name="title" translatable="yes">File</property>
+            <property name="child">
+              <object class="GtkFrame" id="frame">
+                <property name="css-classes">borderless</property>
+                <property name="child">
+                  <object class="GtkGrid" id="grid2">
+                    <property name="row-spacing">18</property>
+                    <property name="column-spacing">15</property>
+                    <child>
+                      <object class="GtkLabel" id="lbl_kjkhkhhkhjk">
+                        <property name="tooltip-text" translatable="yes">Select the folder where the file is saved, if not specific a folder  the file will be saved in the $XDG_VIDEOS_DIR if it exists, or the home directory otherwise.</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">Destination folder</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">3</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label17">
+                        <property name="tooltip-text" translatable="yes">The filename which may contain some escape sequences - %d and %t will be replaced by the start date and time of the recording.</property>
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">File name template</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">2</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="txe_FileNameRec">
+                        <property name="focusable">1</property>
+                        <property name="receives-default">1</property>
+                        <property name="tooltip-text" translatable="yes">The filename which may contain some escape sequences - %d and %t will be replaced by the start date and time of the recording.</property>
+                        <property name="hexpand">1</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">2</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="fcb_FilePathRec">
+                        <property name="visible">True</property>
+                        <property name="can-focus">False</property>
+                        <property name="tooltip-text" translatable="yes">Select the folder where the file is saved, if not specific a folder  the file will be saved in the $XDG_VIDEOS_DIR if it exists, or the home directory otherwise.</property>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">3</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label11">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">File container</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">0</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label15">
+                        <property name="halign">end</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">File resolution</property>
+                        <layout>
+                          <property name="column">0</property>
+                          <property name="row">1</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkComboBoxText" id="cbt_FileContainer">
+                        <items>
+                          <item id="0">WebM  [VP8 encoder + vorbis]</item>
+                          <item id="1">WebM  [VP9 encoder + vorbis]</item>
+                          <item id="2">MP4  [x264 encoder + mp3]</item>
+                          <item id="3">Mkv  [x264 encoder + flac]</item>
+                          <item id="4">Ogg  [Theora encoder + opus]</item>
+                          <item id="5">MP4_AAC [x264 encoder + aac]</item>
+                        </items>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">0</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkStackSwitcher" id="sts_FileResolution">
+                            <property name="vexpand">1</property>
+                            <property name="halign">center</property>
+                            <property name="hexpand">1</property>
+                            <property name="stack">stk_FileResolution</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkStack" id="stk_FileResolution">
+                            <property name="margin-top">10</property>
+                            <property name="vhomogeneous">0</property>
+                            <property name="transition-duration">800</property>
+                            <property name="transition-type">slide-up-down</property>
+                            <property name="interpolate-size">1</property>
+                            <child>
+                              <object class="GtkStackPage">
+                                <property name="name">native</property>
+                                <property name="title" translatable="yes">Native</property>
+                                <property name="child">
+                                  <object class="GtkLabel" id="chd_FileResolution_Native">
+                                    <property name="label" translatable="yes">Native area resolution</property>
+                                    <attributes>
+                                      <attribute name="foreground" value="#cccc00000000"></attribute>
+                                    </attributes>
+                                  </object>
+                                </property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkStackPage">
+                                <property name="name">preset</property>
+                                <property name="title" translatable="yes">Preset</property>
+                                <property name="child">
+                                  <object class="GtkGrid" id="chd_FileResolution_Preset">
+                                    <property name="margin-top">5</property>
+                                    <property name="row-spacing">18</property>
+                                    <property name="column-spacing">15</property>
+                                    <child>
+                                      <object class="GtkLabel">
+                                        <property name="halign">end</property>
+                                        <property name="hexpand">1</property>
+                                        <property name="label" translatable="yes">Preset helper</property>
+                                        <layout>
+                                          <property name="column">0</property>
+                                          <property name="row">0</property>
+                                        </layout>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkComboBoxText" id="cbt_FileResolution">
+                                        <property name="halign">start</property>
+                                        <property name="hexpand">1</property>
+                                        <items>
+                                          <item id="0">VGA [480p] [4:3]</item>
+                                          <item id="1">FWVGA [480p] [16:9]</item>
+                                          <item id="2">SVGA [600p] [4:3]</item>
+                                          <item id="3">SMPTE [720p] [4:3]</item>
+                                          <item id="4" translatable="yes">HD [720p] [16:9]</item>
+                                          <item id="5" translatable="yes">XGA [768p] [4:3]</item>
+                                          <item id="6" translatable="yes">HD ready [768p] [16:9]</item>
+                                          <item id="7" translatable="yes">SXGA [1024p] [5:4]</item>
+                                          <item id="8" translatable="yes">Full HD [1080p] [16:9]</item>
+                                          <item id="9" translatable="yes">UXGA [1200p] [4:3]</item>
+                                          <item id="10" translatable="yes">QHD [1440p] [16:9]</item>
+                                          <item id="11" translatable="yes">QSXGA [2048p] [5:4]</item>
+                                          <item id="12" translatable="yes">4K [2160p] [16:9]</item>
+                                        </items>
+                                        <layout>
+                                          <property name="column">1</property>
+                                          <property name="row">0</property>
+                                        </layout>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                  </object>
+                                </property>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkStackPage">
+                                <property name="name">custom</property>
+                                <property name="title" translatable="yes">Custom</property>
+                                <property name="child">
+                                  <object class="GtkBox" id="chd_FileResolution_Custom">
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <object class="GtkGrid" id="grid6">
+                                        <property name="margin-top">5</property>
+                                        <property name="row-spacing">18</property>
+                                        <property name="column-spacing">15</property>
+                                        <child>
+                                          <object class="GtkLabel">
+                                            <property name="halign">end</property>
+                                            <property name="hexpand">1</property>
+                                            <property name="label" translatable="yes">Width</property>
+                                            <layout>
+                                              <property name="column">0</property>
+                                              <property name="row">0</property>
+                                            </layout>
+                                          </object>
+                                        </child>
+                                        <child>
+                                          <object class="GtkSpinButton" id="spb_ResWidth">
+                                            <property name="focusable">1</property>
+                                            <property name="halign">start</property>
+                                            <property name="hexpand">1</property>
+                                            <layout>
+                                              <property name="column">1</property>
+                                              <property name="row">0</property>
+                                            </layout>
+                                          </object>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkScale" id="scl_ResWidth">
+                                        <property name="vexpand">1</property>
+                                        <property name="draw-value">1</property>
+                                        <property name="focusable">1</property>
+                                        <property name="round-digits">1</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkGrid" id="grid7">
+                                        <property name="margin-top">5</property>
+                                        <property name="row-spacing">18</property>
+                                        <property name="column-spacing">15</property>
+                                        <child>
+                                          <object class="GtkLabel">
+                                            <property name="halign">end</property>
+                                            <property name="hexpand">1</property>
+                                            <property name="label" translatable="yes">Height</property>
+                                            <layout>
+                                              <property name="column">0</property>
+                                              <property name="row">0</property>
+                                            </layout>
+                                          </object>
+                                        </child>
+                                        <child>
+                                          <object class="GtkSpinButton" id="spb_ResHeight">
+                                            <property name="focusable">1</property>
+                                            <property name="halign">start</property>
+                                            <property name="hexpand">1</property>
+                                            <layout>
+                                              <property name="column">1</property>
+                                              <property name="row">0</property>
+                                            </layout>
+                                          </object>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                        <child>
+                                          <placeholder/>
+                                        </child>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkScale" id="scl_ResHeight">
+                                        <property name="vexpand">1</property>
+                                        <property name="draw-value">1</property>
+                                        <property name="focusable">1</property>
+                                        <property name="round-digits">1</property>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkCheckButton" id="chb_FileResolution_kar">
+                                        <property name="vexpand">1</property>
+                                        <property name="label" translatable="yes">keep original aspect ratio</property>
+                                        <property name="focusable">1</property>
+                                        <property name="halign">center</property>
+                                        <property name="margin-top">5</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <layout>
+                          <property name="column">1</property>
+                          <property name="row">1</property>
+                        </layout>
+                      </object>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                </property>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">page5</property>
+            <property name="title" translatable="yes">Support</property>
+            <property name="child">
+              <object class="GtkFrame" id="frame5">
+                <property name="css-classes">borderless</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkGrid" id="grid8">
+                        <property name="margin-bottom">5</property>
+                        <property name="row-spacing">18</property>
+                        <property name="column-spacing">15</property>
+                        <child>
+                          <object class="GtkLabel" id="label9">
+                            <property name="tooltip-text" translatable="yes">This option enable more debug message, to view these run this command into a terminal:
+$ journalctl --since=today --no-pager | grep js
+$ dbus-monitor &quot;interface=org.gnome.Shell.Screencast&quot;</property>
+                            <property name="halign">end</property>
+                            <property name="hexpand">1</property>
+                            <property name="label" translatable="yes">Enable verbose debug</property>
+                            <layout>
+                              <property name="column">0</property>
+                              <property name="row">0</property>
+                            </layout>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkSwitch" id="swt_VerboseDebug">
+                            <property name="focusable">1</property>
+                            <property name="tooltip-text" translatable="yes">This option enable more debug message, to view these run this command into a terminal:
+$ journalctl /usr/bin/gnome-session --since=today | grep js
+$ dbus-monitor &quot;interface=org.gnome.Shell.Screencast&quot;</property>
+                            <property name="halign">start</property>
+                            <property name="hexpand">1</property>
+                            <layout>
+                              <property name="column">1</property>
+                              <property name="row">0</property>
+                            </layout>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkComboBoxText" id="cbt_LogChooser">
+                            <property name="halign">start</property>
+                            <property name="margin-bottom">5</property>
+                            <property name="hexpand">0</property>
+                            <items>
+                              <item id="0" translatable="yes">extension</item>
+                              <item id="1" translatable="yes">last Gstreamer pipeline</item>
+                              <item id="2" translatable="yes">gnome shell</item>
+                            </items>
+                            <layout>
+                              <property name="column">1</property>
+                              <property name="row">1</property>
+                            </layout>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label12">
+                            <property name="tooltip-text" translatable="yes">This option enable more debug message, to view these run this command into a terminal:
+$ journalctl --since=today --no-pager | grep js
+$ dbus-monitor &quot;interface=org.gnome.Shell.Screencast&quot;</property>
+                            <property name="halign">end</property>
+                            <property name="hexpand">1</property>
+                            <property name="label" translatable="yes">Display the log of</property>
+                            <layout>
+                              <property name="column">0</property>
+                              <property name="row">1</property>
+                            </layout>
+                          </object>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow">
+                        <property name="vexpand">1</property>
+                        <property name="focusable">1</property>
+                        <property name="has-frame">True</property>
+                        <property name="child">
+                          <object class="GtkTextView" id="txe_ContainerLog">
+                            <property name="focusable">1</property>
+                            <property name="editable">0</property>
+                            <property name="monospace">1</property>
+                          </object>
+                        </property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparator">
+                        <property name="margin-top">5</property>
+                        <property name="margin-bottom">5</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="btn_DefaultOption">
+                        <property name="valign">center</property>
+                        <property name="label" translatable="yes">Restore default options</property>
+                        <property name="focusable">1</property>
+                        <property name="receives-default">1</property>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkStackPage">
+            <property name="name">page4</property>
+            <property name="title" translatable="yes">Info</property>
+            <property name="child">
+              <object class="GtkFrame" id="frame4">
+                <property name="css-classes">borderless</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkImage" id="img_ESC">
+                        <property name="pixel-size">100</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">EasyScreenCast</property>
+                        <attributes>
+                          <attribute name="weight" value="ultrabold"></attribute>
+                          <attribute name="stretch" value="ultra-expanded"></attribute>
+                          <attribute name="foreground" value="#20204a4a8787"></attribute>
+                        </attributes>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="lbl_Version">
+                        <property name="halign">center</property>
+                        <property name="margin-bottom">10</property>
+                        <property name="hexpand">1</property>
+                        <property name="label" translatable="yes">N/A</property>
+                        <property name="selectable">1</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparator"/>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="valign">center</property>
+                        <property name="margin-top">15</property>
+                        <property name="margin-bottom">15</property>
+                        <property name="label" translatable="yes">This extension simplifies the use of the
+screen recorder  included in gnome shell</property>
+                        <property name="justify">center</property>
+                        <property name="ellipsize">middle</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSeparator"/>
+                    </child>
+                    <child>
+                      <object class="GtkLinkButton" id="btnlink_license">
+                        <property name="label" translatable="yes">This software is licensed under GPL v3</property>
+                        <property name="focusable">1</property>
+                        <property name="receives-default">1</property>
+                        <property name="uri">https://github.com/EasyScreenCast/EasyScreenCast/blob/master/COPYING</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLinkButton" id="btnlink_credits">
+                        <property name="label" translatable="yes">Credits</property>
+                        <property name="focusable">1</property>
+                        <property name="receives-default">1</property>
+                        <property name="uri">https://github.com/EasyScreenCast/EasyScreenCast/graphs/contributors</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="label" translatable="yes">How to contribute?</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLinkButton" id="btnlink_translations">
+                        <property name="label" translatable="yes">With a translation</property>
+                        <property name="focusable">1</property>
+                        <property name="receives-default">1</property>
+                        <property name="uri">https://github.com/EasyScreenCast/EasyScreenCast#translation</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLinkButton" id="btnlink_bugs">
+                        <property name="label" translatable="yes">Reporting bugs</property>
+                        <property name="focusable">1</property>
+                        <property name="receives-default">1</property>
+                        <property name="uri">https://github.com/EasyScreenCast/EasyScreenCast/issues</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLinkButton" id="btnlink_code">
+                        <property name="label" translatable="yes">Add code</property>
+                        <property name="focusable">1</property>
+                        <property name="receives-default">1</property>
+                        <property name="uri">https://github.com/EasyScreenCast/EasyScreenCast/pulls</property>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+                <child type="label_item">
+                  <placeholder/>
+                </child>
+              </object>
+            </property>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkListStore" id="liststore_KeyShortcut">
+    <columns>
+      <column type="gint"/>
+      <column type="gint"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="liststore_QualityWebCam">
+    <columns>
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>

+ 86 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/convenience.js

@@ -0,0 +1,86 @@
+/*
+  Copyright (c) 2011-2012, Giovanni Campagna <scampa.giovanni@gmail.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the GNOME nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+'use strict';
+
+import Gio from 'gi://Gio';
+
+var debugEnabled = false;
+
+/**
+ * @param {boolean} d Enable/Disable debug logging
+ */
+function setDebugEnabled(d) {
+    debugEnabled = d;
+}
+
+/**
+ * @param {string} msg the message to log
+ * @class
+ */
+function TalkativeLog(msg) {
+    if (debugEnabled)
+        log(`[ESC]${msg}`);
+}
+
+/**
+ * Gets the full (semantic) version of this extension.
+ *
+ * <p>Note: The actual value is added during build time.
+ *
+ * @returns {string} the version
+ */
+function getFullVersion() {
+    return '1.9.0'; // FULL_VERSION
+}
+
+/**
+ * Loads an icon from the extension's subdirectory "images".
+ *
+ * @param {Gio.File} extensionDir dir of the extension
+ * @param {string} name filename of the image
+ * @returns {Gio.FileIcon} the icon
+ */
+function loadIcon(extensionDir, name) {
+    return new Gio.FileIcon({
+        file: Gio.File.new_for_path(
+            getImagePath(extensionDir, name)
+        ),
+    });
+}
+
+/**
+ * Gets the path to the image from the extension's subdirectory "images".
+ *
+ * @param {Gio.File} extensionDir dir of the extension
+ * @param {string} name filename of the image
+ * @returns {string} the path
+ */
+function getImagePath(extensionDir, name) {
+    return extensionDir.get_child(`images/${name}`).get_path();
+}
+
+export {TalkativeLog, getFullVersion, setDebugEnabled, loadIcon, getImagePath};

+ 40 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/display_module.js

@@ -0,0 +1,40 @@
+'use strict';
+
+/**
+ * @type {{_display(): Meta_Display, number_of_displays(): int}}
+ */
+var DisplayApi = {
+    /**
+     * Returns the Wayland display or screen
+     *
+     * @returns {Meta.Display}
+     */
+    _display() {
+        return global.display || global.screen;
+    },
+
+    /**
+     * @returns {int}
+     * @public
+     */
+    number_displays() {
+        return this._display().get_n_monitors();
+    },
+
+    /**
+     * @param {number} displayIndex the monitor number
+     * @returns {Meta.Rectangle}
+     */
+    display_geometry_for_index(displayIndex) {
+        return this._display().get_monitor_geometry(displayIndex);
+    },
+
+    /**
+     * @param {Meta.Cursor} cursor the new cursor to set
+     */
+    set_cursor(cursor) {
+        this._display().set_cursor(cursor);
+    },
+};
+
+export {DisplayApi};

+ 1034 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/extension.js

@@ -0,0 +1,1034 @@
+/*
+    Copyright (C) 2013  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import St from 'gi://St';
+import Meta from 'gi://Meta';
+import Shell from 'gi://Shell';
+// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/panelMenu.js
+import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
+import Clutter from 'gi://Clutter';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as Slider from 'resource:///org/gnome/shell/ui/slider.js';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import * as Lib from './convenience.js';
+import * as Settings from './settings.js';
+import * as Time from './timer.js';
+import * as UtilRecorder from './utilrecorder.js';
+import * as UtilAudio from './utilaudio.js';
+import * as UtilWebcam from './utilwebcam.js';
+import * as UtilNotify from './utilnotify.js';
+import * as Selection from './selection.js';
+import * as UtilExeCmd from './utilexecmd.js';
+
+var Indicator;
+let timerD = null;
+let timerC = null;
+
+let isActive = false;
+let pathFile = '';
+
+let keybindingConfigured = false;
+
+/**
+ * @type {EasyScreenCastIndicator}
+ */
+const EasyScreenCastIndicator = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_Indicator',
+}, class EasyScreenCastIndicator extends PanelMenu.Button {
+    constructor(extension) {
+        super(null, 'EasyScreenCast_Indicator');
+
+        this._extension = extension;
+        this._settings = new Settings.Settings(this._extension.getSettings());
+        Lib.setDebugEnabled(this._settings.getOption('b', Settings.VERBOSE_DEBUG_SETTING_KEY));
+        this._settings._settings.connect(
+            `changed::${Settings.VERBOSE_DEBUG_SETTING_KEY}`,
+            () => {
+                Lib.setDebugEnabled(this._settings.getOption('b', Settings.VERBOSE_DEBUG_SETTING_KEY));
+            }
+        );
+
+        this.CtrlAudio = new UtilAudio.MixerAudio();
+        this.CtrlWebcam = new UtilWebcam.HelperWebcam(_('Unspecified webcam'));
+
+        this.CtrlNotify = new UtilNotify.NotifyManager();
+        this.CtrlExe = new UtilExeCmd.ExecuteStuff(this);
+
+        // load indicator icons
+        this._icons = {
+            on: Lib.loadIcon(this._extension.dir, 'icon_recording.svg'),
+            onSel: Lib.loadIcon(this._extension.dir, 'icon_recordingSel.svg'),
+            off: Lib.loadIcon(this._extension.dir, 'icon_default.svg'),
+            offSel: Lib.loadIcon(this._extension.dir, 'icon_defaultSel.svg'),
+        };
+
+        // check audio
+        if (!this.CtrlAudio.checkAudio()) {
+            Lib.TalkativeLog('-*-disable audio recording');
+            this._settings.setOption(Settings.INPUT_AUDIO_SOURCE_SETTING_KEY, 0);
+            this._settings.setOption(
+                Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY,
+                Settings.getGSPstd(false)
+            );
+        }
+
+        // add enter/leave/click event
+        this.connect('enter_event', () => this.refreshIndicator(true));
+        this.connect('leave_event', () => this.refreshIndicator(false));
+        this.connect('button_press_event', (actor, event) =>
+            this._onButtonPress(actor, event)
+        );
+
+        // prepare setting var
+        if (this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY) > 0)
+            this.isDelayActive = true;
+        else
+            this.isDelayActive = false;
+
+
+        // Add the title bar icon and label for time display
+        this.indicatorBox = new St.BoxLayout();
+        this.indicatorIcon = new St.Icon({
+            gicon: this._icons.off,
+            icon_size: 16,
+        });
+        this.timeLabel = new St.Label({
+            text: '',
+            style_class: 'time-label',
+            y_expand: true,
+            y_align: Clutter.ActorAlign.CENTER,
+        });
+
+        this.indicatorBox.add_child(this.timeLabel);
+        this.indicatorBox.add_child(this.indicatorIcon);
+
+        // init var
+        this.recorder = new UtilRecorder.CaptureVideo();
+        this.AreaSelected = null;
+        this.TimeSlider = null;
+
+        this._initMainMenu();
+    }
+
+    /**
+     * @private
+     */
+    _initMainMenu() {
+        this._addStartStopMenuEntry();
+        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+        this.sub_menu_audio_recording = new PopupMenu.PopupSubMenuMenuItem(_('No audio source'), true);
+        this.sub_menu_audio_recording.icon.icon_name = 'audio-input-microphone-symbolic';
+        this.menu.addMenuItem(this.sub_menu_audio_recording);
+        this._addAudioRecordingSubMenu();
+
+        // add sub menu webcam recording
+        this._addSubMenuWebCam();
+
+        this._addAreaRecordingSubMenu();
+        this._addRecordingDelaySubMenu();
+        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+        this.menu_item_options = new PopupMenu.PopupMenuItem(_('Options'));
+        this.menu_item_options.actor.insert_child_at_index(
+            new St.Icon({
+                style_class: 'popup-menu-icon',
+                icon_name: 'preferences-other-symbolic',
+            }),
+            1
+        );
+        this.menu.addMenuItem(this.menu_item_options);
+        this.menu_item_options.connect('activate', () => this._openExtensionPreferences());
+    }
+
+    /**
+     * Set a new value for the time label. Integers are
+     * converted to seconds, minutes, hours. All other
+     * values are converted to strings.
+     *
+     * @param {string|number} newValue new value of the label. if a number, then it's the seconds passed.
+     * @returns {string}
+     */
+    updateTimeLabel(newValue) {
+        /**
+         * @param {number} number a number
+         */
+        function padZeros(number) {
+            if (number < 10)
+                number = `0${number}`;
+
+            return number.toString();
+        }
+
+        if (typeof newValue === 'number') {
+            let hours = Math.floor(newValue / 3600);
+            newValue -= hours * 3600;
+
+            let minutes = Math.floor(newValue / 60);
+            newValue -= minutes * 60;
+
+            newValue = `${padZeros(hours)}:${padZeros(minutes)}:${padZeros(newValue)}`;
+        }
+
+        this.timeLabel.set_text(newValue.toString());
+    }
+
+    /**
+     * Left clicking on the icon toggles the recording
+     * options menu. Any other mouse button will start
+     * the recording.
+     * Some submenus are refreshed to account for new
+     * sources.
+     *
+     * @param {Clutter.Actor} actor the actor
+     * @param {Clutter.Event} event a Clutter.Event
+     */
+    _onButtonPress(actor, event) {
+        let button = event.get_button();
+
+        if (button === 1) {
+            Lib.TalkativeLog('-*-left click indicator');
+
+            this._setupExtensionMenu();
+        } else {
+            Lib.TalkativeLog('-*-right click indicator');
+
+            if (this.menu.isOpen)
+                this.menu.close();
+
+            this.isShowNotify = this._settings.getOption('b', Settings.SHOW_NOTIFY_ALERT_SETTING_KEY);
+            this._doRecording();
+        }
+    }
+
+    /**
+     * Sets up the menu when the user opens it.
+     */
+    _setupExtensionMenu() {
+        this._addAudioRecordingSubMenu();
+        this._addWebcamSubMenu();
+    }
+
+    /**
+     * Sets up all the options for web-cams. Should only run the
+     * first time the icon is clicked an the CtrlWebcam is still
+     * null.
+     */
+    _addWebcamSubMenu() {
+        if (this.CtrlWebcam === null)
+            this.CtrlWebcam = new UtilWebcam.HelperWebcam(_('Unspecified webcam'));
+
+        // add sub menu webcam recording
+        this._populateSubMenuWebcam();
+
+        // start monitoring inputvideo
+        this.CtrlWebcam.startMonitor();
+    }
+
+    /**
+     * Adds individual webcam items to the webcam menu.
+     */
+    _populateSubMenuWebcam() {
+        let arrMI = this._createMIWebCam();
+
+        this.smWebCam.menu.removeAll();
+        for (let element in arrMI)
+            this.smWebCam.menu.addMenuItem(arrMI[element]);
+
+        let i = this._settings.getOption('i', Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY);
+        Lib.TalkativeLog(`-*-populated submenuwebcam. Settings i=${i}`);
+
+        this.smWebCam.label.text = this.WebCamDevice[i];
+    }
+
+    /**
+     * @private
+     */
+    _addStartStopMenuEntry() {
+        this.imRecordAction = new PopupMenu.PopupBaseMenuItem();
+        this.RecordingLabel = new St.Label({
+            text: _('Start recording'),
+            style_class: 'RecordAction-label',
+            content_gravity: Clutter.ContentGravity.CENTER,
+            x_expand: true,
+            x_align: Clutter.ActorAlign.CENTER,
+        });
+        this.imRecordAction.actor.add_child(this.RecordingLabel);
+        this.imRecordAction.x_expand = true;
+        this.imRecordAction.x_fill = true;
+        this.imRecordAction.x_align = Clutter.ActorAlign.CENTER;
+        this.imRecordAction.connect('activate', () => {
+            this.isShowNotify = this._settings.getOption('b', Settings.SHOW_NOTIFY_ALERT_SETTING_KEY);
+            this._doRecording();
+        });
+
+        this.menu.addMenuItem(this.imRecordAction);
+    }
+
+    /**
+     *  Refreshes the submenu for audio recording sources.
+     */
+    _addAudioRecordingSubMenu() {
+        Lib.TalkativeLog('-*-reset the sub menu audio');
+        // remove old menu items
+        this.sub_menu_audio_recording.menu.removeAll();
+
+        Lib.TalkativeLog('-*-add new items to sub menu audio');
+        var arrMI = this._createMIAudioRec();
+        for (var ele in arrMI)
+            this.sub_menu_audio_recording.menu.addMenuItem(arrMI[ele]);
+    }
+
+    /**
+     * @private
+     */
+    _addSubMenuWebCam() {
+        this.smWebCam = new PopupMenu.PopupSubMenuMenuItem('', true);
+        this.smWebCam.icon.icon_name = 'camera-web-symbolic';
+
+        this.menu.addMenuItem(this.smWebCam);
+    }
+
+    /**
+     * @private
+     */
+    _addAreaRecordingSubMenu() {
+        this.sub_menu_area_recording = new PopupMenu.PopupSubMenuMenuItem('', true);
+        this.sub_menu_area_recording.icon.icon_name = 'view-fullscreen-symbolic';
+
+        var arrMI = this._createMIAreaRec();
+        for (var ele in arrMI)
+            this.sub_menu_area_recording.menu.addMenuItem(arrMI[ele]);
+
+        this.sub_menu_area_recording.label.text = this.AreaType[this._settings.getOption('i', Settings.AREA_SCREEN_SETTING_KEY)];
+        this.menu.addMenuItem(this.sub_menu_area_recording);
+    }
+
+    /**
+     * @private
+     */
+    _addRecordingDelaySubMenu() {
+        this.smDelayRec = new PopupMenu.PopupSubMenuMenuItem('', true);
+        this.smDelayRec.icon.icon_name = 'alarm-symbolic';
+
+        var arrMI = this._createMIInfoDelayRec();
+        for (var ele in arrMI)
+            this.smDelayRec.menu.addMenuItem(arrMI[ele]);
+
+        var secDelay = this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY);
+        if (secDelay > 0) {
+            this.smDelayRec.label.text =
+                secDelay + _(' sec. delay before recording');
+        } else {
+            this.smDelayRec.label.text = _('Start recording immediately');
+        }
+
+        this.menu.addMenuItem(this.smDelayRec);
+    }
+
+    /**
+     * @returns {Array}
+     * @private
+     */
+    _createMIAreaRec() {
+        this.AreaType = [
+            _('Record all desktop'),
+            _('Record a selected monitor'),
+            _('Record a selected window'),
+            _('Record a selected area'),
+        ];
+
+        this.AreaMenuItem = new Array(this.AreaType.length);
+
+        for (var i = 0; i < this.AreaMenuItem.length; i++) {
+            this.AreaMenuItem[i] = new PopupMenu.PopupMenuItem(
+                this.AreaType[i],
+                {
+                    reactive: true,
+                    activate: true,
+                    hover: true,
+                    can_focus: true,
+                }
+            );
+
+            (function (areaSetting, arr, item, settings) {
+                this.connectMI = function () {
+                    this.connect('activate', () => {
+                        Lib.TalkativeLog(`-*-set area recording to ${areaSetting} ${arr[areaSetting]}`);
+                        settings.setOption(Settings.AREA_SCREEN_SETTING_KEY, areaSetting);
+
+                        item.label.text = arr[areaSetting];
+                    });
+                };
+                this.connectMI();
+            }.call(
+                this.AreaMenuItem[i],
+                i,
+                this.AreaType,
+                this.sub_menu_area_recording,
+                this._settings
+            ));
+        }
+
+        return this.AreaMenuItem;
+    }
+
+    /**
+     * @returns {Array}
+     * @private
+     */
+    _createMIWebCam() {
+        this.WebCamDevice = [_('No WebCam recording')];
+        // add menu item webcam device from GST
+        const devices = this.CtrlWebcam.getDevicesIV();
+        this.WebCamDevice.push(...this.CtrlWebcam.getNameDevices());
+        Lib.TalkativeLog(`-*-webcam list: ${this.WebCamDevice}`);
+        this.AreaMenuItem = new Array(this.WebCamDevice.length);
+
+        for (var i = 0; i < this.AreaMenuItem.length; i++) {
+            let devicePath = '';
+
+            // i === 0 is "No Webcam selected"
+            if (i > 0) {
+                const device = devices[i - 1];
+                devicePath = device.get_properties().get_string('device.path');
+                Lib.TalkativeLog(`-*-webcam i=${i} devicePath: ${devicePath}`);
+            }
+
+            Lib.TalkativeLog(`-*-webcam i=${i} menu-item-text: ${this.WebCamDevice[i]}`);
+            this.AreaMenuItem[i] = new PopupMenu.PopupMenuItem(
+                this.WebCamDevice[i],
+                {
+                    reactive: true,
+                    activate: true,
+                    hover: true,
+                    can_focus: true,
+                }
+            );
+
+            (function (index, devPath, arr, item, settings) {
+                this.connectMI = function () {
+                    this.connect('activate', () => {
+                        Lib.TalkativeLog(`-*-set webcam device to ${index} ${arr[index]} devicePath=${devPath}`);
+                        settings.setOption(Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY, index);
+                        settings.setOption(Settings.DEVICE_WEBCAM_SETTING_KEY, devPath);
+                        item.label.text = arr[index];
+                    });
+                };
+                this.connectMI();
+            }.call(
+                this.AreaMenuItem[i],
+                i,
+                devicePath,
+                this.WebCamDevice,
+                this.smWebCam,
+                this._settings
+            ));
+        }
+
+        return this.AreaMenuItem;
+    }
+
+    /**
+     * @returns {Array}
+     * @private
+     */
+    _createMIAudioRec() {
+        // add std menu item
+        this.AudioChoice = [
+            {
+                desc: _('No audio source'),
+                name: 'N/A',
+                port: 'N/A',
+                sortable: true,
+                resizeable: true,
+            },
+            {
+                desc: _('Default audio source'),
+                name: 'N/A',
+                port: 'N/A',
+                sortable: true,
+                resizeable: true,
+            },
+        ];
+        // add menu item audio source from PA
+        var audioList = this.CtrlAudio.getListInputAudio();
+        for (var index in audioList)
+            this.AudioChoice.push(audioList[index]);
+
+        this.AudioMenuItem = new Array(this.AudioChoice.length);
+
+        for (var i = 0; i < this.AudioChoice.length; i++) {
+            // create label menu
+            let labelMenu = this.AudioChoice[i].desc;
+            if (i >= 2) {
+                labelMenu +=
+                    _('\n - Port: ') +
+                    this.AudioChoice[i].port +
+                    _('\n - Name: ') +
+                    this.AudioChoice[i].name;
+            }
+            // create submenu
+            this.AudioMenuItem[i] = new PopupMenu.PopupMenuItem(labelMenu, {
+                reactive: true,
+                activate: true,
+                hover: true,
+                can_focus: true,
+            });
+            // add icon on submenu
+            this.AudioMenuItem[i].actor.insert_child_at_index(
+                new St.Icon({
+                    style_class: 'popup-menu-icon',
+                    icon_name: 'audio-card-symbolic',
+                }),
+                1
+            );
+
+            // update choice audio from pref
+            if (i === this._settings.getOption('i', Settings.INPUT_AUDIO_SOURCE_SETTING_KEY)) {
+                Lib.TalkativeLog(`-*-get audio choice from pref ${i}`);
+                this.sub_menu_audio_recording.label.text = this.AudioChoice[i].desc;
+            }
+
+            // add action on menu item
+            (function (audioIndex, arr, item, settings) {
+                this.connectMI = function () {
+                    this.connect('activate', () => {
+                        Lib.TalkativeLog(`-*-set audio choice to ${audioIndex}`);
+                        settings.setOption(Settings.INPUT_AUDIO_SOURCE_SETTING_KEY, audioIndex);
+                        item.label.text = arr[audioIndex].desc;
+                    });
+                };
+                this.connectMI();
+            }.call(
+                this.AudioMenuItem[i],
+                i,
+                this.AudioChoice,
+                this.sub_menu_audio_recording,
+                this._settings
+            ));
+        }
+        return this.AudioMenuItem;
+    }
+
+    /**
+     * @returns {Array}
+     * @private
+     */
+    _createMIInfoDelayRec() {
+        this.DelayTimeTitle = new PopupMenu.PopupMenuItem(_('Delay Time'), {
+            reactive: false,
+        });
+
+        this.DelayTimeLabel = new St.Label({
+            text:
+                Math.floor(
+                    this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY)
+                ).toString() + _(' Sec'),
+        });
+        this.DelayTimeTitle.actor.add_child(this.DelayTimeLabel);
+        // TODO this.DelayTimeTitle.align = St.Align.END;
+
+        this.imSliderDelay = new PopupMenu.PopupBaseMenuItem({
+            activate: false,
+        });
+        this.TimeSlider = new Slider.Slider(this._settings.getOption('i', Settings.TIME_DELAY_SETTING_KEY) / 100);
+        this.TimeSlider.x_expand = true;
+        this.TimeSlider.y_expand = true;
+
+        this.TimeSlider.connect('notify::value', item => {
+            this.DelayTimeLabel.set_text(
+                Math.floor(item.value * 100).toString() + _(' Sec')
+            );
+        });
+
+        this.TimeSlider.connect('drag-end', () => this._onDelayTimeChanged());
+        this.TimeSlider.connect('scroll-event', () =>
+            this._onDelayTimeChanged()
+        );
+
+        this.imSliderDelay.actor.add_child(this.TimeSlider);
+
+        return [this.DelayTimeTitle, this.imSliderDelay];
+    }
+
+    /**
+     * @private
+     */
+    _enable() {
+        // enable key binding
+        this._enableKeybindings();
+        // immediately activate/deactive shortcut on settings change
+        this._settings._settings.connect(
+            `changed::${Settings.ACTIVE_SHORTCUT_SETTING_KEY}`,
+            () => {
+                if (this._settings.getOption('b', Settings.ACTIVE_SHORTCUT_SETTING_KEY)) {
+                    Lib.TalkativeLog('-^-shortcut changed - enabling');
+                    this._enableKeybindings();
+                } else {
+                    Lib.TalkativeLog('-^-shortcut changed - disabling');
+                    this._removeKeybindings();
+                }
+            }
+        );
+
+        // start monitoring inputvideo
+        this.CtrlWebcam.startMonitor();
+
+        // add indicator
+        this.add_child(this.indicatorBox);
+    }
+
+    /**
+     * @private
+     */
+    _disable() {
+        // remove key binding
+        this._removeKeybindings();
+        // stop monitoring inputvideo
+        this.CtrlWebcam.stopMonitor();
+        // unregister mixer control
+        this.CtrlAudio.destroy();
+
+        // remove indicator
+        this.remove_child(this.indicatorBox);
+    }
+
+    /**
+     * @private
+     */
+    _doDelayAction() {
+        if (this.isDelayActive) {
+            Lib.TalkativeLog(`-*-delay recording called | delay= ${this.TimeSlider.value}`);
+            timerD = new Time.TimerDelay(
+                Math.floor(this.TimeSlider.value * 100),
+                this._doPreCommand,
+                this
+            );
+            timerD.begin();
+        } else {
+            Lib.TalkativeLog('-*-instant recording called');
+            // start recording
+            this._doPreCommand();
+        }
+    }
+
+    /**
+     * @private
+     */
+    _doPreCommand() {
+        if (this._settings.getOption('b', Settings.ACTIVE_PRE_CMD_SETTING_KEY)) {
+            Lib.TalkativeLog('-*-execute pre command');
+
+            const PreCmd = this._settings.getOption('s', Settings.PRE_CMD_SETTING_KEY);
+
+            this.CtrlExe.Execute(
+                PreCmd,
+                false,
+                res => {
+                    Lib.TalkativeLog(`-*-pre command final: ${res}`);
+                    if (res === true) {
+                        Lib.TalkativeLog('-*-pre command OK');
+                        this.recorder.start();
+                    } else {
+                        Lib.TalkativeLog('-*-pre command ERROR');
+                        this.CtrlNotify.createNotify(
+                            _('ERROR PRE COMMAND - See logs for more info'),
+                            this._icons.off
+                        );
+                    }
+                },
+                line => {
+                    Lib.TalkativeLog(`-*-pre command output: ${line}`);
+                }
+            );
+        } else {
+            this.recorder.start();
+        }
+    }
+
+    /**
+     * @private
+     */
+    _doRecording() {
+        // start/stop record screen
+        if (isActive === false) {
+            Lib.TalkativeLog('-*-start recording');
+
+            pathFile = '';
+
+            // get selected area
+            const optArea = this._settings.getOption('i', Settings.AREA_SCREEN_SETTING_KEY);
+            if (optArea > 0) {
+                Lib.TalkativeLog(`-*-type of selection of the area to record: ${optArea}`);
+                switch (optArea) {
+                case 3:
+                    new Selection.SelectionArea();
+                    break;
+                case 2:
+                    new Selection.SelectionWindow();
+                    break;
+                case 1:
+                    new Selection.SelectionDesktop();
+                    break;
+                }
+            } else {
+                Lib.TalkativeLog('-*-recording full area');
+                this._doDelayAction();
+            }
+        } else {
+            Lib.TalkativeLog('-*-stop recording');
+            isActive = false;
+
+            this.recorder.stop();
+
+            if (timerC !== null) {
+                // stop counting rec
+                timerC.halt();
+                timerC = null;
+            }
+
+            // execute post-command
+            this._doPostCommand();
+        }
+
+        this.refreshIndicator(false);
+    }
+
+    /**
+     * @private
+     */
+    _doPostCommand() {
+        if (this._settings.getOption('b', Settings.ACTIVE_POST_CMD_SETTING_KEY)) {
+            Lib.TalkativeLog('-*-execute post command');
+
+            // launch cmd after registration
+            const tmpCmd = `/usr/bin/sh -c "${this._settings.getOption('s', Settings.POST_CMD_SETTING_KEY)}"`;
+
+            const mapObj = {
+                _fpath: pathFile,
+                _dirpath: pathFile.substr(0, pathFile.lastIndexOf('/')),
+                _fname: pathFile.substr(
+                    pathFile.lastIndexOf('/') + 1,
+                    pathFile.length
+                ),
+            };
+
+            const Cmd = tmpCmd.replace(/_fpath|_dirpath|_fname/gi, match => {
+                return mapObj[match];
+            });
+
+            Lib.TalkativeLog(`-*-post command:${Cmd}`);
+
+            // execute post command
+            this.CtrlExe.Spawn(Cmd);
+        }
+    }
+
+    /**
+     * @param {boolean} result whether the recording was successful
+     * @param {string} file file path of the recorded file
+     */
+    doRecResult(result, file) {
+        if (result) {
+            isActive = true;
+
+            Lib.TalkativeLog('-*-record OK');
+            // update indicator
+            const indicators = this._settings.getOption('i', Settings.STATUS_INDICATORS_SETTING_KEY);
+            this._replaceStdIndicator(indicators === 1 || indicators === 3);
+
+            if (this.isShowNotify) {
+                Lib.TalkativeLog('-*-show notify');
+                // create counting notify
+                this.notifyCounting = this.CtrlNotify.createNotify(
+                    _('Start Recording'),
+                    this._icons.on
+                );
+                this.notifyCounting.connect('destroy', () => {
+                    Lib.TalkativeLog('-*-notification destroyed');
+                    this.notifyCounting = null;
+                });
+
+                // start counting rec
+                timerC = new Time.TimerCounting((secpassed, alertEnd) => {
+                    this._refreshNotify(secpassed, alertEnd);
+                }, this);
+                timerC.begin();
+            }
+
+            // update path file video
+            pathFile = file;
+            Lib.TalkativeLog(`-*-update abs file path -> ${pathFile}`);
+        } else {
+            Lib.TalkativeLog('-*-record ERROR');
+
+            pathFile = '';
+
+            if (this.isShowNotify) {
+                Lib.TalkativeLog('-*-show error notify');
+                this.CtrlNotify.createNotify(
+                    _('ERROR RECORDER - See logs for more info'),
+                    this._icons.off
+                );
+            }
+        }
+        this.refreshIndicator(false);
+    }
+
+    /**
+     * @param {number} sec the seconds passed
+     * @param {boolean} alertEnd whether the timer is ending
+     */
+    _refreshNotify(sec, alertEnd) {
+        if (this.notifyCounting !== null && this.notifyCounting !== undefined && this.isShowNotify) {
+            if (alertEnd) {
+                this.CtrlNotify.updateNotify(
+                    this.notifyCounting,
+                    _(`Finish Recording / Seconds : ${sec}`),
+                    this._icons.off,
+                    true
+                );
+            } else {
+                this.CtrlNotify.updateNotify(
+                    this.notifyCounting,
+                    _('Recording ... / Seconds passed : ') + sec,
+                    this._icons.on,
+                    false
+                );
+            }
+        }
+    }
+
+    /**
+     * @private
+     */
+    _openExtensionPreferences() {
+        try {
+            this._extension.openPreferences();
+        } catch (e) {
+            Lib.TalkativeLog(`Failed to open preferences: ${e}`);
+        }
+    }
+
+    /**
+     * @private
+     */
+    _onDelayTimeChanged() {
+        const secDelay = Math.floor(this.TimeSlider.value * 100);
+        this._settings.setOption(Settings.TIME_DELAY_SETTING_KEY, secDelay);
+        if (secDelay > 0)
+            this.smDelayRec.label.text = secDelay + _(' sec. delay before recording');
+        else
+            this.smDelayRec.label.text = _('Start recording immediately');
+    }
+
+    /**
+     * @param {boolean} focus selects the correct icon depending on the focus state
+     */
+    refreshIndicator(focus) {
+        Lib.TalkativeLog(`-*-refresh indicator -A ${isActive} -F ${focus}`);
+
+        const indicators = this._settings.getOption('i', Settings.STATUS_INDICATORS_SETTING_KEY);
+
+        if (isActive === true) {
+            if (indicators === 0 || indicators === 1) {
+                if (focus === true)
+                    this.indicatorIcon.set_gicon(this._icons.onSel);
+                else
+                    this.indicatorIcon.set_gicon(this._icons.on);
+            } else if (this._settings.getOption('b', Settings.ACTIVE_SHORTCUT_SETTING_KEY)) {
+                this.indicatorIcon.set_gicon(null);
+            } else if (focus === true) {
+                this.indicatorIcon.set_gicon(this._icons.onSel);
+            } else {
+                this.indicatorIcon.set_gicon(this._icons.on);
+            }
+
+            this.RecordingLabel.set_text(_('Stop recording'));
+        } else {
+            if (focus === true)
+                this.indicatorIcon.set_gicon(this._icons.offSel);
+            else
+                this.indicatorIcon.set_gicon(this._icons.off);
+
+            this.RecordingLabel.set_text(_('Start recording'));
+        }
+    }
+
+    /**
+     * @param {boolean} OPTtemp whether to replace the standard indicator or use it
+     * @private
+     */
+    _replaceStdIndicator(OPTtemp) {
+        if (Main.panel.statusArea === undefined) {
+            Lib.TalkativeLog('-*-no Main.panel.statusArea found');
+            return;
+        }
+
+        var stdMenu = Main.panel.statusArea.quickSettings;
+        if (stdMenu === undefined) {
+            Lib.TalkativeLog('-*-no quickSettings or aggregateMenu in Main.panel.statusArea');
+            return;
+        }
+        if (stdMenu._remoteAccess === undefined) {
+            Lib.TalkativeLog('-*-no _remoteAccess indicator applet found');
+            return;
+        }
+        var indicator = stdMenu._remoteAccess._indicator;
+        if (indicator === undefined) {
+            Lib.TalkativeLog('-*-no _indicator or _recordingIndicator found');
+            return;
+        }
+
+        if (OPTtemp) {
+            Lib.TalkativeLog('-*-replace STD indicator');
+            indicator.visible = false;
+        } else {
+            Lib.TalkativeLog('-*-use STD indicator');
+            indicator.visible = isActive;
+        }
+    }
+
+    /**
+     * @private
+     */
+    _enableKeybindings() {
+        if (this._settings.getOption('b', Settings.ACTIVE_SHORTCUT_SETTING_KEY)) {
+            Lib.TalkativeLog('-*-enable keybinding');
+
+            Main.wm.addKeybinding(
+                Settings.SHORTCUT_KEY_SETTING_KEY,
+                this._settings._settings,
+                Meta.KeyBindingFlags.NONE,
+                // available modes: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/src/shell-action-modes.h
+                Shell.ActionMode.NORMAL |
+                    Shell.ActionMode.OVERVIEW |
+                    Shell.ActionMode.POPUP |
+                    Shell.ActionMode.SYSTEM_MODAL,
+                () => {
+                    Lib.TalkativeLog('-*-intercept key combination');
+                    this._doRecording();
+                }
+            );
+            keybindingConfigured = true;
+        }
+    }
+
+    /**
+     * @private
+     */
+    _removeKeybindings() {
+        if (keybindingConfigured) {
+            Lib.TalkativeLog('-*-remove keybinding');
+            Main.wm.removeKeybinding(Settings.SHORTCUT_KEY_SETTING_KEY);
+            keybindingConfigured = false;
+        }
+    }
+
+    getSelectedRect() {
+        var recX = this._settings.getOption('i', Settings.X_POS_SETTING_KEY);
+        var recY = this._settings.getOption('i', Settings.Y_POS_SETTING_KEY);
+        var recW = this._settings.getOption('i', Settings.WIDTH_SETTING_KEY);
+        var recH = this._settings.getOption('i', Settings.HEIGHT_SETTING_KEY);
+        return [recX, recY, recW, recH];
+    }
+
+    saveSelectedRect(x, y, h, w) {
+        this._settings.setOption(Settings.X_POS_SETTING_KEY, x);
+        this._settings.setOption(Settings.Y_POS_SETTING_KEY, y);
+        this._settings.setOption(Settings.HEIGHT_SETTING_KEY, h);
+        this._settings.setOption(Settings.WIDTH_SETTING_KEY, w);
+    }
+
+    getSettings() {
+        return this._settings;
+    }
+
+    getAudioSource() {
+        return this.CtrlAudio.getAudioSource();
+    }
+
+    /**
+     * Destroy indicator
+     */
+    destroy() {
+        Lib.TalkativeLog('-*-destroy indicator called');
+
+        if (isActive)
+            isActive = false;
+
+        if (this._settings) {
+            this._settings.destroy();
+            this._settings = null;
+        }
+
+        super.destroy();
+    }
+});
+
+export default class EasyScreenCast extends Extension {
+    /**
+     *
+     */
+    enable() {
+        Lib.TalkativeLog('-*-enableExtension called');
+        Lib.TalkativeLog(`-*-version: ${this.metadata.version}`);
+        Lib.TalkativeLog(`-*-install path: ${this.path}`);
+        Lib.TalkativeLog(`-*-version (package.json): ${Lib.getFullVersion()}`);
+
+        if (Indicator === null || Indicator === undefined) {
+            Lib.TalkativeLog('-*-create indicator');
+
+            Indicator = new EasyScreenCastIndicator(this);
+            Main.panel.addToStatusArea('EasyScreenCast-indicator', Indicator);
+        }
+
+        Indicator._enable();
+    }
+
+    /**
+     *
+     */
+    disable() {
+        Lib.TalkativeLog('-*-disableExtension called');
+
+        if (timerD !== null) {
+            Lib.TalkativeLog('-*-timerD stopped');
+            timerD.stop();
+            timerD = null;
+        }
+
+        // this might happen, if the extension is disabled during recording
+        if (timerC !== null) {
+            // stop counting rec
+            timerC.halt();
+            timerC = null;
+        }
+
+
+        if (Indicator !== null) {
+            Lib.TalkativeLog('-*-indicator call destroy');
+
+            Indicator._disable();
+            Indicator.destroy();
+            Indicator = null;
+        }
+    }
+}
+
+export {Indicator};

BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/Icon_Info.png


+ 45 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/Icon_Performance.svg

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   enable-background="new 0 0 128 128"
+   height="128px"
+   id="Layer_1"
+   version="1.1"
+   viewBox="0 0 128 128"
+   width="128px"
+   xml:space="preserve"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="Icon_Performance.svg"><metadata
+     id="metadata9"><rdf:RDF><cc:Work
+         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+     id="defs7" /><sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1366"
+     inkscape:window-height="741"
+     id="namedview5"
+     showgrid="false"
+     inkscape:zoom="1.84375"
+     inkscape:cx="6.4184127"
+     inkscape:cy="22.725708"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="Layer_1" /><path
+     d="m 79.120475,57.28 -7.16142,0 9.95694,-14.93646 c 0.68754,-1.03026 0.75138,-2.35578 0.16716,-3.44862 C 81.497675,37.8025 80.359055,37.12 79.120475,37.12 l -16.8,0 c -1.20582,0 -2.31966,0.64638 -2.9169,1.69302 l -13.44,23.52 c -0.59388,1.04034 -0.59052,2.31672 0.01134,3.35328 0.60018,1.03698 1.70772,1.6737 2.90556,1.6737 l 11.0103,0 -10.5672,18.49302 c -0.83496,1.46034 -0.45948,3.31422 0.8778,4.33776 0.60522,0.46284 1.32384,0.68922 2.03784,0.68922 0.86646,0 1.72578,-0.33138 2.3772,-0.98448 l 26.88,-26.88 c 0.96138,-0.96138 1.24866,-2.40534 0.72828,-3.66198 C 81.704315,58.10026 80.478755,57.28 79.120475,57.28 Z m -26.88,30.24 12.2325,-21.40698 c 0,0 0,-0.31164 0,-0.43302 0,-0.92862 -0.75138,-1.68 -1.68,-1.68 l -13.9125,0 13.44,-23.52 16.8,0 -11.58444,17.3775 c 0,0 -0.38556,0.69552 -0.38556,1.1025 0,0.92862 0.75138,1.68 1.68,1.68 l 10.29,0 -26.88,26.88 z"
+     id="path3"
+     style="fill:#030000;fill-opacity:1"
+     inkscape:connector-curvature="0" /></svg>

+ 45 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/Icon_Quality.svg

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   enable-background="new 0 0 128 128"
+   height="128px"
+   id="Layer_1"
+   version="1.1"
+   viewBox="0 0 128 128"
+   width="128px"
+   xml:space="preserve"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="Icon_Quality.svg"><metadata
+     id="metadata4236"><rdf:RDF><cc:Work
+         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+     id="defs4234" /><sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1366"
+     inkscape:window-height="741"
+     id="namedview4232"
+     showgrid="false"
+     inkscape:zoom="1.84375"
+     inkscape:cx="47.211005"
+     inkscape:cy="62.915254"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="Layer_1" /><path
+     d="m 92.8,47.2 c -3.9774,0 -7.2,3.225 -7.2,7.2 0,1.008 0.2112,1.9638 0.5838,2.8362 L 76,61.6 67.4806,46.267 C 69.688,45.0346 71.2,42.7048 71.2,40 c 0,-3.975 -3.2226,-7.2 -7.2,-7.2 -3.9774,0 -7.2,3.225 -7.2,7.2 0,2.7048 1.512,5.0346 3.7194,6.267 L 52,61.6 41.8162,57.2362 C 42.1888,56.3638 42.4,55.408 42.4,54.4 c 0,-3.975 -3.2226,-7.2 -7.2,-7.2 -3.9774,0 -7.2,3.225 -7.2,7.2 0,3.975 3.2226,7.2 7.2,7.2 0.558,0 1.0932,-0.0798 1.617,-0.1968 L 42.4,85.6 l 0,4.8 c 0,2.6532 2.1492,4.8 4.8,4.8 l 33.6,0 c 2.6508,0 4.8,-2.1468 4.8,-4.8 l 0,-4.8 5.5854,-24.1968 C 91.7056,61.525 92.242,61.6 92.8,61.6 c 3.9774,0 7.2,-3.225 7.2,-7.2 0,-3.975 -3.2226,-7.2 -7.2,-7.2 z M 64,37.6 c 1.3242,0 2.4,1.0782 2.4,2.4 0,1.3218 -1.0758,2.4 -2.4,2.4 -1.3242,0 -2.4,-1.0782 -2.4,-2.4 0,-1.3218 1.0758,-2.4 2.4,-2.4 z M 32.8,54.4 c 0,-1.3218 1.0758,-2.4 2.4,-2.4 1.3242,0 2.4,1.0782 2.4,2.4 0,1.3218 -1.0758,2.4 -2.4,2.4 -1.3242,0 -2.4,-1.0782 -2.4,-2.4 z m 48,36 -33.6,0 0,-4.8 33.6,0 0,4.8 z m 0.9798,-9.6 -35.5596,0 -4.2162,-18.2622 8.1048,3.4734 c 0.6138,0.2622 1.2558,0.3888 1.8888,0.3888 1.6968,0 3.3282,-0.9 4.1976,-2.4702 L 64,49.8862 71.8048,63.9298 C 72.6742,65.5 74.3056,66.4 76.0024,66.4 c 0.633,0 1.275,-0.1266 1.8888,-0.3888 L 85.996,62.5378 81.7798,80.8 Z M 92.8,56.8 c -1.3242,0 -2.4,-1.0782 -2.4,-2.4 0,-1.3218 1.0758,-2.4 2.4,-2.4 1.3242,0 2.4,1.0782 2.4,2.4 0,1.3218 -1.0758,2.4 -2.4,2.4 z"
+     id="path4230"
+     style="fill:#000000;fill-opacity:1"
+     inkscape:connector-curvature="0" /></svg>

+ 99 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_default.svg

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64px"
+   height="64px"
+   id="svg2993"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="icon_recording.svg">
+  <defs
+     id="defs2995" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8.051626"
+     inkscape:cx="33.921944"
+     inkscape:cy="32"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     inkscape:window-width="1366"
+     inkscape:window-height="709"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-global="false" />
+  <metadata
+     id="metadata2998">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <rect
+       style="fill:#cccccc;fill-rule:evenodd;stroke:#cccccc;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+       id="rect3001"
+       width="33.906902"
+       height="22.246866"
+       x="23.587818"
+       y="30.058556"
+       rx="6.6889281"
+       ry="22.246866" />
+    <g
+       id="g3832"
+       style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#cccccc;stroke:#cccccc;fill-opacity:1">
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5"
+         style="fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5-9"
+         style="fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
+    </g>
+    <path
+       style="fill:#cccccc;stroke:#cccccc;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
+       d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
+       id="path3811"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 99 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_defaultSel.svg

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64px"
+   height="64px"
+   id="svg2993"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="icon_defaultSel.svg">
+  <defs
+     id="defs2995" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="16.669382"
+     inkscape:cx="33.921944"
+     inkscape:cy="32"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     inkscape:window-width="1366"
+     inkscape:window-height="709"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-global="false" />
+  <metadata
+     id="metadata2998">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <rect
+       style="fill:#f9f9f9;fill-rule:evenodd;stroke:#f9f9f9;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;fill-opacity:1"
+       id="rect3001"
+       width="33.906902"
+       height="22.246866"
+       x="23.587818"
+       y="30.058556"
+       rx="6.6889281"
+       ry="22.246866" />
+    <g
+       id="g3832"
+       style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#f9f9f9;stroke:#f9f9f9;fill-opacity:1">
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5"
+         style="fill:#f9f9f9;fill-opacity:1;stroke:#f9f9f9;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5-9"
+         style="fill:#f9f9f9;fill-opacity:1;stroke:#f9f9f9;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
+    </g>
+    <path
+       style="fill:#f9f9f9;stroke:#f9f9f9;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
+       d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
+       id="path3811"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 99 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_recording.svg

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64px"
+   height="64px"
+   id="svg2993"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="icon_recordingSel.svg">
+  <defs
+     id="defs2995" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8.051626"
+     inkscape:cx="33.921944"
+     inkscape:cy="32"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     inkscape:window-width="1366"
+     inkscape:window-height="709"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-global="false" />
+  <metadata
+     id="metadata2998">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <rect
+       style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect3001"
+       width="33.906902"
+       height="22.246866"
+       x="23.587818"
+       y="30.058556"
+       rx="6.6889281"
+       ry="22.246866" />
+    <g
+       id="g3832"
+       style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#ff0000;stroke:#ff0000">
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5-9"
+         style="fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
+    </g>
+    <path
+       style="fill:#ff0000;stroke:#ff0000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
+       id="path3811"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

+ 99 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/images/icon_recordingSel.svg

@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="64px"
+   height="64px"
+   id="svg2993"
+   version="1.1"
+   inkscape:version="0.48.4 r9939"
+   sodipodi:docname="icon_recording.svg">
+  <defs
+     id="defs2995" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8.051626"
+     inkscape:cx="33.921944"
+     inkscape:cy="32"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     inkscape:window-width="1366"
+     inkscape:window-height="709"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:snap-global="false" />
+  <metadata
+     id="metadata2998">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <rect
+       style="fill:#ff5555;fill-rule:evenodd;stroke:#ff5555;stroke-width:1.49224782000000000;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+       id="rect3001"
+       width="33.906902"
+       height="22.246866"
+       x="23.587818"
+       y="30.058556"
+       rx="6.6889281"
+       ry="22.246866" />
+    <g
+       id="g3832"
+       style="stroke-width:1.49224782000000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;fill:#ff5555;stroke:#ff5555">
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5"
+         style="fill:#ff5555;fill-opacity:1;stroke:#ff5555;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,18.379885,-17.009556)" />
+      <path
+         sodipodi:end="10.319542"
+         sodipodi:start="4.0645442"
+         d="m 16.771536,16.573455 a 5.752316,5.6205831 0 1 1 -0.127895,0.09738 l 3.599241,4.3844 z"
+         sodipodi:ry="5.6205831"
+         sodipodi:rx="5.752316"
+         sodipodi:cy="21.055231"
+         sodipodi:cx="20.242882"
+         id="path3773-5-9"
+         style="fill:#ff5555;fill-opacity:1;stroke:#ff5555;stroke-width:0.88939973000000005;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
+         sodipodi:type="arc"
+         transform="matrix(1.6863671,0,0,1.6693054,-5.571337,-17.009556)" />
+    </g>
+    <path
+       style="fill:#ff5555;stroke:#ff5555;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="M 19.623366,37.29732 3.4775584,26.243652 3.3533599,57.29328 19.374969,47.357399 z"
+       id="path3811"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>

BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/ca/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/cs/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/de/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/es/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/fr/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/it/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/ja/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/pt_BR/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/ru/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/uk/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/vi/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/locale/zh/LC_MESSAGES/EasyScreenCast@iacopodeenosee.gmail.com.mo


+ 13 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/metadata.json

@@ -0,0 +1,13 @@
+{
+  "_generated": "Generated by SweetTooth, do not edit",
+  "description": "This extension simplifies the use of the video recording function integrated in gnome shell, allows quickly to change the various settings of the desktop recording.\n\nSOURCE CODE ->  https://github.com/EasyScreenCast/EasyScreenCast\n\nVIDEO ->  https://youtu.be/81E9AruraKU\n\n**NOTICE**\nif an error occurs during the update is recommended to reload GNOME Shell (Alt + F2, 'r') and reload the extension's installation page.",
+  "gettext-domain": "EasyScreenCast@iacopodeenosee.gmail.com",
+  "name": "EasyScreenCast",
+  "settings-schema": "org.gnome.shell.extensions.EasyScreenCast",
+  "shell-version": [
+    "46"
+  ],
+  "url": "https://github.com/EasyScreenCast/EasyScreenCast",
+  "uuid": "EasyScreenCast@iacopodeenosee.gmail.com",
+  "version": 50
+}

+ 7 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/prefs.css

@@ -0,0 +1,7 @@
+.options-grid {
+    margin: 18px;
+}
+
+.borderless {
+    border: none;
+}

+ 1184 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/prefs.js

@@ -0,0 +1,1184 @@
+/*
+    Copyright (C) 2013  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GIRepository from 'gi://GIRepository';
+GIRepository.Repository.prepend_search_path('/usr/lib64/gnome-shell');
+GIRepository.Repository.prepend_library_path('/usr/lib64/gnome-shell');
+
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+import Gtk from 'gi://Gtk';
+import Gdk from 'gi://Gdk';
+import Pango from 'gi://Pango';
+
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import * as Lib from './convenience.js';
+import * as UtilWebcam from './utilwebcam.js';
+import * as UtilGSP from './utilgsp.js';
+import * as Settings from './settings.js';
+import * as UtilExeCmd from './utilexecmd.js';
+
+const EasyScreenCastSettingsWidget = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_SettingsWidget',
+}, class EasyScreenCastSettingsWidget extends Gtk.Box {
+    /**
+     * @param {ExtensionPreferences} prefs the prefs instance
+     */
+    constructor(prefs) {
+        super();
+
+        this._prefs = prefs;
+        this._settings = new Settings.Settings(this._prefs.getSettings());
+        Lib.setDebugEnabled(this._settings.getOption('b', Settings.VERBOSE_DEBUG_SETTING_KEY));
+        this.CtrlExe = new UtilExeCmd.ExecuteStuff(this);
+        this.CtrlWebcam = new UtilWebcam.HelperWebcam(_('Unspecified webcam'));
+
+        let cssProvider = new Gtk.CssProvider();
+        cssProvider.load_from_path(`${prefs.path}/prefs.css`);
+        Gtk.StyleContext.add_provider_for_display(
+            Gdk.Display.get_default(),
+            cssProvider,
+            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
+
+        // creates the ui builder and add the main resource file
+        let uiFilePath = `${this._prefs.path}/Options_UI.glade`;
+        let builder = new Gtk.Builder();
+        builder.set_translation_domain(this._prefs.metadata['gettext-domain']);
+
+        if (builder.add_from_file(uiFilePath) === 0) {
+            Lib.TalkativeLog(`-^-could not load the ui file: ${uiFilePath}`);
+            let label = new Gtk.Label({
+                label: _('Could not load the preferences UI file'),
+                vexpand: true,
+            });
+
+            this.append(label);
+        } else {
+            Lib.TalkativeLog(`-^-UI file receive and load: ${uiFilePath}`);
+
+            // gets the interesting builder objects
+            let refBoxMainContainer = builder.get_object('Main_Container');
+            this.append(refBoxMainContainer);
+
+            // setup tab options
+            this._initTabOptions(this, builder, this._settings._settings);
+
+            // setup tab quality
+            this._initTabQuality(this, builder, this._settings._settings);
+
+            // setup tab webcam
+            this._initTabWebcam(this, builder, this._settings._settings);
+
+            // setup tab file
+            this._initTabFile(this, builder, this._settings._settings);
+
+            // setup tab support
+            this._initTabSupport(this, builder, this._settings._settings);
+
+            // setup tab info
+            this._initTabInfo(this, builder);
+
+            // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+            // update GSP area
+            this._setStateGSP(
+                !this._settings.getOption('b', Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY)
+            );
+
+            // update list view
+            this._updateRowShortcut(
+                this._settings.getOption('as', Settings.SHORTCUT_KEY_SETTING_KEY)[0]
+            );
+
+            // update webcam widget state
+            this._updateStateWebcamOptions();
+
+            // connect keywebcam signal
+            this._settings._settings.connect(
+                `changed::${Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY}`,
+                () => {
+                    Lib.TalkativeLog('-^-webcam device changed');
+
+                    this._updateStateWebcamOptions();
+                }
+            );
+        }
+    }
+
+    /**
+     * @param {EasyScreenCastSettingsWidget} ctx the prefs/settings widget
+     * @param {Gtk.Builder} gtkDB the builder that loaded the UI glade file
+     * @param {Gio.Settings} tmpS the current settings
+     * @private
+     */
+    _initTabOptions(ctx, gtkDB, tmpS) {
+        // implements show timer option
+        let refSwitchShowNotifyAlert = gtkDB.get_object('swt_ShowNotifyAlert');
+        tmpS.bind(
+            Settings.SHOW_NOTIFY_ALERT_SETTING_KEY,
+            refSwitchShowNotifyAlert,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements show area option
+        let refSwitchShowAreaRec = gtkDB.get_object('swt_ShowAreaRec');
+        tmpS.bind(
+            Settings.SHOW_AREA_REC_SETTING_KEY,
+            refSwitchShowAreaRec,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements show indicator option
+        let refComboboxIndicatorsRec = gtkDB.get_object('cbt_StatusIndicatorsRec');
+        tmpS.bind(
+            Settings.STATUS_INDICATORS_SETTING_KEY,
+            refComboboxIndicatorsRec,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements draw cursor option
+        let refSwitchDrawCursorRec = gtkDB.get_object('swt_DrawCursorRec');
+        tmpS.bind(
+            Settings.DRAW_CURSOR_SETTING_KEY,
+            refSwitchDrawCursorRec,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements enable keybinding option
+        let refSwitchEnableShortcut = gtkDB.get_object('swt_KeyShortcut');
+        tmpS.bind(
+            Settings.ACTIVE_SHORTCUT_SETTING_KEY,
+            refSwitchEnableShortcut,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements selecting alternative key combo
+        let refTreeviewShortcut = gtkDB.get_object('treeview_KeyShortcut');
+        refTreeviewShortcut.set_sensitive(true);
+        ctx.Ref_liststore_Shortcut = gtkDB.get_object('liststore_KeyShortcut');
+        ctx.Iter_ShortcutRow = ctx.Ref_liststore_Shortcut.append();
+
+        let renderer = new Gtk.CellRendererAccel({
+            editable: true,
+        });
+        renderer.connect(
+            'accel-edited',
+            (_0, _1, key, mods, _2) => {
+                Lib.TalkativeLog(`-^-edited key accel: key=${key} mods=${mods}`);
+
+                let accel = Gtk.accelerator_name(key, mods);
+
+                ctx._updateRowShortcut(accel);
+                this._settings.setOption(Settings.SHORTCUT_KEY_SETTING_KEY, [accel]);
+            }
+        );
+
+        renderer.connect('accel-cleared', () => {
+            Lib.TalkativeLog('-^-cleared key accel');
+
+            ctx._updateRowShortcut(null);
+            this._settings.setOption(Settings.SHORTCUT_KEY_SETTING_KEY, []);
+        });
+
+        let column = new Gtk.TreeViewColumn();
+        column.pack_start(renderer, true);
+        column.add_attribute(
+            renderer,
+            'accel-key',
+            Settings.SHORTCUT_COLUMN_KEY
+        );
+        column.add_attribute(
+            renderer,
+            'accel-mods',
+            Settings.SHORTCUT_COLUMN_MODS
+        );
+
+        refTreeviewShortcut.append_column(column);
+
+        // implements post execute command
+        let refSwitchExecutePostCmd = gtkDB.get_object('swt_executepostcmd');
+        tmpS.bind(
+            Settings.ACTIVE_POST_CMD_SETTING_KEY,
+            refSwitchExecutePostCmd,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        let refTexteditPostCmd = gtkDB.get_object('txe_postcmd');
+        tmpS.bind(
+            Settings.POST_CMD_SETTING_KEY,
+            refTexteditPostCmd,
+            'text',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements pre execute command
+        let refSwitchExecutePreCmd = gtkDB.get_object('swt_executeprecmd');
+        tmpS.bind(
+            Settings.ACTIVE_PRE_CMD_SETTING_KEY,
+            refSwitchExecutePreCmd,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        let refTexteditPreCmd = gtkDB.get_object('txe_precmd');
+        tmpS.bind(
+            Settings.PRE_CMD_SETTING_KEY,
+            refTexteditPreCmd,
+            'text',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+    }
+
+    /**
+     * @param {EasyScreenCastSettingsWidget} ctx the prefs/settings widget
+     * @param {Gtk.Builder} gtkDB the builder that loaded the UI glade file
+     * @param {Gio.Settings} tmpS the current settings
+     * @private
+     */
+    _initTabQuality(ctx, gtkDB, tmpS) {
+        // implements FPS option
+        let refSpinnerFrameRateRec = gtkDB.get_object('spb_FrameRateRec');
+        // Create an adjustment to use for the second spinbutton
+        let adjustment1 = new Gtk.Adjustment({
+            value: 30,
+            lower: 1,
+            upper: 666,
+            step_increment: 1,
+            page_increment: 10,
+        });
+        refSpinnerFrameRateRec.configure(adjustment1, 10, 0);
+        tmpS.bind(
+            Settings.FPS_SETTING_KEY,
+            refSpinnerFrameRateRec,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements command string rec option
+        let refTexteditPipeline = gtkDB.get_object('txe_CommandStringRec');
+        let refBufferPipeline = refTexteditPipeline.get_buffer();
+        tmpS.bind(
+            Settings.PIPELINE_REC_SETTING_KEY,
+            refBufferPipeline,
+            'text',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements label description GSP
+        let refLabelDescGSP = gtkDB.get_object('lbl_GSP_Description');
+        refLabelDescGSP.set_text(
+            UtilGSP.getDescr(
+                this._settings.getOption('i', Settings.QUALITY_SETTING_KEY),
+                this._settings.getOption('i', Settings.FILE_CONTAINER_SETTING_KEY)
+            )
+        );
+        // update label description when container selection changed
+        this._settings._settings.connect(`changed::${Settings.FILE_CONTAINER_SETTING_KEY}`, () => {
+            Lib.TalkativeLog('-^- new setting for file container, update gps description');
+            refLabelDescGSP.set_text(
+                UtilGSP.getDescr(
+                    this._settings.getOption('i', Settings.QUALITY_SETTING_KEY),
+                    this._settings.getOption('i', Settings.FILE_CONTAINER_SETTING_KEY)
+                )
+            );
+        });
+
+
+        // implements quality scale option
+        let refScaleQuality = gtkDB.get_object('scl_Quality');
+        refScaleQuality.set_valign(Gtk.Align.START);
+        let adjustment2 = new Gtk.Adjustment({
+            value: 1,
+            lower: 0,
+            upper: 3,
+            step_increment: 1,
+            page_increment: 1,
+        });
+        refScaleQuality.set_adjustment(adjustment2);
+        refScaleQuality.set_digits(1);
+        let ind = 0;
+        for (; ind < 4; ind++)
+            refScaleQuality.add_mark(ind, Gtk.PositionType.BOTTOM, '');
+
+
+        refScaleQuality.set_value(
+            this._settings.getOption('i', Settings.QUALITY_SETTING_KEY)
+        );
+
+        let oldQualityValue = refScaleQuality.get_value();
+        refScaleQuality.connect('value-changed', self => {
+            // not logging by default - it's too much
+            // Lib.TalkativeLog(`-^-value quality changed : ${self.get_value()}`);
+
+            // round the value
+            let roundTmp = parseInt(self.get_value().toFixed(0));
+            // not logging by default - it's too much
+            // Lib.TalkativeLog(`-^-value quality fixed : ${roundTmp}`);
+
+            self.set_value(roundTmp);
+
+            // only update labels for real changes
+            if (oldQualityValue !== roundTmp) {
+                oldQualityValue = roundTmp;
+                this._settings.setOption(Settings.QUALITY_SETTING_KEY, roundTmp);
+
+                // update label descr GSP
+                refLabelDescGSP.set_text(
+                    UtilGSP.getDescr(
+                        roundTmp,
+                        this._settings.getOption('i', Settings.FILE_CONTAINER_SETTING_KEY)
+                    )
+                );
+
+                // update fps
+                this._settings.setOption(
+                    Settings.FPS_SETTING_KEY,
+                    UtilGSP.getFps(
+                        roundTmp,
+                        this._settings.getOption('i', Settings.FILE_CONTAINER_SETTING_KEY)
+                    )
+                );
+            }
+        });
+
+        // implements image for scale widget
+        let refImagePerformance = gtkDB.get_object('img_Performance');
+        refImagePerformance.set_from_file(Lib.getImagePath(this._prefs.dir, 'Icon_Performance.svg'));
+
+        let refImageQuality = gtkDB.get_object('img_Quality');
+        refImageQuality.set_from_file(Lib.getImagePath(this._prefs.dir, 'Icon_Quality.svg'));
+
+        // implements custom GSPipeline option
+        let refSwitchCustomGSP = gtkDB.get_object('swt_EnableCustomGSP');
+        tmpS.bind(
+            Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY,
+            refSwitchCustomGSP,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+        refSwitchCustomGSP.connect('state-set', () => {
+            // update GSP text area
+            ctx._setStateGSP(this._settings.getOption('b', Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY));
+        });
+
+        ctx.Ref_stack_Quality = gtkDB.get_object('stk_Quality');
+    }
+
+    /**
+     * @param {EasyScreenCastSettingsWidget} ctx the prefs/settings widget
+     * @param {Gtk.Builder} gtkDB the builder that loaded the UI glade file
+     * @param {Gio.Settings} tmpS the current settings
+     * @private
+     */
+    _initTabWebcam(ctx, gtkDB, tmpS) {
+        // implements webcam quality option: Type: GtkListStore
+        ctx.Ref_ListStore_QualityWebCam = gtkDB.get_object(
+            'liststore_QualityWebCam'
+        );
+        let refTreeViewQualityWebCam = gtkDB.get_object(
+            'treeview_QualityWebam'
+        );
+        // create column data
+        let capsColumn = new Gtk.TreeViewColumn({
+            title: _('WebCam Caps'),
+        });
+        let normalColumn = new Gtk.CellRendererText();
+        capsColumn.pack_start(normalColumn, true);
+        capsColumn.add_attribute(normalColumn, 'text', 0);
+
+        // insert caps column into treeview
+        refTreeViewQualityWebCam.insert_column(capsColumn, 0);
+
+        // setup selection liststore
+        let capsSelection = refTreeViewQualityWebCam.get_selection();
+
+        // connect selection signal
+        capsSelection.connect('changed', self => {
+            let [isSelected,, iter] = self.get_selected();
+            if (isSelected) {
+                let Caps = ctx.Ref_ListStore_QualityWebCam.get_value(iter, 0);
+                Lib.TalkativeLog(`-^-treeview row selected : ${Caps}`);
+
+                this._settings.setOption(Settings.QUALITY_WEBCAM_SETTING_KEY, Caps);
+
+                // update label webcam caps
+                ctx.Ref_Label_WebCamCaps.set_ellipsize(Pango.EllipsizeMode.END);
+                ctx.Ref_Label_WebCamCaps.set_text(Caps);
+            }
+        });
+
+        // fill combobox with quality option webcam
+        ctx._updateWebCamCaps(this._settings.getOption('i', Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY));
+
+        // implements webcam corner position option
+        let refComboboxCornerWebCam = gtkDB.get_object('cbt_WebCamCorner');
+        tmpS.bind(
+            Settings.CORNER_POSITION_WEBCAM_SETTING_KEY,
+            refComboboxCornerWebCam,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements webcam margin x position option
+        let refSpinnerMarginXWebCam = gtkDB.get_object('spb_WebCamMarginX');
+        let adjustmentMarginX = new Gtk.Adjustment({
+            value: 0,
+            lower: 0,
+            upper: 10000,
+            step_increment: 1,
+            page_increment: 10,
+        });
+        refSpinnerMarginXWebCam.configure(adjustmentMarginX, 10, 0);
+        tmpS.bind(
+            Settings.MARGIN_X_WEBCAM_SETTING_KEY,
+            refSpinnerMarginXWebCam,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements webcam margin y position option
+        let refSpinnerMarginYWebCam = gtkDB.get_object('spb_WebCamMarginY');
+        let adjustmentMarginY = new Gtk.Adjustment({
+            value: 0,
+            lower: 0,
+            upper: 10000,
+            step_increment: 1,
+            page_increment: 10,
+        });
+        refSpinnerMarginYWebCam.configure(adjustmentMarginY, 10, 0);
+        tmpS.bind(
+            Settings.MARGIN_Y_WEBCAM_SETTING_KEY,
+            refSpinnerMarginYWebCam,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements webcam aplha channel option
+        let refSpinnerAlphaWebCam = gtkDB.get_object('spb_WebCamAlpha');
+        let adjustmentAlpha = new Gtk.Adjustment({
+            value: 0.01,
+            lower: 0.0,
+            upper: 1.0,
+            step_increment: 0.05,
+            page_increment: 0.25,
+        });
+        refSpinnerAlphaWebCam.configure(adjustmentAlpha, 0.25, 2);
+        tmpS.bind(
+            Settings.ALPHA_CHANNEL_WEBCAM_SETTING_KEY,
+            refSpinnerAlphaWebCam,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements webcam type unit dimension option
+        let refComboboxTypeUnitWebCam = gtkDB.get_object('cbt_WebCamUnitMeasure');
+        tmpS.bind(
+            Settings.TYPE_UNIT_WEBCAM_SETTING_KEY,
+            refComboboxTypeUnitWebCam,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements webcam width option
+        let refSpinnerWidthWebCam = gtkDB.get_object('spb_WebCamWidth');
+        let adjustmentWidth = new Gtk.Adjustment({
+            value: 20,
+            lower: 0,
+            upper: 10000,
+            step_increment: 1,
+            page_increment: 10,
+        });
+        refSpinnerWidthWebCam.configure(adjustmentWidth, 10, 0);
+        tmpS.bind(
+            Settings.WIDTH_WEBCAM_SETTING_KEY,
+            refSpinnerWidthWebCam,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements webcam heigth option
+        let refSpinnerHeightWebCam = gtkDB.get_object('spb_WebCamHeight');
+        let adjustmentHeight = new Gtk.Adjustment({
+            value: 10,
+            lower: 0,
+            upper: 10000,
+            step_increment: 1,
+            page_increment: 10,
+        });
+        refSpinnerHeightWebCam.configure(adjustmentHeight, 10, 0);
+        tmpS.bind(
+            Settings.HEIGHT_WEBCAM_SETTING_KEY,
+            refSpinnerHeightWebCam,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements webcam stack menu chooser
+        ctx.Ref_StackSwitcher_WebCam = gtkDB.get_object('sts_Webcam');
+        // implements webcam stack obj
+        ctx.Ref_StackObj_WebCam = gtkDB.get_object('stk_Webcam');
+        // implements webcam stack menu chooser
+        ctx.Ref_Label_WebCam = gtkDB.get_object('lbl_Webcam');
+        // implements webcam caps stack menu chooser
+        ctx.Ref_Label_WebCamCaps = gtkDB.get_object('lbl_WebcamCaps');
+    }
+
+    /**
+     * @param {EasyScreenCastSettingsWidget} ctx the prefs/settings widget
+     * @param {Gtk.Builder} gtkDB the builder that loaded the UI glade file
+     * @param {Gio.Settings} tmpS the current settings
+     * @private
+     */
+    _initTabFile(ctx, gtkDB, tmpS) {
+        // implements file name string rec option
+        let refTexteditFileName = gtkDB.get_object('txe_FileNameRec');
+        tmpS.bind(
+            Settings.FILE_NAME_SETTING_KEY,
+            refTexteditFileName,
+            'text',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements file container option
+        let refComboboxContainer = gtkDB.get_object('cbt_FileContainer');
+        tmpS.bind(
+            Settings.FILE_CONTAINER_SETTING_KEY,
+            refComboboxContainer,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements file container resolution
+        let refStackFileResolution = gtkDB.get_object('stk_FileResolution');
+
+        // implements file resolution preset spinner
+        let refComboboxResolution = gtkDB.get_object('cbt_FileResolution');
+        tmpS.bind(
+            Settings.FILE_RESOLUTION_TYPE_SETTING_KEY,
+            refComboboxResolution,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // intercept combobox res changed and update width/height value
+        refComboboxResolution.connect('changed', self => {
+            var activeRes = self.active;
+            Lib.TalkativeLog(`-^-preset combobox changed: ${activeRes}`);
+            if (activeRes >= 0 && activeRes < 15) {
+                var [h, w] = ctx._getResolutionPreset(activeRes);
+
+                // update width/height
+                this._settings.setOption(Settings.FILE_RESOLUTION_HEIGHT_SETTING_KEY, h);
+                this._settings.setOption(Settings.FILE_RESOLUTION_WIDTH_SETTING_KEY, w);
+                Lib.TalkativeLog(`-^-Res changed h: ${h} w: ${w}`);
+            }
+        });
+
+        // load file resolution pref and upadte UI
+        var tmpRes = this._settings.getOption('i', Settings.FILE_RESOLUTION_TYPE_SETTING_KEY);
+        if (tmpRes < 0)
+            refStackFileResolution.set_visible_child_name('native');
+        else if (tmpRes === 999)
+            refStackFileResolution.set_visible_child_name('custom');
+        else
+            refStackFileResolution.set_visible_child_name('preset');
+
+
+        // setup event on stack switcher
+        refStackFileResolution.connect('notify::visible-child-name', () => {
+            Lib.TalkativeLog('-^-stack_FR event grab');
+            var page = refStackFileResolution.get_visible_child_name();
+            Lib.TalkativeLog(`-^-active page -> ${page}`);
+
+            if (page === 'native') {
+                // set option to -1
+                this._settings.setOption(Settings.FILE_RESOLUTION_TYPE_SETTING_KEY, -1);
+            } else if (page === 'preset') {
+                // set option to fullHD 16:9
+                this._settings.setOption(Settings.FILE_RESOLUTION_TYPE_SETTING_KEY, 8);
+            } else if (page === 'custom') {
+                // set option to 99
+                this._settings.setOption(Settings.FILE_RESOLUTION_TYPE_SETTING_KEY, 999);
+            } else {
+                Lib.TalkativeLog('-^-page error');
+            }
+        });
+
+        // implements file width option
+        let refSpinnerWidthRes = gtkDB.get_object('spb_ResWidth');
+        let adjustmentResWidth = new Gtk.Adjustment({
+            value: 640,
+            lower: 640,
+            upper: 3840,
+            step_increment: 1,
+            page_increment: 100,
+        });
+        refSpinnerWidthRes.configure(adjustmentResWidth, 10, 0);
+        tmpS.bind(
+            Settings.FILE_RESOLUTION_WIDTH_SETTING_KEY,
+            refSpinnerWidthRes,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements file heigth option
+        let refSpinnerHeightRes = gtkDB.get_object('spb_ResHeight');
+        let adjustmentResHeight = new Gtk.Adjustment({
+            value: 480,
+            lower: 480,
+            upper: 2160,
+            step_increment: 1,
+            page_increment: 100,
+        });
+        refSpinnerHeightRes.configure(adjustmentResHeight, 10, 0);
+        tmpS.bind(
+            Settings.FILE_RESOLUTION_HEIGHT_SETTING_KEY,
+            refSpinnerHeightRes,
+            'value',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements keep aspect ratio check box
+        let refCheckboxKeepAspectRatio = gtkDB.get_object('chb_FileResolution_kar');
+        tmpS.bind(
+            Settings.FILE_RESOLUTION_KAR_SETTING_KEY,
+            refCheckboxKeepAspectRatio,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+
+        // implements resolution width scale option
+        let refScaleWidthRes = gtkDB.get_object('scl_ResWidth');
+        refScaleWidthRes.set_valign(Gtk.Align.START);
+        refScaleWidthRes.set_adjustment(adjustmentResWidth);
+        refScaleWidthRes.set_digits(0);
+        refScaleWidthRes.set_value(this._settings.getOption('i', Settings.FILE_RESOLUTION_WIDTH_SETTING_KEY));
+
+        // implements resolution height scale option
+        let refScaleHeightRes = gtkDB.get_object('scl_ResHeight');
+        refScaleHeightRes.set_valign(Gtk.Align.START);
+        refScaleHeightRes.set_adjustment(adjustmentResHeight);
+        refScaleHeightRes.set_digits(0);
+        refScaleHeightRes.set_value(this._settings.getOption('i', Settings.FILE_RESOLUTION_HEIGHT_SETTING_KEY));
+
+        // add marks on width/height file resolution
+        let ind = 0;
+        for (; ind < 13; ind++) {
+            var [h, w] = ctx._getResolutionPreset(ind);
+            refScaleWidthRes.add_mark(w, Gtk.PositionType.BOTTOM, '');
+            refScaleHeightRes.add_mark(h, Gtk.PositionType.BOTTOM, '');
+        }
+
+        // implements file folder string rec option
+        let refFilechooserFileFolder = gtkDB.get_object('fcb_FilePathRec');
+        // check state initial value
+        var tmpFolder = this._settings.getOption('s', Settings.FILE_FOLDER_SETTING_KEY);
+        Lib.TalkativeLog(`-^-folder for screencast: ${tmpFolder}`);
+        if (tmpFolder === '' || tmpFolder === null || tmpFolder === undefined) {
+            let result = null;
+            ctx.CtrlExe.Execute(
+                'xdg-user-dir VIDEOS',
+                true,
+                (success, out) => {
+                    Lib.TalkativeLog(`-^-CALLBACK sync S: ${success} out: ${out}`);
+                    if (success && out !== '' && out !== undefined)
+                        result = out.replace(/(\n)/g, '');
+                },
+                null
+            );
+
+            if (result !== null) {
+                Lib.TalkativeLog(`-^-xdg-user video: ${result}`);
+                tmpFolder = result;
+            } else {
+                Lib.TalkativeLog('-^-NOT SET xdg-user video');
+
+                ctx.CtrlExe.Execute(
+                    '/usr/bin/sh -c "echo $HOME"',
+                    true,
+                    (success, out) => {
+                        Lib.TalkativeLog(`-^-CALLBACK sync S: ${success} out: ${out}`);
+                        if (success && out !== '' && out !== undefined)
+                            tmpFolder = out.replace(/(\n)/g, '');
+                    },
+                    null
+                );
+            }
+
+            // connect keywebcam signal
+            this._settings._settings.connect(
+                `changed::${Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY}`,
+                () => {
+                    Lib.TalkativeLog('-^-webcam device changed');
+                    this._refreshWebcamOptions();
+                }
+            );
+        }
+
+        refFilechooserFileFolder.set_label(`Selected: ${tmpFolder}`);
+
+        refFilechooserFileFolder.connect('clicked', () => {
+            Lib.TalkativeLog('-^- file chooser button clicked...');
+
+            let dialog = new Gtk.FileChooserNative({
+                'title': 'Select folder',
+                'transient-for': refFilechooserFileFolder.get_root(),
+                'action': Gtk.FileChooserAction.SELECT_FOLDER,
+                'accept-label': 'Ok',
+                'cancel-label': 'Cancel',
+            });
+            dialog.connect('response', (self, response) => {
+                if (response === Gtk.ResponseType.ACCEPT) {
+                    var tmpPathFolder = self.get_file().get_path();
+                    Lib.TalkativeLog(`-^-file path get from widget : ${tmpPathFolder}`);
+                    this._settings.setOption(
+                        Settings.FILE_FOLDER_SETTING_KEY,
+                        tmpPathFolder
+                    );
+                    refFilechooserFileFolder.set_label(`Selected: ${tmpPathFolder}`);
+                }
+                ctx.fileChooserDialog = null;
+            });
+            dialog.show();
+            ctx.fileChooserDialog = dialog; // keep a reference to the dialog alive
+        });
+    }
+
+    /**
+     * @param {EasyScreenCastSettingsWidget} ctx the prefs/settings widget
+     * @param {Gtk.Builder} gtkDB the builder that loaded the UI glade file
+     * @param {Gio.Settings} tmpS the current settings
+     * @private
+     */
+    _initTabSupport(ctx, gtkDB, tmpS) {
+        // implements textentry log
+        let refTextViewEscLog = gtkDB.get_object('txe_ContainerLog');
+        let refBufferLog = refTextViewEscLog.get_buffer();
+
+        // implements verbose debug option
+        let refSwitchVerboseDebug = gtkDB.get_object('swt_VerboseDebug');
+        tmpS.bind(
+            Settings.VERBOSE_DEBUG_SETTING_KEY,
+            refSwitchVerboseDebug,
+            'active',
+            Gio.SettingsBindFlags.DEFAULT
+        );
+        this._settings._settings.connect(
+            `changed::${Settings.VERBOSE_DEBUG_SETTING_KEY}`,
+            () => {
+                Lib.setDebugEnabled(this._settings.getOption('b', Settings.VERBOSE_DEBUG_SETTING_KEY));
+            }
+        );
+
+        refSwitchVerboseDebug.connect('state-set', self => {
+            // update log display widgets
+            refTextViewEscLog.sensistive = self.active;
+            refComboboxLogChooser.sensitive = self.active;
+        });
+
+        // implements file resolution preset spinner
+        let refComboboxLogChooser = gtkDB.get_object('cbt_LogChooser');
+
+        // intercept combobox res changed and update width/height value
+        refComboboxLogChooser.connect('changed', self => {
+            const activeLog = self.active;
+            Lib.TalkativeLog(`-^-log combobox changed: ${activeLog}`);
+            switch (activeLog) {
+            case 0:
+                // clear buffer
+                refBufferLog.delete(
+                    refBufferLog.get_start_iter(),
+                    refBufferLog.get_end_iter()
+                );
+
+                ctx.CtrlExe.Execute(
+                    'journalctl --since "15 min ago" --output=cat --no-pager',
+                    false,
+                    success => {
+                        Lib.TalkativeLog(`-^-CALLBACK async S= ${success}`);
+                    },
+                    line => {
+                        let esc = line.indexOf('[ESC]');
+                        if (
+                            line !== '' &&
+                                line !== undefined &&
+                                esc !== -1
+                        ) {
+                            line += '\n';
+                            refBufferLog.insert(
+                                refBufferLog.get_end_iter(),
+                                line,
+                                line.length
+                            );
+                        }
+                    }
+                );
+                break;
+            case 1:
+                // clear buffer
+                refBufferLog.delete(
+                    refBufferLog.get_start_iter(),
+                    refBufferLog.get_end_iter()
+                );
+
+                ctx.CtrlExe.Execute(
+                    'journalctl --since "15 min ago" --output=cat --no-pager',
+                    false,
+                    success => {
+                        Lib.TalkativeLog(`-^-CALLBACK async S= ${success}`);
+                        if (success) {
+                            if (refBufferLog.get_line_count() > 0) {
+                                let strNOgsp = _(
+                                    'No Gstreamer pipeline found'
+                                );
+                                refBufferLog.insert(
+                                    refBufferLog.get_end_iter(),
+                                    strNOgsp,
+                                    strNOgsp.length
+                                );
+                            }
+                        }
+                    },
+                    line => {
+                        let esc = line.indexOf('-§-final GSP :');
+                        if (
+                            line !== '' &&
+                                line !== undefined &&
+                                esc !== -1
+                        ) {
+                            line += '\n';
+                            refBufferLog.insert(
+                                refBufferLog.get_end_iter(),
+                                line,
+                                line.length
+                            );
+                        }
+                    }
+                );
+                break;
+            case 2:
+                // clear buffer
+                refBufferLog.delete(
+                    refBufferLog.get_start_iter(),
+                    refBufferLog.get_end_iter()
+                );
+
+                ctx.CtrlExe.Execute(
+                    'journalctl /usr/bin/gnome-shell --since "15 min ago" --output=cat --no-pager',
+                    false,
+                    success => {
+                        Lib.TalkativeLog(`-^-CALLBACK async S= ${success}`);
+                    },
+                    line => {
+                        if (line !== '' && line !== undefined) {
+                            line += '\n';
+                            refBufferLog.insert(
+                                refBufferLog.get_end_iter(),
+                                line,
+                                line.length
+                            );
+                        }
+                    }
+                );
+                break;
+            default:
+                break;
+            }
+        });
+
+        // update state of get log
+        refComboboxLogChooser.sensistive = this._settings.getOption('b', Settings.VERBOSE_DEBUG_SETTING_KEY);
+
+        // implements default button action
+        let refButtonSetDefaultSettings = gtkDB.get_object('btn_DefaultOption');
+        refButtonSetDefaultSettings.connect('clicked', () =>
+            ctx._setDefaultsettings()
+        );
+    }
+
+    /**
+     * @param {EasyScreenCastSettingsWidget} ctx the prefs/settings widget
+     * @param {Gtk.Builder} gtkDB the builder that loaded the UI glade file
+     * @private
+     */
+    _initTabInfo(ctx, gtkDB) {
+        // implements info img extension
+        let refImageEsc = gtkDB.get_object('img_ESC');
+        refImageEsc.set_from_file(Lib.getImagePath(this._prefs.dir, 'Icon_Info.png'));
+
+        // implements info version label
+        let refLabelVersion = gtkDB.get_object('lbl_Version');
+        refLabelVersion.set_markup(`${_('Version: ')}<span color="blue">${this._prefs.metadata.version}</span> (${Lib.getFullVersion()})`);
+    }
+
+    /**
+     * @param {number} device device index
+     * @private
+     */
+    _updateWebCamCaps(device) {
+        Lib.TalkativeLog(`-^-webcam device index: ${device}`);
+
+        if (device > 0) {
+            this._initializeWebcamHelper();
+            var listCaps = this.CtrlWebcam.getListCapsDevice(device - 1);
+            Lib.TalkativeLog(`-^-webcam caps: ${listCaps.length}`);
+            if (listCaps !== null && listCaps !== undefined) {
+                this.Ref_ListStore_QualityWebCam.clear();
+                for (var index in listCaps) {
+                    this.Ref_ListStore_QualityWebCam.set(
+                        this.Ref_ListStore_QualityWebCam.append(),
+                        [0],
+                        [listCaps[index]]
+                    );
+                }
+            } else {
+                Lib.TalkativeLog('-^-NO List Caps Webcam');
+                this.Ref_ListStore_QualityWebCam.clear();
+                this._settings.setOption(Settings.QUALITY_WEBCAM_SETTING_KEY, '');
+            }
+        } else {
+            Lib.TalkativeLog('-^-NO Webcam recording');
+            this.Ref_ListStore_QualityWebCam.clear();
+            this._settings.setOption(Settings.QUALITY_WEBCAM_SETTING_KEY, '');
+        }
+    }
+
+    /**
+     * Refreshes the webcam settings.
+     *
+     * @private
+     */
+    _refreshWebcamOptions() {
+        Lib.TalkativeLog('-^-refresh webcam options');
+        this._initializeWebcamHelper();
+
+        // fill combobox with quality option webcam
+        this._updateWebCamCaps(this._settings.getOption('i', Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY));
+
+        // update webcam widget state
+        this._updateStateWebcamOptions();
+    }
+
+    /**
+     * Initializes this.CtrlWebcam if it is null.
+     *
+     * @private
+     */
+    _initializeWebcamHelper() {
+        if (this.CtrlWebcam === null)
+            this.CtrlWebcam = new UtilWebcam.HelperWebcam(_('Unspecified webcam'));
+    }
+
+    /**
+     * @param {string} accel accelerator string parsable by Gtk.accelerator_parse, e.g. "&lt;Super&gt;E"
+     * @private
+     */
+    _updateRowShortcut(accel) {
+        Lib.TalkativeLog(`-^-update row combo key accel: ${accel}`);
+
+        let [key, mods] = [0, 0];
+
+        if (accel !== null && accel !== undefined) {
+            let ok;
+            [ok, key, mods] = Gtk.accelerator_parse(accel);
+
+            if (ok !== true) {
+                Lib.TalkativeLog('-^-couldn\'t parse accel');
+                key = 0;
+                mods = 0;
+            }
+        }
+
+        Lib.TalkativeLog(`-^-key: ${key} mods: ${mods}`);
+        this.Ref_liststore_Shortcut.set(
+            this.Iter_ShortcutRow,
+            [Settings.SHORTCUT_COLUMN_KEY, Settings.SHORTCUT_COLUMN_MODS],
+            [key, mods]
+        );
+    }
+
+    /**
+     * @param {boolean} active custom or not custom GStream pipeline
+     * @private
+     */
+    _setStateGSP(active) {
+        // update GSP text area
+        if (!active) {
+            Lib.TalkativeLog('-^-custom GSP');
+
+            this.Ref_stack_Quality.set_visible_child_name('pg_Custom');
+        } else {
+            Lib.TalkativeLog('-^-NOT custom GSP');
+
+            this.Ref_stack_Quality.set_visible_child_name('pg_Preset');
+
+            var audio = false;
+            if (this._settings.getOption('i', Settings.INPUT_AUDIO_SOURCE_SETTING_KEY) > 0)
+                audio = true;
+
+            this._settings.setOption(
+                Settings.PIPELINE_REC_SETTING_KEY,
+                Settings.getGSPstd(audio)
+            );
+        }
+    }
+
+    /**
+     * @private
+     */
+    _updateStateWebcamOptions() {
+        Lib.TalkativeLog('-^-update webcam option widgets');
+
+        var tmpDev = this._settings.getOption(
+            'i',
+            Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY
+        );
+        this._updateWebCamCaps(tmpDev);
+        if (tmpDev > 0) {
+            var arrDev = this.CtrlWebcam.getNameDevices();
+            this.Ref_Label_WebCam.set_text(arrDev[tmpDev - 1]);
+
+            // setup label webcam caps
+            var tmpCaps = this._settings.getOption(
+                's',
+                Settings.QUALITY_WEBCAM_SETTING_KEY
+            );
+            if (tmpCaps === '') {
+                this.Ref_Label_WebCamCaps.use_markup = true;
+                this.Ref_Label_WebCamCaps.set_markup(
+                    _(
+                        '<span foreground="red">No Caps selected, please select one from the caps list</span>'
+                    )
+                );
+            } else {
+                this.Ref_Label_WebCamCaps.set_text(tmpCaps);
+            }
+
+            // webcam recording show widget
+            this.Ref_StackSwitcher_WebCam.set_sensitive(true);
+            this.Ref_StackObj_WebCam.set_sensitive(true);
+        } else {
+            this.Ref_Label_WebCam.set_text(_('No webcam device selected'));
+            // setup label webcam caps
+            this.Ref_Label_WebCamCaps.set_text(_('-'));
+            // webcam NOT recording hide widget
+            this.Ref_StackSwitcher_WebCam.set_sensitive(false);
+            this.Ref_StackObj_WebCam.set_sensitive(false);
+        }
+    }
+
+    /**
+     * @param {number} index index of the predefined resolutions
+     * @returns {Array}
+     * @private
+     */
+    _getResolutionPreset(index) {
+        var arrRes = [
+            [480, 640],
+            [480, 854],
+            [600, 800],
+            [720, 960],
+            [720, 1280],
+            [768, 1024],
+            [768, 1366],
+            [1024, 1280],
+            [1080, 1920],
+            [1200, 1600],
+            [1440, 2560],
+            [2048, 2560],
+            [2160, 3840],
+        ];
+        if (index >= 0 && index < arrRes.length)
+            return arrRes[index];
+        else
+            return null;
+    }
+
+    /**
+     * function to restore default value of the settings
+     *
+     * @private
+     */
+    _setDefaultsettings() {
+        Lib.TalkativeLog('-^-restore default option');
+
+        this._settings.setOption(Settings.SHOW_NOTIFY_ALERT_SETTING_KEY, true);
+        this._settings.setOption(Settings.SHOW_AREA_REC_SETTING_KEY, false);
+        this._settings.setOption(Settings.STATUS_INDICATORS_SETTING_KEY, 1);
+        this._settings.setOption(Settings.DRAW_CURSOR_SETTING_KEY, true);
+        this._settings.setOption(Settings.VERBOSE_DEBUG_SETTING_KEY, false);
+        this._settings.setOption(Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY, false);
+
+        this._settings.setOption(Settings.FPS_SETTING_KEY, 30);
+        this._settings.setOption(Settings.X_POS_SETTING_KEY, 0);
+        this._settings.setOption(Settings.Y_POS_SETTING_KEY, 0);
+        this._settings.setOption(Settings.WIDTH_SETTING_KEY, 600);
+        this._settings.setOption(Settings.HEIGHT_SETTING_KEY, 400);
+
+        this._settings.setOption(Settings.FILE_NAME_SETTING_KEY, 'Screencast_%d_%t');
+        this._settings.setOption(Settings.FILE_FOLDER_SETTING_KEY, '');
+        this._settings.setOption(Settings.ACTIVE_POST_CMD_SETTING_KEY, false);
+        this._settings.setOption(Settings.ACTIVE_PRE_CMD_SETTING_KEY, false);
+        this._settings.setOption(Settings.POST_CMD_SETTING_KEY, 'xdg-open _fpath &');
+        this._settings.setOption(Settings.INPUT_AUDIO_SOURCE_SETTING_KEY, 0);
+        this._settings.setOption(Settings.DEVICE_INDEX_WEBCAM_SETTING_KEY, 0);
+        this._settings.setOption(Settings.DEVICE_WEBCAM_SETTING_KEY, '');
+
+        this._settings.setOption(Settings.TIME_DELAY_SETTING_KEY, 0);
+        this._settings.setOption(Settings.FILE_CONTAINER_SETTING_KEY, 0);
+        this._settings.setOption(Settings.FILE_RESOLUTION_TYPE_SETTING_KEY, -1);
+        this._settings.setOption(Settings.QUALITY_SETTING_KEY, 1);
+        this._settings.setOption(Settings.QUALITY_WEBCAM_SETTING_KEY, '');
+        this._settings.setOption(Settings.WIDTH_WEBCAM_SETTING_KEY, 20);
+        this._settings.setOption(Settings.HEIGHT_WEBCAM_SETTING_KEY, 10);
+        this._settings.setOption(Settings.TYPE_UNIT_WEBCAM_SETTING_KEY, 0);
+        this._settings.setOption(Settings.MARGIN_X_WEBCAM_SETTING_KEY, 0);
+        this._settings.setOption(Settings.MARGIN_Y_WEBCAM_SETTING_KEY, 0);
+        this._settings.setOption(Settings.ALPHA_CHANNEL_WEBCAM_SETTING_KEY, 0.75);
+        this._settings.setOption(Settings.CORNER_POSITION_WEBCAM_SETTING_KEY, 0);
+    }
+});
+
+
+export default class EasyScreenCastPreferences extends ExtensionPreferences {
+    /**
+     * @param {Adw.PreferencesWindow} window preferences window?
+     * @returns {EasyScreenCastSettingsWidget}
+     */
+    fillPreferencesWindow(window) {
+        window._settings = this.getSettings();
+
+        Lib.TalkativeLog('-^-Init pref widget');
+
+        const page = new Adw.PreferencesPage();
+
+        const group = new Adw.PreferencesGroup({
+            title: _('EasyScreenCast'),
+        });
+        page.add(group);
+
+        const widget = new EasyScreenCastSettingsWidget(this);
+        group.add(widget);
+
+        window.add(page);
+    }
+}

BIN
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/schemas/gschemas.compiled


+ 200 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/schemas/org.gnome.shell.extensions.easyscreencast.gschema.xml

@@ -0,0 +1,200 @@
+<schemalist>
+    <schema id="org.gnome.shell.extensions.EasyScreenCast" path="/org/gnome/shell/extensions/EasyScreenCast/">
+        <key name="show-notify-alert" type="b">
+            <default>true</default>
+            <summary>Enable show notify and alert message</summary>
+            <description>Enable show notify and alert message</description>
+        </key>
+        <key name="show-area-rec" type="b">
+            <default>false</default>
+            <summary>Enable show area screencast during recording</summary>
+            <description>Enable show area screencast during recording</description>
+        </key>
+        <key name="input-audio-source" type="i">
+            <default>0</default>
+            <summary>select input audio source</summary>
+            <description>select input audio source</description>
+        </key>
+        <key name="active-custom-gsp" type="b">
+            <default>false</default>
+            <summary>Active custom GStreamer Pipeline</summary>
+            <description>Active custom GStreamer Pipeline</description>
+        </key>
+        <key name="delay-time" type="i">
+            <default>0</default>
+            <summary>Time in seconds fot delay recording</summary>
+            <description>Time in seconds fot delay recording</description>
+        </key>
+        <key name="verbose-debug" type="b">
+            <default>false</default>
+            <summary>Enable verbose debug</summary>
+            <description>Enable verbose debug</description>
+        </key>
+        <key name="x-pos" type="i">
+            <default>0</default>
+            <summary>X position of screencasting</summary>
+            <description>X position of screencasting</description>
+        </key>
+        <key name="y-pos" type="i">
+            <default>0</default>
+            <summary>Y position of screencasting</summary>
+            <description>Y position of screencasting</description>
+        </key>
+        <key name="width-rec" type="i">
+            <default>600</default>
+            <summary>width of screencasting</summary>
+            <description>width of screencasting</description>
+        </key>
+        <key name="height-rec" type="i">
+            <default>400</default>
+            <summary>height of screencasting</summary>
+            <description>height of screencasting</description>
+        </key>
+        <key name="status-indicators" type="i">
+            <default>1</default>
+            <summary>active status indicators</summary>
+            <description>active status indicators</description>
+        </key>
+        <key name="draw-cursor" type="b">
+            <default>true</default>
+            <summary>draw the cursor into screencast</summary>
+            <description>draw the cursor into screencast</description>
+        </key>
+        <key name="fps" type="i">
+            <default>30</default>
+            <summary>fps of screencasting</summary>
+            <description>fps of screencasting</description>
+        </key>
+        <key name="pipeline" type="s">
+            <default>'vp9enc min_quantizer=13 max_quantizer=13 cpu-used=5 deadline=1000000 threads=%T ! queue ! webmmux'</default>
+            <summary>gstreamer pipeline of screencasting</summary>
+            <description>gstreamer pipeline of screencasting</description>
+        </key>
+        <key name="area-screen" type="i">
+            <default>0</default>
+            <summary>select area of screencast</summary>
+            <description>select area of screencast</description>
+        </key>
+        <key name="file-name" type="s">
+            <default>'Screencast_%d_%t'</default>
+            <summary>file name template of screencast</summary>
+            <description>file name template of screencast</description>
+        </key>
+        <key name="file-folder" type="s">
+            <default>''</default>
+            <summary>file folder of screencast</summary>
+            <description>file folder of screencast</description>
+        </key>
+	<key name="execute-pre-cmd" type="b">
+            <default>false</default>
+            <summary>enable execute pre command</summary>
+            <description>enable execute pre command</description>
+        </key>
+        <key name="pre-cmd" type="s">
+            <default>''</default>
+            <summary>command to execute</summary>
+            <description>command to execute</description>
+        </key>
+        <key name="execute-post-cmd" type="b">
+            <default>false</default>
+            <summary>enable execute post command</summary>
+            <description>enable execute post command</description>
+        </key>
+        <key name="post-cmd" type="s">
+            <default>'xdg-open _fpath'</default>
+            <summary>command to execute</summary>
+            <description>command to execute</description>
+        </key>
+        <key name="shortcut-key" type="as">
+            <default>
+                <![CDATA[['<Super>E']]]>
+            </default>
+        </key>
+        <key name="active-shortcut" type="b">
+            <default>false</default>
+            <summary>Enable keyboard shortcut</summary>
+            <description>Enable keyboard shortcut</description>
+        </key>
+        <key name="file-container" type="i">
+            <default>0</default>
+            <summary>file screencast container</summary>
+            <description>file screencast container</description>
+        </key>
+	<key name="file-resolution-type" type="i">
+            <default>-1</default>
+            <summary>file screencast resolution type</summary>
+            <description>file screencast resolution type [native(-1),preset(0-99),custom(99)]</description>
+        </key>
+	<key name="file-resolution-kar" type="b">
+            <default>true</default>
+            <summary>file screencast resolution Keep Aspect Ratio</summary>
+            <description>file screencast resolution Keep Aspect Ratio (add borders)</description>
+        </key>
+	<key name="file-resolution-width" type="i">
+            <default>0</default>
+            <summary>file screencast resolution width</summary>
+            <description>file screencast resolution width</description>
+        </key>
+	<key name="file-resolution-height" type="i">
+            <default>0</default>
+            <summary>file screencast resolution height</summary>
+            <description>file screencast resolution height</description>
+        </key>
+        <key name="quality-index" type="i">
+            <default>1</default>
+            <summary>store the quality index option</summary>
+            <description>store the quality index option</description>
+        </key>
+        <key name="device-webcam-index" type="i">
+            <default>0</default>
+            <summary>select webcam device</summary>
+            <description>select webcam device</description>
+        </key>
+        <key name="device-webcam" type="s">
+            <default>""</default>
+            <summary>selected webcam device path</summary>
+            <description>selected webcam device path</description>
+        </key>
+        <key name="quality-webcam" type="s">
+            <default>""</default>
+            <summary>select webcam quality</summary>
+            <description>select webcam quality</description>
+        </key>
+        <key name="width-webcam" type="i">
+            <default>20</default>
+            <summary>width webcam</summary>
+            <description>width webcam</description>
+        </key>
+        <key name="height-webcam" type="i">
+            <default>10</default>
+            <summary>height webcam</summary>
+            <description>height webcam</description>
+        </key>
+        <key name="type-unit-webcam" type="i">
+            <default>0</default>
+            <summary>type of unit of measure webcam</summary>
+            <description>type of unit of measure</description>
+        </key>
+        <key name="margin-x-webcam" type="i">
+            <default>0</default>
+            <summary>margin x from border webcam</summary>
+            <description>margin x webcam</description>
+        </key>
+        <key name="margin-y-webcam" type="i">
+            <default>0</default>
+            <summary>margin y from border webcam</summary>
+            <description>margin y from border webcam</description>
+        </key>
+        <key name="alpha-channel-webcam" type="d">
+            <default>0.75</default>
+            <range min="0.05" max="1.00"/>
+            <summary>alpha channel webcam</summary>
+            <description>aplha channel webcam</description>
+        </key>
+        <key name="corner-position-webcam" type="i">
+            <default>0</default>
+            <summary>the corner position of webcam</summary>
+            <description>the corner position of webcam</description>
+        </key>
+    </schema>
+</schemalist>

+ 534 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/selection.js

@@ -0,0 +1,534 @@
+/*
+The MIT License (MIT)
+Copyright (c) 2013 otto.allmendinger@gmail.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import Meta from 'gi://Meta';
+import Clutter from 'gi://Clutter';
+import St from 'gi://St';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import * as Lib from './convenience.js';
+import * as Ext from './extension.js';
+import * as UtilNotify from './utilnotify.js';
+import {DisplayApi} from './display_module.js';
+
+/**
+ * @type {Capture}
+ */
+const Capture = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_Capture',
+    Signals: {
+        'captured-event': {
+            param_types: [Clutter.Event.$gtype],
+        },
+        'stop': {},
+    },
+}, class Capture extends GObject.Object {
+    constructor() {
+        super();
+        Lib.TalkativeLog('-£-capture selection init');
+
+        this._mouseDown = false;
+
+        this.monitor = Main.layoutManager.focusMonitor;
+
+        this._areaSelection = new St.Widget({
+            name: 'area-selection',
+            style_class: 'area-selection',
+            visible: 'true',
+            reactive: 'true',
+            x: -10,
+            y: -10,
+        });
+
+        Main.uiGroup.add_child(this._areaSelection);
+
+        this._areaResolution = new St.Label({
+            style_class: 'area-resolution',
+            text: '',
+        });
+        this._areaResolution.opacity = 255;
+        this._areaResolution.set_position(0, 0);
+
+        Main.uiGroup.add_child(this._areaResolution);
+
+        this._grab = Main.pushModal(this._areaSelection);
+
+        if (this._grab) {
+            this._signalCapturedEvent = this._areaSelection.connect(
+                'captured-event',
+                this._onCaptureEvent.bind(this)
+            );
+
+            this._setCaptureCursor();
+        } else {
+            Lib.TalkativeLog('-£-Main.pushModal() === false');
+        }
+    }
+
+    /**
+     * @private
+     */
+    _setDefaultCursor() {
+        DisplayApi.set_cursor(Meta.Cursor.DEFAULT);
+    }
+
+    /**
+     * @private
+     */
+    _setCaptureCursor() {
+        DisplayApi.set_cursor(Meta.Cursor.CROSSHAIR);
+    }
+
+    /**
+     * @param {Clutter.Actor} actor the actor that received the event
+     * @param {Clutter.Event} event a Clutter.Event
+     * @private
+     */
+    _onCaptureEvent(actor, event) {
+        if (event.type() === Clutter.EventType.KEY_PRESS) {
+            if (event.get_key_symbol() === Clutter.KEY_Escape)
+                this._stop();
+        }
+
+        this.emit('captured-event', event);
+    }
+
+    /**
+     * Draws a on-screen rectangle showing the area that will be captured by screen cast.
+     *
+     * @param {object} rect rectangle
+     * @param {number} rect.x left position in pixels
+     * @param {number} rect.y top position in pixels
+     * @param {number} rect.w width in pixels
+     * @param {number} rect.h height in pixels
+     * @param {boolean} showResolution whether to display the size of the selected area
+     */
+    drawSelection({x, y, w, h}, showResolution) {
+        this._areaSelection.set_position(x, y);
+        this._areaSelection.set_size(w, h);
+
+        if (showResolution && w > 100 && h > 50) {
+            this._areaResolution.set_text(`${w} X ${h}`);
+            this._areaResolution.set_position(
+                x + (w / 2 - this._areaResolution.width / 2),
+                y + (h / 2 - this._areaResolution.height / 2)
+            );
+        } else {
+            this._areaResolution.set_position(0, 0);
+            this._areaResolution.set_text('');
+        }
+    }
+
+    /**
+     * Clear drawing selection
+     */
+    clearSelection() {
+        this.drawSelection(
+            {
+                x: -10,
+                y: -10,
+                w: 0,
+                h: 0,
+            },
+            false
+        );
+    }
+
+    /**
+     * @private
+     */
+    _stop() {
+        Lib.TalkativeLog('-£-capture selection stop');
+
+        this._areaSelection.disconnect(this._signalCapturedEvent);
+        this._setDefaultCursor();
+        Main.uiGroup.remove_child(this._areaSelection);
+        Main.popModal(this._grab);
+        Main.uiGroup.remove_child(this._areaResolution);
+        this._areaSelection.destroy();
+        this.emit('stop');
+    }
+
+    _saveRect(x, y, h, w) {
+        Lib.TalkativeLog(`-£-selection x:${x} y:${y} height:${h} width:${w}`);
+
+        Ext.Indicator.saveSelectedRect(x, y, h, w);
+        Ext.Indicator._doDelayAction();
+    }
+
+    toString() {
+        return this.GTypeName;
+    }
+});
+
+var SelectionArea = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_SelectionArea',
+    Signals: {
+        'stop': {},
+    },
+}, class SelectionArea extends GObject.Object {
+    constructor() {
+        super();
+        Lib.TalkativeLog('-£-area selection init');
+
+        this._mouseDown = false;
+        this._capture = new Capture();
+        this._capture.connect('captured-event', this._onEvent.bind(this));
+        this._capture.connect('stop', () => {
+            this._ctrlNotify.resetAlert();
+            this.emit('stop');
+        });
+
+        this._ctrlNotify = new UtilNotify.NotifyManager();
+        this._ctrlNotify.createAlert(
+            _('Select an area for recording or press [ESC] to abort')
+        );
+    }
+
+    /**
+     * @param {Clutter.actor} capture the actor the captured the event
+     * @param {Clutter.Event} event a Clutter.Event
+     * @private
+     */
+    _onEvent(capture, event) {
+        let type = event.type();
+        let [x, y] = global.get_pointer();
+
+        if (type === Clutter.EventType.BUTTON_PRESS) {
+            [this._startX, this._startY] = [x, y];
+            this._mouseDown = true;
+        } else if (this._mouseDown) {
+            let rect = _getRectangle(this._startX, this._startY, x, y);
+            if (type === Clutter.EventType.MOTION) {
+                this._capture.drawSelection(rect, true);
+            } else if (type === Clutter.EventType.BUTTON_RELEASE) {
+                this._capture._stop();
+
+                Lib.TalkativeLog(`-£-area x: ${rect.x} y: ${rect.y} height: ${rect.h} width: ${rect.w}`);
+
+                this._capture._saveRect(rect.x, rect.y, rect.h, rect.w);
+            }
+        }
+    }
+
+    toString() {
+        return this.GTypeName;
+    }
+});
+
+var SelectionWindow = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_SelectionWindow',
+    Signals: {
+        'stop': {},
+    },
+}, class SelectionWindow extends GObject.Object {
+    constructor() {
+        super();
+        Lib.TalkativeLog('-£-window selection init');
+
+        this._windows = global.get_window_actors();
+        this._capture = new Capture();
+        this._capture.connect('captured-event', this._onEvent.bind(this));
+        this._capture.connect('stop', () => {
+            this._ctrlNotify.resetAlert();
+            this.emit('stop');
+        });
+
+        this._ctrlNotify = new UtilNotify.NotifyManager();
+        this._ctrlNotify.createAlert(
+            _('Select a window for recording or press [ESC] to abort')
+        );
+    }
+
+    /**
+     * @param {Clutter.Actor} capture the actor the captured the event
+     * @param {Clutter.Event} event a Clutter.Event
+     * @private
+     */
+    _onEvent(capture, event) {
+        let type = event.type();
+        let [x, y] = global.get_pointer();
+
+        this._selectedWindow = _selectWindow(this._windows, x, y);
+
+        if (this._selectedWindow)
+            this._highlightWindow(this._selectedWindow);
+        else
+            this._clearHighlight();
+
+
+        if (type === Clutter.EventType.BUTTON_PRESS) {
+            if (this._selectedWindow) {
+                this._capture._stop();
+
+                var maxHeight = global.screen_height;
+                var maxWidth = global.screen_width;
+                Lib.TalkativeLog(`-£-global screen area H: ${maxHeight} W: ${maxWidth}`);
+
+                var [w, h] = this._selectedWindow.get_size();
+                var [wx, wy] = this._selectedWindow.get_position();
+
+                Lib.TalkativeLog(`-£-windows pre wx: ${wx} wy: ${wy} height: ${h}  width: ${w}`);
+
+                if (wx < 0)
+                    wx = 0;
+                if (wy < 0)
+                    wy = 0;
+                if (wx + w > maxWidth)
+                    w = maxWidth - wx;
+                if (wy + h > maxHeight)
+                    h = maxHeight - wy;
+
+                Lib.TalkativeLog(`-£-windows post wx: ${wx} wy: ${wy} height: ${h} width: ${w}`);
+
+                this._capture._saveRect(wx, wy, h, w);
+            }
+        }
+    }
+
+    /**
+     * @param {Clutter.Actor} win the window to highlight
+     * @private
+     */
+    _highlightWindow(win) {
+        let rect = _getWindowRectangle(win);
+        Lib.TalkativeLog(`-£-window highlight on, pos/meas: x:${rect.x} y:${rect.y} w:${rect.w} h:${rect.h}`);
+        this._capture.drawSelection(rect, false);
+    }
+
+    /**
+     * @private
+     */
+    _clearHighlight() {
+        Lib.TalkativeLog('-£-window highlight off');
+        this._capture.clearSelection();
+    }
+
+    toString() {
+        return this.GTypeName;
+    }
+});
+
+var SelectionDesktop = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_SelectionDesktop',
+    Signals: {
+        'stop': {},
+    },
+}, class SelectionDesktop extends GObject.Object {
+    constructor() {
+        super();
+        Lib.TalkativeLog('-£-desktop selection init');
+        const displayCount = DisplayApi.number_displays();
+        Lib.TalkativeLog(`-£-Number of monitor ${displayCount}`);
+
+        for (var i = 0; i < displayCount; i++) {
+            var tmpM = DisplayApi.display_geometry_for_index(i);
+            Lib.TalkativeLog(`-£-monitor ${i} geometry x=${tmpM.x} y=${tmpM.y} w=${tmpM.width} h=${tmpM.height}`);
+        }
+
+        this._capture = new Capture();
+        this._capture.connect('captured-event', this._onEvent.bind(this));
+        this._capture.connect('stop', () => {
+            this._ctrlNotify.resetAlert();
+            this.emit('stop');
+        });
+
+        this._ctrlNotify = new UtilNotify.NotifyManager();
+        this._ctrlNotify.createAlert(
+            _('Select a desktop for recording or press [ESC] to abort')
+        );
+    }
+
+    /**
+     * @param {Clutter.Actor} capture the actor that captured the event
+     * @param {Clutter.Event} event a Clutter.Event
+     * @private
+     */
+    _onEvent(capture, event) {
+        let type = event.type();
+
+        if (type === Clutter.EventType.BUTTON_PRESS) {
+            this._capture._stop();
+
+            let tmpM = Main.layoutManager.currentMonitor;
+
+            var x = tmpM.x;
+            var y = tmpM.y;
+            var height = tmpM.height;
+            var width = tmpM.width;
+            Lib.TalkativeLog(`-£-desktop x: ${x} y: ${y} height: ${height} width: ${width}`);
+
+            this._capture._saveRect(x, y, height, width);
+        }
+    }
+
+    toString() {
+        return this.GTypeName;
+    }
+});
+
+var AreaRecording = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_AreaRecording',
+}, class AreaRecording extends GObject.Object {
+    constructor() {
+        super();
+        Lib.TalkativeLog('-£-area recording init');
+
+        this._areaRecording = new St.Widget({
+            name: 'area-recording',
+            style_class: 'area-recording',
+            visible: 'true',
+            reactive: 'false',
+            x: -10,
+            y: -10,
+        });
+
+        var [recX, recY, recW, recH] = Ext.Indicator.getSelectedRect();
+        var tmpH = Main.layoutManager.currentMonitor.height;
+        var tmpW = Main.layoutManager.currentMonitor.width;
+
+        Main.uiGroup.add_child(this._areaRecording);
+
+        Main.overview.connect('showing', () => {
+            Lib.TalkativeLog('-£-overview opening');
+
+            Main.uiGroup.remove_child(this._areaRecording);
+        });
+
+        Main.overview.connect('hidden', () => {
+            Lib.TalkativeLog('-£-overview closed');
+
+            Main.uiGroup.add_child(this._areaRecording);
+        });
+
+        if (recX + recW <= tmpW - 5 && recY + recH <= tmpH - 5)
+            this.drawArea(recX - 2, recY - 2, recW + 4, recH + 4);
+    }
+
+    /**
+     * @param {number} x left position
+     * @param {number} y top position
+     * @param {number} w width
+     * @param {number} h height
+     */
+    drawArea(x, y, w, h) {
+        Lib.TalkativeLog('-£-draw area recording');
+
+        this._visible = true;
+        this._areaRecording.set_position(x, y);
+        this._areaRecording.set_size(w, h);
+    }
+
+    /**
+     * Clears the drawing area
+     */
+    clearArea() {
+        Lib.TalkativeLog('-£-hide area recording');
+
+        this._visible = false;
+        this.drawArea(-10, -10, 0, 0);
+    }
+
+    /**
+     * @returns {boolean}
+     */
+    isVisible() {
+        return this._visible;
+    }
+
+    toString() {
+        return this.GTypeName;
+    }
+});
+
+/**
+ * @param {number} x1 left position
+ * @param {number} y1 top position
+ * @param {number} x2 right position
+ * @param {number} y2 bottom position
+ * @returns {{x: number, y: number, w: number, h: number}}
+ */
+function _getRectangle(x1, y1, x2, y2) {
+    return {
+        x: Math.min(x1, x2),
+        y: Math.min(y1, y2),
+        w: Math.abs(x1 - x2),
+        h: Math.abs(y1 - y2),
+    };
+}
+
+/**
+ * @param {Clutter.Actor} win a Clutter.Actor
+ * @returns {{x: number, y: number, w: number, h: number}}
+ */
+function _getWindowRectangle(win) {
+    let [tw, th] = win.get_size();
+    let [tx, ty] = win.get_position();
+
+    return {
+        x: tx,
+        y: ty,
+        w: tw,
+        h: th,
+    };
+}
+
+/**
+ * @param {Array(Clutter.Actor)} windows all windows on the display
+ * @param {number} x left position
+ * @param {number} y top position
+ * @returns {Clutter.Actor}
+ */
+function _selectWindow(windows, x, y) {
+    let filtered = windows.filter(win => {
+        if (
+            win !== undefined &&
+            win.visible &&
+            typeof win.get_meta_window === 'function'
+        ) {
+            Lib.TalkativeLog(`-£-selectWin x:${x} y:${y}`);
+
+            let [w, h] = win.get_size();
+            let [wx, wy] = win.get_position();
+            Lib.TalkativeLog(`-£-selectWin w:${w} h:${h} wx:${wx} wy:${wy}`);
+
+            return wx <= x && wy <= y && wx + w >= x && wy + h >= y;
+        } else {
+            return false;
+        }
+    });
+
+    filtered.sort((a, b) => {
+        return (
+            a.get_meta_window().get_layer() <= b.get_meta_window().get_layer()
+        );
+    });
+
+    return filtered[0];
+}
+
+export {SelectionArea, SelectionWindow, SelectionDesktop, AreaRecording};

+ 192 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/settings.js

@@ -0,0 +1,192 @@
+/*
+    Copyright (C) 2013  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+
+// setting keys
+var INPUT_AUDIO_SOURCE_SETTING_KEY = 'input-audio-source';
+var ACTIVE_POST_CMD_SETTING_KEY = 'execute-post-cmd';
+var POST_CMD_SETTING_KEY = 'post-cmd';
+var ACTIVE_PRE_CMD_SETTING_KEY = 'execute-pre-cmd';
+var PRE_CMD_SETTING_KEY = 'pre-cmd';
+var ACTIVE_CUSTOM_GSP_SETTING_KEY = 'active-custom-gsp';
+var ACTIVE_SHORTCUT_SETTING_KEY = 'active-shortcut';
+var SHORTCUT_KEY_SETTING_KEY = 'shortcut-key';
+var TIME_DELAY_SETTING_KEY = 'delay-time';
+var SHOW_NOTIFY_ALERT_SETTING_KEY = 'show-notify-alert';
+var SHOW_AREA_REC_SETTING_KEY = 'show-area-rec';
+var VERBOSE_DEBUG_SETTING_KEY = 'verbose-debug';
+var PIPELINE_REC_SETTING_KEY = 'pipeline';
+var FPS_SETTING_KEY = 'fps';
+var STATUS_INDICATORS_SETTING_KEY = 'status-indicators';
+var X_POS_SETTING_KEY = 'x-pos';
+var Y_POS_SETTING_KEY = 'y-pos';
+var WIDTH_SETTING_KEY = 'width-rec';
+var HEIGHT_SETTING_KEY = 'height-rec';
+var DRAW_CURSOR_SETTING_KEY = 'draw-cursor';
+var AREA_SCREEN_SETTING_KEY = 'area-screen';
+var FILE_NAME_SETTING_KEY = 'file-name';
+var FILE_FOLDER_SETTING_KEY = 'file-folder';
+var FILE_CONTAINER_SETTING_KEY = 'file-container';
+var FILE_RESOLUTION_TYPE_SETTING_KEY = 'file-resolution-type';
+var FILE_RESOLUTION_KAR_SETTING_KEY = 'file-resolution-kar';
+var FILE_RESOLUTION_WIDTH_SETTING_KEY = 'file-resolution-width';
+var FILE_RESOLUTION_HEIGHT_SETTING_KEY = 'file-resolution-height';
+var QUALITY_SETTING_KEY = 'quality-index';
+var DEVICE_INDEX_WEBCAM_SETTING_KEY = 'device-webcam-index';
+var DEVICE_WEBCAM_SETTING_KEY = 'device-webcam';
+var QUALITY_WEBCAM_SETTING_KEY = 'quality-webcam';
+var WIDTH_WEBCAM_SETTING_KEY = 'width-webcam';
+var HEIGHT_WEBCAM_SETTING_KEY = 'height-webcam';
+var TYPE_UNIT_WEBCAM_SETTING_KEY = 'type-unit-webcam';
+var MARGIN_X_WEBCAM_SETTING_KEY = 'margin-x-webcam';
+var MARGIN_Y_WEBCAM_SETTING_KEY = 'margin-y-webcam';
+var ALPHA_CHANNEL_WEBCAM_SETTING_KEY = 'alpha-channel-webcam';
+var CORNER_POSITION_WEBCAM_SETTING_KEY = 'corner-position-webcam';
+
+// shortcut tree view columns
+var SHORTCUT_COLUMN_KEY = 0;
+var SHORTCUT_COLUMN_MODS = 1;
+
+var Settings = GObject.registerClass(class EasyScreenCastSettings extends GObject.Object {
+    constructor(settings) {
+        super();
+        this._settings = settings;
+    }
+
+    /**
+     * getter option
+     *
+     * @param {string} type value type of the option. one of 'b', 'i', 's', 'd', 'as'
+     * @param {string} key option key
+     * @returns {string}
+     */
+    getOption(type, key) {
+        switch (type) {
+        case 'b':
+            return this._settings.get_boolean(key);
+        case 'i':
+            return this._settings.get_int(key);
+        case 's':
+            return this._settings.get_string(key);
+        case 'd':
+            return this._settings.get_double(key);
+        case 'as':
+            return this._settings.get_strv(key);
+        }
+
+        return '';
+    }
+
+    /**
+     * setter option
+     *
+     * @param {string} key option key
+     * @param {boolean|number|string|double|object} option option value
+     * @returns {string} empty string if successful, 'ERROR' otherwise
+     */
+    setOption(key, option) {
+        switch (typeof option) {
+        case 'boolean':
+            this._settings.set_boolean(key, option);
+            break;
+
+        case 'number':
+            this._settings.set_int(key, option);
+            break;
+
+        case 'string':
+            this._settings.set_string(key, option);
+            break;
+
+        case 'double':
+            this._settings.set_double(key, option);
+            break;
+
+        case 'object':
+            this._settings.set_strv(key, option);
+            break;
+
+        default:
+            return 'ERROR';
+        }
+        return '';
+    }
+
+    destroy() {
+        if (this._settings)
+            this._settings = null;
+    }
+});
+
+
+/**
+ * get a standard gsp pipeline
+ *
+ * @param {boolean} audio with or without audio
+ * @returns {string}
+ */
+function getGSPstd(audio) {
+    // TODO update gsp
+    if (audio)
+        return 'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videorate ! vp8enc min_quantizer=0 max_quantizer=5 cpu-used=3 deadline=1000000 threads=%T ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. pulsesrc ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! audioconvert ! vorbisenc ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. webmmux name=mux ';
+    else
+        return 'vp9enc min_quantizer=0 max_quantizer=5 cpu-used=3 deadline=1000000 threads=%T ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! webmmux';
+}
+
+export {
+    getGSPstd,
+    Settings,
+    INPUT_AUDIO_SOURCE_SETTING_KEY,
+    ACTIVE_POST_CMD_SETTING_KEY,
+    POST_CMD_SETTING_KEY,
+    ACTIVE_PRE_CMD_SETTING_KEY,
+    PRE_CMD_SETTING_KEY,
+    ACTIVE_CUSTOM_GSP_SETTING_KEY,
+    ACTIVE_SHORTCUT_SETTING_KEY,
+    SHORTCUT_KEY_SETTING_KEY,
+    TIME_DELAY_SETTING_KEY,
+    SHOW_NOTIFY_ALERT_SETTING_KEY,
+    SHOW_AREA_REC_SETTING_KEY,
+    VERBOSE_DEBUG_SETTING_KEY,
+    PIPELINE_REC_SETTING_KEY,
+    FPS_SETTING_KEY,
+    STATUS_INDICATORS_SETTING_KEY,
+    X_POS_SETTING_KEY,
+    Y_POS_SETTING_KEY,
+    WIDTH_SETTING_KEY,
+    HEIGHT_SETTING_KEY,
+    DRAW_CURSOR_SETTING_KEY,
+    AREA_SCREEN_SETTING_KEY,
+    FILE_NAME_SETTING_KEY,
+    FILE_FOLDER_SETTING_KEY,
+    FILE_CONTAINER_SETTING_KEY,
+    FILE_RESOLUTION_TYPE_SETTING_KEY,
+    FILE_RESOLUTION_KAR_SETTING_KEY,
+    FILE_RESOLUTION_WIDTH_SETTING_KEY,
+    FILE_RESOLUTION_HEIGHT_SETTING_KEY,
+    QUALITY_SETTING_KEY,
+    DEVICE_INDEX_WEBCAM_SETTING_KEY,
+    DEVICE_WEBCAM_SETTING_KEY,
+    QUALITY_WEBCAM_SETTING_KEY,
+    WIDTH_WEBCAM_SETTING_KEY,
+    HEIGHT_WEBCAM_SETTING_KEY,
+    TYPE_UNIT_WEBCAM_SETTING_KEY,
+    MARGIN_X_WEBCAM_SETTING_KEY,
+    MARGIN_Y_WEBCAM_SETTING_KEY,
+    ALPHA_CHANNEL_WEBCAM_SETTING_KEY,
+    CORNER_POSITION_WEBCAM_SETTING_KEY,
+    SHORTCUT_COLUMN_KEY,
+    SHORTCUT_COLUMN_MODS
+};

+ 36 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/stylesheet.css

@@ -0,0 +1,36 @@
+.RecordAction-label {
+    font-size: 15px;
+    font-weight: bold;
+}
+
+.area-selection {
+    background-color: rgba(255, 0, 0, 0.5);
+    border: 2px solid rgba(255, 0, 0, 1);
+}
+
+.area-recording {
+    border-style: dashed;
+    border: 2px rgba(255, 0, 0, 1);
+}
+
+.alert-msg {
+    font-size: 40px;
+    font-weight: bold;
+    color: #ffffff;
+    background-color: rgba(10, 10, 10, 0.7);
+    border-radius: 5px;
+    padding: .5em;
+}
+
+.area-resolution {
+    font-size: 20px;
+    font-weight: bold;
+    color: #ffffff;
+    background-color: rgba(0, 0, 0, 0.0);
+}
+
+.time-label {
+    font-size: 14px;
+    padding-right: 5px;
+    color: #ff0000;
+}

+ 259 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/timer.js

@@ -0,0 +1,259 @@
+/*
+    Copyright (C) 2013  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import GLib from 'gi://GLib';
+import * as Lib from './convenience.js';
+
+/*
+                DELAY TIMER
+*/
+let DelaySec = 0;
+let timerDelayId = null;
+let CallbackFuncDelay = null;
+let ElapsedSec;
+
+/**
+ * @type {TimerDelay}
+ */
+const TimerDelay = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_TimerDelay',
+}, class TimerDelay extends GObject.Object {
+    /**
+     * Create a new timer
+     *
+     * @param {number} delay delay in seconds
+     * @param {Function} callback callback function that is called after delay seconds (without arguments)
+     * @param {*} scope scope for the callback
+     */
+    constructor(delay, callback, scope) {
+        super();
+        if (isNaN(delay)) {
+            Lib.TalkativeLog(`-%-delay is NOT a number :${delay}`);
+        } else {
+            Lib.TalkativeLog(`-%-init TimerDelay called - sec : ${delay}`);
+
+            DelaySec = delay;
+            ElapsedSec = 1;
+
+            this.setCallback(callback);
+            this.Scope = scope;
+        }
+    }
+
+    /**
+     * Set the callback-function
+     *
+     * @param {Function} callback callback function that is called after delay seconds (without arguments)
+     */
+    setCallback(callback) {
+        Lib.TalkativeLog('-%-setcallback TimerDelay called');
+
+        if (
+            callback === undefined ||
+            callback === null ||
+            typeof callback !== 'function'
+        )
+            throw TypeError("'callback' needs to be a function.");
+
+        CallbackFuncDelay = callback;
+    }
+
+    /**
+     * Set the delay time
+     *
+     * @param {number} delay delay in seconds
+     */
+    setDelay(delay) {
+        Lib.TalkativeLog(`-%-setdelay TimerDelay called: ${delay}`);
+
+        DelaySec = delay;
+    }
+
+    /**
+     * Start or restart a new timer
+     */
+    begin() {
+        Lib.TalkativeLog('-%-start TimerDelay called');
+        this.stop();
+
+        timerDelayId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () =>
+            this._callbackInternal()
+        );
+    }
+
+    /**
+     * Stop the current timer
+     */
+    stop() {
+        Lib.TalkativeLog('-%-stop TimerDelay called');
+        if (timerDelayId !== null) {
+            if (GLib.source_remove(timerDelayId)) {
+                timerDelayId = null;
+
+                ElapsedSec = 1;
+            }
+        }
+    }
+
+    /**
+     * A convenient way to restart the timer.
+     */
+    restart() {
+        this.stop();
+        this.begin();
+    }
+
+    /**
+     * The internal callback-function.
+     *
+     * @private
+     * @returns {boolean}
+     */
+    _callbackInternal() {
+        Lib.TalkativeLog(`-%-internalFunction TimerDelay called | Sec = ${ElapsedSec} Sec delay = ${DelaySec}`);
+        if (ElapsedSec >= DelaySec) {
+            CallbackFuncDelay.apply(this.Scope, []);
+            ElapsedSec = 1;
+            return false;
+        } else {
+            ElapsedSec++;
+            return true;
+        }
+    }
+});
+
+/*
+                    COUNTING TIMER
+*/
+let timerCountingId = null;
+let CallbackFuncCounting = null;
+let isRunning = false;
+let secpassed = 0;
+
+/**
+ * @type {TimerCounting}
+ */
+var TimerCounting = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_TimerCounting',
+}, class TimerCounting extends GObject.Object {
+    /**
+     * Callback for the counting timer.
+     *
+     * @callback TimerCounting~callback
+     * @param {number} count seconds passed
+     * @param {boolean} alertEnd whether the timer is ending
+     */
+
+    /**
+     * Create a new timer
+     *
+     * @param {TimerCounting~callback} callback callback function that is called every second
+     * @param {EasyScreenCast_Indicator} scope scope for the callback function. This is also used to updateTimeLabel.
+     */
+    constructor(callback, scope) {
+        super();
+        Lib.TalkativeLog('-%-init TimerCounting called');
+
+        this.setCallback(callback);
+        secpassed = 0;
+        this.Scope = scope;
+    }
+
+    /**
+     * Set the callback-function
+     *
+     * @param {TimerCounting~callback} callback callback function that is called every second
+     */
+    setCallback(callback) {
+        Lib.TalkativeLog('-%-setcallback TimerCounting called');
+
+        if (
+            callback === undefined ||
+            callback === null ||
+            typeof callback !== 'function'
+        )
+            throw TypeError("'callback' needs to be a function.");
+
+        CallbackFuncCounting = callback;
+    }
+
+    /**
+     * Start or restart a new timer
+     */
+    begin() {
+        Lib.TalkativeLog('-%-start TimerCounting called');
+
+        if (isRunning)
+            this.stop();
+
+        isRunning = true;
+
+        timerCountingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () =>
+            this._callbackInternal()
+        );
+    }
+
+    /**
+     * Stop the current timer
+     */
+    stop() {
+        Lib.TalkativeLog('-%-stop TimerCounting called');
+
+        isRunning = false;
+
+        if (timerCountingId !== null && GLib.source_remove(timerCountingId))
+            timerCountingId = null;
+    }
+
+    /**
+     * A convenient way to stop timer
+     */
+    halt() {
+        isRunning = false;
+    }
+
+    /**
+     * The internal callback-function. Calls a function that handles
+     * the desktop notifications and one that sets the time label next
+     * to the icon.
+     *
+     * @private
+     * @returns {boolean}
+     */
+    _callbackInternal() {
+        if (isRunning === false) {
+            Lib.TalkativeLog('-%-finish TimerCounting ');
+
+            CallbackFuncCounting.apply(this.Scope, [secpassed, true]);
+            secpassed = 0;
+
+            this.stop();
+            this.Scope.updateTimeLabel('');
+
+            return false;
+        } else {
+            secpassed++;
+
+            Lib.TalkativeLog(`-%-continued TimerCounting | sec: ${secpassed}`);
+
+            CallbackFuncCounting.apply(this.Scope, [secpassed, false]);
+            this.Scope.updateTimeLabel(secpassed);
+
+            return true;
+        }
+    }
+});
+
+export {TimerDelay, TimerCounting};

+ 321 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilaudio.js

@@ -0,0 +1,321 @@
+/*
+    Copyright (C) 2015  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import GIRepository from 'gi://GIRepository';
+GIRepository.Repository.prepend_search_path('/usr/lib/gnome-shell');
+GIRepository.Repository.prepend_library_path('/usr/lib/gnome-shell');
+GIRepository.Repository.prepend_search_path('/usr/lib64/gnome-shell');
+GIRepository.Repository.prepend_library_path('/usr/lib64/gnome-shell');
+import Gvc from 'gi://Gvc';
+
+import * as Lib from './convenience.js';
+import * as Settings from './settings.js';
+import * as Ext from './extension.js';
+
+/**
+ * @type {MixerAudio}
+ */
+var MixerAudio = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_MixerAudio',
+}, class MixerAudio extends GObject.Object {
+    constructor() {
+        super();
+        Lib.TalkativeLog('-#-mixer constructor');
+
+        this._mixerControl = this._createMixerControl();
+        this._isConnected = true;
+        this._mixerControl.connect('state-changed', () =>
+            this._onChangeStatePAC()
+        );
+
+        // more log for debug
+        if (Lib.debugEnabled) {
+            this._mixerControl.connect('stream_added', (control, id) => {
+                this._onStreamAdd(control, id);
+            });
+            this._mixerControl.connect('stream_removed', (control, id) => {
+                this._onStreamRemove(control, id);
+            });
+        }
+    }
+
+    /**
+     * @returns {Gvc.MixerControl}
+     * @private
+     */
+    _createMixerControl() {
+        Lib.TalkativeLog('-#-mixer create');
+
+        // https://gitlab.gnome.org/GNOME/libgnome-volume-control
+        var _mixerTmp = new Gvc.MixerControl({
+            name: 'ESC Mixer Control',
+        });
+        _mixerTmp.open();
+
+        return _mixerTmp;
+    }
+
+    _onChangeStatePAC() {
+        Lib.TalkativeLog('-#-mixer state changed');
+
+        switch (this._mixerControl.get_state()) {
+        case Gvc.MixerControlState.CLOSED:
+            Lib.TalkativeLog('-#-Mixer close');
+            this._isConnected = false;
+            break;
+        case Gvc.MixerControlState.CONNECTING:
+            Lib.TalkativeLog('-#-Mixer connecting');
+            this._isConnected = false;
+            break;
+        case Gvc.MixerControlState.FAILED:
+            Lib.TalkativeLog('-#-Mixer failed');
+            this._isConnected = false;
+            break;
+        case Gvc.MixerControlState.READY:
+            Lib.TalkativeLog('-#-Mixer ready');
+            this._isConnected = true;
+
+            // more log for debug
+            if (Lib.debugEnabled)
+                this._getInfoPA();
+
+            break;
+        default:
+            Lib.TalkativeLog('-#-Mixer UNK');
+            this._isConnected = false;
+            break;
+        }
+    }
+
+    /**
+     * Gets a list of input audio sources.
+     *
+     * @returns {Array}
+     */
+    getListInputAudio() {
+        Lib.TalkativeLog('-#-get list input audio');
+
+        if (this._isConnected) {
+            var arrayTmp = [];
+
+            var tmpSinks = this._mixerControl.get_sinks();
+            Lib.TalkativeLog(`-#-Mixer sink -> ${tmpSinks.length}`);
+            for (let x in tmpSinks) {
+                Lib.TalkativeLog(`-#-sink index: ${tmpSinks[x].index}`);
+                Lib.TalkativeLog(`-#-sink name: ${tmpSinks[x].name}`);
+                Lib.TalkativeLog(
+                    `-#-sink description: ${tmpSinks[x].description}`
+                );
+                Lib.TalkativeLog(`-#-sink port: ${tmpSinks[x].port}`);
+
+                arrayTmp.push({
+                    desc: tmpSinks[x].description,
+                    name: tmpSinks[x].name,
+                    port: tmpSinks[x].port,
+                    sortable: true,
+                    resizeable: true,
+                });
+            }
+
+            var tmpSources = this._mixerControl.get_sources();
+            Lib.TalkativeLog(`-#-Mixer sources -> ${tmpSources.length}`);
+            for (let x in tmpSources) {
+                Lib.TalkativeLog(`-#-source index: ${tmpSources[x].index}`);
+                Lib.TalkativeLog(`-#-source name: ${tmpSources[x].name}`);
+                Lib.TalkativeLog(
+                    `-#-source description: ${tmpSources[x].description}`
+                );
+                Lib.TalkativeLog(`-#-source port: ${tmpSources[x].port}`);
+
+                arrayTmp.push({
+                    desc: tmpSources[x].description,
+                    name: tmpSources[x].name,
+                    port: tmpSources[x].port,
+                    sortable: true,
+                    resizeable: true,
+                });
+            }
+
+            Lib.TalkativeLog(`-#-MIXER SOURCE TOT -> ${arrayTmp.length}`);
+
+            return arrayTmp;
+        } else {
+            Lib.TalkativeLog('-#-Error lib pulse NOT present or NOT respond');
+        }
+
+        return [];
+    }
+
+    /**
+     * @returns {string}
+     */
+    getAudioSource() {
+        Lib.TalkativeLog('-#-get source audio choosen');
+
+        var arrtmp = this.getListInputAudio();
+        var index = Ext.Indicator.getSettings().getOption('i', Settings.INPUT_AUDIO_SOURCE_SETTING_KEY) - 2;
+
+        if (index >= 0 && index < arrtmp.length) {
+            return arrtmp[index].name;
+        } else {
+            Lib.TalkativeLog('-#-ERROR, audio source missing');
+            Ext.Indicator.getSettings().setOption(Settings.INPUT_AUDIO_SOURCE_SETTING_KEY, 0);
+
+            return '';
+        }
+    }
+
+    /**
+     * @param {Gvc.MixerControl} control the mixer control to which the stream was added
+     * @param {number} id the stream id
+     * @private
+     */
+    _onStreamAdd(control, id) {
+        Lib.TalkativeLog(`-#-mixer stream add - ID: ${id}`);
+        var streamTmp = control.lookup_stream_id(id);
+
+        if (
+            streamTmp.name === 'GNOME Shell' &&
+            streamTmp.description === 'Record Stream'
+        ) {
+            Lib.TalkativeLog('-#-stream gnome recorder captured');
+
+            Lib.TalkativeLog(`-#-stream index: ${streamTmp.index}`);
+            Lib.TalkativeLog(`-#-stream card index: ${streamTmp.card_index}`);
+            Lib.TalkativeLog(`-#-application_ID: ${streamTmp.application_id}`);
+            Lib.TalkativeLog(`-#-stream name: ${streamTmp.name}`);
+            Lib.TalkativeLog(`-#-stream icon: ${streamTmp.icon_name}`);
+            Lib.TalkativeLog(`-#-stream description: ${streamTmp.description}`);
+            Lib.TalkativeLog(`-#-stream port: ${streamTmp.port}`);
+        }
+    }
+
+    /**
+     * @param {Gvc.MixerControl} control the mixer control from where the stream was removed
+     * @param {number} id stream id
+     * @private
+     */
+    _onStreamRemove(control, id) {
+        Lib.TalkativeLog(`-#-mixer stream remove - ID: ${id}`);
+        // note: the stream has been already removed, so
+        // control.lookup_stream_id(id) won't return anything
+    }
+
+    /**
+     * @private
+     */
+    _getInfoPA() {
+        var tmp = this._mixerControl.get_cards();
+        Lib.TalkativeLog(`#-# mixer cards -> ${tmp.length}`);
+        for (let x in tmp) {
+            Lib.TalkativeLog(`-#-card index: ${tmp[x].index}`);
+            Lib.TalkativeLog(`-#-card name: ${tmp[x].name}`);
+            Lib.TalkativeLog(`-#-card icon: ${tmp[x].icon_name}`);
+            Lib.TalkativeLog(`-#-card profile: ${tmp[x].profile}`);
+            Lib.TalkativeLog(`-#-card human profile: ${tmp[x].human_profile}`);
+        }
+
+        tmp = this._mixerControl.get_sources();
+        Lib.TalkativeLog(`#-# mixer sources -> ${tmp.length}`);
+        for (let x in tmp) {
+            Lib.TalkativeLog(`-#-source index: ${tmp[x].index}`);
+            Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
+            Lib.TalkativeLog(`-#-source name: ${tmp[x].name}`);
+            Lib.TalkativeLog(`-#-source icon: ${tmp[x].icon_name}`);
+            Lib.TalkativeLog(`-#-source description: ${tmp[x].description}`);
+            Lib.TalkativeLog(`-#-source port: ${tmp[x].port}`);
+        }
+
+        tmp = this._mixerControl.get_source_outputs();
+        Lib.TalkativeLog(`#-# mixer source output -> ${tmp.length}`);
+        for (let x in tmp) {
+            Lib.TalkativeLog(`-#-sourceouput index: ${tmp[x].index}`);
+            Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
+            Lib.TalkativeLog(`-#-sourceouput name: ${tmp[x].name}`);
+            Lib.TalkativeLog(`-#-sourceoutput icon: ${tmp[x].icon_name}`);
+            Lib.TalkativeLog(
+                `-#-sourceoutput description: ${tmp[x].description}`
+            );
+            Lib.TalkativeLog(`-#-sourceoutput port: ${tmp[x].port}`);
+        }
+
+        tmp = this._mixerControl.get_sinks();
+        Lib.TalkativeLog(`#-# mixer sink -> ${tmp.length}`);
+        for (let x in tmp) {
+            Lib.TalkativeLog(`-#-sink index: ${tmp[x].index}`);
+            Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
+            Lib.TalkativeLog(`-#-sink name: ${tmp[x].name}`);
+            Lib.TalkativeLog(`-#-sink icon: ${tmp[x].icon_name}`);
+            Lib.TalkativeLog(`-#-sink description: ${tmp[x].description}`);
+            Lib.TalkativeLog(`-#-sink port: ${tmp[x].port}`);
+        }
+
+        tmp = this._mixerControl.get_sink_inputs();
+        Lib.TalkativeLog(`#-# mixer sink input -> ${tmp.length}`);
+        for (let x in tmp) {
+            Lib.TalkativeLog(`-#-sink input index: ${tmp[x].index}`);
+            Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
+            Lib.TalkativeLog(`-#-sink input name: ${tmp[x].name}`);
+            Lib.TalkativeLog(`-#-sink input icon: ${tmp[x].icon_name}`);
+            Lib.TalkativeLog(
+                `-#-sink input description: ${tmp[x].description}`
+            );
+            Lib.TalkativeLog(`-#-sink input port: ${tmp[x].port}`);
+        }
+
+        tmp = this._mixerControl.get_streams();
+        Lib.TalkativeLog(`#-# mixer stream -> ${tmp.length}`);
+        for (let x in tmp) {
+            Lib.TalkativeLog(`-#-STREAM index: ${tmp[x].index}`);
+            Lib.TalkativeLog(`-#-application_ID: ${tmp[x].application_id}`);
+            Lib.TalkativeLog(`-#-stream name: ${tmp[x].name}`);
+            Lib.TalkativeLog(`-#-stream icon: ${tmp[x].icon_name}`);
+            Lib.TalkativeLog(`-#-stream description: ${tmp[x].description}`);
+
+            var tmp1 = tmp[x].get_ports();
+            for (let y in tmp1) {
+                Lib.TalkativeLog(`-##-stream port number: ${y}`);
+                Lib.TalkativeLog(`-##-stream port name: ${tmp1[y].port}`);
+                Lib.TalkativeLog(
+                    `-##-stream port human name: ${tmp1[y].human_port}`
+                );
+                Lib.TalkativeLog(
+                    `-##-stream port priority: ${tmp1[y].priority}`
+                );
+            }
+        }
+    }
+
+    /**
+     * @returns {boolean}
+     */
+    checkAudio() {
+        Lib.TalkativeLog(`-#-check GVC lib presence: ${this._isConnected}`);
+
+        return this._isConnected;
+    }
+
+    /**
+     * Destroy mixer control
+     */
+    destroy() {
+        if (this._mixerControl) {
+            this._mixerControl.close();
+            this._mixerControl = null;
+        }
+    }
+});
+
+export {MixerAudio};

+ 266 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilexecmd.js

@@ -0,0 +1,266 @@
+/*
+    Copyright (C) 2017  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import * as Lib from './convenience.js';
+
+/**
+ * @type {ExecuteStuff}
+ */
+var ExecuteStuff = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_ExecuteStuff',
+}, class ExecuteStuff extends GObject.Object {
+    /**
+     * @param {EasyScreenCastSettingsWidget|EasyScreenCastIndicator} scope the scope for executing callback methods
+     * @private
+     */
+    constructor(scope) {
+        super();
+        Lib.TalkativeLog(`-¶-init scope:${scope}`);
+
+        this.Scope = scope;
+        this.Callback = null;
+    }
+
+    /**
+     * @param {string} cmd the command to be executed
+     * @returns {*[]}
+     * @private
+     */
+    _parseCmd(cmd) {
+        let successP, argv;
+
+        try {
+            [successP, argv] = GLib.shell_parse_argv(cmd);
+        } catch (err) {
+            Lib.TalkativeLog('-¶-ERROR PARSE');
+            successP = false;
+        }
+        if (successP) {
+            Lib.TalkativeLog(`-¶-parse: ${successP} argv: ${argv}`);
+            return [successP, argv];
+        } else {
+            return [successP, null];
+        }
+    }
+
+    /**
+     * Result callback.
+     *
+     * @callback ExecuteStuff~resultCallback
+     * @param {boolean} result whether the executed command exited successfully or not
+     * @param {string} stdout (optional) output of the result, if it was executed synchronously
+     */
+
+    /**
+     * Line output callback for asynchronous commands.
+     *
+     * @callback ExecuteStuff~lineCallback
+     * @param {string} line a single line of output
+     */
+
+    /**
+     * @param {string} cmd the command to be executed
+     * @param {boolean} sync execute synchronously (wait for return) or fork a process
+     * @param {ExecuteStuff~resultCallback} resCb callback after the command is finished
+     * @param {ExecuteStuff~lineCallback} lineCb callback for stdout when command is executed asynchronosly
+     * @class
+     */
+    Execute(cmd, sync, resCb, lineCb) {
+        Lib.TalkativeLog(`-¶-execute: ${cmd}`);
+
+        this.CommandString = cmd;
+        if (
+            resCb === undefined &&
+            resCb === null &&
+            typeof resCb !== 'function'
+        ) {
+            Lib.TalkativeLog('-¶-resCallback NEED to be a function');
+
+            this.Callback = null;
+        } else {
+            this.Callback = resCb;
+        }
+
+        if (sync === true) {
+            Lib.TalkativeLog('-¶-sync execute (wait for return)');
+            this._syncCmd(this.CommandString);
+        } else {
+            Lib.TalkativeLog('-¶-async execute (fork process)');
+            if (
+                lineCb === undefined &&
+                lineCb === null &&
+                typeof lineCb !== 'function'
+            ) {
+                Lib.TalkativeLog('-¶-lineCallback NEED to be a function');
+
+                this.lineCallback = null;
+            } else {
+                this.lineCallback = lineCb;
+            }
+            this._asyncCmd(this.CommandString);
+        }
+    }
+
+    /**
+     * @param {string} cmd the command to be executed
+     * @returns {*}
+     * @class
+     */
+    Spawn(cmd) {
+        let [successP, argv] = this._parseCmd(cmd);
+        if (successP) {
+            let successS, pid;
+            try {
+                [successS, pid] = GLib.spawn_async(
+                    null,
+                    argv,
+                    null,
+                    GLib.SpawnFlags.SEARCH_PATH |
+                        GLib.SpawnFlags.DO_NOT_REAP_CHILD,
+                    null
+                );
+            } catch (err) {
+                Lib.TalkativeLog(
+                    `-¶-ERROR SPAWN err:${err.message.toString()}`
+                );
+                successS = false;
+            }
+
+            if (successS) {
+                Lib.TalkativeLog(`-¶-spawn: ${successS} pid: ${pid}`);
+                return true;
+            } else {
+                Lib.TalkativeLog('-¶-spawn ERROR');
+                return null;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @param {string} cmd the command to be executed
+     * @private
+     */
+    _syncCmd(cmd) {
+        let [successP, argv] = this._parseCmd(cmd);
+        let decoder = new TextDecoder();
+        if (successP) {
+            Lib.TalkativeLog(`-¶-argv: ${argv}`);
+            let successS, stdOut, stdErr, exit;
+            try {
+                [successS, stdOut, stdErr, exit] = GLib.spawn_sync(
+                    null,
+                    argv,
+                    null,
+                    GLib.SpawnFlags.SEARCH_PATH,
+                    () => {}
+                );
+            } catch (err) {
+                Lib.TalkativeLog('-¶-ERROR SPAWN');
+                successS = false;
+            }
+            if (successS) {
+                Lib.TalkativeLog(`-¶-argv: ${argv}`);
+                Lib.TalkativeLog(`-¶-stdOut: ${decoder.decode(stdOut)}`);
+                Lib.TalkativeLog(`-¶-stdErr: ${decoder.decode(stdErr)}`);
+                Lib.TalkativeLog(`-¶-exit: ${exit}`);
+
+                Lib.TalkativeLog('-¶-exe RC');
+                if (this.Callback !== null) {
+                    this.Callback.apply(this.Scope, [
+                        true,
+                        decoder.decode(stdOut),
+                    ]);
+                }
+            } else {
+                Lib.TalkativeLog(`-¶-ERROR exe WC - exit status: ${exit}`);
+                if (this.Callback !== null)
+                    this.Callback.apply(this.Scope, [false]);
+            }
+        }
+    }
+
+    /**
+     * @param {string} cmd the command to be executed
+     * @private
+     */
+    _asyncCmd(cmd) {
+        let [successP, argv] = this._parseCmd(cmd);
+        let decoder = new TextDecoder();
+        if (successP) {
+            Lib.TalkativeLog(`-¶-argv: ${argv}`);
+            let successS, pid, stdIn, stdOut, stdErr;
+
+            try {
+                [
+                    successS,
+                    pid,
+                    stdIn,
+                    stdOut,
+                    stdErr,
+                ] = GLib.spawn_async_with_pipes(
+                    null, // working directory
+                    argv, // argv
+                    null, // envp
+                    GLib.SpawnFlags.SEARCH_PATH, // flags
+                    () => {} // child_setup
+                );
+            } catch (err) {
+                Lib.TalkativeLog('-¶-ERROR SPAWN');
+                successS = false;
+            }
+            if (successS) {
+                Lib.TalkativeLog(`-¶-argv: ${argv}`);
+                Lib.TalkativeLog(`-¶-pid: ${pid}`);
+                Lib.TalkativeLog(`-¶-stdIn: ${stdIn}`);
+                Lib.TalkativeLog(`-¶-stdOut: ${stdOut}`);
+                Lib.TalkativeLog(`-¶-stdErr: ${stdErr}`);
+
+                let outReader = new Gio.DataInputStream({
+                    base_stream: new Gio.UnixInputStream({
+                        fd: stdOut,
+                    }),
+                });
+                let inWriter = new Gio.DataOutputStream({
+                    base_stream: new Gio.UnixOutputStream({
+                        fd: stdIn,
+                    }),
+                });
+                inWriter.close(null);
+
+                let [out] = outReader.read_line(null);
+                while (out !== null) {
+                    if (this.lineCallback !== null)
+                        this.lineCallback.apply(this.Scope, [decoder.decode(out)]);
+
+                    [out] = outReader.read_line(null);
+                }
+
+                if (this.Callback !== null) {
+                    Lib.TalkativeLog('-¶-exe RC');
+                    this.Callback.apply(this.Scope, [true]);
+                }
+            } else {
+                Lib.TalkativeLog('-¶-ERROR exe WC');
+                if (this.Callback !== null)
+                    this.Callback.apply(this.Scope, [false]);
+            }
+        }
+    }
+});
+
+export {ExecuteStuff};

+ 737 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilgsp.js

@@ -0,0 +1,737 @@
+/*
+    Copyright (C) 2016  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import * as Lib from './convenience.js';
+import * as Settings from './settings.js';
+
+// CONST GSP - base
+const SCREEN =
+    '_SCREENCAST_RES_ _ENCODER_VIDEO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! _CONTAINER_';
+
+// CONST GSP - base plus sound
+const SCREEN_SOUND =
+    'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! _SCREENCAST_RES_ _ENCODER_VIDEO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. pulsesrc ! audioconvert ! _ENCODER_AUDIO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. _CONTAINER_ name=mux ';
+
+// CONST GSP - base plus webcam
+const SCREEN_WEBCAM =
+    'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videomixer name=mix _WEBCAM_OPT_ ! videoconvert ! _SCREENCAST_RES_ _ENCODER_VIDEO_ ! mux. v4l2src _WEBCAM_DEV_ ! _WEBCAM_CAP_ ! videoscale ! video/x-raw, width=_WEBCAM_W_, height=_WEBCAM_H_, add-borders=false ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mix. _CONTAINER_ name=mux';
+
+// CONST GSP - base plus sound and webcam stream
+const SCREEN_WEBCAM_SOUND =
+    'queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! videomixer name=mix _WEBCAM_OPT_ ! videoconvert ! _SCREENCAST_RES_ _ENCODER_VIDEO_ ! mux. v4l2src _WEBCAM_DEV_ ! _WEBCAM_CAP_ ! videoscale ! video/x-raw, width=_WEBCAM_W_, height=_WEBCAM_H_,  add-borders=false ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mix. pulsesrc ! audioconvert ! _ENCODER_AUDIO_ ! queue max-size-buffers=0 max-size-time=0 max-size-bytes=0 ! mux. _CONTAINER_ name=mux';
+
+// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+// CONST CONTAINER - WebM
+const webmVP8 = {
+    fileExt: '.webm',
+    nameGSP: 'webmmux',
+    quality: [
+        {
+            // quality level 0
+            fps: 15,
+            vq:
+                'vp8enc min_quantizer=13 max_quantizer=20 cpu-used=5 deadline=1000000 sharpness=2 target-bitrate=10000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 15 \nVideo -> VP8  Encoder:\n-min_quantizer=13\n-max_quantizer=20\n-cpu-used=5\n-deadline=1000000\n-sharpness=2\n-target-bitrate=10000\nAudio -> Vorbis Encoder',
+        },
+        {
+            // quality level 1
+            fps: 30,
+            vq:
+                'vp8enc min_quantizer=4 max_quantizer=13 cpu-used=2 deadline=500000 sharpness=0 target-bitrate=15000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 30 \nVideo -> VP8  Encoder:\n-min_quantizer=4\n-max_quantizer=13\n-cpu-used=2\n-deadline=500000\n-sharpness=0\n-target-bitrate=15000\nAudio -> Vorbis Encoder',
+        },
+        {
+            // quality level 2
+            fps: 30,
+            vq:
+                'vp8enc min_quantizer=0 max_quantizer=7 cpu-used=1 deadline=500000 sharpness=0 target-bitrate=25000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 30 \nVideo -> VP8  Encoder:\n-min_quantizer=0\n-max_quantizer=7\n-cpu-used=1\n-deadline=500000\n-sharpness=0\n-target-bitrate=25000\nAudio -> Vorbis Encoder',
+        },
+        {
+            // quality level 3
+            fps: 60,
+            vq:
+                'vp8enc min_quantizer=0 max_quantizer=0 cpu-used=0 deadline=100000 sharpness=0 target-bitrate=40000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 60 \nVideo -> VP8  Encoder:\n-min_quantizer=0\n-max_quantizer=0\n-cpu-used=0\n-deadline=100000\n-sharpness=0\n-target-bitrate=40000\nAudio -> Vorbis Encoder',
+        },
+    ],
+};
+
+const webmVP9 = {
+    fileExt: '.webm',
+    nameGSP: 'webmmux',
+    quality: [
+        {
+            // quality level 0
+            fps: 15,
+            vq:
+                'vp9enc min_quantizer=13 max_quantizer=20 cpu-used=5 deadline=1000000 sharpness=2 target-bitrate=10000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 15 \nVideo -> VP9  Encoder:\n-min_quantizer=13\n-max_quantizer=20\n-cpu-used=5\n-deadline=1000000\n-sharpness=2\n-target-bitrate=10000\nAudio -> Vorbis Encoder',
+        },
+        {
+            // quality level 1
+            fps: 30,
+            vq:
+                'vp9enc min_quantizer=4 max_quantizer=13 cpu-used=2 deadline=500000 sharpness=0 target-bitrate=15000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 30 \nVideo -> VP9  Encoder:\n-min_quantizer=4\n-max_quantizer=13\n-cpu-used=2\n-deadline=500000\n-sharpness=0\n-target-bitrate=15000\nAudio -> Vorbis Encoder',
+        },
+        {
+            // quality level 2
+            fps: 30,
+            vq:
+                'vp9enc min_quantizer=0 max_quantizer=7 cpu-used=1 deadline=500000 sharpness=0 target-bitrate=25000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 30 \nVideo -> VP9  Encoder:\n-min_quantizer=0\n-max_quantizer=7\n-cpu-used=1\n-deadline=500000\n-sharpness=0\n-target-bitrate=25000\nAudio -> Vorbis Encoder',
+        },
+        {
+            // quality level 3
+            fps: 60,
+            vq:
+                'vp9enc min_quantizer=0 max_quantizer=0 cpu-used=0 deadline=100000 sharpness=0 target-bitrate=40000 threads=%T',
+            aq: 'vorbisenc',
+            descr:
+                'FPS: 60 \nVideo -> VP9  Encoder:\n-min_quantizer=0\n-max_quantizer=0\n-cpu-used=0\n-deadline=100000\n-sharpness=0\n-target-bitrate=40000\nAudio -> Vorbis Encoder',
+        },
+    ],
+};
+
+// CONST CONTAINER - Mp4
+const mp4 = {
+    fileExt: '.mp4',
+    nameGSP: 'mp4mux',
+    quality: [
+        {
+            // quality level 0
+            fps: 15,
+            vq:
+                'x264enc psy-tune="none" speed-preset="superfast" subme=1 qp-min=28 qp-max=40 threads=%T',
+            aq: 'lamemp3enc',
+            descr:
+                'FPS: 15 \nVideo -> x264enc  Encoder:\n-psy-tune="none"\n-speed-preset="superfast"\n-subme=1\n-qp-min=28\n-qp-max=40\nAudio -> Mp3 Encoder',
+        },
+        {
+            // quality level 1
+            fps: 30,
+            vq:
+                'x264enc psy-tune="animation" speed-preset="fast" subme=5 qp-min=18 qp-max=28 threads=%T',
+            aq: 'lamemp3enc',
+            descr:
+                'FPS: 30 \nVideo -> x264enc  Encoder:\n-psy-tune="animation"\n-speed-preset="fast"\n-subme=5\n-qp-min=18\n-qp-max=28\nAudio -> Mp3 Encoder',
+        },
+        {
+            // quality level 2
+            fps: 30,
+            vq:
+                'x264enc psy-tune="animation" speed-preset="medium" subme=8 qp-min=10 qp-max=18 threads=%T',
+            aq: 'lamemp3enc',
+            descr:
+                'FPS: 30 \nVideo -> x264enc  Encoder:\n-psy-tune="animation"\n-speed-preset="medium"\n-subme=8\n-qp-min=10\n-qp-max=18\nAudio -> Mp3 Encoder',
+        },
+        {
+            // quality level 3
+            fps: 60,
+            vq:
+                'x264enc psy-tune="film" speed-preset="slower" subme=10 qp-min=0 qp-max=10 threads=%T',
+            aq: 'lamemp3enc',
+            descr:
+                'FPS: 60 \nVideo -> x264enc  Encoder:\n-psy-tune="film"\n-speed-preset="slower"\n-subme=10\n-qp-min=0\n-qp-max=10\nAudio -> Mp3 Encoder',
+        },
+    ],
+};
+
+const mp4Aac = {
+    fileExt: '.mp4',
+    nameGSP: 'mp4mux',
+    quality: [
+        {
+            // quality level 0
+            fps: 15,
+            vq:
+                'x264enc psy-tune="none" speed-preset="superfast" subme=1 qp-min=28 qp-max=40 threads=%T',
+            aq: 'avenc_aac',
+            descr:
+                'FPS: 15 \nVideo -> x264enc  Encoder:\n-psy-tune="none"\n-speed-preset="superfast"\n-subme=1\n-qp-min=28\n-qp-max=40\nAudio -> AAC Encoder',
+        },
+        {
+            // quality level 1
+            fps: 30,
+            vq:
+                'x264enc psy-tune="animation" speed-preset="fast" subme=5 qp-min=18 qp-max=28 threads=%T',
+            aq: 'avenc_aac',
+            descr:
+                'FPS: 30 \nVideo -> x264enc  Encoder:\n-psy-tune="animation"\n-speed-preset="fast"\n-subme=5\n-qp-min=18\n-qp-max=28\nAudio -> AAC Encoder',
+        },
+        {
+            // quality level 2
+            fps: 30,
+            vq:
+                'x264enc psy-tune="animation" speed-preset="medium" subme=8 qp-min=10 qp-max=18 threads=%T',
+            aq: 'avenc_aac',
+            descr:
+                'FPS: 30 \nVideo -> x264enc  Encoder:\n-psy-tune="animation"\n-speed-preset="medium"\n-subme=8\n-qp-min=10\n-qp-max=18\nAudio -> AAC Encoder',
+        },
+        {
+            // quality level 3
+            fps: 60,
+            vq:
+                'x264enc psy-tune="film" speed-preset="slower" subme=10 qp-min=0 qp-max=10 threads=%T',
+            aq: 'avenc_aac',
+            descr:
+                'FPS: 60 \nVideo -> x264enc  Encoder:\n-psy-tune="film"\n-speed-preset="slower"\n-subme=10\n-qp-min=0\n-qp-max=10\nAudio -> AAC Encoder',
+        },
+    ],
+};
+
+// CONST CONTAINER - Mkv
+const mkv = {
+    fileExt: '.mkv',
+    nameGSP: 'matroskamux',
+    quality: [
+        {
+            // quality level 0
+            fps: 15,
+            vq:
+                'x264enc psy-tune="none" speed-preset="superfast" subme=1 qp-min=28 qp-max=40 threads=%T',
+            aq: 'flacenc',
+            descr:
+                'FPS: 15 \nVideo -> x264enc  Encoder:\n-psy-tune="none"\n-speed-preset="superfast"\n-subme=1\n-qp-min=28\n-qp-max=40\nAudio -> Flac Encoder',
+        },
+        {
+            // quality level 1
+            fps: 30,
+            vq:
+                'x264enc psy-tune="animation" speed-preset="fast" subme=5 qp-min=18 qp-max=28 threads=%T',
+            aq: 'flacenc',
+            descr:
+                'FPS: 30 \nVideo -> x264enc  Encoder:\n-psy-tune="animation"\n-speed-preset="fast"\n-subme=5\n-qp-min=18\n-qp-max=28\nAudio -> Flac Encoder',
+        },
+        {
+            // quality level 2
+            fps: 30,
+            vq:
+                'x264enc psy-tune="animation" speed-preset="medium" subme=8 qp-min=10 qp-max=18 threads=%T',
+            aq: 'flacenc',
+            descr:
+                'FPS: 30 \nVideo -> x264enc  Encoder:\n-psy-tune="animation"\n-speed-preset="medium"\n-subme=8\n-qp-min=10\n-qp-max=18\nAudio -> Flac Encoder',
+        },
+        {
+            // quality level 3
+            fps: 60,
+            vq:
+                'x264enc psy-tune="film" speed-preset="slower" subme=10 qp-min=0 qp-max=10 threads=%T',
+            aq: 'flacenc',
+            descr:
+                'FPS: 60 \nVideo -> x264enc  Encoder:\n-psy-tune="film"\n-speed-preset="slower"\n-subme=10\n-qp-min=0\n-qp-max=10\nAudio -> Flac Encoder',
+        },
+    ],
+};
+
+// CONST CONTAINER - Ogg
+const ogg = {
+    fileExt: '.ogg',
+    nameGSP: 'oggmux',
+    quality: [
+        {
+            // quality level 0
+            fps: 15,
+            vq:
+                'theoraenc speed-level=3 vp3-compatible=true quality=10 bitrate=10000',
+            aq: 'opusenc',
+            descr:
+                'FPS: 15 \nVideo -> Theora Encoder:\n-speed-level=3\n-vp3-compatible=true\n-quality=10\n-bitrate=10000\nAudio -> Opus Encoder',
+        },
+        {
+            // quality level 1
+            fps: 30,
+            vq:
+                'theoraenc speed-level=1 vp3-compatible=false quality=30 bitrate=25000',
+            aq: 'opusenc',
+            descr:
+                'FPS: 30 \nVideo -> Theora Encoder:\n-speed-level=1\n-vp3-compatible=false\n-quality=30\n-bitrate=25000\nAudio -> Opus Encoder',
+        },
+        {
+            // quality level 2
+            fps: 30,
+            vq:
+                'theoraenc speed-level=0 vp3-compatible=false quality=50 bitrate=50000',
+            aq: 'opusenc',
+            descr:
+                'FPS: 30 \nVideo -> Theora Encoder:\n-speed-level=0\n-vp3-compatible=false\n-quality=50\n-bitrate=50000\nAudio -> Opus Encoder',
+        },
+        {
+            // quality level 3
+            fps: 60,
+            vq:
+                'theoraenc speed-level=0 vp3-compatible=false quality=60 bitrate=100000',
+            aq: 'opusenc',
+            descr:
+                'FPS: 60 \nVideo -> Theora Encoder\n-speed-level=0\n-vp3-compatible=false\n-quality=60\n-bitrate=100000\nAudio -> Opus Encoder',
+        },
+    ],
+};
+
+// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+// CONST RESOLUTION
+const RESOLUTION = [
+    // NATIVE SCREENCAST RESOLUTION
+    '',
+    // PRESET/CUSTOM SCREENCAST RESOLUTION
+    'videoscale ! video/x-raw, width=_RES_WIDTH_, height=_RES_HEIGHT_, add-borders=_RES_KAR_ ! ',
+];
+
+// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+// VAR ARRAY CONTAINER
+// see cbt_FileContainer in *.glade files for the combo box
+const CONTAINER = [webmVP8, webmVP9, mp4, mkv, ogg, mp4Aac];
+
+// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+/**
+ * Compose GSP
+ *
+ * @param {EasyScreenCastSettings} settings the extension's settings
+ * @param {MixerAudio} mixer the mixer
+ *
+ * @returns {string}
+ */
+function composeGSP(settings, mixer) {
+    Lib.TalkativeLog('-§-COMPOSE GSP');
+
+    let tmpGSP = '';
+
+    // retrieve options
+    let deviceWebcam = settings.getOption('s', Settings.DEVICE_WEBCAM_SETTING_KEY);
+    let deviceAudio = settings.getOption('i', Settings.INPUT_AUDIO_SOURCE_SETTING_KEY);
+    let qualityGSP = settings.getOption('i', Settings.QUALITY_SETTING_KEY);
+    let qualityWebcam = settings.getOption('s', Settings.QUALITY_WEBCAM_SETTING_KEY);
+    let resolutionType = settings.getOption('i', Settings.FILE_RESOLUTION_TYPE_SETTING_KEY);
+    let resolutionKAR = settings.getOption('b', Settings.FILE_RESOLUTION_KAR_SETTING_KEY);
+    let resolutionHeight = settings.getOption('i', Settings.FILE_RESOLUTION_HEIGHT_SETTING_KEY);
+    let resolutionWidth = settings.getOption('i', Settings.FILE_RESOLUTION_WIDTH_SETTING_KEY);
+    let container = settings.getOption('i', Settings.FILE_CONTAINER_SETTING_KEY);
+
+    Lib.TalkativeLog(
+        `-§-get option||devW: ${deviceWebcam}||devA: ${deviceAudio}||Qgsp: ${qualityGSP}||Qwc: ${qualityWebcam}||Res: ${resolutionType}||Cont: ${container}`
+    );
+
+    if (deviceWebcam !== '') {
+        switch (deviceAudio) {
+        case 0:
+            Lib.TalkativeLog('-§- SCREEN-WEBCAM');
+
+            tmpGSP = SCREEN_WEBCAM;
+
+            // replace WEBCAM_DEVICE/WEBCAM_CAPS
+            tmpGSP = _replaceWebcam(
+                tmpGSP,
+                deviceWebcam,
+                qualityWebcam,
+                settings
+            );
+
+            break;
+        case 1:
+            Lib.TalkativeLog('-§-SCREEN-WEBCAM-AUDIO(d)');
+
+            tmpGSP = SCREEN_WEBCAM_SOUND;
+
+            // replace WEBCAM_DEVICE/WEBCAM_CAPS/ENCODER-AUDIO
+            tmpGSP = _replaceAudio(
+                _replaceWebcam(tmpGSP, deviceWebcam, qualityWebcam, settings),
+                true,
+                container,
+                qualityGSP,
+                mixer
+            );
+
+            break;
+        default:
+            Lib.TalkativeLog('-§-SCREEN-WEBCAM-AUDIO');
+
+            tmpGSP = SCREEN_WEBCAM_SOUND;
+
+            // replace WEBCAM_DEVICE/WEBCAM_CAPS/ENCODER-AUDIO/AUDIO_DEVICE
+            tmpGSP = _replaceAudio(
+                _replaceWebcam(tmpGSP, deviceWebcam, qualityWebcam, settings),
+                false,
+                container,
+                qualityGSP,
+                mixer
+            );
+        }
+    } else {
+        switch (deviceAudio) {
+        case 0:
+            Lib.TalkativeLog('-§-SCREEN');
+
+            tmpGSP = SCREEN;
+
+            break;
+        case 1:
+            Lib.TalkativeLog('-§-SCREEN-AUDIO(d)');
+
+            tmpGSP = SCREEN_SOUND;
+
+            // replace ENCODER-AUDIO
+            tmpGSP = _replaceAudio(
+                tmpGSP,
+                true,
+                container,
+                qualityGSP,
+                mixer
+            );
+
+            break;
+        default:
+            Lib.TalkativeLog('-§-SCREEN-AUDIO');
+
+            tmpGSP = SCREEN_SOUND;
+
+            // replace ENCODER-AUDIO/AUDIO_DEVICE
+            tmpGSP = _replaceAudio(
+                tmpGSP,
+                false,
+                container,
+                qualityGSP,
+                mixer
+            );
+        }
+    }
+
+    // compose resolution string
+    var resolution = _composeResolution(
+        resolutionType,
+        resolutionHeight,
+        resolutionWidth,
+        resolutionKAR
+    );
+
+    // replace RESOLUTION/ENCODER-VIDEO/CONTAINER
+    var mapObj = {
+        _SCREENCAST_RES_: resolution,
+        _ENCODER_VIDEO_: CONTAINER[container].quality[qualityGSP].vq,
+        _CONTAINER_: CONTAINER[container].nameGSP,
+    };
+
+    tmpGSP = tmpGSP.replace(
+        /_SCREENCAST_RES_|_ENCODER_VIDEO_|_CONTAINER_/gi,
+        match => {
+            return mapObj[match];
+        }
+    );
+
+    Lib.TalkativeLog(`-§-final GSP :${tmpGSP}`);
+
+    return tmpGSP;
+}
+
+/**
+ * replace audio
+ *
+ * @param {string} gspRA input pipeline to be modified
+ * @param {boolean} defaultAudio whether to use the default audio device
+ * @param {int} ConTMP selected output container. Used to determine correct audio encoder
+ * @param {int} QGSPtmp quality setting
+ * @param {MixerAudio} mixer audio mixer
+ * @returns {string} pipeline with audio
+ */
+function _replaceAudio(gspRA, defaultAudio, ConTMP, QGSPtmp, mixer) {
+    Lib.TalkativeLog(`-§-replace audio default->${defaultAudio}`);
+    // replace device/encoder
+    var aq = CONTAINER[ConTMP].quality[QGSPtmp].aq;
+    Lib.TalkativeLog(`-§-pipeline pre-audio:${gspRA} aq:${aq}`);
+    var audioPipeline;
+
+    if (defaultAudio) {
+        Lib.TalkativeLog('-§-default audio source');
+        audioPipeline = gspRA.replace(/_ENCODER_AUDIO_/gi, aq);
+    } else {
+        var audiosource = mixer.getAudioSource();
+        if (audiosource === undefined) {
+            Lib.TalkativeLog('-§-failure combination of array audio sources');
+            audioPipeline = gspRA.replace(/_ENCODER_AUDIO_/gi, aq);
+        } else {
+            Lib.TalkativeLog('-§-correct audio source assignment');
+            if (audiosource.indexOf('output') !== -1)
+                audiosource += '.monitor';
+
+            var reDev = `pulsesrc device="${audiosource}"`;
+
+            var mapObj = {
+                pulsesrc: reDev,
+                _ENCODER_AUDIO_: aq,
+            };
+
+            audioPipeline = gspRA.replace(
+                /pulsesrc|_ENCODER_AUDIO_/gi,
+                match => {
+                    return mapObj[match];
+                }
+            );
+        }
+    }
+
+    Lib.TalkativeLog(`-§-pipeline post-audio:${audioPipeline}`);
+
+    return audioPipeline;
+}
+
+/**
+ * replace webcam
+ *
+ * @param {string} gspRW input pipeline to be modified
+ * @param {string} device webcam device file (e.g. /dev/video0)
+ * @param {string} caps quality options
+ * @param {EasyScreenCastSettings} settings the extension's settings
+ * @returns {string} pipeline with webcam settings
+ */
+function _replaceWebcam(gspRW, device, caps, settings) {
+    Lib.TalkativeLog(`-§-replace webcam -> ${device} caps: ${caps}`);
+
+    // replace device/caps
+    var reDev = `device=${device}`;
+    var reWCopt = _composeWebCamOption(settings);
+    var [reWCwidth, reWCheight] = _getWebCamDimension(settings);
+
+    Lib.TalkativeLog(`-§-pipeline pre-webcam:${gspRW}`);
+
+    var mapObj = {
+        _WEBCAM_DEV_: reDev,
+        _WEBCAM_CAP_: caps,
+        _WEBCAM_OPT_: reWCopt,
+        _WEBCAM_W_: reWCwidth,
+        _WEBCAM_H_: reWCheight,
+    };
+
+    var webcamPipeline = gspRW.replace(
+        /_WEBCAM_DEV_|_WEBCAM_CAP_|_WEBCAM_OPT_|_WEBCAM_W_|_WEBCAM_H_/gi,
+        match => {
+            return mapObj[match];
+        }
+    );
+
+    Lib.TalkativeLog(`-§-pipeline post-webcam:${webcamPipeline}`);
+
+    return webcamPipeline;
+}
+
+/**
+ * replace resolution
+ *
+ * @param {int} tmpRes resolution type: native/custom
+ * @param {int} h custom height
+ * @param {int} w custom width
+ * @param {boolean} kar whether to keep aspect ratio
+ * @returns {string} pipeline part for scaling resolution
+ */
+function _composeResolution(tmpRes, h, w, kar) {
+    Lib.TalkativeLog(`-§-resolution option: ${tmpRes}`);
+    var strRes = RESOLUTION[0];
+    var mapObj = {};
+
+    switch (tmpRes) {
+    case -1:
+        break;
+    case 999:
+        mapObj = {
+            _RES_KAR_: kar ? 'true' : 'false',
+            _RES_HEIGHT_: h,
+            _RES_WIDTH_: w,
+        };
+
+        strRes = RESOLUTION[1].replace(
+            /_RES_KAR_|_RES_HEIGHT_|_RES_WIDTH_/gi,
+            match => {
+                return mapObj[match];
+            }
+        );
+        break;
+    default:
+        mapObj = {
+            _RES_KAR_: 'true',
+            _RES_HEIGHT_: h,
+            _RES_WIDTH_: w,
+        };
+
+        strRes = RESOLUTION[1].replace(
+            /_RES_KAR_|_RES_WIDTH_|_RES_HEIGHT_/gi,
+            match => {
+                return mapObj[match];
+            }
+        );
+    }
+
+    Lib.TalkativeLog(`-§-compose resolution: ${strRes}`);
+    return strRes;
+}
+
+/**
+ * compose option webcam position
+ *
+ * @param {EasyScreenCastSettings} settings the extension's settings
+ *
+ * @returns {string}
+ */
+function _composeWebCamOption(settings) {
+    Lib.TalkativeLog('-§-compose webcam option');
+
+    // retrieve option webcam
+    var webcamAlpha = settings.getOption('d', Settings.ALPHA_CHANNEL_WEBCAM_SETTING_KEY);
+    var webcamMarginX = settings.getOption('i', Settings.MARGIN_X_WEBCAM_SETTING_KEY);
+    var webcamMarginY = settings.getOption('i', Settings.MARGIN_Y_WEBCAM_SETTING_KEY);
+    var webcamCornerPosition = settings.getOption('i', Settings.CORNER_POSITION_WEBCAM_SETTING_KEY);
+    var [webcamWidth, webcamHeight, screenWidth, screenHeight] = _getWebCamDimension(settings);
+
+    var posX = 0;
+    var posY = 0;
+
+    Lib.TalkativeLog(
+        `-§-alpha=${webcamAlpha} |marX=${webcamMarginX} |marY=${webcamMarginY} |corner=${webcamCornerPosition}`
+    );
+
+    // corner top-left
+    posX = webcamMarginX;
+    posY = webcamMarginY;
+
+    switch (webcamCornerPosition) {
+    case 0:
+        // corner bottom-right
+        posX = Math.floor(screenWidth - (webcamWidth + webcamMarginX));
+        posY = Math.floor(screenHeight - (webcamHeight + webcamMarginY));
+        break;
+    case 1:
+        // corner bottom-left
+        posX = webcamMarginX;
+        posY = Math.floor(screenHeight - (webcamHeight + webcamMarginY));
+        break;
+    case 2:
+        // corner top-right
+        posX = Math.floor(screenWidth - (webcamWidth + webcamMarginX));
+        posY = webcamMarginY;
+        break;
+    default:
+    }
+
+    // check valid position
+    if ((posX < 0 || posX > screenWidth) && (posY < 0 || posY > screenHeight)) {
+        Lib.TalkativeLog('-§-NOT valid position');
+        posX = 0;
+        posY = 0;
+    }
+
+    var tmpWCopt =
+        `sink_0::alpha=1 sink_1::alpha=${
+            webcamAlpha
+        } sink_1::xpos=${
+            posX
+        } sink_1::ypos=${
+            posY
+        } `;
+
+    Lib.TalkativeLog(`-§-posX=${posX} |posY=${posY}`);
+    Lib.TalkativeLog(`-§-webcam option=${tmpWCopt}`);
+
+    return tmpWCopt;
+}
+
+/**
+ * retrieve dimension webcam
+ *
+ * @param {EasyScreenCastSettings} settings the extension's settings
+ *
+ * @returns {*[]} array with webcam width,height,screen-width,screen-height
+ */
+function _getWebCamDimension(settings) {
+    Lib.TalkativeLog('-§-get webcam dimension');
+
+    var webcamWidth = settings.getOption('i', Settings.WIDTH_WEBCAM_SETTING_KEY);
+    var webcamHeight = settings.getOption('i', Settings.HEIGHT_WEBCAM_SETTING_KEY);
+    var webcamUnit = settings.getOption('i', Settings.TYPE_UNIT_WEBCAM_SETTING_KEY);
+    var screenWidth = settings.getOption('i', Settings.WIDTH_SETTING_KEY);
+    var screenHeight = settings.getOption('i', Settings.HEIGHT_SETTING_KEY);
+
+    if (settings.getOption('i', Settings.AREA_SCREEN_SETTING_KEY) === 0) {
+        screenWidth = global.screen_width;
+        screenHeight = global.screen_height;
+    }
+
+    Lib.TalkativeLog(
+        `-§-WC w=${webcamWidth} WC h=${webcamHeight} WCtype=${webcamUnit} screen W=${screenWidth} screen H=${screenHeight}`
+    );
+
+    if (webcamUnit === 0) {
+        webcamWidth = Math.floor((screenWidth * webcamWidth) / 100);
+        webcamHeight = Math.floor((screenHeight * webcamHeight) / 100);
+    }
+
+    Lib.TalkativeLog(`-§-after percentage WCw=${webcamWidth} WCh=${webcamHeight}`);
+
+    return [webcamWidth, webcamHeight, screenWidth, screenHeight];
+}
+
+/**
+ * get description
+ *
+ * @param {int} quality selected quality
+ * @param {int} container selected container format
+ * @returns {string}
+ */
+function getDescr(quality, container) {
+    Lib.TalkativeLog(`-§-get description Q-> ${quality} C-> ${container}`);
+
+    return CONTAINER[container].quality[quality].descr;
+}
+
+/**
+ * get fps
+ *
+ * @param {int} quality selected quality
+ * @param {int} container selected container format
+ * @returns {number}
+ */
+function getFps(quality, container) {
+    Lib.TalkativeLog(`-§-get fps Q-> ${quality} C-> ${container}`);
+
+    return CONTAINER[container].quality[quality].fps;
+}
+
+/**
+ * get file extension
+ *
+ * @param {int} container selected container format
+ * @returns {string}
+ */
+function getFileExtension(container) {
+    Lib.TalkativeLog(`-§-get file extension C-> ${container}`);
+
+    return CONTAINER[container].fileExt;
+}
+
+export {composeGSP, getDescr, getFps, getFileExtension};

+ 147 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilnotify.js

@@ -0,0 +1,147 @@
+/*
+    Copyright (C) 2016  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/messageTray.js
+import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
+import St from 'gi://St';
+
+import * as Lib from './convenience.js';
+import * as Settings from './settings.js';
+import * as Ext from './extension.js';
+
+import {gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+/**
+ * @type {NotifyManager}
+ */
+var NotifyManager = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_NotifyManager',
+}, class NotifyManager extends GObject.Object {
+    /**
+     * Create a notify manager
+     */
+    constructor() {
+        super();
+        Lib.TalkativeLog('-°-init notify manager');
+        this._alertWidget = null;
+    }
+
+    /**
+     * create notify
+     *
+     * @param {string} msg the title
+     * @param {Gio.FileIcon} icon the icon
+     * @param {boolean} sound whether to play a sound
+     * @returns {MessageTray.Notification}
+     */
+    createNotify(msg, icon, sound) {
+        Lib.TalkativeLog(`-°-create notify :${msg}`);
+        var source = new MessageTray.Source({
+            title: _('EasyScreenCast'),
+        });
+        var notify = new MessageTray.Notification({
+            source,
+            title: msg,
+            body: null,
+            gicon: icon,
+            isTransient: false,
+            resident: true,
+        });
+
+        Main.messageTray.add(source);
+        source.addNotification(notify);
+
+        if (sound)
+            notify.playSound();
+
+        return notify;
+    }
+
+    /**
+     * update notify
+     *
+     * @param {MessageTray.Notification} notify the already existing notification to update
+     * @param {string} msg the title
+     * @param {Gio.FileIcon} icon the icon
+     * @param {boolean} sound whether to play a sound
+     */
+    updateNotify(notify, msg, icon, sound) {
+        Lib.TalkativeLog('-°-update notify');
+
+        notify.set({
+            title: msg,
+            body: null,
+            gicon: icon,
+        });
+
+        if (sound)
+            notify.playSound();
+    }
+
+    /**
+     * create alert
+     *
+     * @param {string} msg the message
+     */
+    createAlert(msg) {
+        Lib.TalkativeLog(`-°-show alert tweener : ${msg}`);
+        if (Ext.Indicator.getSettings().getOption('b', Settings.SHOW_NOTIFY_ALERT_SETTING_KEY)) {
+            var monitor = Main.layoutManager.focusMonitor;
+
+            this.resetAlert();
+            this._alertWidget = new St.Label({
+                style_class: 'alert-msg',
+                opacity: 255,
+                text: msg,
+            });
+            Main.uiGroup.add_child(this._alertWidget);
+
+            this._alertWidget.set_position(
+                Math.floor(monitor.width / 2 - this._alertWidget.width / 2),
+                Math.floor(monitor.height / 2 - this._alertWidget.height / 2)
+            );
+
+            Lib.TalkativeLog(`-°-show alert tweener : opacity=${this._alertWidget.opacity}`);
+
+            // see org/gnome/shell/ui/environment.js#_easeActor
+            // TODO: for some reason, no transition is created, so the onComplete
+            // callback is called _immediately_
+            /*
+            import Clutter from 'gi://Clutter';
+            this._alertWidget.ease({
+                opacity: 0,
+                duration: 400,
+                mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+                onComplete: () => {
+                    Lib.TalkativeLog(`-°-show alert tweener completed: opacity=${this._alertWidget.opacity}`);
+                    Main.uiGroup.remove_child(this._alertWidget);
+                    this._alertWidget = null;
+                },
+            });
+            */
+        }
+    }
+
+    resetAlert() {
+        if (this._alertWidget !== null) {
+            this._alertWidget.hide();
+            Main.uiGroup.remove_child(this._alertWidget);
+            this._alertWidget = null;
+        }
+    }
+});
+
+export {NotifyManager};

+ 216 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilrecorder.js

@@ -0,0 +1,216 @@
+/*
+    Copyright (C) 2013  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import {loadInterfaceXML} from 'resource:///org/gnome/shell/misc/dbusUtils.js';
+const ScreencastIface = loadInterfaceXML('org.gnome.Shell.Screencast');
+
+import * as Lib from './convenience.js';
+import * as Settings from './settings.js';
+import * as Selection from './selection.js';
+import * as UtilGSP from './utilgsp.js';
+import * as Ext from './extension.js';
+
+/**
+ * @type {CaptureVideo}
+ */
+var CaptureVideo = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_CaptureVideo',
+}, class CaptureVideo extends GObject.Object {
+    /**
+     * Create a video recorder
+     */
+    constructor() {
+        super();
+        Lib.TalkativeLog('-&-init recorder');
+
+        this.AreaSelected = null;
+
+        // connect to d-bus service
+        const ScreenCastProxy = Gio.DBusProxy.makeProxyWrapper(ScreencastIface);
+        this._screenCastService = new ScreenCastProxy(
+            Gio.DBus.session,
+            'org.gnome.Shell.Screencast',
+            '/org/gnome/Shell/Screencast',
+            (proxy, error) => {
+                if (error)
+                    Lib.TalkativeLog(`-&-ERROR(d-bus proxy connected) - ${error.message}`);
+                else
+                    Lib.TalkativeLog('-&-d-bus proxy connected');
+            }
+        );
+    }
+
+    /**
+     * start recording
+     */
+    start() {
+        Lib.TalkativeLog('-&-start video recording');
+        this.recordingActive = false;
+
+        let fileExt = UtilGSP.getFileExtension(
+            Ext.Indicator.getSettings().getOption('i', Settings.FILE_CONTAINER_SETTING_KEY)
+        );
+        // prepare variable for screencast
+        let fileRec = Ext.Indicator.getSettings().getOption('s', Settings.FILE_NAME_SETTING_KEY);
+
+        let folderRec = '';
+        if (Ext.Indicator.getSettings().getOption('s', Settings.FILE_FOLDER_SETTING_KEY) !== '')
+            folderRec = Ext.Indicator.getSettings().getOption('s', Settings.FILE_FOLDER_SETTING_KEY);
+
+        let pipelineRec = '';
+
+        if (Ext.Indicator.getSettings().getOption('b', Settings.ACTIVE_CUSTOM_GSP_SETTING_KEY)) {
+            pipelineRec = Ext.Indicator.getSettings().getOption(
+                's',
+                Settings.PIPELINE_REC_SETTING_KEY
+            );
+        } else {
+            // compose GSP
+            pipelineRec = UtilGSP.composeGSP(Ext.Indicator.getSettings(), Ext.Indicator.CtrlAudio);
+        }
+
+        Lib.TalkativeLog(`-&-file template : ${fileRec}`);
+        fileRec = this._generateFileName(fileRec);
+        Lib.TalkativeLog(`-&-file final : ${fileRec}`);
+        const completeFileRecPath = folderRec !== ''
+            ? `${folderRec}/${fileRec}`
+            : fileRec;
+        Lib.TalkativeLog(`-&-file rec path complete : ${completeFileRecPath}${fileExt}`);
+
+        // prefix with a videoconvert element
+        // see DEFAULT_PIPELINE in https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/dbusServices/screencast/screencastService.js#L26
+        // this videoconvert element was added always previously (< Gnome 40) and needs to be added now explicitly
+        // https://gitlab.gnome.org/GNOME/gnome-shell/-/commit/51bf7ec17617a9ed056dd563afdb98e17da07373
+        pipelineRec = `videoconvert chroma-mode=GST_VIDEO_CHROMA_MODE_NONE dither=GST_VIDEO_DITHER_NONE matrix-mode=GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY n-threads=%T ! queue ! ${pipelineRec}`;
+        Lib.TalkativeLog(`-&-pipeline : pipeline: ${pipelineRec}`);
+
+        var optionsRec = {
+            'draw-cursor': new GLib.Variant(
+                'b',
+                Ext.Indicator.getSettings().getOption('b', Settings.DRAW_CURSOR_SETTING_KEY)
+            ),
+            framerate: new GLib.Variant(
+                'i',
+                Ext.Indicator.getSettings().getOption('i', Settings.FPS_SETTING_KEY)
+            ),
+            pipeline: new GLib.Variant('s', pipelineRec),
+        };
+
+        if (Ext.Indicator.getSettings().getOption('i', Settings.AREA_SCREEN_SETTING_KEY) === 0) {
+            this._screenCastService.ScreencastRemote(
+                completeFileRecPath,
+                optionsRec,
+                (result, error) => {
+                    if (error) {
+                        Lib.TalkativeLog(`-&-ERROR(screencast execute) - ${error.message}`);
+
+                        this.stop();
+                        Ext.Indicator.doRecResult(false);
+                    } else {
+                        Lib.TalkativeLog(`-&-screencast execute - ${result[0]} - ${result[1]}`);
+
+                        let resultingFilePath = result[1];
+                        if (resultingFilePath.endsWith('.undefined')) {
+                            resultingFilePath = resultingFilePath.substring(0, resultingFilePath.length - '.undefined'.length);
+                            resultingFilePath = `${resultingFilePath}${fileExt}`;
+                        }
+                        this._originalFilePath = result[1];
+                        this._filePathWithExtension = resultingFilePath;
+
+                        Ext.Indicator.doRecResult(result[0], resultingFilePath);
+                    }
+                }
+            );
+        } else {
+            this._screenCastService.ScreencastAreaRemote(
+                Ext.Indicator.getSettings().getOption('i', Settings.X_POS_SETTING_KEY),
+                Ext.Indicator.getSettings().getOption('i', Settings.Y_POS_SETTING_KEY),
+                Ext.Indicator.getSettings().getOption('i', Settings.WIDTH_SETTING_KEY),
+                Ext.Indicator.getSettings().getOption('i', Settings.HEIGHT_SETTING_KEY),
+                completeFileRecPath,
+                optionsRec,
+                (result, error) => {
+                    if (error) {
+                        Lib.TalkativeLog(`-&-ERROR(screencast execute) - ${error.message}`);
+
+                        this.stop();
+                        Ext.Indicator.doRecResult(false);
+                    } else {
+                        Lib.TalkativeLog(`-&-screencast execute - ${result[0]} - ${result[1]}`);
+
+                        // draw area recording
+                        if (Ext.Indicator.getSettings().getOption('b', Settings.SHOW_AREA_REC_SETTING_KEY))
+                            this.AreaSelected = new Selection.AreaRecording();
+
+                        let resultingFilePath = result[1];
+                        if (resultingFilePath.endsWith('.undefined')) {
+                            resultingFilePath = resultingFilePath.substring(0, resultingFilePath.length - '.undefined'.length);
+                            resultingFilePath = `${resultingFilePath}${fileExt}`;
+                        }
+                        this._originalFilePath = result[1];
+                        this._filePathWithExtension = resultingFilePath;
+
+                        Ext.Indicator.doRecResult(result[0], resultingFilePath);
+                    }
+                }
+            );
+        }
+    }
+
+    /**
+     * Stop recording
+     *
+     * @returns {boolean}
+     */
+    stop() {
+        Lib.TalkativeLog('-&-stop video recording');
+
+        this._screenCastService.StopScreencastRemote((result, error) => {
+            if (error) {
+                Lib.TalkativeLog(`-&-ERROR(screencast stop) - ${error.message}`);
+                return false;
+            } else {
+                Lib.TalkativeLog(`-&-screencast stop - ${result[0]}`);
+
+
+                // rename the file...
+                Lib.TalkativeLog(`-&-screencast: rename ${this._originalFilePath} to ${this._filePathWithExtension}`);
+                const sourceFile = Gio.File.new_for_path(this._originalFilePath);
+                const destFile = Gio.File.new_for_path(this._filePathWithExtension);
+                sourceFile.move(destFile, 0, null, null);
+            }
+
+            // clear area recording
+            if (this.AreaSelected !== null && this.AreaSelected.isVisible())
+                this.AreaSelected.clearArea();
+
+            return true;
+        });
+    }
+
+    // without file extension
+    _generateFileName(template) {
+        template = template.replaceAll('%d', '%0x').replaceAll('%t', '%0X');
+        const datetime = GLib.DateTime.new_now_local();
+        let result = datetime.format(template);
+        result = result.replaceAll(' ', '_'); // remove white space
+        result = result.replaceAll('/', '_'); // remove path separators
+        return result;
+    }
+});
+
+export {CaptureVideo};

+ 315 - 0
gnome/.local/share/gnome-shell/extensions/EasyScreenCast@iacopodeenosee.gmail.com/utilwebcam.js

@@ -0,0 +1,315 @@
+/*
+    Copyright (C) 2015  Borsato Ivano
+
+    The JavaScript code in this page is free software: you can
+    redistribute it and/or modify it under the terms of the GNU
+    General Public License (GNU GPL) as published by the Free Software
+    Foundation, either version 3 of the License, or (at your option)
+    any later version.  The code is distributed WITHOUT ANY WARRANTY;
+    without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
+*/
+
+'use strict';
+
+import GObject from 'gi://GObject';
+import GLib from 'gi://GLib';
+import Gst from 'gi://Gst?version=1.0';
+import * as Lib from './convenience.js';
+
+var HelperWebcam = GObject.registerClass({
+    GTypeName: 'EasyScreenCast_HelperWebcam',
+}, class HelperWebcam extends GObject.Object {
+    /**
+     * Create a device monitor inputvideo
+     *
+     * @param {string} unspecifiedWebcamText localized text for "Unspecified Webcam"
+     */
+    constructor(unspecifiedWebcamText) {
+        super();
+        this._unspecified_webcam_text = unspecifiedWebcamText;
+        Lib.TalkativeLog('-@-init webcam');
+
+        Gst.init(null);
+
+        // get gstreamer lib version
+        var [M, m, micro, nano] = Gst.version();
+        Lib.TalkativeLog(
+            `-@-gstreamer version: ${M}.${m}.${micro}.${nano}`
+        );
+        if (M === 1 && m >= 8) {
+            // gstreamer version equal or higher 1.8
+            this.deviceMonitor = new Gst.DeviceMonitor({
+                show_all: true,
+            });
+        } else {
+            // gstreamer version lower 1.8
+            this.deviceMonitor = new Gst.DeviceMonitor();
+        }
+
+        // get gstreamer plugin avaiable
+        let registry = new Gst.Registry();
+        let listPI = registry.get_plugin_list();
+        Lib.TalkativeLog(`-@-plugin list: ${listPI.length}`);
+        for (var ind in listPI) {
+            Lib.TalkativeLog(
+                `-@-plugin name: ${
+                    listPI[ind].get_name()
+                } Pfilename: ${
+                    listPI[ind].get_filename()
+                } Pdesc:  ${
+                    listPI[ind].get_description()
+                } Pversion: ${
+                    listPI[ind].get_version()
+                } Pload: ${
+                    listPI[ind].is_loaded()}`
+            );
+        }
+
+        // create device monitor
+        if (this.deviceMonitor !== null && this.deviceMonitor !== undefined) {
+            Lib.TalkativeLog('-@-device monitor created');
+            this.dmBus = this.deviceMonitor.get_bus();
+            if (this.dmBus !== null && this.dmBus !== undefined) {
+                Lib.TalkativeLog('-@-dbus created');
+                this.dmBus.add_watch(GLib.PRIORITY_DEFAULT, this._getMsg.bind(this));
+                let caps = Gst.Caps.new_empty_simple('video/x-raw');
+                this.deviceMonitor.add_filter('Video/Source', caps);
+                this.startMonitor();
+                // update device and caps
+                this.refreshAllInputVideo();
+            } else {
+                Lib.TalkativeLog('-@-ERROR dbus creation');
+            }
+        } else {
+            Lib.TalkativeLog('-@-ERROR device monitor creation');
+        }
+    }
+
+    /**
+     * Callback for the DeviceMonitor watcher
+     *
+     * @param {Gst.Bus} bus the DeviceMonitor Bus of gstreamer
+     * @param {Gst.Message} message the message
+     * @returns {boolean}
+     */
+    _getMsg(bus, message) {
+        Lib.TalkativeLog('-@-event getmsg');
+        switch (message.type) {
+        case Gst.MessageType.DEVICE_ADDED:
+            Lib.TalkativeLog('Device added');
+
+            // update device and caps
+            this.refreshAllInputVideo();
+            break;
+        case Gst.MessageType.DEVICE_REMOVED:
+            Lib.TalkativeLog('Device removed');
+
+            // update device and caps
+            this.refreshAllInputVideo();
+            break;
+        default:
+            Lib.TalkativeLog('Device UNK');
+            break;
+        }
+
+        return GLib.SOURCE_CONTINUE;
+    }
+
+    /**
+     * refresh all devices info
+     */
+    refreshAllInputVideo() {
+        Lib.TalkativeLog('-@-refresh all video input');
+
+        this._listDevices = this.getDevicesIV();
+        // compose devices array
+        this._listCaps = [];
+        for (var index in this._listDevices) {
+            this._listCaps[index] = this.getCapsForIV(this._listDevices[index].caps);
+
+            Lib.TalkativeLog(`-@-webcam /dev/video${index} name: ${this._listDevices[index].display_name}`);
+            Lib.TalkativeLog(`-@-caps available: ${this._listCaps[index].length}`);
+            Lib.TalkativeLog(`-@-ListCaps[${index}]: ${this._listCaps[index]}`);
+        }
+    }
+
+    /**
+     * get caps from device.
+     * A single capability might look like: <code>video/x-raw, format=(string)YUY2, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction)30/1</code>
+     * This encodes a single capability (fixed), but there might also be capabilities which represent options, e.g.
+     * <code>video/x-raw, format=(string)YUY2, width=(int)640, height=(int)480, pixel-aspect-ratio=(fraction)1/1, framerate=(fraction) { 30/1, 25/1, 20/1 }</code>.
+     * <br>
+     * The code here will take always the first option and unroll the options for framerate.
+     *
+     * @param {Gst.Caps} tmpCaps capabilities of a device
+     * @returns {string[]}
+     */
+    getCapsForIV(tmpCaps) {
+        Lib.TalkativeLog('-@-get all caps from a input video');
+        Lib.TalkativeLog(`-@-caps available before filtering for video/x-raw: ${tmpCaps.get_size()}`);
+
+        let cleanCaps = [];
+        for (let i = 0; i < tmpCaps.get_size(); i++) {
+            let capsStructure = tmpCaps.get_structure(i);
+
+            // only consider "video/x-raw"
+            if (capsStructure.get_name() === 'video/x-raw') {
+                Lib.TalkativeLog(`-@-cap : ${i} : original : ${capsStructure.to_string()}`);
+
+                let tmpStr = 'video/x-raw';
+                let result, number, fraction;
+                result = capsStructure.get_string('format');
+                if (result !== null)
+                    tmpStr += `, format=(string)${result}`;
+                [result, number] = capsStructure.get_int('width');
+                if (result === true)
+                    tmpStr += `, width=(int)${number}`;
+                [result, number] = capsStructure.get_int('height');
+                if (result === true)
+                    tmpStr += `, height=(int)${number}`;
+                [result, number, fraction] = capsStructure.get_fraction('pixel-aspect-ratio');
+                if (result === true)
+                    tmpStr += `, pixel-aspect-ratio=(fraction)${number}/${fraction}`;
+
+
+                if (capsStructure.has_field('framerate')) {
+                    [result, number, fraction] = capsStructure.get_fraction('framerate');
+                    if (result === true) {
+                        // a single framerate
+                        this._addAndLogCapability(cleanCaps, i, `${tmpStr}, framerate=(fraction)${number}/${fraction}`);
+                    } else {
+                        // multiple framerates
+
+                        // unfortunately GstValueList is not supported in this gjs-binding
+                        // "Error: Don't know how to convert GType GstValueList to JavaScript object"
+                        // -> capsStructure.get_value('framerate') <- won't work
+                        // -> capsStructure.get_list('framerate') <- only returns the numerator of the fraction
+                        //
+                        // therefore manually parsing the framerate values from the string representation
+                        let framerates = capsStructure.to_string();
+                        framerates = framerates.substring(framerates.indexOf('framerate=(fraction){') + 21);
+                        framerates = framerates.substring(0, framerates.indexOf('}'));
+                        framerates.split(',').forEach(element => {
+                            let [numerator, denominator] = element.split('/', 2);
+                            this._addAndLogCapability(cleanCaps, i, `${tmpStr}, framerate=(fraction)${numerator.trim()}/${denominator.trim()}`);
+                        });
+                    }
+                } else {
+                    // no framerate at all
+                    this._addAndLogCapability(cleanCaps, i, tmpStr);
+                }
+            } else {
+                Lib.TalkativeLog(`-@-cap : ${i} : skipped : ${capsStructure.to_string()}`);
+            }
+        }
+        return cleanCaps;
+    }
+
+    /**
+     * Adds the capability str to the array caps, if it is not already there. Avoids duplicates.
+     *
+     * @param {Array} caps the list of capabilities
+     * @param {int} originalIndex index of the original capabilities list from the device
+     * @param {string} str the capability string to add
+     */
+    _addAndLogCapability(caps, originalIndex, str) {
+        if (caps.indexOf(str) === -1) {
+            caps.push(str);
+            Lib.TalkativeLog(`-@-cap : ${originalIndex} : added cap : ${str}`);
+        } else {
+            Lib.TalkativeLog(`-@-cap : ${originalIndex} : ignore duplicated cap : ${str}`);
+        }
+    }
+
+    /**
+     * get devices IV
+     *
+     * @returns {Gst.Device[]}
+     */
+    getDevicesIV() {
+        Lib.TalkativeLog('-@-get devices');
+
+        var list = this.deviceMonitor.get_devices();
+        Lib.TalkativeLog(`-@-devices number: ${list.length}`);
+
+        // Note:
+        // Although the computer may have just one webcam connected to
+        // it, more than one GstDevice may be listed and all pointing to
+        // the same video device (for example /dev/video0. Each
+        // GstDevice is supposed to be used with a specific source, for
+        // example, a pipewiresrc or a v4l2src. For now, we are only
+        // using v4l2src.
+        // See also: Gst.DeviceMonitor.get_providers: pipewiredeviceprovider,decklinkdeviceprovider,v4l2deviceprovider
+        //
+        // So, here we filter the devices, that have a device.path property, which
+        // means, these are only v4l2 devices
+        var filtered = list.filter(device => device.get_properties().get_string('device.path') !== null);
+        Lib.TalkativeLog(`-@-devices number after filtering for v4l2: ${filtered.length}`);
+
+        return filtered;
+    }
+
+    /**
+     * get array name devices IV
+     *
+     * @returns {Array}
+     */
+    getNameDevices() {
+        Lib.TalkativeLog('-@-get name devices');
+        let tmpArray = [];
+
+        for (var index in this._listDevices) {
+            var wcName = this._unspecified_webcam_text;
+
+            if (this._listDevices[index].display_name !== '')
+                wcName = this._listDevices[index].display_name;
+
+            tmpArray.push(wcName);
+        }
+
+        Lib.TalkativeLog(`-@-list devices name: ${tmpArray}`);
+        return tmpArray;
+    }
+
+    /**
+     * get array caps
+     *
+     * @param {int} index device
+     * @returns {string[]}
+     */
+    getListCapsDevice(index) {
+        const tmpArray = this._listCaps[index];
+        Lib.TalkativeLog(`-@-list caps of device: ${tmpArray}`);
+        return tmpArray;
+    }
+
+    /**
+     * start listening
+     */
+    startMonitor() {
+        Lib.TalkativeLog('-@-start video devicemonitor');
+        this.deviceMonitor.start();
+    }
+
+    /**
+     * Stop listening
+     */
+    stopMonitor() {
+        Lib.TalkativeLog('-@-stop video devicemonitor');
+        this.disconnectSourceBus();
+        this.deviceMonitor.stop();
+    }
+
+    /**
+     * disconect bus
+     */
+    disconnectSourceBus() {
+        if (this.dmBusId) {
+            this.dmBus.disconnect(this.dmBusId);
+            this.dmBusId = 0;
+        }
+    }
+});
+
+export {HelperWebcam};

+ 122 - 33
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/extension.js

@@ -40,15 +40,23 @@ var VitalsMenuButton = GObject.registerClass({
                            'icon-rx': 'network-download-symbolic.svg',
                            'icon-tx': 'network-upload-symbolic.svg' },
                 'storage' : { 'icon': 'storage-symbolic.svg' },
-                'battery' : { 'icon': 'battery-symbolic.svg' }
+                'battery' : { 'icon': 'battery-symbolic.svg' },
+                    'gpu' : { 'icon': 'gpu-symbolic.svg' }
         }
 
+        // list with the prefixes for the according themes, the index of each 
+        // item must match the index on the combo box
+        this._sensorsIconPathPrefix = ['/icons/original/', '/icons/gnome/'];
+
         this._warnings = [];
         this._sensorMenuItems = {};
         this._hotLabels = {};
         this._hotIcons = {};
         this._groups = {};
         this._widths = {};
+        this._numGpus = 1;
+        this._newGpuDetected = false;
+        this._newGpuDetectedCount = 0;
         this._last_query = new Date().getTime();
 
         this._sensors = new Sensors.Sensors(this._settings, this._sensorIcons);
@@ -64,15 +72,16 @@ var VitalsMenuButton = GObject.registerClass({
         });
 
         this._drawMenu();
-        this.add_actor(this._menuLayout);
+        this.add_child(this._menuLayout);
         this._settingChangedSignals = [];
         this._refreshTimeoutId = null;
 
         this._addSettingChangedSignal('update-time', this._updateTimeChanged.bind(this));
         this._addSettingChangedSignal('position-in-panel', this._positionInPanelChanged.bind(this));
         this._addSettingChangedSignal('menu-centered', this._positionInPanelChanged.bind(this));
+        this._addSettingChangedSignal('icon-style', this._iconStyleChanged.bind(this));
 
-        let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info' ];
+        let settings = [ 'use-higher-precision', 'alphabetize', 'hide-zeros', 'fixed-widths', 'hide-icons', 'unit', 'memory-measurement', 'include-public-ip', 'network-speed-format', 'storage-measurement', 'include-static-info', 'include-static-gpu-info' ];
         for (let setting of Object.values(settings))
             this._addSettingChangedSignal(setting, this._redrawMenu.bind(this));
 
@@ -95,21 +104,14 @@ var VitalsMenuButton = GObject.registerClass({
             // groups associated sensors under accordion menu
             if (sensor in this._groups) continue;
 
-            this._groups[sensor] = new PopupMenu.PopupSubMenuMenuItem(_(this._ucFirst(sensor)), true);
-            this._groups[sensor].icon.gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons[sensor]['icon']);
-
-            // hide menu items that user has requested to not include
-            if (!this._settings.get_boolean('show-' + sensor))
-                this._groups[sensor].actor.hide();
-
-            if (!this._groups[sensor].status) {
-                this._groups[sensor].status = this._defaultLabel();
-                this._groups[sensor].actor.insert_child_at_index(this._groups[sensor].status, 4);
-                this._groups[sensor].status.text = _('No Data');
-            }
+            //handle gpus separately.
+            if (sensor === 'gpu') continue;
 
-            this.menu.addMenuItem(this._groups[sensor]);
+            this._initializeMenuGroup(sensor, sensor);
         }
+        
+        for (let i = 1; i <= this._numGpus; i++)
+            this._initializeMenuGroup('gpu#' + i, 'gpu', (this._numGpus > 1 ? ' ' + i : ''));
 
         // add separator
         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@@ -135,7 +137,7 @@ var VitalsMenuButton = GObject.registerClass({
         refreshButton.connect('clicked', (self) => {
             // force refresh by clearing history
             this._sensors.resetHistory();
-            this._values.resetHistory();
+            this._values.resetHistory(this._numGpus);
 
             // make sure timer fires at next full interval
             this._updateTimeChanged();
@@ -143,7 +145,7 @@ var VitalsMenuButton = GObject.registerClass({
             // refresh sensors now
             this._querySensors();
         });
-        customButtonBox.add_actor(refreshButton);
+        customButtonBox.add_child(refreshButton);
 
         // custom round monitor button
         let monitorButton = this._createRoundButton('org.gnome.SystemMonitor-symbolic', _('System Monitor'));
@@ -151,7 +153,7 @@ var VitalsMenuButton = GObject.registerClass({
             this.menu._getTopMenu().close();
             Util.spawn(this._settings.get_string('monitor-cmd').split(" "));
         });
-        customButtonBox.add_actor(monitorButton);
+        customButtonBox.add_child(monitorButton);
 
         // custom round preferences button
         let prefsButton = this._createRoundButton('preferences-system-symbolic', _('Preferences'));
@@ -159,10 +161,10 @@ var VitalsMenuButton = GObject.registerClass({
             this.menu._getTopMenu().close();
             this._extensionObject.openPreferences();
         });
-        customButtonBox.add_actor(prefsButton);
+        customButtonBox.add_child(prefsButton);
 
         // now add the buttons to the top bar
-        item.actor.add_actor(customButtonBox);
+        item.actor.add_child(customButtonBox);
 
         // add buttons
         this.menu.addMenuItem(item);
@@ -179,6 +181,24 @@ var VitalsMenuButton = GObject.registerClass({
         });
     }
 
+    _initializeMenuGroup(groupName, optionName, menuSuffix = '', position = -1) {
+        this._groups[groupName] = new PopupMenu.PopupSubMenuMenuItem(_(this._ucFirst(groupName) + menuSuffix), true);
+        this._groups[groupName].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(groupName));
+
+        // hide menu items that user has requested to not include
+        if (!this._settings.get_boolean('show-' + optionName))
+            this._groups[groupName].actor.hide();
+
+        if (!this._groups[groupName].status) {
+            this._groups[groupName].status = this._defaultLabel();
+            this._groups[groupName].actor.insert_child_at_index(this._groups[groupName].status, 4);
+            this._groups[groupName].status.text = _('No Data');
+        }
+
+        if(position == -1) this.menu.addMenuItem(this._groups[groupName]);
+        else this.menu.addMenuItem(this._groups[groupName], position);
+    }
+
     _createRoundButton(iconName) {
         let button = new St.Button({
             style_class: 'message-list-clear-button button vitals-button-action'
@@ -239,7 +259,7 @@ var VitalsMenuButton = GObject.registerClass({
     _createHotItem(key, value) {
         let icon = this._defaultIcon(key);
         this._hotIcons[key] = icon;
-        this._menuLayout.add_actor(icon)
+        this._menuLayout.add_child(icon)
 
         // don't add a label when no sensors are in the panel
         if (key == '_default_icon_') return;
@@ -248,7 +268,7 @@ var VitalsMenuButton = GObject.registerClass({
             style_class: 'vitals-panel-label',
             text: (value)?value:'\u2026', // ...
             y_expand: true,
-            y_align: Clutter.ActorAlign.START
+            y_align: Clutter.ActorAlign.CENTER
         });
 
         // attempt to prevent ellipsizes
@@ -258,7 +278,7 @@ var VitalsMenuButton = GObject.registerClass({
         this._hotLabels[key] = label;
 
         // prevent "called on the widget"  "which is not in the stage" errors by adding before width below
-        this._menuLayout.add_actor(label);
+        this._menuLayout.add_child(label);
 
         // support for fixed widths #55, save label (text) width
         this._widths[key] = label.width;
@@ -266,11 +286,17 @@ var VitalsMenuButton = GObject.registerClass({
 
     _showHideSensorsChanged(self, sensor) {
         this._sensors.resetHistory();
-        this._groups[sensor.substr(5)].visible = this._settings.get_boolean(sensor);
+
+        const sensorName = sensor.substr(5);
+        if(sensorName === 'gpu') {
+            for(let i = 1; i <= this._numGpus; i++)
+                this._groups[sensorName + '#' + i].visible = this._settings.get_boolean(sensor);
+        } else 
+            this._groups[sensorName].visible = this._settings.get_boolean(sensor);
     }
 
     _positionInPanelChanged() {
-        this.container.get_parent().remove_actor(this.container);
+        this.container.get_parent().remove_child(this.container);
         let position = this._positionInPanel();
 
         // allows easily addressable boxes
@@ -284,6 +310,27 @@ var VitalsMenuButton = GObject.registerClass({
         boxes[position[0]].insert_child_at_index(this.container, position[1]);
     }
 
+    _redrawDetailsMenuIcons() {
+        // updates the icons on the 'details' menu, the one 
+        // you have to click to appear
+        this._sensors.resetHistory();
+        for (const sensor in this._sensorIcons) {
+            if (sensor == "gpu") continue;
+            this._groups[sensor].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(sensor));
+        }
+
+        // gpu's are indexed differently, handle them here
+        const gpuKeys = Object.keys(this._groups).filter(key => key.startsWith("gpu#"));
+        gpuKeys.forEach((gpuKey) => {
+            this._groups[gpuKey].icon.gicon = Gio.icon_new_for_string(this._sensorIconPath("gpu"));
+        }); 
+    }
+
+    _iconStyleChanged() {
+        this._redrawDetailsMenuIcons();
+        this._redrawMenu();
+    }
+
     _removeHotLabel(key) {
         if (key in this._hotLabels) {
             let label = this._hotLabels[key];
@@ -322,7 +369,7 @@ var VitalsMenuButton = GObject.registerClass({
 
         this._drawMenu();
         this._sensors.resetHistory();
-        this._values.resetHistory();
+        this._values.resetHistory(this._numGpus);
         this._querySensors();
     }
 
@@ -370,7 +417,8 @@ var VitalsMenuButton = GObject.registerClass({
                 }
             }
         }
-
+        if(key === "_gpu#1_domain_number_")
+            console.error('UPDATING: ', key);
         // have we added this sensor before?
         let item = this._sensorMenuItems[key];
         if (item) {
@@ -394,7 +442,7 @@ var VitalsMenuButton = GObject.registerClass({
         let split = sensor.type.split('-');
         let type = split[0];
         let icon = (split.length == 2)?'icon-' + split[1]:'icon';
-        let gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons[type][icon]);
+        let gicon = Gio.icon_new_for_string(this._sensorIconPath(type, icon));
 
         let item = new MenuItem.MenuItem(gicon, key, sensor.label, sensor.value, this._hotLabels[key]);
         item.connect('toggle', (self) => {
@@ -460,17 +508,28 @@ var VitalsMenuButton = GObject.registerClass({
         });
 
         // second condition prevents crash due to issue #225, which started when _max_ was moved to the end
-        if (type == 'default' || !(type in this._sensorIcons)) {
-            icon.gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons['system']['icon']);
+        // don't use the default system icon if the type is a gpu; use the universal gpu icon instead
+        if (type == 'default' || (!(type in this._sensorIcons) && !type.startsWith('gpu'))) {
+            icon.gicon = Gio.icon_new_for_string(this._sensorIconPath('system'));
         } else if (!this._settings.get_boolean('hide-icons')) { // support for hide icons #80
             let iconObj = (split.length == 2)?'icon-' + split[1]:'icon';
-            icon.gicon = Gio.icon_new_for_string(this._extensionObject.path + '/icons/' + this._sensorIcons[type][iconObj]);
+            icon.gicon = Gio.icon_new_for_string(this._sensorIconPath(type, iconObj));
         }
 
         return icon;
     }
 
+    _sensorIconPath(sensor, icon = 'icon') {
+        // If the sensor is a numbered gpu, use the gpu icon. Otherwise use whatever icon associated with the sensor name.
+        let sensorKey = sensor;
+        if(sensor.startsWith('gpu')) sensorKey = 'gpu';
+
+        const iconPathPrefixIndex = this._settings.get_int('icon-style');
+        return this._extensionObject.path + this._sensorsIconPathPrefix[iconPathPrefixIndex] + this._sensorIcons[sensorKey][icon];
+    }
+
     _ucFirst(string) {
+        if(string.startsWith('gpu')) return 'Graphics';
         return string.charAt(0).toUpperCase() + string.slice(1);
     }
 
@@ -524,7 +583,8 @@ var VitalsMenuButton = GObject.registerClass({
         this._last_query = now;
 
         this._sensors.query((label, value, type, format) => {
-            let key = '_' + type.replace('-group', '') + '_' + label.replace(' ', '_').toLowerCase() + '_';
+            const typeKey = type.replace('-group', '');
+            let key = '_' + typeKey + '_' + label.replace(' ', '_').toLowerCase() + '_';
 
             // if a sensor is disabled, gray it out
             if (key in this._sensorMenuItems) {
@@ -534,11 +594,39 @@ var VitalsMenuButton = GObject.registerClass({
                 if (value == 'disabled') return;
             }
 
+            // add/initialize any gpu groups that we haven't added yet
+            if(typeKey.startsWith('gpu') && typeKey !== 'gpu#1') {
+                const split = typeKey.split('#');
+                if(split.length == 2 && this._numGpus < parseInt(split[1])) {
+                    // occasionally two lines from nvidia-smi will be read at once
+                    // so we only actually update the number of gpus if we have recieved multiple lines at least 3 times in a row
+                    // i.e. we make sure that mutiple queries have detected a new gpu back-to-back
+                    if(this._newGpuDetectedCount < 2) {
+                        this._newGpuDetected = true;
+                        return;
+                    }
+                    
+                    this._numGpus = parseInt(split[1]);
+                    this._newGpuDetectedCount = 0;
+                    this._newGpuDetected = false;
+                    // change label for gpu 1 from "Graphics" to "Graphics 1" since we have multiple gpus now
+                    this._groups['gpu#1'].label.text = this._ucFirst('gpu#1') + ' 1';
+                    for(let i = 2; i <= this._numGpus; i++)
+                        if(!('gpu#' + i in this._groups))
+                            this._initializeMenuGroup('gpu#' + i, 'gpu', ' ' + i, Object.keys(this._groups).length);
+                }
+            }
+
             let items = this._values.returnIfDifferent(dwell, label, value, type, format, key);
             for (let item of Object.values(items))
                 this._updateDisplay(_(item[0]), item[1], item[2], item[3]);
         }, dwell);
 
+        //if a new gpu has been detected during the last query, then increment the amount of times we've detected a new gpu
+        if(this._newGpuDetected) this._newGpuDetectedCount++;
+        else this._newGpuDetectedCount = 0;
+        this._newGpuDetected = false;
+
         if (this._warnings.length > 0) {
             this._notify('Vitals', this._warnings.join("\n"), 'folder-symbolic');
             this._warnings = [];
@@ -555,6 +643,7 @@ var VitalsMenuButton = GObject.registerClass({
 
     destroy() {
         this._destroyTimer();
+        this._sensors.destroy();
 
         for (let signal of Object.values(this._settingChangedSignals))
             this._settings.disconnect(signal);

+ 54 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/helpers/subprocess.js

@@ -0,0 +1,54 @@
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+
+// convert Uint8Array into a literal string
+function convertUint8ArrayToString(contents) {
+    const decoder = new TextDecoder('utf-8');
+    return decoder.decode(contents).trim();
+}
+
+export function SubProcess(command) {
+    this.sub_process = Gio.Subprocess.new(command, Gio.SubprocessFlags.STDOUT_PIPE);
+    this.stdout = this.sub_process.get_stdout_pipe();
+}
+
+SubProcess.prototype.read = function(delimiter = '') {
+    return new Promise((resolve, reject) => {
+        this.stdout.read_bytes_async(512, GLib.PRIORITY_LOW, null, function(stdout, res) {
+            try {
+                let read_bytes = stdout.read_bytes_finish(res).get_data();
+
+                // convert contents to string
+                let read_str = convertUint8ArrayToString(read_bytes);
+
+                // split read_str by delimiter if passed in
+                if (delimiter) {
+                    if (read_str == '')
+                        read_str = []; // EOF, ''.split(delimiter) would return ['']
+                    else
+                        read_str = read_str.split(delimiter);
+                }
+
+                // return results
+                resolve(read_str);
+            } catch (e) {
+                if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING)) {
+                    // previous read attempt is still waiting for something from stdout
+                    // ignore second attempt, return empty data (like EOF)
+                    if (delimiter) resolve([]);
+                    else resolve('');
+                } else {
+                    reject(e.message);
+                }
+            }
+        });
+    });
+};
+
+SubProcess.prototype.terminate = function() {
+    const SIGINT = 2;
+    this.sub_process.send_signal(SIGINT);
+    this.sub_process = null;
+    this.stdout.close_async(GLib.PRIORITY_LOW, null, null);
+    this.stdout = null;
+};

+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/battery-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg


+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#2e3436" d="M5 0v2s-.707-.016-1.45.355C2.814 2.727 2 3.668 2 5v1H0v1h2v1H0v1h2v1H0v1h2s-.016.707.355 1.445C2.727 13.187 3.668 14 5 14h1v2h1v-2h1v2h1v-2h1v2h1v-2s.707.016 1.45-.355C13.186 13.273 14 12.332 14 11v-1h2V9h-2V8h2V7h-2V6h2V5h-2c0-1.332-.813-2.273-1.55-2.645C11.706 1.985 11 2 11 2h-1V0H9v2H8V0H7v2H6V0zm0 4h6c.555 0 1 .445 1 1v6c0 .555-.445 1-1 1H5c-.555 0-1-.445-1-1V5c0-.555.445-1 1-1zm0 1v6h6V5z"/></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#2e3434"><path d="M8 0c-.234 0-.459.012-.688.031-1.832 1.216-1.77 3.335-.859 5.405A3 3 0 0 1 8 5a3 3 0 0 1 .418.031c.919-2.154 2.91-3.702 3.55-3.968A7.955 7.955 0 0 0 8 0zM4 1.094a7.982 7.982 0 0 0-3.219 3.5c.12 2.215 1.974 3.23 4.225 3.472A3 3 0 0 1 5 8a3 3 0 0 1 .646-1.85C4.248 4.28 3.912 1.787 4 1.094zm9.156 1.968c-1.423-.044-2.706.924-3.719 2.305a3 3 0 0 1 1.35 1.528C13.127 6.624 15.48 7.587 16 8a7.956 7.956 0 0 0-1.438-4.563 3.386 3.386 0 0 0-1.406-.374zm-2.158 4.872L11 8a3 3 0 0 1-.646 1.85c1.398 1.87 1.734 4.363 1.646 5.056a7.982 7.982 0 0 0 3.219-3.5c-.12-2.214-1.972-3.228-4.221-3.472zM0 8c0 1.698.536 3.268 1.438 4.563 1.973.987 3.79-.113 5.128-1.936A3 3 0 0 1 5.22 9.105C2.877 9.38.52 8.413 0 8zm9.547 2.564A3 3 0 0 1 8 11a3 3 0 0 1-.418-.031c-.919 2.154-2.91 3.702-3.55 3.969a7.955 7.955 0 0 0 4.655 1.03c1.833-1.215 1.772-3.334.86-5.404z"/><circle cx="8" cy="8" r="2"/></g></svg>

+ 15 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
+    <g transform="matrix(3.38015e-16,5.5202,-5.5202,3.38015e-16,526.335,-21.2284)">
+        <path d="M18,18L18,8L6,8L6,12L14,12L14,24L6,24L6,41.942L14,41.942L14,50.565L6,50.565L6,68.188L14,68.188L14,84L18,84L18,76L43,76L43,83C43.262,84.981 44.621,85.866 46.73,86L76.27,86C77.384,86 78.257,85.827 78.879,85.369C79.549,84.876 80,84.108 80,83L80,76L84.83,76C88.477,76.314 90.397,75.227 91.886,73.383C93.128,71.845 94.269,69.879 94,66.83L94,18L18,18ZM14,64.188L10,64.188L10,54.565L14,54.565L14,64.188ZM14,37.942L10,37.942L10,28L14,28L14,37.942ZM76,81.05C76,81.783 75.758,82.05 74.84,82.05L48.16,82.05C46.867,82.364 46.769,81.824 47,81.05L47,76L76,76L76,81.05ZM90,65.17C90.472,69.878 88.349,72.291 83.17,72L18,72L18,22L90,22L90,65.17Z" style="fill-rule:nonzero;"/>
+    </g>
+    <g transform="matrix(2.90206e-16,4.73942,-4.73942,2.90206e-16,491.213,161.673)">
+        <path d="M44,66.88C54.906,66.88 63.88,57.906 63.88,47C63.88,36.094 54.906,27.12 44,27.12C33.094,27.12 24.12,36.094 24.12,47C24.131,57.902 33.098,66.869 44,66.88ZM42,62.74C37.914,62.223 34.185,60.135 31.61,56.92L37.67,52.92C38.832,54.175 40.338,55.062 42,55.47L42,62.74ZM46,62.74L46,55.46C47.653,55.082 49.159,54.227 50.33,53L56.39,57C53.801,60.185 50.074,62.244 46,62.74ZM58.06,39.65C60.332,43.951 60.484,49.072 58.47,53.5L52.34,49.5C52.582,48.689 52.707,47.847 52.71,47C52.712,45.831 52.474,44.673 52.01,43.6L58.06,39.65ZM46,31.26C49.762,31.74 53.23,33.554 55.77,36.37L49.63,40.37C48.581,39.483 47.337,38.856 46,38.54L46,31.26ZM48.71,47C48.71,49.584 46.584,51.71 44,51.71C41.416,51.71 39.29,49.584 39.29,47C39.29,44.416 41.416,42.29 44,42.29C46.582,42.295 48.705,44.418 48.71,47ZM42,31.26L42,38.53C40.664,38.854 39.421,39.484 38.37,40.37L32.23,36.37C34.77,33.554 38.238,31.74 42,31.26ZM36,43.6C35.536,44.673 35.298,45.831 35.3,47C35.304,47.843 35.429,48.682 35.67,49.49L29.53,53.49C27.53,49.059 27.681,43.945 29.94,39.64L36,43.6Z" style="fill-rule:nonzero;"/>
+    </g>
+    <g transform="matrix(2.65262e-16,4.33206,-4.33206,2.65262e-16,472.204,-12.7952)">
+        <g>
+            <path d="M44,66.88C54.906,66.88 63.88,57.906 63.88,47C63.88,36.094 54.906,27.12 44,27.12C33.094,27.12 24.12,36.094 24.12,47C24.131,57.902 33.098,66.869 44,66.88ZM42,62.74C37.914,62.223 34.185,60.135 31.61,56.92L37.67,52.92C38.832,54.175 40.338,55.062 42,55.47L42,62.74ZM46,62.74L46,55.46C47.653,55.082 49.159,54.227 50.33,53L56.39,57C53.801,60.185 50.074,62.244 46,62.74ZM58.06,39.65C60.332,43.951 60.484,49.072 58.47,53.5L52.34,49.5C52.582,48.689 52.707,47.847 52.71,47C52.712,45.831 52.474,44.673 52.01,43.6L58.06,39.65ZM46,31.26C49.762,31.74 53.23,33.554 55.77,36.37L49.63,40.37C48.581,39.483 47.337,38.856 46,38.54L46,31.26ZM48.71,47C48.71,49.584 46.584,51.71 44,51.71C41.416,51.71 39.29,49.584 39.29,47C39.29,44.416 41.416,42.29 44,42.29C46.582,42.295 48.705,44.418 48.71,47ZM42,31.26L42,38.53C40.664,38.854 39.421,39.484 38.37,40.37L32.23,36.37C34.77,33.554 38.238,31.74 42,31.26ZM36,43.6C35.536,44.673 35.298,45.831 35.3,47C35.304,47.843 35.429,48.682 35.67,49.49L29.53,53.49C27.53,49.059 27.681,43.945 29.94,39.64L36,43.6Z" style="fill-rule:nonzero;"/>
+        </g>
+    </g>
+</svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#2e3436"><path d="M3 2C1.34 2 0 3.34 0 5v4c0 1.66 1.34 3 3 3h10c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3zm0 2h10c.555 0 1 .445 1 1v4c0 .555-.445 1-1 1H3c-.555 0-1-.445-1-1V5c0-.555.445-1 1-1z"/><path fill-opacity=".5" d="M4 5h2v4H4zm3 0h2v4H7zm3 0h2v4h-2z"/><path d="M2 10h12v4H2z"/></g></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#2e3436"><path fill-opacity=".349" d="M12 1a1 1 0 0 1 .707.293l3 3a1 1 0 0 1 0 1.414l-3 3a1 1 0 1 1-1.414-1.414L12.586 6H5c-.55 0-1-.45-1-1s.45-1 1-1h7.586l-1.293-1.293A1 1 0 0 1 12 1zm0 0"/><path d="M4 15a1 1 0 0 1-.707-.293l-3-3a1 1 0 0 1 0-1.414l3-3a1 1 0 1 1 1.414 1.414L3.414 10H11c.55 0 1 .45 1 1s-.45 1-1 1H3.414l1.293 1.293A1 1 0 0 1 4 15zm0 0"/></g></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#2e3436" d="M12 1a1 1 0 0 0-.707 1.707L12.586 4H5c-.55 0-1 .45-1 1s.45 1 1 1h7.586l-1.293 1.293a1 1 0 1 0 1.414 1.414l3-3a1 1 0 0 0 0-1.414l-3-3A1 1 0 0 0 12 1zM4 7a.993.993 0 0 0-.707.293l-3 3a1 1 0 0 0 0 1.414l3 3a1 1 0 1 0 1.414-1.414L3.414 12H11c.55 0 1-.45 1-1s-.45-1-1-1H3.414l1.293-1.293A1 1 0 0 0 4 7zm0 0"/></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#2e3436"><path d="M12 1a1 1 0 0 1 .707.293l3 3a1 1 0 0 1 0 1.414l-3 3a1 1 0 1 1-1.414-1.414L12.586 6H5c-.55 0-1-.45-1-1s.45-1 1-1h7.586l-1.293-1.293A1 1 0 0 1 12 1zm0 0"/><path fill-opacity=".349" d="M4 15a1 1 0 0 1-.707-.293l-3-3a1 1 0 0 1 0-1.414l3-3a1 1 0 1 1 1.414 1.414L3.414 10H11c.55 0 1 .45 1 1s-.45 1-1 1H3.414l1.293 1.293A1 1 0 0 1 4 15zm0 0"/></g></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#2e3436" d="M4 0C2.355 0 1 1.355 1 3v10c0 1.645 1.355 3 3 3h8c1.645 0 3-1.355 3-3V3c0-1.645-1.355-3-3-3zm0 2h8c.57 0 1 .43 1 1v9c0 .57-.43 1-1 1H4c-.555 0-1-.445-1-1V3c0-.555.445-1 1-1zm4 1C5.79 3 4 4.79 4 7v4h4c2.5 0 4-1.79 4-4s-1.79-4-4-4zm0 2a2 2 0 1 1-2 2 2 2 0 0 1 2-2zm0 0"/></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#2e3436" d="M3 0C1.34 0 0 1.34 0 3v7c0 1.66 1.34 3 3 3h10c1.66 0 3-1.34 3-3V3c0-1.66-1.34-3-3-3zm0 2h10c.555 0 1 .445 1 1v7c0 .555-.445 1-1 1H3c-.555 0-1-.445-1-1V3c0-.555.445-1 1-1zm2 12a2 2 0 0 0-2 2h10a2 2 0 0 0-2-2zm0 0"/></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#2e3434" d="M8 0C6.338 0 5 1.338 5 3v6c0 .116.007.23.02.342A4 4 0 0 0 4 12a4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-1.018-2.666A3.05 3.05 0 0 0 11 9V3c0-1.662-1.338-3-3-3zm0 2c.554 0 1 .446 1 1v1H8v1h1v1H8v1h1v1H8v1h1v1.27a2 2 0 0 1 .316.23 2 2 0 0 1 .082.082 2 2 0 0 1 .182.203 2 2 0 0 1 .08.117 2 2 0 0 1 .125.215 2 2 0 0 1 .063.135A2 2 0 0 1 10 12a2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 .15-.732 2 2 0 0 1 .07-.155 2 2 0 0 1 .12-.205 2 2 0 0 1 .082-.119 2 2 0 0 1 .197-.219 2 2 0 0 1 .06-.06A2 2 0 0 1 7 10.277V3c0-.554.446-1 1-1zm0 9a1 1 0 0 0-1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1z"/></svg>

+ 1 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M4 .004v3H2.977A1.97 1.97 0 0 0 1 4.977v2.05c0 .13.012.254.035.375A4.99 4.99 0 0 0 6 13.004 3.015 3.015 0 0 0 9 16h2c1.094 0 2-.906 2-2h1v2h2v-2c0-1.094-.906-2-2-2h-1c-1.094 0-2 .906-2 2H9c-.563 0-1-.434-1-.996a4.99 4.99 0 0 0 4.965-5.602A1.97 1.97 0 0 0 13 7.027v-2.05a1.97 1.97 0 0 0-1.977-1.973H10v-3H8v3H6v-3Zm0 0" style="stroke:none;fill-rule:nonzero;fill:#2e3434;fill-opacity:1"/></svg>

+ 8 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16.001" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#474747">
+  <path d="M5 5v2h6V5z" overflow="visible"/>
+  <path d="M5.469 0c-.49 0-.797.216-1.032.456C4.202.696 4 1.012 4 1.486V2H2v14h12V2h-2v-.406l-.002-.028a1.616 1.616 0 0 0-.416-1.011c-.236-.28-.62-.585-1.2-.553L10.438 0zm.53 2h4v2h2v10H4V4h2z" color="#bebebe" font-family="sans-serif" font-weight="400" overflow="visible" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none" white-space="normal"/>
+  <path d="m5 8v2h6v-2zm0 3v2h6v-2z" overflow="visible"/>
+ </g>
+</svg>

+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/cpu-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/fan-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg


+ 15 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
+    <g transform="matrix(3.38015e-16,5.5202,-5.5202,3.38015e-16,526.335,-21.2284)">
+        <path d="M18,18L18,8L6,8L6,12L14,12L14,24L6,24L6,41.942L14,41.942L14,50.565L6,50.565L6,68.188L14,68.188L14,84L18,84L18,76L43,76L43,83C43.262,84.981 44.621,85.866 46.73,86L76.27,86C77.384,86 78.257,85.827 78.879,85.369C79.549,84.876 80,84.108 80,83L80,76L84.83,76C88.477,76.314 90.397,75.227 91.886,73.383C93.128,71.845 94.269,69.879 94,66.83L94,18L18,18ZM14,64.188L10,64.188L10,54.565L14,54.565L14,64.188ZM14,37.942L10,37.942L10,28L14,28L14,37.942ZM76,81.05C76,81.783 75.758,82.05 74.84,82.05L48.16,82.05C46.867,82.364 46.769,81.824 47,81.05L47,76L76,76L76,81.05ZM90,65.17C90.472,69.878 88.349,72.291 83.17,72L18,72L18,22L90,22L90,65.17Z" style="fill-rule:nonzero;"/>
+    </g>
+    <g transform="matrix(2.90206e-16,4.73942,-4.73942,2.90206e-16,491.213,161.673)">
+        <path d="M44,66.88C54.906,66.88 63.88,57.906 63.88,47C63.88,36.094 54.906,27.12 44,27.12C33.094,27.12 24.12,36.094 24.12,47C24.131,57.902 33.098,66.869 44,66.88ZM42,62.74C37.914,62.223 34.185,60.135 31.61,56.92L37.67,52.92C38.832,54.175 40.338,55.062 42,55.47L42,62.74ZM46,62.74L46,55.46C47.653,55.082 49.159,54.227 50.33,53L56.39,57C53.801,60.185 50.074,62.244 46,62.74ZM58.06,39.65C60.332,43.951 60.484,49.072 58.47,53.5L52.34,49.5C52.582,48.689 52.707,47.847 52.71,47C52.712,45.831 52.474,44.673 52.01,43.6L58.06,39.65ZM46,31.26C49.762,31.74 53.23,33.554 55.77,36.37L49.63,40.37C48.581,39.483 47.337,38.856 46,38.54L46,31.26ZM48.71,47C48.71,49.584 46.584,51.71 44,51.71C41.416,51.71 39.29,49.584 39.29,47C39.29,44.416 41.416,42.29 44,42.29C46.582,42.295 48.705,44.418 48.71,47ZM42,31.26L42,38.53C40.664,38.854 39.421,39.484 38.37,40.37L32.23,36.37C34.77,33.554 38.238,31.74 42,31.26ZM36,43.6C35.536,44.673 35.298,45.831 35.3,47C35.304,47.843 35.429,48.682 35.67,49.49L29.53,53.49C27.53,49.059 27.681,43.945 29.94,39.64L36,43.6Z" style="fill-rule:nonzero;"/>
+    </g>
+    <g transform="matrix(2.65262e-16,4.33206,-4.33206,2.65262e-16,472.204,-12.7952)">
+        <g>
+            <path d="M44,66.88C54.906,66.88 63.88,57.906 63.88,47C63.88,36.094 54.906,27.12 44,27.12C33.094,27.12 24.12,36.094 24.12,47C24.131,57.902 33.098,66.869 44,66.88ZM42,62.74C37.914,62.223 34.185,60.135 31.61,56.92L37.67,52.92C38.832,54.175 40.338,55.062 42,55.47L42,62.74ZM46,62.74L46,55.46C47.653,55.082 49.159,54.227 50.33,53L56.39,57C53.801,60.185 50.074,62.244 46,62.74ZM58.06,39.65C60.332,43.951 60.484,49.072 58.47,53.5L52.34,49.5C52.582,48.689 52.707,47.847 52.71,47C52.712,45.831 52.474,44.673 52.01,43.6L58.06,39.65ZM46,31.26C49.762,31.74 53.23,33.554 55.77,36.37L49.63,40.37C48.581,39.483 47.337,38.856 46,38.54L46,31.26ZM48.71,47C48.71,49.584 46.584,51.71 44,51.71C41.416,51.71 39.29,49.584 39.29,47C39.29,44.416 41.416,42.29 44,42.29C46.582,42.295 48.705,44.418 48.71,47ZM42,31.26L42,38.53C40.664,38.854 39.421,39.484 38.37,40.37L32.23,36.37C34.77,33.554 38.238,31.74 42,31.26ZM36,43.6C35.536,44.673 35.298,45.831 35.3,47C35.304,47.843 35.429,48.682 35.67,49.49L29.53,53.49C27.53,49.059 27.681,43.945 29.94,39.64L36,43.6Z" style="fill-rule:nonzero;"/>
+        </g>
+    </g>
+</svg>

+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/memory-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/network-download-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/network-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/network-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/network-upload-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/storage-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/system-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/system-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/temperature-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg


+ 0 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/voltage-symbolic.svg → gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg


BIN
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo


BIN
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo


BIN
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo


+ 3 - 3
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/menuItem.js

@@ -21,18 +21,18 @@ export const MenuItem = GObject.registerClass({
         this._gIcon = icon;
 
         // add icon
-        this.add(new St.Icon({ style_class: 'popup-menu-icon', gicon : this._gIcon }));
+        this.add_child(new St.Icon({ style_class: 'popup-menu-icon', gicon : this._gIcon }));
 
         // add label
         this._labelActor = new St.Label({ text: label });
-        this.add(this._labelActor);
+        this.add_child(this._labelActor);
 
         // add value
         this._valueLabel = new St.Label({ text: value });
         this._valueLabel.set_x_align(Clutter.ActorAlign.END);
         this._valueLabel.set_x_expand(true);
         this._valueLabel.set_y_expand(true);
-        this.add(this._valueLabel);
+        this.add_child(this._valueLabel);
 
         this.actor._delegate = this;
     }

+ 6 - 2
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/metadata.json

@@ -1,13 +1,17 @@
 {
   "_generated": "Generated by SweetTooth, do not edit",
   "description": "A glimpse into your computer's temperature, voltage, fan speed, memory usage, processor load, system resources, network speed and storage stats. This is a one stop shop to monitor all of your vital sensors. Uses asynchronous polling to provide a smooth user experience. Feature requests or bugs? Please use GitHub.",
+  "donations": {
+    "paypal": "corecoding"
+  },
   "gettext-domain": "vitals",
   "name": "Vitals",
   "settings-schema": "org.gnome.shell.extensions.vitals",
   "shell-version": [
-    "45"
+    "45",
+    "46"
   ],
   "url": "https://github.com/corecoding/Vitals",
   "uuid": "Vitals@CoreCoding.com",
-  "version": 63
+  "version": 66
 }

+ 4 - 3
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/prefs.js

@@ -48,7 +48,8 @@ const Settings = new GObject.Class({
                         'show-network', 'show-storage', 'use-higher-precision',
                         'alphabetize', 'hide-zeros', 'include-public-ip',
                         'show-battery', 'fixed-widths', 'hide-icons', 
-                        'menu-centered', 'include-static-info' ];
+                        'menu-centered', 'include-static-info', 
+                        'show-gpu', 'include-static-gpu-info' ];
 
         for (let key in sensors) {
             let sensor = sensors[key];
@@ -61,7 +62,7 @@ const Settings = new GObject.Class({
         }
 
         // process individual drop down sensor preferences
-        sensors = [ 'position-in-panel', 'unit', 'network-speed-format', 'memory-measurement', 'storage-measurement', 'battery-slot' ];
+        sensors = [ 'position-in-panel', 'unit', 'network-speed-format', 'memory-measurement', 'storage-measurement', 'battery-slot', 'icon-style' ];
         for (let key in sensors) {
             let sensor = sensors[key];
 
@@ -90,7 +91,7 @@ const Settings = new GObject.Class({
         }
 
         // makes individual sensor preference boxes appear
-        sensors = [ 'temperature', 'network', 'storage', 'memory', 'battery', 'system', 'processor' ];
+        sensors = [ 'temperature', 'network', 'storage', 'memory', 'battery', 'system', 'processor', 'gpu' ];
         for (let key in sensors) {
             let sensor = sensors[key];
 

File diff suppressed because it is too large
+ 453 - 362
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/prefs.ui


BIN
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/schemas/gschemas.compiled


+ 15 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml

@@ -136,5 +136,20 @@
       <summary>Include processor static information</summary>
       <description>Display processor static information that doesn't change</description>
     </key>
+    <key type="b" name="show-gpu">
+      <default>false</default>
+      <summary>Monitor GPU</summary>
+      <description>Display GPU information (requires the nvidia-smi tool)</description>
+    </key>
+    <key type="b" name="include-static-gpu-info">
+      <default>false</default>
+      <summary>Include GPU static information</summary>
+      <description>Display GPU static information that doesn't change</description>
+    </key>
+    <key type="i" name="icon-style">
+      <default>0</default>
+      <summary>Icon styles</summary>
+      <description>Set the style for the displayed sensor icons ('original', 'updated')</description>
+    </key>
   </schema>
 </schemalist>

+ 252 - 0
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/sensors.js

@@ -25,6 +25,7 @@
 */
 
 import GObject from 'gi://GObject';
+import * as SubProcessModule from './helpers/subprocess.js';
 import * as FileModule from './helpers/file.js';
 import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js';
 import NM from 'gi://NM';
@@ -48,6 +49,15 @@ export const Sensors = GObject.registerClass({
 
         this._last_processor = { 'core': {}, 'speed': [] };
 
+        this._settingChangedSignals = [];
+        this._addSettingChangedSignal('show-gpu', this._reconfigureNvidiaSmiProcess.bind(this));
+        this._addSettingChangedSignal('update-time', this._reconfigureNvidiaSmiProcess.bind(this));
+        //this._addSettingChangedSignal('include-static-gpu-info', this._reconfigureNvidiaSmiProcess.bind(this));
+
+        this._nvidia_smi_process = null;
+        this._nvidia_labels = [];
+        this._bad_split_count = 0;
+
         if (hasGTop) {
             this.storage = new GTop.glibtop_fsusage();
             this._storageDevice = '';
@@ -58,6 +68,10 @@ export const Sensors = GObject.registerClass({
         }
     }
 
+    _addSettingChangedSignal(key, callback) {
+        this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback));
+    }
+
     _refreshIPAddress(callback) {
         // check IP address
         new FileModule.File('https://corecoding.com/vitals.php').read().then(contents => {
@@ -473,6 +487,166 @@ export const Sensors = GObject.registerClass({
         }).catch(err => { });
     }
 
+    _queryGpu(callback) {
+        if (!this._nvidia_smi_process) {
+            this._disableGpuLabels(callback);
+            return;
+        }
+        
+        this._nvidia_smi_process.read('\n').then(lines => {
+            /// for debugging multi-gpu on systems with only one gpu
+            /// duplicates the first gpu's data 3 times, for 4 total gpus
+            ///if(lines.length == 0) return;
+            ///for(let _gpuNum = 1; _gpuNum <= 3; _gpuNum++)
+            ///    lines.push(lines[0]);
+
+            for (let i = 0; i < lines.length; i++) {
+                this._parseNvidiaSmiLine(callback, lines[i], i + 1, lines.length > 1);
+            }
+            
+
+            // if we've already updated the static info during the last parse, then stop doing so. 
+            // this is so the _parseNvidiaSmiLine function won't return static info anymore 
+            // and the nvidia-smi commmand won't be queried for static info either
+            if(!this._nvidia_static_returned) {
+                this._nvidia_static_returned = true;
+                //reconfigure the process to stop querying static info
+                this._reconfigureNvidiaSmiProcess();
+            }
+        }).catch(err => {
+            this._disableGpuLabels(callback);
+            this._terminateNvidiaSmiProcess();
+        });
+    }
+
+    _parseNvidiaSmiLine(callback, csv, gpuNum, multiGpu) {
+        const expectedSplitLength = 19;
+        let csv_split = csv.split(',');
+
+        // occasionally the nvidia-smi command can get cut off before it can be fully read, thus the parse function only gets part of a line
+        // hence we count the number of bad splits and only terminate the process after a few bad splits in a row
+        // this prevents anomalous readings from terminating the process
+        if (csv_split.length < expectedSplitLength) {
+            this._bad_split_count++;
+            //if we've had 2 bad splits/reads in a row, try to restart the process
+            if (this._bad_split_count == 2) this._reconfigureNvidiaSmiProcess();
+            //if we still get a bad read after that, then it's not an anomaly; terminate the process
+            else if (this._bad_split_count >= 3) this._terminateNvidiaSmiProcess();
+            return;
+        }
+        this._bad_split_count = 0;
+
+        let [
+            label, 
+            fan_speed_pct,
+            temp_gpu, temp_mem, 
+            mem_total, mem_used, mem_reserved, mem_free,
+            util_gpu, util_mem, util_encoder, util_decoder,
+            clock_gpu, clock_mem, clock_encode_decode,
+            power, power_avg, 
+            link_gen_current, link_width_current
+        ] = csv_split;
+
+        const staticNames = [
+            'temp_limit', 'power_limit',
+            'link_gen_max', 'link_width_max',
+            'addressing_mode',
+            'driver_version', 'vbios', 'serial',
+            'domain_num', 'bus_num', 'device_num', 'device_id', 'sub_device_id'
+        ];
+        let staticInfo = {};
+
+        // if we have queried static info this time around, populate our static info object
+        if(csv_split.length == (expectedSplitLength + staticNames.length)){
+            for(let i = 0; i < staticNames.length; i++) {
+                //set the static info to a default (0) if it's undefined
+                const value = csv_split[expectedSplitLength + i];
+                staticInfo[staticNames[i]] = (typeof value !== "undefined") ? value : 0;
+            }
+        }
+
+
+        const typeName = 'gpu#' + gpuNum;
+        const globalLabel = 'GPU' + (multiGpu ? ' ' + gpuNum : '');
+        
+        const memTempValid = !isNaN(parseInt(temp_mem));
+        
+
+        this._returnGpuValue(callback, 'Graphics', parseInt(util_gpu) * 0.01, typeName + '-group', 'percent');
+
+        this._returnGpuValue(callback, 'Name', label, typeName, '');
+
+        this._returnGpuValue(callback, globalLabel, parseInt(fan_speed_pct) * 0.01, 'fan', 'percent');
+        this._returnGpuValue(callback, 'Fan', parseInt(fan_speed_pct) * 0.01, typeName, 'percent');
+
+        this._returnGpuValue(callback, globalLabel, parseInt(temp_gpu) * 1000, 'temperature', 'temp');
+        this._returnGpuValue(callback, 'Temperature', parseInt(temp_gpu) * 1000, typeName, 'temp');
+        this._returnGpuValue(callback, 'Memory Temperature', parseInt(temp_mem) * 1000, typeName, 'temp', memTempValid);
+        this._returnStaticGpuValue(callback, 'Temperature Limit', parseInt(staticInfo['temp_limit']) * 1000, typeName, 'temp');
+
+        this._returnGpuValue(callback, 'Memory Usage', parseInt(mem_used) / parseInt(mem_total), typeName, 'percent');
+        this._returnGpuValue(callback, 'Memory Total', parseInt(mem_total) * 1000, typeName, 'memory');
+        this._returnGpuValue(callback, 'Memory Used', parseInt(mem_used) * 1000, typeName, 'memory');
+        this._returnGpuValue(callback, 'Memory Reserved', parseInt(mem_reserved) * 1000, typeName, 'memory');
+        this._returnGpuValue(callback, 'Memory Free', parseInt(mem_free) * 1000, typeName, 'memory');
+
+        this._returnGpuValue(callback, 'Memory Utilization', parseInt(util_mem) * 0.01, typeName, 'percent');
+        this._returnGpuValue(callback, 'Utilization', parseInt(util_gpu) * 0.01, typeName, 'percent');
+        this._returnGpuValue(callback, 'Encoder Utilization', parseInt(util_encoder) * 0.01, typeName, 'percent');
+        this._returnGpuValue(callback, 'Decoder Utilization', parseInt(util_decoder) * 0.01, typeName, 'percent');
+
+        this._returnGpuValue(callback, 'Frequency', parseInt(clock_gpu) * 1000 * 1000, typeName, 'hertz');
+        this._returnGpuValue(callback, 'Memory Frequency', parseInt(clock_mem) * 1000 * 1000, typeName, 'hertz');
+        this._returnGpuValue(callback, 'Encoder/Decoder Frequency', parseInt(clock_encode_decode) * 1000 * 1000, typeName, 'hertz');
+
+        //this._returnGpuValue(callback, 'Encoder Sessions', parseInt(encoder_sessions), typeName, 'string');
+
+        this._returnGpuValue(callback, 'Power', power, typeName, 'watt-gpu');
+        this._returnGpuValue(callback, 'Average Power', power_avg, typeName, 'watt-gpu');
+        this._returnStaticGpuValue(callback, 'Power Limit', parseInt(staticInfo['power_limit']), typeName, 'watt-gpu');
+
+        this._returnGpuValue(callback, 'Link Speed', link_gen_current + 'x' + link_width_current, typeName, 'pcie');
+        this._returnStaticGpuValue(callback, 'Maximum Link Speed', staticInfo['link_gen_max'] + 'x' + staticInfo['link_width_max'], typeName, 'pcie');
+
+        this._returnStaticGpuValue(callback, 'Addressing Mode', staticInfo['addressing_mode'], typeName, 'string');
+
+        this._returnStaticGpuValue(callback, 'Driver Version', staticInfo['driver_version'], typeName, 'string');
+        this._returnStaticGpuValue(callback, 'vBIOS Version', staticInfo['vbios'], typeName, 'string');
+        this._returnStaticGpuValue(callback, 'Serial Number', staticInfo['serial'], typeName, 'string');
+
+        this._returnStaticGpuValue(callback, 'Domain Number', staticInfo['domain_num'], typeName, 'string');
+        this._returnStaticGpuValue(callback, 'Bus Number', staticInfo['bus_num'], typeName, 'string');
+        this._returnStaticGpuValue(callback, 'Device Number', staticInfo['device_num'], typeName, 'string');
+        this._returnStaticGpuValue(callback, 'Device ID', staticInfo['device_id'], typeName, 'string');
+        this._returnStaticGpuValue(callback, 'Sub Device ID', staticInfo['sub_device_id'], typeName, 'string');
+    }
+
+    _disableGpuLabels(callback) {
+        for (let labelObj of this._nvidia_labels)
+            this._returnValue(callback, labelObj.label, 'disabled', labelObj.type, labelObj.format);
+    }
+
+    _returnStaticGpuValue(callback, label, value, type, format) {
+        //if we've already tried to return existing static info before or if the option isn't enabled, then do nothing.
+        if (this._nvidia_static_returned || !this._settings.get_boolean('include-static-gpu-info')) 
+            return;
+
+        //we don't need to disable static info labels, so just use ordinary returnValue function
+        this._returnValue(callback, label, value, type, format);
+    }
+
+    _returnGpuValue(callback, label, value, type, format, display = true) {
+        if(!display) return;
+
+        if(value === 'N/A' || value === '[N/A]' || isNaN(value)) return;
+
+        let nvidiaLabel = {'label': label, 'type': type, 'format': format};
+        if (!this._nvidia_labels.includes(nvidiaLabel))
+            this._nvidia_labels.push(nvidiaLabel);
+
+        this._returnValue(callback, label, value, type, format);
+    }
+
     _returnValue(callback, label, value, type, format) {
         // don't return if value is not a number - will revisit later
         //if (isNaN(value)) return;
@@ -564,6 +738,74 @@ export const Sensors = GObject.registerClass({
                 this._returnValue(callback, 'Kernel', kernelArray[2], 'system', 'string');
             }).catch(err => { });
         }
+
+        // Launch nvidia-smi subprocess if nvidia querying is enabled
+        this._reconfigureNvidiaSmiProcess();
+    }
+
+    // The nvidia-smi subprocess will keep running and print new sensor data to stdout every
+    // `update_time` seconds. _queryNvidiaSmi() will be called at roughly the same interval and
+    // read from the subprocess's stdout to get new sensor data.
+
+    // Regarding "keeping main process & sub process in sync", there are two possible scenarios:
+    // - For some reason, nvidia-smi prints at a somewhat higher frequency than we call
+    //   _queryNvidiaSmi() to read data. This is okay, eventually one call to _queryNvidiaSmi()
+    //   will read two sensor data updates in a single call.
+    // - For some reason, _queryNvidiaSmi() is called at a somewhat higher frequency than
+    //   nvidia-smi prints data. This is the more likely scenario with user actions triggering
+    //   additional reads. This eventually triggers an "IO PENDING" error while attempting to
+    //   read, because the previous async read is still waiting. To solve this, the subprocess
+    //   module simply ignores PENDING errors. After ignoring the error, the earlier read will
+    //   eventually return and sensor data will be updated, so this scenario is handled correctly.
+
+    // Generally speaking, the call to _queryNvidiaSmi() and nvidia-smi's printing to stdout do
+    // not happen at the same time. So the async call in _queryNvidiaSmi() will usually have to
+    // wait up to `update_time` seconds before getting any results and reporting them through the
+    // callback.
+    _reconfigureNvidiaSmiProcess() {
+        if (this._settings.get_boolean('show-gpu')) {
+            this._terminateNvidiaSmiProcess();
+            
+            try {
+                let update_time = this._settings.get_int('update-time');
+                let query_interval = Math.max(update_time, 1);
+                let command = [
+                    'nvidia-smi',
+                    '--query-gpu=name,' +
+                    'fan.speed,' +
+                    'temperature.gpu,temperature.memory,' +
+                    'memory.total,memory.used,memory.reserved,memory.free,' +
+                    'utilization.gpu,utilization.memory,utilization.encoder,utilization.decoder,' +
+                    'clocks.gr,clocks.mem,clocks.video,' +
+                    'power.draw.instant,power.draw.average,' +
+                    'pcie.link.gen.gpucurrent,pcie.link.width.current,' +
+                    (!this._nvidia_static_returned && this._settings.get_boolean('include-static-gpu-info') ? 
+                        'temperature.gpu.tlimit,' +
+                        'power.limit,' +
+                        'pcie.link.gen.max,pcie.link.width.max,'   +
+                        'addressing_mode,'+
+                        'driver_version,vbios_version,serial,' +
+                        'pci.domain,pci.bus,pci.device,pci.device_id,pci.sub_device_id,' 
+                    : ''),
+                    '--format=csv,noheader,nounits',
+                    '-l', query_interval.toString()
+                ];
+
+                this._nvidia_smi_process = new SubProcessModule.SubProcess(command);
+            } catch(e) {
+                // proprietary nvidia driver not installed
+                this._terminateNvidiaSmiProcess();
+            }
+        } else {
+            this._terminateNvidiaSmiProcess();
+        }
+    }
+
+    _terminateNvidiaSmiProcess() {
+        if (this._nvidia_smi_process) {
+            this._nvidia_smi_process.terminate();
+            this._nvidia_smi_process = null;
+        }
     }
 
     _processTempVoltFan(callback, sensor_types, name, path, file) {
@@ -664,8 +906,18 @@ export const Sensors = GObject.registerClass({
     resetHistory() {
         this._next_public_ip_check = 0;
         this._hardware_detected = false;
+        this._nvidia_static_returned = false;
         this._processor_uses_cpu_info = true;
         this._battery_time_left_history = [];
         this._battery_charge_status = '';
+        this._nvidia_labels = [];
+        this._bad_split_count = 0;
+    }
+
+    destroy() {
+        this._terminateNvidiaSmiProcess();
+
+        for (let signal of Object.values(this._settingChangedSignals))
+            this._settings.disconnect(signal);
     }
 });

+ 18 - 1
gnome/.local/share/gnome-shell/extensions/Vitals@CoreCoding.com/values.js

@@ -190,6 +190,10 @@ export const Values = GObject.registerClass({
                 value = value / 1000000;
                 ending = 'W';
                 break;
+            case 'watt-gpu':
+                format = (use_higher_precision)?'%.2f %s':'%.1f %s';
+                ending = 'W';
+                break;
             case 'watt-hour':
                 format = (use_higher_precision)?'%.2f %s':'%.1f %s';
                 value = value / 1000000;
@@ -198,6 +202,11 @@ export const Values = GObject.registerClass({
             case 'load':
                 format = (use_higher_precision)?'%.2f %s':'%.1f %s';
                 break;
+            case 'pcie':
+                let split = value.split('x');
+                value = 'PCIe ' + parseInt(split[0]) + (split.length > 1 ? ' x' + parseInt(split[1]) : '');
+                format = '%s';
+                break;
             default:
                 format = '%s';
                 break;
@@ -324,14 +333,22 @@ export const Values = GObject.registerClass({
         return output;
     }
 
-    resetHistory() {
+    resetHistory(numGpus) {
         // don't call this._history = {}, as we want to keep network-rx and network-tx
         // otherwise network history statistics will start over
         for (let sensor in this._sensorIcons) {
+            //each gpu has it's own sensor name and thus must be handled separately
+            if(sensor === 'gpu') continue;
+
             this._history[sensor] = {};
             this._history[sensor + '-group'] = {};
             //this._history2[sensor] = {};
             //this._history2[sensor + '-group'] = {};
         }
+
+        for(let i = 1; i <= numGpus; i++){
+            this._history['gpu#' + i] = {};
+            this._history['gpu#' + i + '-group'] = {};
+        }
     }
 });

+ 4 - 2
gnome/.local/share/gnome-shell/extensions/auto-activities@CleoMenezesJr.github.io/metadata.json

@@ -11,9 +11,11 @@
   "original-author": "mi-jan-sena@proton.me",
   "settings-schema": "org.gnome.shell.extensions.auto-activities",
   "shell-version": [
-    "45"
+    "45",
+    "46"
   ],
   "url": "https://github.com/CleoMenezesJr/auto-activities",
   "uuid": "auto-activities@CleoMenezesJr.github.io",
-  "version": 12
+  "version": 13,
+  "version-name": "46.1"
 }

+ 0 - 28
gnome/.local/share/gnome-shell/extensions/block-caribou-36@lxylxy123456.ercli.dev/extension.js

@@ -1,28 +0,0 @@
-import * as KeyboardUI from 'resource:///org/gnome/shell/ui/keyboard.js';
-
-function _modifiedLastDeviceIsTouchscreen() {
-    return false;
-}
-
-export default class BlockCaribou {
-    constructor() {
-        this._originalLastDeviceIsTouchscreen = null;
-    }
-
-    enable() {
-        this._originalLastDeviceIsTouchscreen = KeyboardUI.KeyboardManager.prototype._lastDeviceIsTouchscreen;
-        KeyboardUI.KeyboardManager.prototype._lastDeviceIsTouchscreen = _modifiedLastDeviceIsTouchscreen;
-    }
-
-    /*
-     * In the lock screen, the on-screen keyboard (Caribou) also pops up by
-     * default. So this extension requires the "unlock-dialog" session mode to
-     * block Caribou in lock screen.
-     */
-    disable() {
-        if (this._originalLastDeviceIsTouchscreen !== null) {
-            KeyboardUI.KeyboardManager.prototype._lastDeviceIsTouchscreen = this._originalLastDeviceIsTouchscreen;
-            this._originalLastDeviceIsTouchscreen = null;
-        }
-    }
-}

+ 0 - 15
gnome/.local/share/gnome-shell/extensions/block-caribou-36@lxylxy123456.ercli.dev/metadata.json

@@ -1,15 +0,0 @@
-{
-  "_generated": "Generated by SweetTooth, do not edit",
-  "description": "Blocks caribou (the on screen keyboard) from popping up when you use a touchscreen. Even if it's disabled in the accessibility services menu. Continuation of keringar's work. Tested on GNOME Shell version 3.36 - 45 on Fedora 32 - 39. For a higher version see https://github.com/lxylxy123456/cariboublocker#installing-on-high-gnome-shell-version .",
-  "name": "Block Caribou 36",
-  "session-modes": [
-    "unlock-dialog",
-    "user"
-  ],
-  "shell-version": [
-    "45"
-  ],
-  "url": "https://github.com/lxylxy123456/cariboublocker",
-  "uuid": "block-caribou-36@lxylxy123456.ercli.dev",
-  "version": 10
-}

+ 4 - 4
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/appfolders.js

@@ -37,11 +37,11 @@ let _zoomAndFadeIn = function () {
 
     let blur_effect = this.get_effect("appfolder-blur");
 
-    blur_effect.sigma = 0;
+    blur_effect.radius = 0;
     blur_effect.brightness = 1.0;
     Tweener.addTween(blur_effect,
         {
-            sigma: sigma,
+            radius: sigma * 2,
             brightness: brightness,
             time: FOLDER_DIALOG_ANIMATION_TIME / 1000,
             transition: 'easeOutQuad'
@@ -85,7 +85,7 @@ let _zoomAndFadeOut = function () {
     let blur_effect = this.get_effect("appfolder-blur");
     Tweener.addTween(blur_effect,
         {
-            sigma: 0,
+            radius: 0,
             brightness: 1.0,
             time: FOLDER_DIALOG_ANIMATION_TIME / 1000,
             transition: 'easeInQuad'
@@ -165,7 +165,7 @@ export const AppFoldersBlur = class AppFoldersBlur {
 
             let blur_effect = new Shell.BlurEffect({
                 name: "appfolder-blur",
-                sigma: sigma,
+                radius: sigma * 2,
                 brightness: brightness,
                 mode: Shell.BlurMode.BACKGROUND
             });

+ 13 - 11
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/applications.js

@@ -1,18 +1,20 @@
 import Shell from 'gi://Shell';
 import Clutter from 'gi://Clutter';
 import Meta from 'gi://Meta';
+import Gio from 'gi://Gio';
 import * as Main from 'resource:///org/gnome/shell/ui/main.js';
 
 import { PaintSignals } from '../effects/paint_signals.js';
 import { ApplicationsService } from '../dbus/services.js';
 
-
 export const ApplicationsBlur = class ApplicationsBlur {
     constructor(connections, settings, _) {
         this.connections = connections;
         this.settings = settings;
         this.paint_signals = new PaintSignals(connections);
 
+        this.mutter_gsettings = new Gio.Settings({ schema: 'org.gnome.mutter' });
+
         // stores every blurred window
         this.window_map = new Map();
         // stores every blur actor
@@ -67,14 +69,11 @@ export const ApplicationsBlur = class ApplicationsBlur {
             this.connections.connect(
                 Main.overview, 'hidden',
                 _ => {
-                    let active_workspace =
-                        global.workspace_manager.get_active_workspace();
-
                     this.window_map.forEach((meta_window, _pid) => {
                         let window_actor = meta_window.get_compositor_private();
 
                         if (
-                            meta_window.get_workspace() !== active_workspace
+                            !meta_window.get_workspace().active
                         )
                             window_actor.hide();
                     });
@@ -325,7 +324,7 @@ export const ApplicationsBlur = class ApplicationsBlur {
     /// Add the blur effect to the window.
     create_blur_effect(pid, window_actor, meta_window, brightness, sigma) {
         let blur_effect = new Shell.BlurEffect({
-            sigma: sigma,
+            radius: sigma * 2,
             brightness: brightness,
             mode: Shell.BlurMode.BACKGROUND
         });
@@ -420,13 +419,15 @@ export const ApplicationsBlur = class ApplicationsBlur {
     }
 
     /// Compute the size and position for a blur actor.
-    /// On wayland, it seems like we need to divide by the scale to get the
-    /// correct result.
+    /// If `scale-monitor-framebuffer` experimental feature if on, we don't need to manage scaling.
+    /// Else, on wayland, we need to divide by the scale to get the correct result.
     compute_allocation(meta_window) {
+        const scale_monitor_framebuffer = this.mutter_gsettings.get_strv('experimental-features')
+            .includes('scale-monitor-framebuffer');
         const is_wayland = Meta.is_wayland_compositor();
         const monitor_index = meta_window.get_monitor();
         // check if the window is using wayland, or xwayland/xorg for rendering
-        const scale = is_wayland && meta_window.get_client_type() == 0
+        const scale = !scale_monitor_framebuffer && is_wayland && meta_window.get_client_type() == 0
             ? Main.layoutManager.monitors[monitor_index].geometry_scale
             : 1;
 
@@ -464,7 +465,7 @@ export const ApplicationsBlur = class ApplicationsBlur {
     /// Updates the blur effect by overwriting its sigma and brightness values.
     update_blur_effect(blur_actor, brightness, sigma) {
         let effect = blur_actor.get_effect('blur-effect');
-        effect.sigma = sigma;
+        effect.radius = sigma * 2;
         effect.brightness = brightness;
     }
 
@@ -500,6 +501,7 @@ export const ApplicationsBlur = class ApplicationsBlur {
         this._log("removing blur from applications...");
 
         this.service?.unexport();
+        delete this.mutter_gsettings;
 
         this.blur_actor_map.forEach(((_blur_actor, pid) => {
             this.remove_blur(pid);
@@ -545,4 +547,4 @@ export const ApplicationsBlur = class ApplicationsBlur {
         if (this.settings.DEBUG)
             console.log(`[Blur my Shell > applications] ${str}`);
     }
-};
+};

+ 315 - 101
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/dash_to_dock.js

@@ -1,9 +1,12 @@
 import St from 'gi://St';
 import Shell from 'gi://Shell';
+import Meta from 'gi://Meta';
+import Mtk from 'gi://Mtk';
 import * as Main from 'resource:///org/gnome/shell/ui/main.js';
 const Signals = imports.signals;
 
 import { PaintSignals } from '../effects/paint_signals.js';
+import { BlurEffect } from '../effects/blur_effect.js';
 
 const DASH_STYLES = [
     "transparent-dash",
@@ -12,72 +15,191 @@ const DASH_STYLES = [
 ];
 
 
+// An helper function to find the monitor in which an actor is situated,
+/// there might be a pre-existing function in GLib already
+function find_monitor_for(actor) {
+    let extents = actor.get_transformed_extents();
+    let rect = new Mtk.Rectangle({
+        x: extents.get_x(),
+        y: extents.get_y(),
+        width: extents.get_width(),
+        height: extents.get_height(),
+    });
+
+    let index = global.display.get_monitor_index_for_rect(rect);
+
+    return Main.layoutManager.monitors[index];
+}
+
+
 /// This type of object is created for every dash found, and talks to the main
 /// DashBlur thanks to signals.
 ///
 /// This allows to dynamically track the created dashes for each screen.
 class DashInfos {
-    constructor(dash_blur, dash, background_parent, effect, settings) {
+    constructor(dash_blur, dash, dash_container, dash_background, background, background_parent, effect) {
         // the parent DashBlur object, to communicate
         this.dash_blur = dash_blur;
+        this.dash_container = dash_container;
         // the blurred dash
         this.dash = dash;
+        this.dash_background = dash_background;
         this.background_parent = background_parent;
+        this.background = background;
         this.effect = effect;
-        this.settings = settings;
+        this.settings = dash_blur.settings;
         this.old_style = this.dash._background.style;
 
         dash_blur.connections.connect(dash_blur, 'remove-dashes', () => {
             this._log("removing blur from dash");
             this.dash.get_parent().remove_child(this.background_parent);
-            this.dash._background.style = this.old_style;
-
-            DASH_STYLES.forEach(
-                style => this.dash.remove_style_class_name(style)
-            );
+            this.remove_style();
         });
 
         dash_blur.connections.connect(dash_blur, 'update-sigma', () => {
-            this.effect.sigma = this.dash_blur.sigma;
+            if (this.dash_blur.is_static) {
+                this.dash_blur.update_size();
+            }
+            this.effect.radius = 2 * this.dash_blur.sigma * this.effect.scale;
         });
 
         dash_blur.connections.connect(dash_blur, 'update-brightness', () => {
             this.effect.brightness = this.dash_blur.brightness;
         });
 
-        dash_blur.connections.connect(dash_blur, 'override-background', () => {
-            this.dash._background.style = null;
+        dash_blur.connections.connect(dash_blur, 'update-corner-radius', () => {
+            if (this.dash_blur.is_static) {
+                let monitor = find_monitor_for(this.dash);
+                let corner_radius = this.dash_blur.corner_radius * monitor.geometry_scale;
+                this.effect.corner_radius = Math.min(
+                    corner_radius, this.effect.width / 2, this.effect.height / 2
+                );
+            }
+        });
 
-            DASH_STYLES.forEach(
-                style => this.dash.remove_style_class_name(style)
-            );
+        dash_blur.connections.connect(dash_blur, 'override-background', () => {
+            this.remove_style();
 
             this.dash.set_style_class_name(
                 DASH_STYLES[this.settings.dash_to_dock.STYLE_DASH_TO_DOCK]
             );
         });
 
-        dash_blur.connections.connect(dash_blur, 'reset-background', () => {
-            this.dash._background.style = this.old_style;
-
-            DASH_STYLES.forEach(
-                style => this.dash.remove_style_class_name(style)
-            );
-        });
+        dash_blur.connections.connect(dash_blur, 'reset-background', () => this.remove_style());
 
         dash_blur.connections.connect(dash_blur, 'show', () => {
-            this.effect.sigma = this.dash_blur.sigma;
+            if (this.dash_blur.is_static)
+                this.background_parent.show();
+            else
+                this.effect.radius = this.dash_blur.sigma * 2 * this.effect.scale;
         });
 
         dash_blur.connections.connect(dash_blur, 'hide', () => {
-            this.effect.sigma = 0;
+            if (this.dash_blur.is_static)
+                this.background_parent.hide();
+            else
+                this.effect.radius = 0;
+        });
+
+        dash_blur.connections.connect(dash_blur, 'update-wallpaper', () => {
+            if (this.dash_blur.is_static) {
+                let bg = Main.layoutManager._backgroundGroup.get_child_at_index(
+                    Main.layoutManager.monitors.length
+                    - find_monitor_for(this.dash).index - 1
+                );
+                if (bg && bg.get_content()) {
+                    this.background.content.set({
+                        background: bg.get_content().background
+                    });
+                    this._log('wallpaper updated');
+                } else {
+                    this._warn("could not get background for dash-to-dock");
+                }
+            }
         });
+
+        dash_blur.connections.connect(dash_blur, 'update-size', () => {
+            if (this.dash_blur.is_static) {
+                let [x, y] = this.get_dash_position(this.dash_container, this.dash_background);
+
+                this.background.x = -x;
+                this.background.y = -y;
+
+                this.effect.width = this.dash_background.width;
+                this.effect.height = this.dash_background.height;
+
+                this.dash_blur.set_corner_radius(this.dash_blur.corner_radius);
+
+                if (dash_container.get_style_class_name().includes("top")) {
+                    this.background.set_clip(x, y + this.dash.y + this.dash_background.y, this.dash_background.width, this.dash_background.height);
+                } else if (dash_container.get_style_class_name().includes("bottom")) {
+                    this.background.set_clip(x, y + this.dash.y + this.dash_background.y, this.dash_background.width, this.dash_background.height);
+                } else if (dash_container.get_style_class_name().includes("left")) {
+                    this.background.set_clip(x + this.dash.x + this.dash_background.x, y + this.dash.y + this.dash_background.y, this.dash_background.width, this.dash_background.height);
+                } else if (dash_container.get_style_class_name().includes("right")) {
+                    this.background.set_clip(x + this.dash.x + this.dash_background.x, y + this.dash.y + this.dash_background.y, this.dash_background.width, this.dash_background.height);
+                }
+            } else {
+                this.background.width = this.dash_background.width;
+                this.background.height = this.dash_background.height;
+
+                this.background.x = this.dash_background.x;
+                this.background.y = this.dash_background.y + this.dash.y;
+            }
+        });
+
+        dash_blur.connections.connect(dash_blur, 'change-blur-type', () => {
+            this.background_parent.remove_child(this.background);
+            if (this.effect.chained_effect)
+                this.effect.get_actor()?.remove_effect(this.effect.chained_effect);
+            this.effect.get_actor()?.remove_effect(this.effect);
+
+            let [background, effect] = this.dash_blur.add_blur(this.dash, this.dash_background, this.dash_container);
+            this.background = background;
+            this.effect = effect;
+            this.background_parent.add_child(this.background);
+        });
+    }
+
+    remove_style() {
+        this.dash._background.style = this.old_style;
+
+        DASH_STYLES.forEach(
+            style => this.dash.remove_style_class_name(style)
+        );
+    }
+
+    get_dash_position(dash_container, dash_background) {
+        var x, y;
+
+        let monitor = find_monitor_for(dash_container);
+        let dash_box = dash_container._slider.get_child();
+
+        if (dash_container.get_style_class_name().includes("top")) {
+            x = (monitor.width - dash_background.width) / 2;
+            y = dash_box.y;
+        } else if (dash_container.get_style_class_name().includes("bottom")) {
+            x = (monitor.width - dash_background.width) / 2;
+            y = monitor.height - dash_container.height;
+        } else if (dash_container.get_style_class_name().includes("left")) {
+            x = dash_box.x;
+            y = dash_container.y + (dash_container.height - dash_background.height) / 2 - dash_background.y;
+        } else if (dash_container.get_style_class_name().includes("right")) {
+            x = monitor.width - dash_container.width;
+            y = dash_container.y + (dash_container.height - dash_background.height) / 2 - dash_background.y;
+        }
+
+        return [x, y];
     }
 
     _log(str) {
         if (this.settings.DEBUG)
             console.log(`[Blur my Shell > dash]         ${str}`);
     }
+
+    _warn(str) {
+        console.warn(`[Blur my Shell > dash] ${str}`);
+    }
 }
 
 export const DashBlur = class DashBlur {
@@ -92,11 +214,13 @@ export const DashBlur = class DashBlur {
         this.brightness = this.settings.dash_to_dock.CUSTOMIZE
             ? this.settings.dash_to_dock.BRIGHTNESS
             : this.settings.BRIGHTNESS;
+        this.corner_radius = this.settings.dash_to_dock.CORNER_RADIUS;
+        this.is_static = this.settings.dash_to_dock.STATIC_BLUR;
         this.enabled = false;
     }
 
     enable() {
-        this.connections.connect(Main.uiGroup, 'actor-added', (_, actor) => {
+        this.connections.connect(Main.uiGroup, 'child-added', (_, actor) => {
             if (
                 (actor.get_name() === "dashtodockContainer") &&
                 (actor.constructor.name === 'DashToDock')
@@ -107,6 +231,9 @@ export const DashBlur = class DashBlur {
         this.blur_existing_dashes();
         this.connect_to_overview();
 
+        this.update_wallpaper();
+        this.update_size();
+
         this.enabled = true;
     }
 
@@ -143,13 +270,6 @@ export const DashBlur = class DashBlur {
 
     // Blurs the dash and returns a `DashInfos` containing its information
     blur_dash_from(dash, dash_container) {
-        // the effect to be applied
-        let effect = new Shell.BlurEffect({
-            brightness: this.brightness,
-            sigma: this.sigma,
-            mode: Shell.BlurMode.BACKGROUND
-        });
-
         // dash background parent, not visible
         let background_parent = new St.Widget({
             name: 'dash-blurred-background-parent',
@@ -158,99 +278,179 @@ export const DashBlur = class DashBlur {
             height: 0
         });
 
-        // dash background widget
-        let background = new St.Widget({
-            name: 'dash-blurred-background',
-            style_class: 'dash-blurred-background',
-            x: 0,
-            y: dash_container._slider.y,
-            width: dash.width,
-            height: dash.height,
+        // finally blur the dash
+        let dash_background = dash.get_children().find(child => {
+            return child.get_style_class_name() === 'dash-background';
         });
 
+        let [background, effect] = this.add_blur(dash, dash_background, dash_container);
+
+        this.update_wallpaper();
+        this.update_size();
+
         // updates size and position on change
-        this.connections.connect(dash_container._slider, 'notify::y', _ => {
-            background.y = dash_container._slider.y;
-        });
         this.connections.connect(dash, 'notify::width', _ => {
-            background.width = dash.width;
+            this.update_size();
         });
         this.connections.connect(dash, 'notify::height', _ => {
-            background.height = dash.height;
+            this.update_size();
+        });
+        this.connections.connect(dash_container, 'notify::width', _ => {
+            this.update_size();
+        });
+        this.connections.connect(dash_container, 'notify::height', _ => {
+            this.update_size();
+        });
+        this.connections.connect(dash_container, 'notify::y', _ => {
+            this.update_wallpaper();
+            this.update_size();
+        });
+        this.connections.connect(dash_container, 'notify::x', _ => {
+            this.update_wallpaper();
+            this.update_size();
         });
 
-        // add the widget to the dash
-        background.add_effect(effect);
         background_parent.add_child(background);
         dash.get_parent().insert_child_at_index(background_parent, 0);
 
-        // HACK
-        //
-        //`Shell.BlurEffect` does not repaint when shadows are under it. [1]
-        //
-        // This does not entirely fix this bug (shadows caused by windows
-        // still cause artifacts), but it prevents the shadows of the panel
-        // buttons to cause artifacts on the panel itself
-        //
-        // [1]: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2857
-
-        if (this.settings.HACKS_LEVEL === 1) {
-            this._log("dash hack level 1");
-            this.paint_signals.disconnect_all();
-
-            let rp = () => {
-                effect.queue_repaint();
-            };
-
-            dash._box.get_children().forEach((icon) => {
-                try {
-                    let zone = icon.get_child_at_index(0);
-
-                    this.connections.connect(zone, [
-                        'enter-event', 'leave-event', 'button-press-event'
-                    ], rp);
-                } catch (e) {
-                    this._warn(`${e}, continuing`);
-                }
-            });
+        // create infos
+        let infos = new DashInfos(
+            this,
+            dash,
+            dash_container,
+            dash_background,
+            background,
+            background_parent,
+            effect
+        );
 
-            this.connections.connect(dash._box, 'actor-added', (_, actor) => {
-                try {
-                    let zone = actor.get_child_at_index(0);
+        // update the background
+        this.update_background();
 
-                    this.connections.connect(zone, [
-                        'enter-event', 'leave-event', 'button-press-event'
-                    ], rp);
-                } catch (e) {
-                    this._warn(`${e}, continuing`);
-                }
-            });
+        // returns infos
+        return infos;
+    }
 
-            let show_apps = dash._showAppsIcon;
+    add_blur(dash, dash_background, dash_container) {
+        let monitor = find_monitor_for(dash);
 
-            this.connections.connect(show_apps, [
-                'enter-event', 'leave-event', 'button-press-event'
-            ], rp);
+        // dash background widget
+        let background = this.is_static
+            ? new Meta.BackgroundActor({
+                meta_display: global.display,
+                monitor: monitor.index,
+            })
+            : new St.Widget({
+                name: 'dash-blurred-background',
+                style_class: 'dash-blurred-background',
+                x: dash_background.x,
+                y: dash_background.y + dash.y,
+                width: dash_background.width,
+                height: dash_background.height,
+            });
 
-            this.connections.connect(dash, 'leave-event', rp);
-        } else if (this.settings.HACKS_LEVEL === 2) {
-            this._log("dash hack level 2");
+        // the effect to be applied
+        let effect;
+        if (this.is_static) {
+            let corner_radius = this.corner_radius * monitor.geometry_scale;
+            corner_radius = Math.min(corner_radius, dash_background.width / 2, dash_background.height / 2);
+
+            effect = new BlurEffect({
+                radius: 2 * this.sigma * monitor.geometry_scale,
+                brightness: this.brightness,
+                width: dash_background.width,
+                height: dash_background.height,
+                corner_radius: corner_radius
+            });
 
-            this.paint_signals.connect(background, effect);
+            // connect to every background change (even without changing image)
+            // FIXME this signal is fired very often, so we should find another one
+            //       fired only when necessary (but that still catches all cases)
+            this.connections.connect(
+                Main.layoutManager._backgroundGroup,
+                'notify',
+                _ => this.update_wallpaper()
+            );
         } else {
-            this.paint_signals.disconnect_all();
+            effect = new Shell.BlurEffect({
+                brightness: this.brightness,
+                radius: this.sigma * 2 * monitor.geometry_scale,
+                mode: Shell.BlurMode.BACKGROUND
+            });
+
+            // HACK
+            //
+            //`Shell.BlurEffect` does not repaint when shadows are under it. [1]
+            //
+            // This does not entirely fix this bug (shadows caused by windows
+            // still cause artifacts), but it prevents the shadows of the panel
+            // buttons to cause artifacts on the panel itself
+            //
+            // [1]: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2857
+
+            if (this.settings.HACKS_LEVEL === 1) {
+                this._log("dash hack level 1");
+                this.paint_signals.disconnect_all();
+
+                let rp = () => {
+                    effect.queue_repaint();
+                };
+
+                dash._box.get_children().forEach((icon) => {
+                    try {
+                        let zone = icon.get_child_at_index(0);
+
+                        this.connections.connect(zone, [
+                            'enter-event', 'leave-event', 'button-press-event'
+                        ], rp);
+                    } catch (e) {
+                        this._warn(`${e}, continuing`);
+                    }
+                });
+
+                this.connections.connect(dash._box, 'actor-added', (_, actor) => {
+                    try {
+                        let zone = actor.get_child_at_index(0);
+
+                        this.connections.connect(zone, [
+                            'enter-event', 'leave-event', 'button-press-event'
+                        ], rp);
+                    } catch (e) {
+                        this._warn(`${e}, continuing`);
+                    }
+                });
+
+                let show_apps = dash._showAppsIcon;
+
+                this.connections.connect(show_apps, [
+                    'enter-event', 'leave-event', 'button-press-event'
+                ], rp);
+
+                this.connections.connect(dash, 'leave-event', rp);
+            } else if (this.settings.HACKS_LEVEL === 2) {
+                this._log("dash hack level 2");
+
+                this.paint_signals.connect(background, effect);
+            } else {
+                this.paint_signals.disconnect_all();
+            }
         }
 
-        // create infos
-        let infos = new DashInfos(
-            this, dash, background_parent, effect, this.settings
-        );
+        // store the scale in the effect in order to retrieve it in set_sigma
+        effect.scale = monitor.geometry_scale;
 
-        // update the background
-        this.update_background();
+        background.add_effect(effect);
 
-        // returns infos
-        return infos;
+        return [background, effect];
+    }
+
+    change_blur_type() {
+        this.is_static = this.settings.dash_to_dock.STATIC_BLUR;
+        this.emit('change-blur-type', true);
+
+        this.update_wallpaper();
+        this.update_background();
+        this.update_size();
     }
 
     /// Connect when overview if opened/closed to hide/show the blur accordingly
@@ -276,6 +476,15 @@ export const DashBlur = class DashBlur {
             this.emit('reset-background', true);
     }
 
+    update_wallpaper() {
+        if (this.is_static)
+            this.emit('update-wallpaper', true);
+    }
+
+    update_size() {
+        this.emit('update-size', true);
+    }
+
     set_sigma(sigma) {
         this.sigma = sigma;
         this.emit('update-sigma', true);
@@ -286,6 +495,11 @@ export const DashBlur = class DashBlur {
         this.emit('update-brightness', true);
     }
 
+    set_corner_radius(radius) {
+        this.corner_radius = radius;
+        this.emit('update-corner-radius', true);
+    }
+
     // not implemented for dynamic blur
     set_color(c) { }
     set_noise_amount(n) { }

+ 7 - 2
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/lockscreen.js

@@ -21,6 +21,7 @@ export const LockscreenBlur = class LockscreenBlur {
         this.connections = connections;
         this.settings = settings;
         this.effects_manager = effects_manager;
+        this.enabled = false;
     }
 
     enable() {
@@ -43,6 +44,8 @@ export const LockscreenBlur = class LockscreenBlur {
             : this.settings.NOISE_LIGHTNESS;
 
         this.update_lockscreen();
+
+        this.enabled = true;
     }
 
     update_lockscreen() {
@@ -64,7 +67,7 @@ export const LockscreenBlur = class LockscreenBlur {
 
         let blur_effect = new Shell.BlurEffect({
             name: 'blur',
-            sigma: sigma,
+            radius: sigma * 2,
             brightness: brightness
         });
 
@@ -118,7 +121,7 @@ export const LockscreenBlur = class LockscreenBlur {
             if (blur_effect) {
                 blur_effect.set({
                     brightness: brightness,
-                    sigma: sigma * blur_effect.scale,
+                    radius: sigma * 2 * blur_effect.scale,
                 });
             }
         }
@@ -158,6 +161,8 @@ export const LockscreenBlur = class LockscreenBlur {
             original_updateBackgroundEffects;
 
         this.connections.disconnect_all();
+
+        this.enabled = false;
     }
 
     _log(str) {

+ 11 - 9
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/overview.js

@@ -160,6 +160,7 @@ export const OverviewBlur = class OverviewBlur {
 
     create_background_actor(monitor, is_transition) {
         let bg_actor = new Meta.BackgroundActor({
+            name: "blur-my-shell_background_actor",
             meta_display: global.display,
             monitor: monitor.index
         });
@@ -184,10 +185,9 @@ export const OverviewBlur = class OverviewBlur {
             brightness: this.settings.overview.CUSTOMIZE
                 ? this.settings.overview.BRIGHTNESS
                 : this.settings.BRIGHTNESS,
-            sigma: this.settings.overview.CUSTOMIZE
+            radius: (this.settings.overview.CUSTOMIZE
                 ? this.settings.overview.SIGMA
-                : this.settings.SIGMA
-                * monitor.geometry_scale,
+                : this.settings.SIGMA) * 2 * monitor.geometry_scale,
             mode: Shell.BlurMode.ACTOR
         });
 
@@ -235,7 +235,7 @@ export const OverviewBlur = class OverviewBlur {
 
     set_sigma(s) {
         this.effects.forEach(effect => {
-            effect.blur_effect.sigma = s * effect.blur_effect.scale;
+            effect.blur_effect.radius = s * 2 * effect.blur_effect.scale;
         });
     }
 
@@ -264,13 +264,15 @@ export const OverviewBlur = class OverviewBlur {
     }
 
     remove_background_actors() {
-        Main.layoutManager.overviewGroup.get_children().forEach(actor => {
-            if (actor.constructor.name === 'Meta_BackgroundActor') {
-                actor.get_effects().forEach(effect => {
+        Main.layoutManager.overviewGroup.get_children().forEach(child => {
+            if (child instanceof Meta.BackgroundActor
+                && child.get_name() == "blur-my-shell_background_actor"
+            ) {
+                child.get_effects().forEach(effect => {
                     this.effects_manager.remove(effect);
                 });
-                Main.layoutManager.overviewGroup.remove_child(actor);
-                actor.destroy();
+                Main.layoutManager.overviewGroup.remove_child(child);
+                child.destroy();
             }
         });
         this.effects = [];

+ 24 - 8
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/panel.js

@@ -53,6 +53,9 @@ export const PanelBlur = class PanelBlur {
         // the blur when a window is near a panel
         this.connect_to_windows_and_overview();
 
+        // update the classname if the panel to have or have not light text
+        this.update_light_text_classname();
+
         // connect to every background change (even without changing image)
         // FIXME this signal is fired very often, so we should find another one
         //       fired only when necessary (but that still catches all cases)
@@ -167,10 +170,9 @@ export const PanelBlur = class PanelBlur {
             brightness: this.settings.panel.CUSTOMIZE
                 ? this.settings.panel.BRIGHTNESS
                 : this.settings.BRIGHTNESS,
-            sigma: this.settings.panel.CUSTOMIZE
+            radius: (this.settings.panel.CUSTOMIZE
                 ? this.settings.panel.SIGMA
-                : this.settings.SIGMA
-                * monitor.geometry_scale,
+                : this.settings.SIGMA) * 2 * monitor.geometry_scale,
             mode: this.settings.panel.STATIC_BLUR
                 ? Shell.BlurMode.ACTOR
                 : Shell.BlurMode.BACKGROUND
@@ -317,7 +319,7 @@ export const PanelBlur = class PanelBlur {
                 Main.layoutManager.monitors.length
                 - this.find_monitor_for(actors.widgets.panel).index - 1
             );
-            if (bg)
+            if (bg && bg.get_content())
                 actors.widgets.background.content.set({
                     background: bg.get_content().background
                 });
@@ -408,6 +410,9 @@ export const PanelBlur = class PanelBlur {
                 this.connections.connect(
                     appDisplay, 'hide', this.show.bind(this)
                 );
+                this.connections.connect(
+                    Main.overview, 'hidden', this.show.bind(this)
+                );
             }
 
         }
@@ -436,10 +441,10 @@ export const PanelBlur = class PanelBlur {
             }
 
             // manage windows at their creation/removal
-            this.connections.connect(global.window_group, 'actor-added',
+            this.connections.connect(global.window_group, 'child-added',
                 this.on_window_actor_added.bind(this)
             );
-            this.connections.connect(global.window_group, 'actor-removed',
+            this.connections.connect(global.window_group, 'child-removed',
                 this.on_window_actor_removed.bind(this)
             );
 
@@ -487,6 +492,14 @@ export const PanelBlur = class PanelBlur {
         this.window_signal_ids = new Map();
     }
 
+    /// Update the css classname of the panel for light theme
+    update_light_text_classname(disable = false) {
+        if (this.settings.panel.FORCE_LIGHT_TEXT && !disable)
+            Main.panel.add_style_class_name("panel-light-text");
+        else
+            Main.panel.remove_style_class_name("panel-light-text");
+    }
+
     /// Callback when a new window is added
     on_window_actor_added(container, meta_window_actor) {
         this.window_signal_ids.set(meta_window_actor, [
@@ -532,6 +545,7 @@ export const PanelBlur = class PanelBlur {
             && meta_window.get_window_type() !== Meta.WindowType.DESKTOP
             // exclude Desktop Icons NG
             && meta_window.get_gtk_application_id() !== "com.rastersoft.ding"
+            && meta_window.get_gtk_application_id() !== "com.desktop.ding"
         );
 
         // check if at least one window is near enough to each panel and act
@@ -590,7 +604,7 @@ export const PanelBlur = class PanelBlur {
     /// enabling/disabling other effects.
     invalidate_blur(actors) {
         if (this.settings.panel.STATIC_BLUR && actors.widgets.background)
-            actors.widgets.background.get_content().invalidate();
+            actors.widgets.background.get_content()?.invalidate();
     }
 
     invalidate_all_blur() {
@@ -599,7 +613,7 @@ export const PanelBlur = class PanelBlur {
 
     set_sigma(s) {
         this.actors_list.forEach(actors => {
-            actors.effects.blur.sigma = s * actors.effects.blur.scale;
+            actors.effects.blur.radius = s * 2 * actors.effects.blur.scale;
             this.invalidate_blur(actors);
         });
     }
@@ -662,6 +676,8 @@ export const PanelBlur = class PanelBlur {
 
         this.disconnect_from_windows_and_overview();
 
+        this.update_light_text_classname(true);
+
         this.actors_list.forEach(actors => {
             this.set_should_override_panel(actors, false);
             this.effects_manager.remove(actors.effects.noise);

+ 4 - 4
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/screenshot.js

@@ -82,10 +82,9 @@ export const ScreenshotBlur = class ScreenshotBlur {
             brightness: this.settings.screenshot.CUSTOMIZE
                 ? this.settings.screenshot.BRIGHTNESS
                 : this.settings.BRIGHTNESS,
-            sigma: this.settings.screenshot.CUSTOMIZE
+            radius: (this.settings.screenshot.CUSTOMIZE
                 ? this.settings.screenshot.SIGMA
-                : this.settings.SIGMA
-                * monitor.geometry_scale,
+                : this.settings.SIGMA) * 2 * monitor.geometry_scale,
             mode: Shell.BlurMode.ACTOR
         });
 
@@ -117,7 +116,7 @@ export const ScreenshotBlur = class ScreenshotBlur {
 
     set_sigma(s) {
         this.effects.forEach(effect => {
-            effect.blur_effect.sigma = s * effect.blur_effect;
+            effect.blur_effect.radius = s * 2 * effect.blur_effect;
         });
     }
 
@@ -150,6 +149,7 @@ export const ScreenshotBlur = class ScreenshotBlur {
             if (actor._blur_actor) {
                 actor.remove_child(actor._blur_actor);
                 actor._blur_actor.destroy();
+                delete actor._blur_actor;
             }
         });
         this.effects = [];

+ 6 - 6
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/components/window_list.js

@@ -24,7 +24,7 @@ export const WindowListBlur = class WindowListBlur {
         // if is window-list
         this.connections.connect(
             Main.layoutManager.uiGroup,
-            'actor-added',
+            'child-added',
             (_, child) => this.try_blur(child)
         );
 
@@ -46,9 +46,9 @@ export const WindowListBlur = class WindowListBlur {
 
             let blur_effect = new Shell.BlurEffect({
                 name: 'window-list-blur',
-                sigma: this.settings.window_list.CUSTOMIZE
-                    ? this.settings.window_list.SIGMA
-                    : this.settings.SIGMA,
+                radius: (this.settings.window_list.CUSTOMIZE
+                        ? this.settings.window_list.SIGMA
+                        : this.settings.SIGMA) * 2,
                 brightness: this.settings.window_list.CUSTOMIZE
                     ? this.settings.window_list.BRIGHTNESS
                     : this.settings.BRIGHTNESS,
@@ -65,7 +65,7 @@ export const WindowListBlur = class WindowListBlur {
 
             this.connections.connect(
                 child._windowList,
-                'actor-added',
+                'child-added',
                 (_, window) => this.blur_window_button(window)
             );
 
@@ -117,7 +117,7 @@ export const WindowListBlur = class WindowListBlur {
 
     set_sigma(s) {
         this.effects.forEach(effect => {
-            effect.blur_effect.sigma = s;
+            effect.blur_effect.radius = s * 2;
         });
     }
 

+ 2 - 0
gnome/.local/share/gnome-shell/extensions/blur-my-shell@aunetx/conveniences/keys.js

@@ -49,6 +49,7 @@ export const Keys = [
             { type: Type.D, name: "noise-lightness" },
             { type: Type.B, name: "static-blur" },
             { type: Type.B, name: "unblur-in-overview" },
+            { type: Type.B, name: "force-light-text" },
             { type: Type.B, name: "override-background" },
             { type: Type.I, name: "style-panel" },
             { type: Type.B, name: "override-background-dynamically" },
@@ -67,6 +68,7 @@ export const Keys = [
             { type: Type.B, name: "unblur-in-overview" },
             { type: Type.B, name: "override-background" },
             { type: Type.I, name: "style-dash-to-dock" },
+            { type: Type.I, name: "corner-radius" },
         ]
     },
     {

Some files were not shown because too many files changed in this diff