summaryrefslogtreecommitdiff
path: root/raveos-gnome-theme/theme-data/extensions/installed
diff options
context:
space:
mode:
authorAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
committerAlexanderCurl <alexc@alexc.hu>2026-04-18 17:46:06 +0100
commit70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 (patch)
treeab1123d4067c1b086dd6faa7ee4ea643236b565a /raveos-gnome-theme/theme-data/extensions/installed
parent5d94c0a7d44a2255b81815a52a7056a94a39842d (diff)
downloadRaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.tar.gz
RaveOS-PKGBUILD-70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69.zip
Replaced file structures for themes in the correct format
Diffstat (limited to 'raveos-gnome-theme/theme-data/extensions/installed')
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/CC-BY-SA-4.0.txt170
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-2.0-or-later.txt117
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-3.0-or-later.txt232
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/LGPL-2.0-or-later.txt174
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/MIT.txt9
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.freedesktop.login1.Manager.xml112
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.SessionManager.xml20
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.Shell.Extensions.ShutdownTimer.xml26
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js217
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js235
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js72
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js286
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/extension.js58
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg4
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg.license3
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/cs/LC_MESSAGES/ShutdownTimer.mobin0 -> 1927 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/de/LC_MESSAGES/ShutdownTimer.mobin0 -> 11198 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/el/LC_MESSAGES/ShutdownTimer.mobin0 -> 2281 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/es/LC_MESSAGES/ShutdownTimer.mobin0 -> 1036 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/fr/LC_MESSAGES/ShutdownTimer.mobin0 -> 1890 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/it/LC_MESSAGES/ShutdownTimer.mobin0 -> 1929 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/nl/LC_MESSAGES/ShutdownTimer.mobin0 -> 3538 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pl/LC_MESSAGES/ShutdownTimer.mobin0 -> 2033 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pt/LC_MESSAGES/ShutdownTimer.mobin0 -> 1857 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/ru/LC_MESSAGES/ShutdownTimer.mobin0 -> 1203 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/sk/LC_MESSAGES/ShutdownTimer.mobin0 -> 11440 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/tr/LC_MESSAGES/ShutdownTimer.mobin0 -> 11408 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/zh_CN/LC_MESSAGES/ShutdownTimer.mobin0 -> 1880 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json22
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json.license3
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/info-fetcher.js108
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/injection.js192
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/install.js64
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/menu-item.js573
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/quicksettings.js77
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/schedule-info.js150
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/session-mode-aware.js41
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/text-box.js130
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/translation.js51
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/util.js178
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules8
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules.legacy12
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/dem.shutdowntimer.policy.in20
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/prefs.js240
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/gschemas.compiledbin0 -> 1797 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/org.gnome.shell.extensions.shutdowntimer-deminder.gschema.xml150
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/stylesheet.css29
-rwxr-xr-xraveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/installer.sh300
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/shutdowntimerctl55
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/ui/prefs.ui482
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE339
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js656
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js82
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/subprocess.js54
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg2
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg2
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg2
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg8
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg133
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg15
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg3
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg3
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg45
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg125
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mobin0 -> 13793 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mobin0 -> 10985 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mobin0 -> 3343 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mobin0 -> 12216 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mobin0 -> 11776 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mobin0 -> 5518 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mobin0 -> 4004 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mobin0 -> 12096 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mobin0 -> 3030 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mobin0 -> 17140 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mobin0 -> 6939 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mobin0 -> 3817 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mobin0 -> 3501 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mobin0 -> 3935 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mobin0 -> 8396 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mobin0 -> 10711 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mobin0 -> 8559 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mobin0 -> 8416 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mobin0 -> 15510 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mobin0 -> 7900 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js77
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json21
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js138
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui1394
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiledbin0 -> 1972 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml155
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js1072
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css19
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js358
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/appIndicator.js1607
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusMenu.js990
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusProxy.js108
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusUtils.js121
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/extension.js90
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconCache.js179
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconTheme.js12
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/custom-icons-symbolic.svg46
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/general-preferences-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/indicatorStatusIcon.js613
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/DBusMenu.xml66
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierItem.xml130
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierWatcher.xml38
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces.js52
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ar/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2722 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/bg/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2557 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/cs/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2221 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/de/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 949 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/fr/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2377 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/hu/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2646 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/it/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 1205 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ja/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 972 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ka/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 3640 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ko/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2373 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/nl/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 1024 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/oc/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2334 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pl/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2451 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pt_BR/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 1067 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ru/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2727 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sk/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2239 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 1132 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr@latin/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 1020 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ta/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 3523 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/tr/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2497 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/uk/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 3061 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_CN/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2338 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_TW/LC_MESSAGES/AppIndicatorExtension.mobin0 -> 2449 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/logger.js105
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/metadata.json18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/pixmapsUtils.js68
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/customIconPage.js240
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/generalPage.js159
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/prefs.js45
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/promiseUtils.js324
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/gschemas.compiledbin0 -> 820 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/org.gnome.shell.extensions.appindicator.gschema.xml49
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/settingsManager.js55
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/statusNotifierWatcher.js320
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/tools/busAnalyzer.js46
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/trayIconsManager.js103
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/util.js271
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/LICENSE674
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js265
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js614
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/coverflow_alt_tab.js91
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js421
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/lockscreen.js89
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js226
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js577
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/screenshot.js110
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js148
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js106
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js112
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js90
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js190
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js91
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js223
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js168
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js363
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js40
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js38
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/client.js58
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/iface.xml12
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/services.js93
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.glsl111
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.js168
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.glsl93
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.js211
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.glsl71
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.js120
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.glsl76
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.js140
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/effects.js362
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.glsl70
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.js213
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.glsl14
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.js31
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.glsl52
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.js207
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_dynamic_gaussian_blur.js43
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_static_gaussian_blur.js43
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.glsl20
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.js76
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/pixelize.js88
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.glsl18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.js31
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.glsl57
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.js120
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/extension.js614
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/add-row-symbolic.svg4
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/applications-symbolic.svg56
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/bottom-panel-symbolic.svg44
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dash-symbolic.svg44
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/down-symbolic.svg2
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/duplicate-row-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dynamic-mode-symbolic.svg71
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/edit-row-symbolic.svg14
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/heart-symbolic.svg2
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/other-symbolic.svg1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/overview-symbolic.svg56
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/pipelines-symbolic.svg64
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/remove-row-symbolic.svg3
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/reset-symbolic.svg7
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-row-symbolic.svg2
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-window-symbolic.svg2
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/static-mode-symbolic.svg71
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/af/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 7989 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ar/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 16821 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/az/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 1203 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/be/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 3499 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/bg/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 16153 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ca/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17615 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/cs/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 16877 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/da/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 927 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/de/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17826 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/el/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 15541 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/es/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17367 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/et/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 2815 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fi/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 10156 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fr/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17531 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/he/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 592 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hi/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 25812 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hr/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 8690 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hu/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17520 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/id/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 16481 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/it/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17361 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ja/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 18455 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ka/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 6964 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ko/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 14985 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ml/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 523 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/mrh/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 521 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/my/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 362 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nb_NO/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 7966 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nl/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17331 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nn/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 1185 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pl/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 15190 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 16967 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt_BR/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17153 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ro/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 10249 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ru/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 22437 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sk/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 1083 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sl/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 11678 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sv/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 16934 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ta/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 28773 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/th/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 362 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/tr/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 17288 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/uk/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 21663 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/vi/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 18415 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_CN/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 15517 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_TW/LC_MESSAGES/blur-my-shell@aunetx.mobin0 -> 15248 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/metadata.json28
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications.js217
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications_management/application_row.js112
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/dash.js81
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/menu.js68
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/other.js86
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/overview.js59
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/panel.js102
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines.js98
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effect_row.js222
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effects_dialog.js174
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_choose_row.js106
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_group.js99
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/prefs.js51
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/gschemas.compiledbin0 -> 9976 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/org.gnome.shell.extensions.blur-my-shell.gschema.xml595
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/stylesheet.css573
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/application-row.ui47
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/applications.ui276
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/dash.ui172
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/effects-dialog.ui77
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/menu.ui37
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/other.ui208
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/overview.ui143
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/panel.ui240
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-choose-row.ui39
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-group.ui29
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipelines.ui44
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/LICENSE674
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/extension.js631
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ar/LC_MESSAGES/burn-my-windows.mobin0 -> 5281 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/az/LC_MESSAGES/burn-my-windows.mobin0 -> 3864 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/cs/LC_MESSAGES/burn-my-windows.mobin0 -> 7277 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/da/LC_MESSAGES/burn-my-windows.mobin0 -> 9220 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/de/LC_MESSAGES/burn-my-windows.mobin0 -> 8930 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/el/LC_MESSAGES/burn-my-windows.mobin0 -> 12729 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/eo/LC_MESSAGES/burn-my-windows.mobin0 -> 4166 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/es/LC_MESSAGES/burn-my-windows.mobin0 -> 10081 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/et/LC_MESSAGES/burn-my-windows.mobin0 -> 953 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fa/LC_MESSAGES/burn-my-windows.mobin0 -> 11241 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fi/LC_MESSAGES/burn-my-windows.mobin0 -> 9123 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fr/LC_MESSAGES/burn-my-windows.mobin0 -> 7749 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/he/LC_MESSAGES/burn-my-windows.mobin0 -> 2201 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hi/LC_MESSAGES/burn-my-windows.mobin0 -> 10441 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hu/LC_MESSAGES/burn-my-windows.mobin0 -> 8009 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/id/LC_MESSAGES/burn-my-windows.mobin0 -> 9406 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/it/LC_MESSAGES/burn-my-windows.mobin0 -> 9014 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ja/LC_MESSAGES/burn-my-windows.mobin0 -> 5511 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/kab/LC_MESSAGES/burn-my-windows.mobin0 -> 863 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ko/LC_MESSAGES/burn-my-windows.mobin0 -> 9898 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/lt/LC_MESSAGES/burn-my-windows.mobin0 -> 6126 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nb_NO/LC_MESSAGES/burn-my-windows.mobin0 -> 2586 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nl/LC_MESSAGES/burn-my-windows.mobin0 -> 9656 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ota/LC_MESSAGES/burn-my-windows.mobin0 -> 1870 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pl/LC_MESSAGES/burn-my-windows.mobin0 -> 9924 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt/LC_MESSAGES/burn-my-windows.mobin0 -> 7908 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt_BR/LC_MESSAGES/burn-my-windows.mobin0 -> 8067 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ro/LC_MESSAGES/burn-my-windows.mobin0 -> 7798 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ru/LC_MESSAGES/burn-my-windows.mobin0 -> 12502 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sk/LC_MESSAGES/burn-my-windows.mobin0 -> 7797 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sv/LC_MESSAGES/burn-my-windows.mobin0 -> 3939 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ta/LC_MESSAGES/burn-my-windows.mobin0 -> 12410 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/tr/LC_MESSAGES/burn-my-windows.mobin0 -> 9773 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/uk/LC_MESSAGES/burn-my-windows.mobin0 -> 12444 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hans/LC_MESSAGES/burn-my-windows.mobin0 -> 9180 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hant/LC_MESSAGES/burn-my-windows.mobin0 -> 6977 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/metadata.json23
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/prefs.js941
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/resources/burn-my-windows.gresourcebin0 -> 2527448 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/gschemas.compiledbin0 -> 11804 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml1119
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml60
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/42_custom_reboot11
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/bootloader.js36
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/efibootmgr.js101
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/extension.js170
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/grub.js205
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/metadata.json17
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/prefs.js101
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/gschemas.compiledbin0 -> 413 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/org.gnome.extensions.customreboot.gschema.xml15
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/systemdBoot.js121
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/utils.js89
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING339
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md53
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui3100
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js1305
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js1635
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js168
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js235
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js1177
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js280
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js14
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js1
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js5
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js170
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js2698
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js201
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js17
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js337
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js287
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mobin0 -> 16188 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mobin0 -> 17759 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mobin0 -> 3761 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mobin0 -> 10315 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mobin0 -> 14194 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mobin0 -> 12042 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mobin0 -> 12854 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mobin0 -> 10171 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mobin0 -> 14414 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mobin0 -> 8811 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mobin0 -> 14071 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mobin0 -> 8549 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mobin0 -> 11038 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mobin0 -> 12116 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mobin0 -> 21983 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mobin0 -> 18116 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mobin0 -> 14009 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mobin0 -> 13719 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mobin0 -> 9191 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mobin0 -> 10065 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mobin0 -> 10837 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mobin0 -> 9758 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mobin0 -> 11998 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mobin0 -> 14377 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mobin0 -> 18114 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mobin0 -> 8918 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mobin0 -> 13120 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mobin0 -> 11174 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mobin0 -> 8812 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mobin0 -> 13656 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mobin0 -> 14912 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mobin0 -> 18074 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mobin0 -> 12245 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mobin0 -> 12073 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js1535
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js91
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg139
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg82
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg82
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg528
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js121
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js1168
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiledbin0 -> 8725 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml644
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css1478
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js572
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js735
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js678
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/COPYING339
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/NEWS1652
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/extension.js211
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ab/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 643 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/af/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 686 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/an/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 556 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ar/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 749 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/as/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 550 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/be/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 778 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bg/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 698 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bn_IN/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 799 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bs/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 504 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 686 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca@valencia/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 643 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/cs/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 675 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/da/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 657 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/de/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 811 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/el/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 764 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/en_GB/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 669 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eo/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 708 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/es/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 700 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/et/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 656 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eu/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 839 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fa/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 754 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fi/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 756 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fr/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 687 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fur/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 657 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gd/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 781 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gl/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 790 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gu/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 607 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/he/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 720 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hi/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 788 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hr/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 714 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hu/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 686 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ia/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 786 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/id/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 678 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/is/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 646 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/it/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 654 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ja/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 695 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ka/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 697 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kab/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 754 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kk/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 666 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/km/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 532 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kn/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 540 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ko/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 629 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lt/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 707 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lv/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 678 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mjw/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 549 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ml/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 980 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mr/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 564 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ms/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 638 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nb/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 666 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ne/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 790 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nl/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 697 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/oc/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 645 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/or/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 543 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pa/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 757 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pl/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 709 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 788 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt_BR/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 692 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ro/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 775 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ru/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 767 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sk/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 678 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sl/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 769 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 839 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr@latin/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 757 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sv/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 616 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ta/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 568 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/te/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 547 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tg/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 671 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/th/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 747 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tr/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 656 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/uk/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 818 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/vi/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 683 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_CN/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 648 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_HK/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 510 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_TW/LC_MESSAGES/gnome-shell-extension-drive-menu.mobin0 -> 736 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/metadata.json14
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/stylesheet.css13
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/LICENSE674
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/extension.js39
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/metadata.json12
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/extension.js18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/metadata.json19
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/CHANGELOG.md29
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/LICENSE21
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/README.md24
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/extension.js133
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/metadata.json19
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefs.js47
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefspage.js275
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/gschemas.compiledbin0 -> 424 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/org.gnome.shell.extensions.notification-timeout.gschema.xml21
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/extension.js180
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/locale/de/LC_MESSAGES/rebootintoWindows@nimz.cool.mobin0 -> 604 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/metadata.json18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/extension.js159
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/de/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 514 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/es/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 705 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fa/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 728 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fr/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 709 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/hu/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 695 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/it/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 591 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/nl/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 664 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pl/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 640 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pt_BR/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 688 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/ru/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 778 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/tr/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 724 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/zh_TW/LC_MESSAGES/reboottouefi@ubaygd.com.mobin0 -> 605 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/metadata.json18
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/COPYING339
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/NEWS1652
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/extension.js91
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/metadata.json14
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/extension.js580
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/en/LC_MESSAGES/unity-window-switcher.mobin0 -> 1732 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/hu/LC_MESSAGES/unity-window-switcher.mobin0 -> 1839 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/metadata.json14
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/prefs.js256
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/gschemas.compiledbin0 -> 1060 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/org.gnome.shell.extensions.unity-window-switcher-reforged.gschema.xml66
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/settings.ui215
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js346
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/COPYING339
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/NEWS1652
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/extension.js58
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/metadata.json17
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/prefs.js136
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/gschemas.compiledbin0 -> 296 bytes
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/org.gnome.shell.extensions.user-theme.gschema.xml15
-rw-r--r--raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/util.js26
550 files changed, 61462 insertions, 0 deletions
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/CC-BY-SA-4.0.txt b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/CC-BY-SA-4.0.txt
new file mode 100644
index 0000000..835a683
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/CC-BY-SA-4.0.txt
@@ -0,0 +1,170 @@
+Creative Commons Attribution-ShareAlike 4.0 International
+
+ Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses.
+
+Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors.
+
+Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described.
+
+Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public.
+
+Creative Commons Attribution-ShareAlike 4.0 International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.
+
+Section 1 – Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.
+
+ c. BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License.
+
+ d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.
+
+ e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.
+
+ f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.
+
+ g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike.
+
+ h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License.
+
+ i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.
+
+ j. Licensor means the individual(s) or entity(ies) granting rights under this Public License.
+
+ k. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.
+
+ l. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.
+
+ m. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.
+
+Section 2 – Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:
+
+ A. reproduce and Share the Licensed Material, in whole or in part; and
+
+ B. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section 6(a).
+
+ 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.
+
+ B. Additional offer from the Licensor – Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply.
+
+ C. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this Public License.
+
+ 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties.
+
+Section 3 – License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified form), You must:
+
+ A. retain the following if it is supplied by the Licensor with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;
+
+ B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and
+
+ C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.
+
+ 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.
+
+ b. ShareAlike.In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply.
+
+ 1. The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License.
+
+ 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material.
+
+ 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply.
+
+Section 4 – Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and
+
+ c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.
+For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.
+
+Section 5 – Disclaimer of Warranties and Limitation of Liability.
+
+ a. Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.
+
+ b. To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.
+
+ c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.
+
+Section 6 – Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ c. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.
+
+ d. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.
+
+ e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.
+
+Section 7 – Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.
+
+Section 8 – Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.
+
+ c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.
+
+Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-2.0-or-later.txt b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-2.0-or-later.txt
new file mode 100644
index 0000000..17cb286
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-2.0-or-later.txt
@@ -0,0 +1,117 @@
+GNU GENERAL PUBLIC LICENSE
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+ one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
+
+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-3.0-or-later.txt b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-3.0-or-later.txt
new file mode 100644
index 0000000..d41c0bd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/GPL-3.0-or-later.txt
@@ -0,0 +1,232 @@
+GNU GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+
+Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The GNU General Public License is a free, copyleft license for software and other kinds of works.
+
+The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.
+
+To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others.
+
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
+
+Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it.
+
+For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions.
+
+Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users.
+
+Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free.
+
+The precise terms and conditions for copying, distribution and modification follow.
+
+TERMS AND CONDITIONS
+
+0. Definitions.
+
+“This License” refers to version 3 of the GNU General Public License.
+
+“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.
+
+“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations.
+
+To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work.
+
+A “covered work” means either the unmodified Program or a work based on the Program.
+
+To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.
+
+To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.
+
+1. Source Code.
+The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work.
+
+A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.
+
+The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.
+
+The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same work.
+
+2. Basic Permissions.
+All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
+
+3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.
+
+When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures.
+
+4. Conveying Verbatim Copies.
+You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.
+
+5. Conveying Modified Source Versions.
+You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”.
+
+ c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.
+
+A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.
+
+6. Conveying Non-Source Forms.
+You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
+
+ d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.
+
+A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.
+
+“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).
+
+The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.
+
+Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.
+
+7. Additional Terms.
+“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.
+
+All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.
+
+8. Termination.
+You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).
+
+However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.
+
+Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.
+
+9. Acceptance Not Required for Having Copies.
+You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.
+
+10. Automatic Licensing of Downstream Recipients.
+Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.
+
+An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.
+
+11. Patents.
+A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”.
+
+A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.
+
+In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.
+
+A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.
+
+12. No Surrender of Others' Freedom.
+If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.
+
+13. Use with the GNU Affero General Public License.
+Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such.
+
+14. Revised Versions of this License.
+The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program.
+
+Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.
+
+15. Disclaimer of Warranty.
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. Limitation of Liability.
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+17. Interpretation of Sections 15 and 16.
+If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”.
+
+You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>.
+
+The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/LGPL-2.0-or-later.txt b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/LGPL-2.0-or-later.txt
new file mode 100644
index 0000000..eb3a4cd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/LGPL-2.0-or-later.txt
@@ -0,0 +1,174 @@
+GNU LIBRARY GENERAL PUBLIC LICENSE
+
+Version 2, June 1991
+
+Copyright (C) 1991 Free Software Foundation, Inc.
+51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
+
+This license, the Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too.
+
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library, or if you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link a program with the library, you must provide complete object files to the recipients so that they can relink them with the library, after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
+
+Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library.
+
+Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license.
+
+The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such.
+
+Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better.
+
+However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries.
+
+The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, while the latter only works together with the library.
+
+Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library General Public License (also called "this License"). Each licensee is addressed as "you".
+
+A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
+
+The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
+
+"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
+
+Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
+
+1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
+
+You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
+
+(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
+
+3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
+
+Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
+
+This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
+
+4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
+
+If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
+
+5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
+
+However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
+
+When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
+
+If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
+
+Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
+
+6. As an exception to the Sections above, you may also compile or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
+
+You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
+
+ a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
+
+For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
+
+It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
+
+7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
+
+ b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
+
+8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
+
+9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
+
+10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
+
+11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
+
+12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
+
+13. The Free Software Foundation may publish revised and/or new versions of the Library General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
+
+14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
+
+NO WARRANTY
+
+15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License).
+
+To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
+
+ one line to give the library's name and an idea of what it does.
+ Copyright (C) year name of author
+
+ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+
+ This library 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 Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/MIT.txt b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/MIT.txt
new file mode 100644
index 0000000..2071b23
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/LICENSES/MIT.txt
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+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.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.freedesktop.login1.Manager.xml b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.freedesktop.login1.Manager.xml
new file mode 100644
index 0000000..5c7fdc0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.freedesktop.login1.Manager.xml
@@ -0,0 +1,112 @@
+<!--
+ SPDX-FileCopyrightText: 2023 dbus.freedesktop.org
+ SPDX-License-Identifier: GPL-3.0-or-later
+-->
+<node>
+ <interface name="org.freedesktop.login1.Manager">
+ <method name="PowerOff">
+ <arg type="b" name="interactive" direction="in"/>
+ </method>
+ <method name="PowerOffWithFlags">
+ <arg type="t" name="flags" direction="in"/>
+ </method>
+ <method name="Reboot">
+ <arg type="b" name="interactive" direction="in"/>
+ </method>
+ <method name="RebootWithFlags">
+ <arg type="t" name="flags" direction="in"/>
+ </method>
+ <method name="Halt">
+ <arg type="b" name="interactive" direction="in"/>
+ </method>
+ <method name="HaltWithFlags">
+ <arg type="t" name="flags" direction="in"/>
+ </method>
+ <method name="Suspend">
+ <arg type="b" name="interactive" direction="in"/>
+ </method>
+ <method name="SuspendWithFlags">
+ <arg type="t" name="flags" direction="in"/>
+ </method>
+ <method name="Hibernate">
+ <arg type="b" name="interactive" direction="in"/>
+ </method>
+ <method name="HibernateWithFlags">
+ <arg type="t" name="flags" direction="in"/>
+ </method>
+ <method name="HybridSleep">
+ <arg type="b" name="interactive" direction="in"/>
+ </method>
+ <method name="HybridSleepWithFlags">
+ <arg type="t" name="flags" direction="in"/>
+ </method>
+ <method name="SuspendThenHibernate">
+ <arg type="b" name="interactive" direction="in"/>
+ </method>
+ <method name="SuspendThenHibernateWithFlags">
+ <arg type="t" name="flags" direction="in"/>
+ </method>
+ <method name="CanPowerOff">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="CanReboot">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="CanHalt">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="CanSuspend">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="CanHibernate">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="CanHybridSleep">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="CanSuspendThenHibernate">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="ScheduleShutdown">
+ <arg type="s" name="type" direction="in"/>
+ <arg type="t" name="usec" direction="in"/>
+ </method>
+ <method name="CancelScheduledShutdown">
+ <arg type="b" name="cancelled" direction="out"/>
+ </method>
+ <method name="CanRebootParameter">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="SetRebootParameter">
+ <arg type="s" name="parameter" direction="in"/>
+ </method>
+ <method name="CanRebootToFirmwareSetup">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="SetRebootToFirmwareSetup">
+ <arg type="b" name="enable" direction="in"/>
+ </method>
+ <method name="CanRebootToBootLoaderMenu">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="SetRebootToBootLoaderMenu">
+ <arg type="t" name="timeout" direction="in"/>
+ </method>
+ <method name="CanRebootToBootLoaderEntry">
+ <arg type="s" name="result" direction="out"/>
+ </method>
+ <method name="SetRebootToBootLoaderEntry">
+ <arg type="s" name="boot_loader_entry" direction="in"/>
+ </method>
+ <method name="SetWallMessage">
+ <arg type="s" name="wall_message" direction="in"/>
+ <arg type="b" name="enable" direction="in"/>
+ </method>
+ <signal name="PrepareForShutdown">
+ <arg type="b" name="start"/>
+ </signal>
+ <signal name="PrepareForSleep">
+ <arg type="b" name="start"/>
+ </signal>
+ </interface>
+</node>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.SessionManager.xml b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.SessionManager.xml
new file mode 100644
index 0000000..fb398fc
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.SessionManager.xml
@@ -0,0 +1,20 @@
+<!--
+ SPDX-FileCopyrightText: 2024 dbus.gnome.org
+ SPDX-License-Identifier: GPL-3.0-or-later
+-->
+<node>
+ <interface name="org.gnome.SessionManager">
+ <method name="Inhibit">
+ <arg type="s" name="app_id" direction="in"/>
+ <arg type="u" name="toplevel_xid" direction="in"/>
+ <arg type="s" name="reason" direction="in"/>
+ <arg type="u" name="flags" direction="in"/>
+ <arg type="u" name="inhibit_cookie" direction="out"/>
+ </method>
+ <method name="Uninhibit">
+ <arg type="u" name="inhibit_cookie" direction="in"/>
+ </method>
+ <method name="Shutdown"/>
+ <method name="Reboot"/>
+ </interface>
+</node>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.Shell.Extensions.ShutdownTimer.xml b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.Shell.Extensions.ShutdownTimer.xml
new file mode 100644
index 0000000..b9769d7
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-interfaces/org.gnome.Shell.Extensions.ShutdownTimer.xml
@@ -0,0 +1,26 @@
+<!--
+ SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+ SPDX-License-Identifier: GPL-3.0-or-later
+-->
+<node>
+ <interface name="org.gnome.shell.Extensions.ShutdownTimer">
+ <method name="ScheduleShutdown">
+ <arg type="b" direction="in" name="shutdown"/>
+ <arg type="s" direction="in" name="action"/>
+ </method>
+ <method name="ScheduleWake">
+ <arg type="b" direction="in" name="wake"/>
+ </method>
+ <method name="GetState">
+ <arg type="s" direction="out" name="state"/>
+ </method>
+ <signal name="OnMessage">
+ <arg type="s" direction="out" name="message"/>
+ </signal>
+ <signal name="OnStateChange">
+ <arg type="s" direction="out" name="state"/>
+ </signal>
+ <signal name="OnExternalChange"/>
+ </interface>
+</node>
+
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js
new file mode 100644
index 0000000..4b5bf9f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js
@@ -0,0 +1,217 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import Gio from 'gi://Gio';
+import * as Control from './control.js';
+import { proxyPromise } from '../modules/util.js';
+import { pgettext as C_, gettext as _ } from '../modules/translation.js';
+import { logDebug } from '../modules/util.js';
+
+export const ACTIONS = {
+ PowerOff: 0,
+ Reboot: 1,
+ Suspend: 2,
+ SuspendThenHibernate: 3,
+ Hibernate: 4,
+ HybridSleep: 5,
+ Halt: 6,
+};
+
+export const WAKE_ACTIONS = { wake: 100, 'no-wake': 101 };
+
+/**
+ * Get supported actions.
+ * In order to show an error when shutdown or reboot are not supported
+ * they are always included here. */
+export async function* supportedActions() {
+ const actionDbus = new Action();
+ for await (const action of Object.keys(ACTIONS).map(async a =>
+ ['PowerOff', 'Reboot'].includes(a) ||
+ (await actionDbus.canShutdownAction(a))
+ ? a
+ : null
+ )) {
+ if (action) {
+ yield action;
+ }
+ }
+}
+
+export class UnsupportedActionError extends Error {}
+
+export class Action {
+ #cancellable = new Gio.Cancellable();
+ #cookie = null;
+
+ #loginProxy = proxyPromise(
+ 'org.freedesktop.login1.Manager',
+ Gio.DBus.system,
+ 'org.freedesktop.login1',
+ '/org/freedesktop/login1',
+ this.#cancellable
+ );
+
+ #screenSaverProxy = proxyPromise(
+ 'org.gnome.ScreenSaver',
+ Gio.DBus.session,
+ 'org.gnome.ScreenSaver',
+ '/org/gnome/ScreenSaver',
+ this.#cancellable
+ );
+
+ #sessionProxy = proxyPromise(
+ 'org.gnome.SessionManager',
+ Gio.DBus.session,
+ 'org.gnome.SessionManager',
+ '/org/gnome/SessionManager',
+ this.#cancellable
+ );
+
+ destroy() {
+ if (this.#cancellable !== null) {
+ this.#cancellable.cancel();
+ this.#cancellable = null;
+ }
+ }
+
+ #poweroffOrReboot(action) {
+ return [ACTIONS.PowerOff, ACTIONS.Reboot].includes(ACTIONS[action]);
+ }
+
+ /**
+ * Perform the shutdown action.
+ *
+ * @param {string} action the shutdown action
+ * @param {boolean} showEndSessionDialog show the end session dialog or directly shutdown
+ *
+ * @returns {Promise} resolves on action completion
+ */
+ async shutdownAction(action, showEndSessionDialog) {
+ if (!(action in ACTIONS))
+ throw new Error(`Unknown shutdown action: ${action}`);
+ logDebug('[shutdownAction]', action);
+
+ await this.uninhibitSuspend();
+
+ const screenSaverProxy = await this.#screenSaverProxy;
+ const [screenSaverActive] = await screenSaverProxy.GetActiveAsync();
+ if (
+ showEndSessionDialog &&
+ !screenSaverActive &&
+ this.#poweroffOrReboot(action)
+ ) {
+ const sessionProxy = await this.#sessionProxy;
+ if (action === 'PowerOff') {
+ await sessionProxy.ShutdownAsync();
+ } else {
+ await sessionProxy.RebootAsync();
+ }
+ } else {
+ const loginProxy = await this.#loginProxy;
+ if (await this.canShutdownAction(action)) {
+ await loginProxy[`${action}Async`](true);
+ } else if (this.#poweroffOrReboot(action)) {
+ await Control.shutdown('now', ACTIONS[action] === ACTIONS.Reboot);
+ } else {
+ throw new UnsupportedActionError();
+ }
+ }
+ }
+
+ /**
+ * Check if a shutdown action can be performed (without authentication).
+ *
+ * @returns {Promise} resolves to `true` if action can be performed, otherwise `false`.
+ */
+ async canShutdownAction(action) {
+ const loginProxy = await this.#loginProxy;
+ if (!(action in ACTIONS))
+ throw new Error(`Unknown shutdown action: ${action}`);
+ const [result] = await loginProxy[`Can${action}Async`]();
+ return result === 'yes';
+ }
+
+ /**
+ * Schedule a wake after some minutes or cancel
+ *
+ * @param {boolean} wake
+ * @param {number} minutes
+ */
+ async wakeAction(wake, minutes) {
+ if (wake) {
+ await Control.wake(minutes);
+ } else {
+ await Control.wakeCancel();
+ }
+ }
+
+ async inhibitSuspend() {
+ if (this.#cookie === null) {
+ const sessionProxy = await this.#sessionProxy;
+ const [cookie] = await sessionProxy.InhibitAsync(
+ 'user',
+ 0,
+ 'Inhibit by Shutdown Timer (GNOME-Shell extension)',
+ /* Suspend flag */ 4
+ );
+ this.#cookie = cookie;
+ }
+ }
+
+ async uninhibitSuspend() {
+ if (this.#cookie !== null) {
+ const sessionProxy = await this.#sessionProxy;
+ await sessionProxy.UninhibitAsync(this.#cookie);
+ this.#cookie = null;
+ }
+ }
+}
+
+/**
+ * Get the translated action label
+ *
+ * @param action
+ */
+export function actionLabel(action) {
+ return {
+ SuspendThenHibernate: _('Suspend then Hibernate'),
+ HybridSleep: _('Hybrid Sleep'),
+ Hibernate: _('Hibernate'),
+ Halt: _('Halt'),
+ Suspend: _('Suspend'),
+ PowerOff: _('Power Off'),
+ Reboot: _('Restart'),
+ wake: _('Wake'),
+ 'no-wake': _('No Wake'),
+ }[action];
+}
+
+export function untilText(action) {
+ return {
+ SuspendThenHibernate: C_('untiltext', 'suspend and hibernate'),
+ HybridSleep: C_('untiltext', 'hybrid sleep'),
+ Hibernate: C_('untiltext', 'hibernate'),
+ Halt: C_('untiltext', 'halt'),
+ Suspend: C_('untiltext', 'suspend'),
+ PowerOff: C_('untiltext', 'shutdown'),
+ Reboot: C_('untiltext', 'reboot'),
+ wake: C_('untiltext', 'wakeup'),
+ }[action];
+}
+
+export function mapLegacyAction(action) {
+ return action in ACTIONS || ['wake', ''].includes(action)
+ ? action
+ : {
+ poweroff: 'PowerOff',
+ shutdown: 'PowerOff',
+ reboot: 'Reboot',
+ suspend: 'Suspend',
+ }[action] ??
+ {
+ p: 'PowerOff',
+ r: 'Reboot',
+ s: 'Suspend',
+ h: 'SuspendThenHibernate',
+ }[action[0].toLowerCase()];
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js
new file mode 100644
index 0000000..28d184a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js
@@ -0,0 +1,235 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import { gettext as _ } from '../modules/translation.js';
+
+import { logDebug } from '../modules/util.js';
+
+function readLine(stream, cancellable) {
+ return new Promise((resolve, reject) => {
+ stream.read_line_async(0, cancellable, (s, res) => {
+ try {
+ const line = s.read_line_finish_utf8(res)[0];
+
+ if (line !== null) {
+ resolve(line);
+ } else {
+ reject(new Error('No line was read!'));
+ }
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+}
+
+function quoteEscape(str) {
+ return str.replaceAll('\\', '\\\\').replaceAll('"', '\\"');
+}
+
+/**
+ * Execute a command asynchronously and check the exit status.
+ *
+ * If given, @cancellable can be used to stop the process before it finishes.
+ *
+ * @param {string[] | string} argv - a list of string arguments or command line that will be parsed
+ * @param {Gio.Cancellable} [cancellable] - optional cancellable object
+ * @param {boolean} shell - run command as shell command
+ * @param logFunc
+ * @returns {Promise<void>} - The process success
+ */
+export function execCheck(
+ argv,
+ cancellable = null,
+ shell = true,
+ logFunc = undefined
+) {
+ if (!shell && typeof argv === 'string') {
+ argv = GLib.shell_parse_argv(argv)[1];
+ }
+
+ const isRootProc = argv[0] && argv[0].endsWith('pkexec');
+
+ if (shell && Array.isArray(argv)) {
+ argv = argv.map(c => `"${quoteEscape(c)}"`).join(' ');
+ }
+ let cancelId = 0;
+ let proc = new Gio.Subprocess({
+ argv: (shell ? ['/bin/sh', '-c'] : []).concat(argv),
+ flags:
+ logFunc !== undefined
+ ? Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE
+ : Gio.SubprocessFlags.NONE,
+ });
+ proc.init(cancellable);
+
+ if (cancellable instanceof Gio.Cancellable) {
+ cancelId = cancellable.connect(() => {
+ if (logFunc !== undefined) {
+ if (isRootProc) {
+ logFunc(`# ${_('Can not cancel root process!')}`);
+ } else {
+ logFunc(`[${_('CANCEL')}]`);
+ }
+ }
+ proc.force_exit();
+ });
+ }
+ let stdoutStream = null;
+ let stderrStream = null;
+ let readLineCancellable = null;
+
+ if (logFunc !== undefined) {
+ readLineCancellable = new Gio.Cancellable();
+ stdoutStream = new Gio.DataInputStream({
+ base_stream: proc.get_stdout_pipe(),
+ close_base_stream: true,
+ });
+
+ stderrStream = new Gio.DataInputStream({
+ base_stream: proc.get_stderr_pipe(),
+ close_base_stream: true,
+ });
+ const readNextLine = async (stream, prefix) => {
+ try {
+ const line = await readLine(stream, readLineCancellable);
+ logFunc(prefix + line);
+ logDebug(line);
+ await readNextLine(stream, prefix);
+ } catch {
+ if (!stream.is_closed()) {
+ stream.close_async(0, null, (s, sRes) => {
+ try {
+ s.close_finish(sRes);
+ } catch (e) {
+ logDebug(`[StreamCloseError] ${e}`);
+ }
+ });
+ }
+ }
+ };
+ // read stdout and stderr asynchronously
+ readNextLine(stdoutStream, '');
+ readNextLine(stderrStream, '# ');
+ }
+
+ return new Promise((resolve, reject) => {
+ proc.wait_check_async(null, (p, res) => {
+ try {
+ const success = p.wait_check_finish(res);
+ if (!success) {
+ let status = p.get_exit_status();
+
+ throw new Gio.IOErrorEnum({
+ code: Gio.io_error_from_errno(status),
+ message: GLib.strerror(status),
+ });
+ }
+
+ resolve();
+ } catch (e) {
+ reject(e);
+ } finally {
+ if (readLineCancellable) readLineCancellable.cancel();
+ readLineCancellable = null;
+ if (cancelId > 0) cancellable.disconnect(cancelId);
+ }
+ });
+ });
+}
+
+export function sleepUntilDeadline(deadlineSeconds, cancellable) {
+ return new Promise((resolve, reject) => {
+ const secondsLeft = () =>
+ deadlineSeconds - GLib.DateTime.new_now_utc().to_unix();
+
+ let timeoutId = 0;
+ let handlerId = cancellable.connect(() => {
+ handlerId = 0;
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ reject(new Error('Sleep until deadline cancelled!'));
+ });
+
+ const continueSleep = () => {
+ timeoutId = setTimeout(() => {
+ timeoutId = 0;
+ if (secondsLeft() > 0) {
+ continueSleep();
+ } else {
+ if (handlerId) {
+ cancellable.disconnect(handlerId);
+ }
+ resolve();
+ }
+ }, 1000);
+ };
+ continueSleep();
+ });
+}
+
+export function installedScriptPath() {
+ for (const name of [
+ 'shutdowntimerctl',
+ `shutdowntimerctl-${GLib.get_user_name()}`,
+ ]) {
+ const standard = GLib.find_program_in_path(name);
+ if (standard !== null) {
+ return standard;
+ }
+ for (const bindir of ['/usr/local/bin/', '/usr/bin/']) {
+ const path = bindir + name;
+ logDebug(`Looking for: ${path}`);
+ if (Gio.File.new_for_path(path).query_exists(null)) {
+ return path;
+ }
+ }
+ }
+ return null;
+}
+
+function execControlScript(args, noScriptArgs) {
+ const installedScript = installedScriptPath();
+ if (installedScript !== null) {
+ return execCheck(['pkexec', installedScript].concat(args), null, false);
+ }
+ if (noScriptArgs === undefined) {
+ throw new Error(_('Privileged script installation required!'));
+ }
+ return execCheck(noScriptArgs, null, false);
+}
+
+export function shutdown(minutes, reboot = false) {
+ logDebug(`[root-shutdown] ${minutes} minutes, reboot: ${reboot}`);
+ return execControlScript(
+ [reboot ? 'reboot' : 'shutdown', `${minutes}`],
+ ['shutdown', reboot ? '-r' : '-P', `${minutes}`]
+ );
+}
+
+function shutdownCancel() {
+ logDebug('[root-shutdown] cancel');
+ return execControlScript(['shutdown-cancel'], ['shutdown', '-c']);
+}
+
+export function wake(minutes) {
+ return execControlScript(['wake', `${minutes}`]);
+}
+
+export function wakeCancel() {
+ return execControlScript(['wake-cancel']);
+}
+
+export async function stopRootModeProtection(info) {
+ if (['PowerOff', 'Reboot'].includes(info.mode)) {
+ await shutdownCancel();
+ }
+}
+export async function startRootModeProtection(info) {
+ if (['PowerOff', 'Reboot'].includes(info.mode)) {
+ await shutdown(Math.max(0, info.minutes) + 1, info.mode === 'Reboot');
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js
new file mode 100644
index 0000000..8db37a4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+
+import { loadInterfaceXML, logDebug } from '../modules/util.js';
+import { Timer } from './timer.js';
+
+export const ShutdownTimerName = 'org.gnome.Shell.Extensions.ShutdownTimer';
+export const ShutdownTimerObjectPath =
+ '/org/gnome/Shell/Extensions/ShutdownTimer';
+export const ShutdownTimerIface = await loadInterfaceXML(ShutdownTimerName);
+
+export class ShutdownTimerDBus {
+ constructor({ settings }) {
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(
+ ShutdownTimerIface,
+ this
+ );
+ this._dbusImpl.export(Gio.DBus.session, ShutdownTimerObjectPath);
+
+ this._timer = new Timer({ settings });
+ this._timer.connect('message', (_, msg) => {
+ logDebug('[shutdown-timer-dbus] msg', msg);
+ this._dbusImpl.emit_signal('OnMessage', new GLib.Variant('(s)', [msg]));
+ });
+ this._timer.connect('change', () => {
+ logDebug('[shutdown-timer-dbus] state', this._timer.state);
+ this._dbusImpl.emit_signal(
+ 'OnStateChange',
+ new GLib.Variant('(s)', [this._timer.state])
+ );
+ });
+ this._timer.connect('change-external', () => {
+ this._dbusImpl.emit_signal('OnExternalChange', null);
+ });
+ }
+
+ async ScheduleShutdownAsync(parameters, invocation) {
+ const [shutdown, action] = parameters;
+ logDebug(
+ '[sdt-dbus] [ScheduleShutdownAsync] shutdown',
+ shutdown,
+ 'action',
+ action
+ );
+ await this._timer.toggleShutdown(shutdown, action);
+ invocation.return_value(null);
+ }
+
+ async ScheduleWakeAsync(parameters, invocation) {
+ const [wake] = parameters;
+ logDebug('[sdt-dbus] [ScheduleWakeAsync] wake', wake);
+ await this._timer.toggleWake(wake);
+ invocation.return_value(null);
+ }
+
+ GetStateAsync(_, invocation) {
+ logDebug('[sdt-dbus] [GetStateAsync]');
+ invocation.return_value(new GLib.Variant('(s)', [this._timer.state]));
+ return Promise.resolve();
+ }
+
+ destroy() {
+ logDebug('[sdt-dbus] destroy');
+ this._dbusImpl.unexport();
+ this._dbusImpl = null;
+ this._timer.destroy();
+ this._timer = null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js
new file mode 100644
index 0000000..6c439ab
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js
@@ -0,0 +1,286 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import {
+ gettext as _,
+ ngettext as _n,
+ pgettext as C_,
+} from '../modules/translation.js';
+
+import { logDebug, throttleTimeout } from '../modules/util.js';
+import * as Control from './control.js';
+import {
+ longDurationString,
+ ScheduleInfo,
+ getShutdownScheduleFromSettings,
+ getSliderMinutesFromSettings,
+} from '../modules/schedule-info.js';
+import {
+ actionLabel,
+ Action,
+ mapLegacyAction,
+ UnsupportedActionError,
+} from './action.js';
+
+const Signals = imports.signals;
+
+export class Timer {
+ _timerCancellable = null;
+ _updateScheduleCancel = null;
+ _action = new Action();
+ info = {
+ externalShutdown: new ScheduleInfo({
+ external: true,
+ }),
+ externalWake: new ScheduleInfo({
+ external: false,
+ mode: 'wake',
+ }),
+ internalShutdown: new ScheduleInfo({ mode: 'PowerOff' }),
+ };
+
+ constructor({ settings }) {
+ this._settings = settings;
+
+ const [updateScheduleThrottled, updateScheduleCancel] = throttleTimeout(
+ () => this.updateSchedule(),
+ 20
+ );
+ this._updateScheduleCancel = updateScheduleCancel;
+ this._settingsIds = [
+ 'root-mode-value',
+ 'shutdown-timestamp-value',
+ 'shutdown-mode-value',
+ ].map(settingName =>
+ settings.connect(`changed::${settingName}`, updateScheduleThrottled)
+ );
+
+ this.updateSchedule();
+ }
+
+ destroy() {
+ if (this._settings === null) {
+ throw new Error('should not destroy twice');
+ }
+ // Disconnect settings
+ this._settingsIds.forEach(id => this._settings.disconnect(id));
+ this._settingsIds = [];
+ this._settings = null;
+
+ // Cancel schedule updates
+ if (this._updateScheduleCancel !== null) {
+ this._updateScheduleCancel();
+ this._updateScheduleCancel = null;
+ }
+
+ // Cancel internal timer
+ if (this._timerCancellable !== null) {
+ this._timerCancellable.cancel();
+ this._timerCancellable = null;
+ }
+
+ // External schedules (for 'shutdown' and 'wake') are not stopped
+ }
+
+ updateSchedule() {
+ const oldInternal = this.info.internalShutdown;
+ const internal = getShutdownScheduleFromSettings(this._settings);
+ this.info.internalShutdown = internal;
+ logDebug(
+ `[updateSchedule] internal schedule: ${internal.label} (old internal schedule: ${oldInternal.label})`
+ );
+ this._updateRootModeProtection(oldInternal);
+ if (
+ internal.mode !== oldInternal.mode ||
+ internal.deadline !== oldInternal.deadline
+ ) {
+ this.emit('change');
+ if (internal.scheduled) {
+ if (internal.minutes > 0) {
+ // Show schedule info
+ this.emit(
+ 'message',
+ C_('StartSchedulePopup', '%s in %s').format(
+ actionLabel(internal.mode),
+ longDurationString(
+ internal.minutes,
+ h => _n('%s hour', '%s hours', h),
+ m => _n('%s minute', '%s minutes', m)
+ )
+ )
+ );
+ } else {
+ logDebug(`[updateSchedule] hidden message for '< 1 minute' schedule`);
+ }
+ if (this._timerCancellable !== null) {
+ this._timerCancellable.cancel();
+ this._timerCancellable = null;
+ }
+ this.executeActionDelayed()
+ .then(() => {
+ logDebug('[executeActionDelayed] done');
+ })
+ .catch(err => {
+ console.error('executeActionDelayed', err);
+ });
+ } else {
+ if (this._timerCancellable !== null) {
+ this._timerCancellable.cancel();
+ this._timerCancellable = null;
+ }
+ if (oldInternal.scheduled) {
+ this.emit('message', _('Shutdown Timer stopped'));
+ }
+ }
+ }
+ }
+
+ async executeAction() {
+ const internal = this.info.internalShutdown;
+ if (!internal.scheduled) {
+ logDebug(`Refusing to exectute non scheduled action! '${internal.mode}'`);
+ return;
+ }
+ logDebug(`Running '${internal.mode}' timer action...`);
+ try {
+ this.emit('change');
+ // Refresh root mode protection
+ await Promise.all([
+ this._updateRootModeProtection(),
+ // Do shutdown
+ this._action.shutdownAction(
+ internal.mode,
+ this._settings.get_boolean('show-end-session-dialog-value')
+ ),
+ ]);
+ } catch (err) {
+ if (/* destroyed */ this._settings === null) {
+ throw err;
+ }
+ const newInternal = this.info.internalShutdown;
+ if (newInternal.scheduled && newInternal.secondsLeft > 0) {
+ logDebug('[timer] Replaced by new schedule.');
+ return;
+ }
+ if (err instanceof UnsupportedActionError) {
+ this.emit(
+ 'message',
+ _('%s is not supported!').format(actionLabel(internal.mode))
+ );
+ }
+ }
+ await this.toggleShutdown(false, '');
+ }
+
+ async executeActionDelayed() {
+ const internal = this.info.internalShutdown;
+ const secs = internal.secondsLeft;
+ if (secs > 0) {
+ logDebug(`Started delayed action: ${internal.minutes}min remaining`);
+ try {
+ this._timerCancellable = new Gio.Cancellable();
+ await Control.sleepUntilDeadline(
+ internal.deadline,
+ this._timerCancellable
+ );
+ this._timerCancellable = null;
+ } catch {
+ logDebug(`Canceled delayed action: ${internal.minutes}min remaining`);
+ return;
+ }
+ }
+ await this.executeAction();
+ }
+
+ async toggleWake(wake) {
+ try {
+ logDebug('[toggleWake] wake', wake);
+ await this._action.wakeAction(
+ wake,
+ getSliderMinutesFromSettings(this._settings, 'wake')
+ );
+ this.emit('change-external');
+ } catch (err) {
+ this.emit(
+ 'message',
+ C_('Error', '%s\n%s').format(_('Wake action failed!'), err)
+ );
+ this._settings.set_int('shutdown-timestamp-value', -1);
+ }
+ }
+
+ async toggleShutdown(shutdown, legacyAction) {
+ // Update shutdown action
+ const action = mapLegacyAction(legacyAction);
+ if (action === undefined) {
+ this.emit(
+ 'message',
+ _('Unknown shutdown action: "%s"!').format(legacyAction)
+ );
+ return;
+ }
+ logDebug('[toggleShutdown] shutdown', shutdown, 'action', action);
+ if (action !== '') {
+ this._settings.set_string('shutdown-mode-value', action);
+ }
+
+ // Update shutdown timestamp
+ this._settings.set_int(
+ 'shutdown-timestamp-value',
+ shutdown
+ ? GLib.DateTime.new_now_utc().to_unix() +
+ Math.max(
+ 1,
+ getSliderMinutesFromSettings(this._settings, 'shutdown') * 60
+ )
+ : -1
+ );
+ if (shutdown) {
+ await this._action.inhibitSuspend();
+ } else {
+ await this._action.uninhibitSuspend();
+ }
+ if (this._settings.get_boolean('auto-wake-value')) {
+ await this.toggleWake(shutdown);
+ }
+ }
+
+ get state() {
+ return this.info.internalShutdown.scheduled
+ ? this.info.internalShutdown.secondsLeft > 0
+ ? 'active'
+ : 'action'
+ : 'inactive';
+ }
+
+ /**
+ * Ensure that shutdown/reboot is executed even if the Timer fails by running
+ * the `shutdown` command delayed by 1 minute.
+ */
+ async _updateRootModeProtection(oldInternal) {
+ if (this._settings.get_boolean('root-mode-value')) {
+ const internal = this.info.internalShutdown;
+ logDebug('[updateRootModeProtection] mode ', internal.mode);
+ try {
+ if (oldInternal?.scheduled && oldInternal.mode !== internal.mode) {
+ await Control.stopRootModeProtection(oldInternal);
+ }
+ if (internal.scheduled) {
+ await Control.startRootModeProtection(internal);
+ } else {
+ await Control.stopRootModeProtection(internal);
+ }
+ } catch (err) {
+ this.emit(
+ 'message',
+ C_('Error', '%s\n%s').format(_('Root mode protection failed!'), err)
+ );
+ console.error('[updateRootModeProtection]', err);
+ }
+ this.emit('change-external');
+ }
+ }
+}
+Signals.addSignalMethods(Timer.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/extension.js
new file mode 100644
index 0000000..ffb9142
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/extension.js
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>, D. Neumann <neumann89@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import { currentSessionMode } from './modules/session-mode-aware.js';
+import { ShutdownTimerIndicator } from './modules/menu-item.js';
+import { logDebug } from './modules/util.js';
+import { addExternalIndicator } from './modules/quicksettings.js';
+import { InjectionTracker } from './modules/injection.js';
+import { ShutdownTimerDBus } from './dbus-service/shutdown-timer-dbus.js';
+
+export default class ShutdownTimer extends Extension {
+ #sdt = null;
+ #disableTimestamp = 0;
+
+ enable() {
+ const settings = this.getSettings();
+
+ if (!this.#disableTimestamp || Date.now() > this.#disableTimestamp + 100) {
+ logDebug('[ENABLE] Clear shutdown schedule');
+ settings.set_int('shutdown-timestamp-value', -1);
+ }
+
+ logDebug(`[ENABLE] '${currentSessionMode()}'`);
+ this.#sdt = new ShutdownTimerDBus({ settings });
+
+ const indicator = new ShutdownTimerIndicator({
+ path: this.path,
+ settings,
+ });
+ indicator.connect('open-preferences', () => this.openPreferences());
+
+ // Add ShutdownTimer indicator to quicksettings menu
+ this._tracker = new InjectionTracker();
+ addExternalIndicator(this._tracker, indicator);
+
+ this._indicator = indicator;
+
+ logDebug(`[ENABLE-DONE] '${currentSessionMode()}'`);
+ }
+
+ disable() {
+ // Extension should not be disabled during unlock-dialog`:
+ // When the `unlock-dialog` is active, the quicksettings indicator and item
+ // should remain visible.
+ logDebug(`[DISABLE] '${currentSessionMode()}'`);
+ this._tracker.clearAll();
+ this._tracker = null;
+
+ this._indicator.destroy();
+ this._indicator = null;
+
+ this.#sdt.destroy();
+ this.#sdt = null;
+ this.#disableTimestamp = Date.now();
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg
new file mode 100644
index 0000000..8e85029
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" viewBox="0 0 16 16">
+ <path d="M 6.6757812,0.10370312 V 4.6564375 H 9.3242188 V 0.10370312 Z M 4.2460938,1.8947188 c 0,0 -3.7336862,1.8715687 -3.75000005,6.4765625 -0.0163138,4.6049937 3.75000005,6.5253907 3.75000005,6.5253907 0,0 1.7791591,1.021306 3.78125,1.015625 2.0020912,-0.0057 3.7441402,-1.015625 3.7441402,-1.015625 0,0 3.724059,-2.124435 3.736328,-6.5253907 0.01227,-4.4009557 -3.732421,-6.4648438 -3.732421,-6.4648438 v 3.4003906 c 0,0 0.02378,0.0065 0.144531,0.1660157 0.120768,0.1595124 0.126953,0.1992187 0.126953,0.1992187 L 13.222656,4.9943281 13.544922,5.5412031 12.375,6.2189375 c 0,0 0.180927,0.4073893 0.324219,0.9003906 0.143291,0.4930013 0.173828,0.9414063 0.173828,0.9414063 l 1.337891,-0.00391 0.0059,0.6328126 -1.330078,-0.00781 c 0,0 -0.0089,0.3677374 -0.152344,0.9375 -0.143417,0.569763 -0.337891,0.919922 -0.337891,0.919922 l 1.142578,0.679688 -0.3125,0.541015 -1.152343,-0.671875 c 0,0 -0.178508,0.29967 -0.576172,0.708985 -0.397581,0.409316 -0.773438,0.640625 -0.773438,0.640625 l 0.66211,1.154297 -0.554688,0.324218 -0.658203,-1.160156 c 0,0 -0.3427972,0.186493 -0.9296874,0.34375 -0.5868902,0.157282 -0.9140625,0.148438 -0.9140625,0.148438 l -0.011719,1.335937 c -0.2043011,0.0017 -0.4070271,0 -0.6113282,0 l -0.011719,-1.335937 c 0,-10e-7 -0.3271723,0.0088 -0.9140625,-0.148438 C 6.1943598,12.94254 5.8515625,12.756047 5.8515625,12.756047 L 5.1933594,13.916203 4.6386719,13.591985 5.3007812,12.437688 c 0,0 -0.3757763,-0.231309 -0.7734374,-0.640625 -0.3976649,-0.409315 -0.578125,-0.708985 -0.578125,-0.708985 l -1.1503907,0.671875 -0.3125,-0.541015 1.1425781,-0.679688 c 1e-7,0 -0.1944733,-0.350159 -0.3378906,-0.9199219 -0.1434221,-0.5697626 -0.1523437,-0.9375 -0.1523437,-0.9375 l -1.3300781,0.00781 0.00586,-0.6328126 1.3378907,0.00391 c 0,0 0.030536,-0.448405 0.1738281,-0.9414063 0.143291,-0.4930024 0.324218,-0.9003917 0.324218,-0.9003917 L 2.4804688,5.5412031 2.8027344,4.9943281 3.9785156,5.6720625 c 0,0 0.00621,-0.039706 0.1269532,-0.1992187 C 4.2262215,5.3133328 4.25,5.3068281 4.25,5.3068281 Z M 6.6757812,5.0138594 V 7.0568281 H 9.3242188 V 5.0158125 Z M 6.671875,7.4259688 V 8.2443281 H 9.3183594 V 7.4279219 Z m 0,1.1855469 V 9.3693281 H 9.3203125 V 8.6134688 Z" style="fill:#2e3436;stroke:#000000;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"/>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg.license b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg.license
new file mode 100644
index 0000000..2090d9b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/icons/shutdown-timer-symbolic.svg.license
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: 2023 Deminder
+
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/cs/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/cs/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..9b9bc2e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/cs/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/de/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/de/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..c99426e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/de/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/el/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/el/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..2e7021c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/el/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/es/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/es/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..55c480b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/es/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/fr/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/fr/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..58f9038
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/fr/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/it/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/it/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..f085d71
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/it/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/nl/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/nl/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..8101bff
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/nl/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pl/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pl/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..bc14535
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pl/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pt/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pt/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..65d2bb3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/pt/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/ru/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/ru/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..274ad93
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/ru/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/sk/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/sk/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..dedde97
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/sk/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/tr/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/tr/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..565ce5d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/tr/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/zh_CN/LC_MESSAGES/ShutdownTimer.mo b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/zh_CN/LC_MESSAGES/ShutdownTimer.mo
new file mode 100644
index 0000000..666b1ac
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/locale/zh_CN/LC_MESSAGES/ShutdownTimer.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json
new file mode 100644
index 0000000..cc76196
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json
@@ -0,0 +1,22 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Shutdown/reboot/suspend the device after a specific time or wake with a rtc alarm.\n\nThe screen-saver will not interrupt the timer. A privileged control script may be installed to control shutdown and rtcwake as user.",
+ "gettext-domain": "ShutdownTimer",
+ "name": "Shutdown Timer",
+ "session-modes": [
+ "unlock-dialog",
+ "user"
+ ],
+ "settings-schema": "org.gnome.shell.extensions.shutdowntimer-deminder",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/Deminder/ShutdownTimer",
+ "uuid": "ShutdownTimer@deminder",
+ "version": 55
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json.license b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json.license
new file mode 100644
index 0000000..5c6594d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/metadata.json.license
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/info-fetcher.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/info-fetcher.js
new file mode 100644
index 0000000..e18df7b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/info-fetcher.js
@@ -0,0 +1,108 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
+
+import { throttleTimeout, logDebug, readFileAsync } from './util.js';
+
+export class InfoFetcher extends Signals.EventEmitter {
+ constructor() {
+ super();
+ this._intervalId = null;
+ this._tickPromise = null;
+ this._shutdownInfo = {};
+ this._wakeInfo = {};
+ this._rtc = 'rtc0';
+ this._cancellable = new Gio.Cancellable();
+ [this.refresh, this._refreshCancel] = throttleTimeout(
+ this._refresh.bind(this),
+ 300
+ );
+ }
+
+ _refresh() {
+ this._refreshCancel();
+ this._clearInterval();
+ logDebug('Extra info refresh...');
+ this._intervalId = setInterval(this.tick.bind(this), 5000);
+ this.tick();
+ }
+
+ _clearInterval() {
+ if (this._intervalId !== null) {
+ clearInterval(this._intervalId);
+ this._intervalId = null;
+ }
+ }
+
+ tick() {
+ if (this._tickPromise === null) {
+ this._tickPromise = Promise.all([
+ this._fetchShutdownInfo(),
+ this._fetchWakeInfo(this._rtc),
+ ]).then(([shutdown, wake]) => {
+ this._tickPromise = null;
+ this._shutdownInfo = shutdown;
+ this._wakeInfo = wake;
+ this.emit('changed');
+ });
+ }
+ }
+
+ _readFile(path) {
+ return readFileAsync(path, this._cancellable);
+ }
+
+ async _isWakeInfoLocal() {
+ const content = await this._readFile('/etc/adjtime').catch(() => '');
+ return content.trim().toLowerCase().endsWith('local');
+ }
+
+ async _fetchWakeInfo(rtc) {
+ const content = await this._readFile(
+ `/sys/class/rtc/${rtc}/wakealarm`
+ ).catch(() => '');
+ let timestamp = content !== '' ? parseInt(content) : -1;
+ if (timestamp > -1 && (await this._isWakeInfoLocal())) {
+ const dt = GLib.DateTime.new_from_unix_local(timestamp);
+ timestamp = dt.to_unix() - dt.get_utc_offset() / 1000000;
+ dt.unref();
+ }
+ return { deadline: timestamp };
+ }
+
+ async _fetchShutdownInfo() {
+ const content = await this._readFile(
+ '/run/systemd/shutdown/scheduled'
+ ).catch(() => '');
+ if (content === '') {
+ return { deadline: -1 };
+ }
+ // content: schedule unix-timestamp (micro-seconds), warn-all, shutdown-mode
+ const [usec, _, mode] = content.split('\n').map(l => l.split('=')[1]);
+ return {
+ mode,
+ deadline: parseInt(usec) / 1000000,
+ };
+ }
+
+ get shutdownInfo() {
+ return this._shutdownInfo;
+ }
+
+ get wakeInfo() {
+ return this._wakeInfo;
+ }
+
+ destroy() {
+ this.disconnectAll();
+ this._refreshCancel();
+ this._clearInterval();
+ if (this._cancellable !== null) {
+ this._cancellable.cancel();
+ this._cancellable = null;
+ }
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/injection.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/injection.js
new file mode 100644
index 0000000..7d5bdd1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/injection.js
@@ -0,0 +1,192 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+import { logDebug } from './util.js';
+
+/**
+ * The InjectionTracker helps revert object property injections 'out of order'
+ * by keeping track of the injection history.
+ * This is useful when overriding the same method from mulitple extensions,
+ * for instance, `Main.panel.statusArea.quickSettings._addItems`.
+ *
+ * The InjectionManager does not work when multiple extensions override the same method.
+ * Its `restoreMethod` only restores correctly if it is called in reverse order to `overrideMethod`.
+ *
+ */
+export class InjectionTracker {
+ localInjections = [];
+
+ /**
+ * Inject a property into an object.
+ *
+ * To revert the injection call `injection.clear()`.
+ *
+ * @param {object} obj the target object
+ * @param {string} prop the property name
+ * @param {any} value the new property value
+ * @param {boolean} isPropertyDescriptor whether the value is a property descriptor
+ *
+ * @returns {object} an injection object
+ * provding `injection.original` and `injection.previous` property values
+ */
+ injectProperty(obj, prop, value, isPropertyDescriptor) {
+ let propertyDescriptor = Object.getOwnPropertyDescriptor(obj, prop);
+ if (!propertyDescriptor) {
+ if (prop in obj) {
+ propertyDescriptor = {
+ value: obj[prop],
+ writable: true,
+ configurable: true,
+ };
+ } else {
+ throw new Error(
+ `Injection target object does not have a '${prop}' property!`
+ );
+ }
+ }
+ // Keep history; mutated by each injection
+ const histories = obj.__injectionHistories ?? {};
+ const history = histories[prop] ?? [];
+ // Push old property descriptor to history
+ history.push(propertyDescriptor);
+ histories[prop] = history;
+ obj.__injectionHistories = histories;
+
+ const injectionId = history.length;
+ logDebug('[new] injectionid', injectionId);
+
+ // Override value
+ Object.defineProperty(
+ obj,
+ prop,
+ isPropertyDescriptor
+ ? value
+ : { value, writable: false, configurable: true }
+ );
+
+ const localInjectionId = this.localInjections.length;
+ const injection = createInjection(obj, prop, history, injectionId, () => {
+ // Remove from local injections
+ this.localInjections[localInjectionId] = undefined;
+ const pruneIndex =
+ this.localInjections.length -
+ 1 -
+ [...this.localInjections].reverse().findIndex(inj => inj !== undefined);
+ if (pruneIndex === this.localInjections.length) {
+ logDebug('[local-cleanclear]');
+ this.localInjections = [];
+ } else {
+ logDebug('[local-nocleanclear]');
+ this.localInjections = this.localInjections.slice(0, pruneIndex);
+ }
+ });
+ this.localInjections.push(injection);
+ return injection;
+ }
+
+ /**
+ * Clear all injections made by this instance.
+ */
+ clearAll() {
+ this.localInjections
+ .filter(inj => inj !== undefined)
+ .forEach(inj => inj.clear());
+ }
+}
+
+function createInjection(obj, prop, history, injectionId, clearHook) {
+ let reverted = false;
+ const readDescriptorValue = descriptor =>
+ 'get' in descriptor ? descriptor.get.call(obj) : descriptor.value;
+ return {
+ /**
+ * Read the original property value before any injections.
+ */
+ get original() {
+ // Using h instead history to remain valid after `clear()`
+ const h = obj.__injectionHistories?.[prop];
+ return readDescriptorValue(
+ h?.length ? h[0] : Object.getOwnPropertyDescriptor(obj, prop)
+ );
+ },
+
+ /**
+ * Read the previous property value from the injection history.
+ *
+ * Valid after `clear()` as long as no new injection occurs.
+ */
+ get previous() {
+ return history.length
+ ? readDescriptorValue(
+ injectionId > history.length
+ ? Object.getOwnPropertyDescriptor(obj, prop)
+ : popPropertyDescriptorFromHistory(
+ // previous history
+ history.slice(0, injectionId)
+ )
+ )
+ : this.original;
+ },
+
+ /**
+ * Clear the injection from the injection history.
+ */
+ clear() {
+ if (!reverted) {
+ reverted = true;
+ clearHook();
+
+ // Remove from global injections
+ if (injectionId >= history.length) {
+ logDebug(
+ '[remclear] injectionID',
+ injectionId,
+ 'historylen',
+ history.length
+ );
+ // Restore property of obj
+ Object.defineProperty(
+ obj,
+ prop,
+ popPropertyDescriptorFromHistory(history)
+ );
+ // Cleanup empty history
+ if (history.length === 0) {
+ logDebug('[cleanclear]');
+ delete obj.__injectionHistories[prop];
+
+ // Cleanup empty history store
+ if (Object.keys(obj.__injectionHistories).length === 0) {
+ delete obj.__injectionHistories;
+ }
+ } else {
+ logDebug('[nocleanclear] history.length', history.length);
+ }
+ } else {
+ logDebug(
+ '[setclear] injectionID',
+ injectionId,
+ 'historylen',
+ history.length
+ );
+ // Clear injection from history
+ history[injectionId] = undefined;
+ }
+ }
+ },
+
+ /**
+ * Count of previous injections. Is 1 if there is only this injection.
+ */
+ get count() {
+ return history.slice(0, injectionId).filter(i => i !== undefined).length;
+ },
+ };
+}
+
+function popPropertyDescriptorFromHistory(history) {
+ let propertyDescriptor;
+ do {
+ propertyDescriptor = history.pop();
+ } while (history.length && propertyDescriptor === undefined);
+ return propertyDescriptor;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/install.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/install.js
new file mode 100644
index 0000000..701da6a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/install.js
@@ -0,0 +1,64 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import { gettext as _ } from './translation.js';
+
+import { execCheck, installedScriptPath } from '../dbus-service/control.js';
+import { logDebug } from './util.js';
+
+export class Install {
+ destroy() {
+ if (this.installCancel !== undefined) {
+ this.installCancel.cancel();
+ }
+ this.installCancel = undefined;
+ }
+
+ /**
+ * @param installerScriptPath
+ * @param action
+ * @param logInstall
+ */
+ async installAction(installerScriptPath, action, logInstall) {
+ const label = this.actionLabel(action);
+ if (this.installCancel !== undefined) {
+ logDebug(`Trigger cancel install. ${label}`);
+ this.installCancel.cancel();
+ } else {
+ logDebug(`Trigger ${action} action.`);
+ this.installCancel = new Gio.Cancellable();
+ logInstall(`[${_('START')} ${label}]`);
+ try {
+ const user = GLib.get_user_name();
+ logDebug(`? installer.sh --tool-user ${user} ${action}`);
+ await execCheck(
+ ['pkexec', installerScriptPath, '--tool-user', user, action],
+ this.installCancel,
+ false,
+ logInstall
+ );
+ logInstall(`[${_('END')} ${label}]`);
+ } catch (err) {
+ logInstall(`[${_('FAIL')} ${label}]\n# ${err}`);
+ console.error(err, 'InstallError');
+ } finally {
+ this.installCancel = undefined;
+ }
+ }
+ }
+
+ checkInstalled() {
+ const scriptPath = installedScriptPath();
+ const isScriptInstalled = scriptPath !== null;
+ if (isScriptInstalled) {
+ logDebug(`Existing installation at: ${scriptPath}`);
+ }
+ return isScriptInstalled;
+ }
+
+ actionLabel(action) {
+ return { install: _('install'), uninstall: _('uninstall') }[action];
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/menu-item.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/menu-item.js
new file mode 100644
index 0000000..3a80198
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/menu-item.js
@@ -0,0 +1,573 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import GObject from 'gi://GObject';
+import St from 'gi://St';
+import Gio from 'gi://Gio';
+import Clutter from 'gi://Clutter';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as Slider from 'resource:///org/gnome/shell/ui/slider.js';
+import * as QuickSettings from 'resource:///org/gnome/shell/ui/quickSettings.js';
+
+import { InfoFetcher } from './info-fetcher.js';
+import { Textbox } from './text-box.js';
+import { gettext as _, ngettext as _n, pgettext as C_ } from './translation.js';
+import * as SessionModeAware from './session-mode-aware.js';
+import { getShutdownScheduleFromSettings } from './schedule-info.js';
+import {
+ ShutdownTimerName,
+ ShutdownTimerObjectPath,
+} from '../dbus-service/shutdown-timer-dbus.js';
+
+import {
+ foregroundActive,
+ observeForegroundActive,
+ unobserveForegroundActive,
+} from './session-mode-aware.js';
+import {
+ durationString,
+ longDurationString,
+ absoluteTimeString,
+ getSliderMinutesFromSettings,
+ ScheduleInfo,
+} from './schedule-info.js';
+
+import { logDebug, proxyPromise } from './util.js';
+import {
+ WAKE_ACTIONS,
+ actionLabel,
+ ACTIONS,
+ mapLegacyAction,
+} from '../dbus-service/action.js';
+
+/**
+ * The ShutdownTimerItem controls wake/shutdown action time and mode.
+ * Additionally, it shows wake and external/internal shutdown schedules.
+ *
+ * The external schedule of the `shutdown` command is fetched by the InfoFetcher.
+ * Note that there is no external schedule for `suspend`.
+ */
+const ShutdownTimerItem = GObject.registerClass(
+ {
+ Properties: {
+ 'shutdown-text': GObject.ParamSpec.string(
+ 'shutdown-text',
+ '',
+ '',
+ GObject.ParamFlags.READWRITE,
+ ''
+ ),
+ 'indicator-icon-name': GObject.ParamSpec.string(
+ 'indicator-icon-name',
+ '',
+ '',
+ GObject.ParamFlags.READWRITE,
+ 'go-down-symbolic'
+ ),
+ },
+ Signals: {
+ 'open-preferences': {},
+ shutdown: {
+ param_types: [GObject.TYPE_BOOLEAN, GObject.TYPE_STRING],
+ },
+ wake: {
+ param_types: [GObject.TYPE_BOOLEAN],
+ },
+ },
+ },
+ class ShutdownTimerItem extends QuickSettings.QuickMenuToggle {
+ _init({ path, settings }) {
+ const gicon = Gio.icon_new_for_string(
+ `${path}/icons/shutdown-timer-symbolic.svg`
+ );
+ super._init({ gicon, accessible_name: _('Shutdown Timer') });
+ this.info = {
+ internalShutdown: getShutdownScheduleFromSettings(settings),
+ externalShutdown: new ScheduleInfo({ external: true }),
+ externalWake: new ScheduleInfo({ mode: 'wake' }),
+ state: 'inactive',
+ };
+ this._settings = settings;
+ this.shutdownTimerIcon = gicon;
+ this._updatingSwitcherState = false;
+
+ // submenu in status area menu with slider and toggle button
+ this.sliderItems = {};
+ this.sliders = {};
+ ['shutdown', 'wake'].forEach(prefix => {
+ const [item, slider] = this._createSliderItem(prefix);
+ this.sliderItems[prefix] = item;
+ this.sliders[prefix] = slider;
+ this._onShowSliderChanged(prefix);
+ });
+ this.switcher = new PopupMenu.PopupSwitchMenuItem('', false);
+ // start/stop shutdown timer
+ this.switcher.connect('toggled', () => {
+ if (!this._updatingSwitcherState) {
+ this.emit(
+ 'shutdown',
+ this.switcher.state,
+ this.info.internalShutdown.mode
+ );
+ }
+ });
+ this.switcherSettingsButton = new St.Button({
+ reactive: true,
+ can_focus: true,
+ track_hover: true,
+ accessible_name: _('Settings'),
+ style_class: 'icon-button settings-button',
+ });
+ this.switcherSettingsButton.child = new St.Icon({
+ icon_name: 'applications-system-symbolic',
+ });
+ this.switcherSettingsButton.connect('clicked', () =>
+ this.emit('open-preferences')
+ );
+ this.switcher.add_child(this.switcherSettingsButton);
+
+ this.menu.addMenuItem(this.switcher);
+ // make switcher toggle without popup menu closing
+ this.switcher.activate = __ => {
+ if (this.switcher._switch.mapped) {
+ this.switcher.toggle();
+ }
+ };
+ this.menu.addMenuItem(this.sliderItems['shutdown']);
+
+ this.modeItems = Object.keys(ACTIONS).map(action => {
+ const modeItem = new PopupMenu.PopupMenuItem(actionLabel(action));
+ modeItem.connect('activate', () => {
+ this.emit('shutdown', true, action);
+ });
+ this.menu.addMenuItem(modeItem);
+ return [action, modeItem];
+ });
+
+ this.wakeItems = [
+ new PopupMenu.PopupSeparatorMenuItem(),
+ this.sliderItems['wake'],
+ ...Object.keys(WAKE_ACTIONS).map(action => {
+ const modeItem = new PopupMenu.PopupMenuItem(actionLabel(action));
+ if (action === 'wake') {
+ this.wakeModeItem = modeItem;
+ }
+ modeItem.connect('activate', () =>
+ this.emit('wake', action === 'wake')
+ );
+ return modeItem;
+ }),
+ ];
+ this.wakeItems.forEach(item => {
+ this.menu.addMenuItem(item);
+ });
+ this._updateWakeModeItem();
+ this._updateSwitchLabel();
+
+ // handlers for changed values in settings
+ const settingsHandlerIds = [
+ [
+ [
+ 'shutdown-max-timer-value',
+ 'nonlinear-shutdown-slider-value',
+ 'shutdown-ref-timer-value',
+ 'show-shutdown-absolute-timer-value',
+ 'root-mode-value',
+ 'shutdown-slider-value',
+ ],
+ () => this._updateSwitchLabel(),
+ ],
+ [
+ [
+ 'wake-max-timer-value',
+ 'wake-ref-timer-value',
+ 'show-wake-absolute-timer-value',
+ 'nonlinear-wake-slider-value',
+ 'wake-slider-value',
+ ],
+ () => this._updateWakeModeItem(),
+ ],
+ [['shutdown-slider-value'], () => this._updateSlider('shutdown')],
+ [['wake-slider-value'], () => this._updateSlider('wake')],
+ [
+ [
+ 'show-wake-items-value',
+ 'show-shutdown-mode-value',
+ 'show-shutdown-slider-value',
+ 'show-wake-slider-value',
+ 'show-shutdown-indicator-value',
+ 'show-settings-value',
+ ],
+ () => this._sync(),
+ ],
+ ]
+ .flatMap(([names, func]) => names.map(n => [n, func]))
+ .map(([name, func]) =>
+ this._settings.connect(`changed::${name}`, func)
+ );
+
+ this.connect('clicked', () => this.switcher.toggle());
+ observeForegroundActive(this, () => this._sync());
+ const tickHandlerId = setInterval(() => this.updateShutdownInfo(), 1000);
+ this.connect('destroy', () => {
+ unobserveForegroundActive(this);
+ clearInterval(tickHandlerId);
+ settingsHandlerIds.forEach(handlerId => {
+ this._settings.disconnect(handlerId);
+ });
+ });
+ }
+
+ _sync() {
+ // Update wake mode items
+ this.wakeItems.forEach(item => {
+ item.visible = this._settings.get_boolean('show-wake-items-value');
+ });
+ this._onShowSliderChanged('wake');
+
+ // Update shutdown mode items
+ const activeActions = this._settings
+ .get_string('show-shutdown-mode-value')
+ .split(',')
+ .map(s => mapLegacyAction(s.trim()))
+ .filter(action => action in ACTIONS);
+ logDebug('[menu-item]', activeActions);
+ this.modeItems.forEach(([action, item]) => {
+ const position = activeActions.indexOf(action);
+ if (position > -1) {
+ this.menu.moveMenuItem(item, position + 2);
+ }
+ item.visible = position > -1;
+ });
+
+ this._onShowSliderChanged('shutdown');
+
+ this.switcherSettingsButton.visible =
+ foregroundActive() && this._settings.get_boolean('show-settings-value');
+
+ this.updateShutdownInfo();
+ }
+
+ updateShutdownInfo() {
+ const schedule = this.info.internalShutdown;
+ const externalSchedule = this.info.externalShutdown;
+ const externalWake = this.info.externalWake;
+ const urgendSchedule = externalSchedule.isMoreUrgendThan(schedule)
+ ? externalSchedule
+ : schedule;
+ const active = schedule.scheduled || this.info.state !== 'inactive';
+ const showIndicator = this._settings.get_boolean(
+ 'show-shutdown-indicator-value'
+ );
+
+ // Update Indicator
+ this.set({
+ checked: active,
+ shutdownText:
+ urgendSchedule.scheduled && showIndicator
+ ? urgendSchedule.secondsLeft > 0
+ ? durationString(urgendSchedule.secondsLeft)
+ : _('now')
+ : '',
+ indicatorIconName:
+ showIndicator &&
+ (this.info.internalShutdown.scheduled ||
+ this.info.externalShutdown.scheduled)
+ ? 'go-down-symbolic'
+ : '',
+ });
+
+ // Update selected shutdown mode
+ this.modeItems.forEach(([mode, item]) => {
+ item.setOrnament(
+ mode === schedule.mode
+ ? PopupMenu.Ornament.DOT
+ : PopupMenu.Ornament.NONE
+ );
+ });
+
+ // Update title with scheduled action
+ this.title = actionLabel(schedule.mode);
+
+ // Update toggle state of switcher
+ this._updatingSwitcherState = true;
+ this.switcher.setToggleState(active);
+ this._updatingSwitcherState = false;
+
+ // Update long status description in menu
+ this.menu.setHeader(
+ this.shutdownTimerIcon,
+ _('Shutdown Timer'),
+ [schedule.label, externalWake.label].filter(v => !!v).join('\n')
+ );
+ if (this._settings.get_boolean('show-shutdown-absolute-timer-value')) {
+ this._updateSwitchLabel();
+ }
+ if (this._settings.get_boolean('show-wake-absolute-timer-value')) {
+ this._updateWakeModeItem();
+ }
+ this._updateSubtitle();
+ }
+
+ // update timer value if slider has changed
+ _updateSlider(prefix) {
+ this.sliders[prefix].value =
+ this._settings.get_double(`${prefix}-slider-value`) / 100.0;
+ }
+
+ _createSliderItem(settingsPrefix) {
+ const valueName = `${settingsPrefix}-slider-value`;
+ const sliderValue = this._settings.get_double(valueName) / 100.0;
+ const item = new PopupMenu.PopupBaseMenuItem({ activate: false });
+ const sliderIcon = new St.Icon({
+ icon_name:
+ settingsPrefix === 'wake'
+ ? 'alarm-symbolic'
+ : 'system-shutdown-symbolic',
+ style_class: 'popup-menu-icon',
+ });
+ item.add_child(sliderIcon);
+ const slider = new Slider.Slider(sliderValue);
+ slider.connect('notify::value', () => {
+ const v = slider.value * 100;
+ if (v !== this._settings.get_double(valueName))
+ this._settings.set_double(valueName, v);
+ });
+ item.add_child(slider);
+ return [item, slider];
+ }
+
+ get shutdownTimeStr() {
+ const minutes = Math.abs(
+ getSliderMinutesFromSettings(this._settings, 'shutdown')
+ );
+ return this._settings.get_boolean('show-shutdown-absolute-timer-value')
+ ? absoluteTimeString(minutes, C_('absolute time notation', '%a, %R'))
+ : longDurationString(
+ minutes,
+ h => _n('%s hr', '%s hrs', h),
+ m => _n('%s min', '%s mins', m)
+ );
+ }
+
+ _updateSwitchLabel() {
+ this.switcher.label.text = this._settings.get_boolean('root-mode-value')
+ ? _('%s (protect)').format(this.shutdownTimeStr)
+ : this.shutdownTimeStr;
+
+ if (this._settings.get_string('wake-ref-timer-value') === 'shutdown') {
+ this._updateWakeModeItem();
+ }
+ if (!this.info.internalShutdown.scheduled) {
+ this._updateSubtitle();
+ }
+ }
+
+ _updateSubtitle() {
+ this.subtitle = this.info.internalShutdown.scheduled
+ ? this._settings.get_boolean('show-shutdown-absolute-timer-value')
+ ? this.info.internalShutdown.absoluteTimeString
+ : durationString(this.info.internalShutdown.secondsLeft)
+ : this.shutdownTimeStr;
+ }
+
+ _updateWakeModeItem() {
+ const minutes = getSliderMinutesFromSettings(this._settings, 'wake');
+ const abs = this._settings.get_boolean('show-wake-absolute-timer-value');
+ this.wakeModeItem.label.text = (
+ abs
+ ? C_('WakeButtonText', '%s at %s')
+ : C_('WakeButtonText', '%s after %s')
+ ).format(
+ actionLabel('wake'),
+ abs
+ ? absoluteTimeString(minutes, C_('absolute time notation', '%a, %R'))
+ : longDurationString(
+ minutes,
+ h => _n('%s hour', '%s hours', h),
+ m => _n('%s minute', '%s minutes', m)
+ )
+ );
+ }
+
+ _onShowSliderChanged(settingsPrefix) {
+ this.sliderItems[settingsPrefix].visible =
+ (settingsPrefix !== 'wake' ||
+ this._settings.get_boolean('show-wake-items-value')) &&
+ this._settings.get_boolean(`show-${settingsPrefix}-slider-value`);
+ }
+ }
+);
+
+export const ShutdownTimerIndicator = GObject.registerClass(
+ {
+ Properties: {
+ 'wake-minutes': GObject.ParamSpec.int(
+ 'wake-minutes',
+ '',
+ '',
+ GObject.ParamFlags.READABLE,
+ 0
+ ),
+ 'shutdown-minutes': GObject.ParamSpec.int(
+ 'shutdown-minutes',
+ '',
+ '',
+ GObject.ParamFlags.READABLE,
+ 0
+ ),
+ },
+ Signals: {
+ 'open-preferences': {},
+ shutdown: {
+ param_types: [GObject.TYPE_BOOLEAN, GObject.TYPE_STRING],
+ },
+ wake: {
+ param_types: [GObject.TYPE_BOOLEAN],
+ },
+ },
+ },
+ class ShutdownTimerIndicator extends QuickSettings.SystemIndicator {
+ _init({ path, settings }) {
+ super._init();
+ this._state = 'inactive';
+ this._settings = settings;
+ this._textbox = new Textbox({ settings });
+ const item = new ShutdownTimerItem({ path, settings });
+ const infoFetcher = new InfoFetcher();
+ const proxyCancel = new Gio.Cancellable();
+ this._sdtProxy = null;
+ this._initProxy(item, this._textbox, infoFetcher, proxyCancel).catch(
+ err => {
+ if (!proxyCancel.is_cancelled()) console.error('[sdt-proxy]', err);
+ }
+ );
+ // React to changes in external shutdown and wake schedule
+ infoFetcher.connect('changed', () => this._syncShutdownInfo());
+ this._infoFetcher = infoFetcher;
+ this._shutdownTimerItem = item;
+
+ item.connect('open-preferences', () => this.emit('open-preferences'));
+
+ const icon = new St.Icon({ style_class: 'system-status-icon' });
+ const updateIcon = () => {
+ const name = item.indicatorIconName;
+ icon.visible = !!name;
+ if (icon.visible) icon.iconName = name;
+ this._syncIndicatorsVisible();
+ };
+
+ const scheduleLabel = new St.Label({
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ const updateLabel = () => {
+ const text = item.shutdownText;
+ scheduleLabel.text = text;
+ scheduleLabel.visible = !!text;
+ this._syncIndicatorsVisible();
+ };
+
+ this.add_child(icon);
+ this.add_child(scheduleLabel);
+
+ item.connect('notify::indicator-icon-name', () => updateIcon());
+ item.connect('notify::shutdown-text', () => updateLabel());
+
+ updateLabel();
+ updateIcon();
+
+ this.quickSettingsItems.push(item);
+ this.connect('destroy', () => {
+ proxyCancel.cancel();
+ infoFetcher.destroy();
+ this._infoFetcher = null;
+ this._textbox.destroy();
+ this.quickSettingsItems.forEach(i => i.destroy());
+ this.quickSettingsItems = [];
+ this._shutdownTimerItem = null;
+ if (this._sdtProxy !== null) {
+ this._sdtProxy.destroy();
+ this._sdtProxy = null;
+ }
+ this._settings = null;
+ });
+ item.connect('destroy', () => {
+ // Mitigate already destroyed error when logging out
+ // Does js/ui/layout.js:256 destroy quickSettingsItems?
+ this.quickSettingsItems = [];
+ });
+ }
+
+ async _initProxy(item, textbox, infoFetcher, cancellable) {
+ const proxy = await proxyPromise(
+ ShutdownTimerName,
+ Gio.DBus.session,
+ 'org.gnome.Shell',
+ ShutdownTimerObjectPath,
+ cancellable
+ );
+ if (cancellable?.is_cancelled()) return;
+ item.connect('shutdown', (__, shutdown, action) => {
+ logDebug(
+ '[menu-item] [shutdown-signal] shutdown: ',
+ shutdown,
+ 'action:',
+ action
+ );
+ proxy
+ .ScheduleShutdownAsync(shutdown, action)
+ .catch(err => console.error('[shutdown]', err));
+ });
+ item.connect('wake', (__, wake) => {
+ proxy
+ .ScheduleWakeAsync(wake)
+ .catch(err => console.error('[wake]', err));
+ });
+
+ const signalIds = [
+ proxy.connectSignal('OnMessage', (__, ___, [msg]) => {
+ textbox.showTextbox(msg);
+ }),
+ proxy.connectSignal('OnStateChange', (__, ___, [state]) => {
+ logDebug('[menu-item] [OnStateChange-signal] state:', state);
+ this._state = state;
+ this._syncShutdownInfo();
+ }),
+ proxy.connectSignal('OnExternalChange', () => {
+ infoFetcher.refresh();
+ }),
+ ];
+ proxy.destroy = function () {
+ signalIds.forEach(signalId => this.disconnectSignal(signalId));
+ };
+
+ this._sdtProxy = proxy;
+ [this._state] = await this._sdtProxy.GetStateAsync();
+ if (cancellable?.is_cancelled()) return;
+ logDebug('[sdt-proxy] state', this._state);
+ this._syncShutdownInfo();
+ infoFetcher.refresh();
+ }
+
+ _syncShutdownInfo() {
+ const item = this._shutdownTimerItem;
+ if (this._state === 'action') {
+ if (SessionModeAware.foregroundActive()) Main.overview.hide();
+ this._textbox.hideAll();
+ }
+ item.info = {
+ internalShutdown: getShutdownScheduleFromSettings(this._settings),
+ externalShutdown: item.info.externalShutdown.copy({
+ ...this._infoFetcher.shutdownInfo,
+ }),
+ externalWake: item.info.externalWake.copy({
+ ...this._infoFetcher.wakeInfo,
+ }),
+ state: this._state,
+ };
+ item.updateShutdownInfo();
+ }
+ }
+);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/quicksettings.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/quicksettings.js
new file mode 100644
index 0000000..dd7b161
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/quicksettings.js
@@ -0,0 +1,77 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+/**
+ * Add an external indicator after an existing indicator and above an existing indicator item.
+ */
+export function addExternalIndicator(
+ tracker,
+ indicator,
+ after = '_system',
+ above = '_backgroundApps',
+ colSpan
+) {
+ const qs = Main.panel.statusArea.quickSettings;
+ const indicatorItems = indicator.quickSettingsItems;
+ const aboveIndicator = qs[above];
+ if (aboveIndicator === undefined) {
+ if ('_addItems' in qs) {
+ // 45.beta.1: _setupIndicators is not done
+ const injection = tracker.injectProperty(
+ qs,
+ '_addItems',
+ (items, col) => {
+ if (Object.is(items, qs[above].quickSettingsItems)) {
+ injection.clear();
+ // Insert after: insert_child_above(a,b): inserts 'a' after 'b'
+ qs._indicators.insert_child_above(indicator, qs[after]);
+ // Insert above
+ injection.original.call(qs, indicatorItems, colSpan);
+ }
+ injection.previous.call(qs, items, col);
+ }
+ );
+ } else {
+ // 45.rc: _setupIndicators is not done
+ const qsm = qs.menu;
+ const injection = tracker.injectProperty(
+ qsm,
+ '_completeAddItem',
+ (item, col) => {
+ const firstAboveItem = qs[above]?.quickSettingsItems.at(-1);
+ if (Object.is(firstAboveItem, item)) {
+ injection.clear();
+ // Insert after: insert_child_above(a,b): inserts 'a' after 'b'
+ qs._indicators.insert_child_above(indicator, qs[after]);
+ // Insert above
+ indicatorItems.forEach(newItem =>
+ qsm.insertItemBefore(newItem, item, colSpan)
+ );
+ }
+ injection.previous.call(qsm, item, col);
+ }
+ );
+ }
+ } else if ('_addItems' in qs) {
+ // 45.beta.1: _setupIndicators is done
+ // Insert after: insert_child_above(a,b): inserts 'a' after 'b'
+ qs._indicators.insert_child_above(indicator, qs[after]);
+ // Insert above
+ qs._addItems(indicatorItems, colSpan);
+ const firstAboveItem = aboveIndicator.quickSettingsItems.at(-1);
+ indicatorItems.forEach(item => {
+ qs.menu._grid.remove_child(item);
+ qs.menu._grid.insert_child_below(item, firstAboveItem);
+ });
+ } else {
+ // 45.rc: _setupIndicators is done
+ // Insert after
+ qs._indicators.insert_child_above(indicator, qs[after]);
+
+ // Insert above
+ const firstAboveItem = aboveIndicator.quickSettingsItems.at(-1);
+ qs._addItemsBefore(indicatorItems, firstAboveItem, colSpan);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/schedule-info.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/schedule-info.js
new file mode 100644
index 0000000..394ebc8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/schedule-info.js
@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import GLib from 'gi://GLib';
+import { gettext as _, ngettext as _n, pgettext as C_ } from './translation.js';
+import { mapLegacyAction, untilText } from '../dbus-service/action.js';
+
+export class ScheduleInfo {
+ constructor({ mode = '?', deadline = -1, external = false }) {
+ this._v = { mode: mapLegacyAction(mode), deadline, external };
+ }
+
+ copy(vals) {
+ return new ScheduleInfo({ ...this._v, ...vals });
+ }
+
+ get deadline() {
+ return this._v.deadline;
+ }
+
+ get external() {
+ return this._v.external;
+ }
+
+ get mode() {
+ return this._v.mode;
+ }
+
+ get scheduled() {
+ return this.deadline > -1;
+ }
+
+ get secondsLeft() {
+ return this.deadline - GLib.DateTime.new_now_utc().to_unix();
+ }
+
+ get minutes() {
+ return Math.floor(this.secondsLeft / 60);
+ }
+
+ get label() {
+ let label = '';
+ if (this.scheduled) {
+ label = _('{durationString} until {untiltext}')
+ .replace('{durationString}', durationString(this.secondsLeft))
+ .replace('{untiltext}', untilText(this.mode));
+ if (this.external) {
+ label = _('{label} (sys)').replace('{label}', label);
+ }
+ }
+ return label;
+ }
+
+ get absoluteTimeString() {
+ return GLib.DateTime.new_from_unix_utc(this.deadline)
+ .to_local()
+ .format(C_('absolute schedule notation', '%a, %T'));
+ }
+
+ isMoreUrgendThan(otherInfo) {
+ return (
+ !otherInfo.scheduled ||
+ (this.scheduled &&
+ // external deadline is instant, internal deadline has 1 min slack time
+ (this.external ? this.deadline : this.deadline + 58) <
+ otherInfo.deadline)
+ );
+ }
+}
+
+export function getShutdownScheduleFromSettings(settings) {
+ return new ScheduleInfo({
+ mode: settings.get_string('shutdown-mode-value'),
+ deadline: settings.get_int('shutdown-timestamp-value'),
+ });
+}
+
+export function getSliderMinutesFromSettings(settings, prefix) {
+ const sliderValue = settings.get_double(`${prefix}-slider-value`) / 100.0;
+ const rampUp = settings.get_double(`nonlinear-${prefix}-slider-value`);
+ const ramp = x => Math.expm1(rampUp * x) / Math.expm1(rampUp);
+ let minutes = Math.floor(
+ (rampUp === 0 ? sliderValue : ramp(sliderValue)) *
+ settings.get_int(`${prefix}-max-timer-value`)
+ );
+
+ const refstr = settings.get_string(`${prefix}-ref-timer-value`);
+ // default: 'now'
+ const MS = 1000 * 60;
+ if (refstr.includes(':')) {
+ const mh = refstr
+ .split(':')
+ .map(s => Number.parseInt(s))
+ .filter(n => !Number.isNaN(n) && n >= 0);
+ if (mh.length >= 2) {
+ const d = new Date();
+ const nowTime = d.getTime();
+ d.setHours(mh[0]);
+ d.setMinutes(mh[1]);
+
+ if (d.getTime() + MS * minutes < nowTime) {
+ d.setDate(d.getDate() + 1);
+ }
+ minutes += Math.floor(new Date(d.getTime() - nowTime).getTime() / MS);
+ }
+ } else if (prefix !== 'shutdown' && refstr === 'shutdown') {
+ minutes += getSliderMinutesFromSettings(settings, 'shutdown');
+ }
+ return minutes;
+}
+
+/**
+ * A short duration string showing >=3 hours, >=1 mins, or secs.
+ *
+ * @param {number} seconds duration in seconds
+ */
+export function durationString(seconds) {
+ const sign = Math.sign(seconds);
+ const absSec = Math.floor(Math.abs(seconds));
+ const minutes = Math.floor(absSec / 60);
+ const hours = Math.floor(minutes / 60);
+ if (hours >= 3) {
+ return _n('%s hour', '%s hours', hours).format(sign * hours);
+ } else if (minutes === 0) {
+ return _n('%s sec', '%s secs', absSec).format(
+ sign * (absSec > 5 ? 10 * Math.ceil(absSec / 10) : absSec)
+ );
+ }
+ return _n('%s min', '%s mins', minutes).format(sign * minutes);
+}
+
+/**
+ *
+ * @param minutes
+ * @param hrFmt
+ * @param minFmt
+ */
+export function longDurationString(minutes, hrFmt, minFmt) {
+ const hours = Math.floor(minutes / 60);
+ const residualMinutes = minutes % 60;
+ let parts = [minFmt(residualMinutes).format(residualMinutes)];
+ if (hours) {
+ parts = [hrFmt(hours).format(hours)].concat(parts);
+ }
+ return parts.join(' ');
+}
+
+export function absoluteTimeString(minutes, timeFmt) {
+ return GLib.DateTime.new_now_local().add_minutes(minutes).format(timeFmt);
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/session-mode-aware.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/session-mode-aware.js
new file mode 100644
index 0000000..cb6e562
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/session-mode-aware.js
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+export function currentSessionMode() {
+ return Main.sessionMode.currentMode;
+}
+
+/**
+ * Check if we want to show foreground activity
+ */
+export function foregroundActive() {
+ // ubuntu22.04 uses 'ubuntu' as 'user' sessionMode
+ return Main.sessionMode.currentMode !== 'unlock-dialog';
+}
+
+/**
+ * Observe foreground activity changes
+ *
+ * @param {object} obj bind obj as observer
+ * @param {Function} callback called upon change
+ */
+export function observeForegroundActive(obj, callback) {
+ if (!obj._sessionModeSignalId) {
+ obj._sessionModeSignalId = Main.sessionMode.connect('updated', () => {
+ callback(foregroundActive());
+ });
+ }
+ callback(foregroundActive());
+}
+
+/**
+ * Unobserve session mode
+ */
+export function unobserveForegroundActive(obj) {
+ if (obj._sessionModeSignalId) {
+ Main.sessionMode.disconnect(obj._sessionModeSignalId);
+ delete obj._sessionModeSignalId;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/text-box.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/text-box.js
new file mode 100644
index 0000000..b97cdb1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/text-box.js
@@ -0,0 +1,130 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import St from 'gi://St';
+import Clutter from 'gi://Clutter';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import { throttleTimeout, logDebug } from './util.js';
+import {
+ foregroundActive,
+ observeForegroundActive,
+ unobserveForegroundActive,
+} from './session-mode-aware.js';
+
+export class Textbox {
+ textboxes = [];
+ constructor({ settings }) {
+ this.settings = settings;
+ [this.syncThrottled, this.syncThrottledCancel] = throttleTimeout(
+ this.sync.bind(this),
+ 50
+ );
+ this.showSettingsId = settings.connect(
+ 'changed::show-textboxes-value',
+ this.sync.bind(this)
+ );
+ observeForegroundActive(this, fgActive => {
+ if (!fgActive) {
+ this.hideAll();
+ }
+ });
+ }
+
+ destroy() {
+ if (this.showSettingsId) {
+ unobserveForegroundActive(this);
+ this.hideAll();
+ this.settings.disconnect(this.showSettingsId);
+ this.showSettingsId = null;
+ }
+ }
+
+ sync() {
+ this.syncThrottledCancel();
+ // remove hidden textboxes
+ this.textboxes = this.textboxes.filter(t => {
+ if (t['_hidden']) {
+ const sid = t['_sourceId'];
+ if (sid) {
+ clearTimeout(sid);
+ }
+ delete t['_sourceId'];
+ t.destroy();
+ }
+ return !t['_hidden'];
+ });
+ const monitor = Main.layoutManager.primaryMonitor;
+ let heightOffset = 0;
+ this.textboxes.forEach((textbox, i) => {
+ if (!('_sourceId' in textbox)) {
+ // start fadeout of textbox after 3 seconds
+ textbox['_sourceId'] = setTimeout(() => {
+ textbox['_sourceId'] = 0;
+ textbox.ease({
+ opacity: 0,
+ duration: 1000,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ textbox['_hidden'] = 1;
+ this.syncThrottled();
+ },
+ });
+ }, 3000);
+ }
+ textbox.visible = this.settings.get_boolean('show-textboxes-value');
+ if (!textbox.visible) return;
+
+ if (i === 0) {
+ heightOffset = -textbox.height / 2;
+ }
+ textbox.set_position(
+ monitor.x + Math.floor(monitor.width / 2 - textbox.width / 2),
+ monitor.y + Math.floor(monitor.height / 2 + heightOffset)
+ );
+ heightOffset += textbox.height + 10;
+ if (textbox['_sourceId']) {
+ // set opacity before fadeout starts
+ textbox.opacity =
+ i === 0
+ ? 255
+ : Math.max(
+ 25,
+ 25 + 230 * (1 - heightOffset / (monitor.height / 2))
+ );
+ }
+ });
+ }
+
+ hideAll() {
+ for (const t of this.textboxes) {
+ t['_hidden'] = 1;
+ }
+ this.sync();
+ }
+
+ /**
+ * Show a textbox message on the primary monitor
+ *
+ * @param textmsg
+ */
+ showTextbox(textmsg) {
+ if (textmsg && foregroundActive()) {
+ for (const t of this.textboxes) {
+ // replace old textbox if it has the same text
+ if (t.text === textmsg) {
+ t['_hidden'] = 1;
+ }
+ }
+ logDebug(`show textbox: ${textmsg}`);
+ const textbox = new St.Label({
+ style_class: 'textbox-label',
+ text: textmsg,
+ opacity: 0,
+ });
+ Main.uiGroup.add_child(textbox);
+ this.textboxes.unshift(textbox);
+ this.syncThrottled();
+ }
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/translation.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/translation.js
new file mode 100644
index 0000000..af18232
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/translation.js
@@ -0,0 +1,51 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import GLib from 'gi://GLib';
+
+/**
+ * The import paths for translations differ for `extension.js` and `prefs.js`.
+ * https://gjs.guide/extensions/upgrading/gnome-shell-45.html#esm
+ *
+ * This module provides shared access to translations.
+ * It is assumed that the gettext-domain is already bound.
+ */
+const domain = 'ShutdownTimer';
+
+/**
+ * Translate `str` using the extension's gettext domain
+ *
+ * @param {string} str - the string to translated
+ *
+ * @returns {string} the translated string
+ */
+export function gettext(str) {
+ return GLib.dgettext(domain, str);
+}
+
+/**
+ * Translate `str` and choose plural form using the extension's
+ * gettext domain
+ *
+ * @param {string} str - the string to translate
+ * @param {string} strPlural - the plural form of the string
+ * @param {number} n - the quantity for which translation is needed
+ *
+ * @returns {string} the translated string
+ */
+export function ngettext(str, strPlural, n) {
+ return GLib.dngettext(domain, str, strPlural, n);
+}
+
+/**
+ * Translate `str` in the context of `context` using the extension's
+ * gettext domain
+ *
+ * @param {string} context - context to disambiguate `str`
+ * @param {string} str - the string to translate
+ *
+ * @returns {string} the translated string
+ */
+export function pgettext(context, str) {
+ return GLib.dpgettext2(domain, context, str);
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/util.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/util.js
new file mode 100644
index 0000000..6f360aa
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/modules/util.js
@@ -0,0 +1,178 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+
+export const debugMode = false;
+
+/**
+ * Log debug message if debug is enabled .
+ *
+ * @param {...any} args log arguments
+ */
+export function logDebug(...args) {
+ if ('logDebug' in globalThis) {
+ globalThis.logDebug(...args);
+ } else if (debugMode) {
+ console.log('[SDT]', ...args);
+ }
+}
+
+export async function proxyPromise(
+ ProxyTypeOrName,
+ session,
+ dest,
+ objectPath,
+ cancellable = null
+) {
+ if (typeof ProxyTypeOrName === 'string') {
+ try {
+ ProxyTypeOrName = Gio.DBusProxy.makeProxyWrapper(
+ await loadInterfaceXML(ProxyTypeOrName, cancellable)
+ );
+ } catch (err) {
+ throw new Error('Failed to load proxy interface!', { cause: err });
+ }
+ }
+ const p = await new Promise((resolve, reject) => {
+ new ProxyTypeOrName(
+ session,
+ dest,
+ objectPath,
+ (proxy, error) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(proxy);
+ }
+ },
+ cancellable
+ );
+ });
+ return p;
+}
+
+export class Idle {
+ #idleSourceId = null;
+ #idleResolves = [];
+
+ destroy() {
+ // Ensure that promises are resolved
+ for (const resolve of this.#idleResolves) {
+ resolve();
+ }
+ this.#idleResolves = [];
+ if (this.#idleSourceId) {
+ GLib.Source.remove(this.#idleSourceId);
+ }
+ this.#idleSourceId = null;
+ }
+
+ /**
+ * Resolves when event loop is idle
+ */
+ guiIdle() {
+ return new Promise(resolve => {
+ this.#idleResolves.push(resolve);
+ if (!this.#idleSourceId) {
+ this.#idleSourceId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ for (const res of this.#idleResolves) {
+ res();
+ }
+ this.#idleResolves = [];
+ this.#idleSourceId = null;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ });
+ }
+}
+
+/**
+ * Calls to `event` are delayed (throttled).
+ * Call `cancel` drop last event.
+ *
+ * @param {Function} timeoutFunc called function after delay
+ * @param {number} delayMillis delay in milliseconds
+ * @returns [event, cancel]
+ */
+export function throttleTimeout(timeoutFunc, delayMillis) {
+ let current = null;
+ return [
+ () => {
+ if (current === null) {
+ current = setTimeout(() => {
+ current = null;
+ timeoutFunc();
+ }, delayMillis);
+ }
+ },
+ () => {
+ if (current) {
+ clearTimeout(current);
+ current = null;
+ }
+ },
+ ];
+}
+
+export function extensionDirectory() {
+ const utilModulePath = /(.*)@file:\/\/(.*):\d+:\d+$/.exec(
+ new Error().stack.split('\n')[1]
+ )[2];
+ const extOrModuleDir = GLib.path_get_dirname(
+ GLib.path_get_dirname(utilModulePath)
+ );
+ // This file is either at /modules/util.js or /modules/sdt/util.js
+ return GLib.path_get_basename(extOrModuleDir) === 'modules'
+ ? GLib.path_get_dirname(extOrModuleDir)
+ : extOrModuleDir;
+}
+
+export function readFileAsync(pathOrFile, cancellable = null) {
+ return new Promise((resolve, reject) => {
+ try {
+ const file =
+ typeof pathOrFile === 'string'
+ ? Gio.File.new_for_path(pathOrFile)
+ : pathOrFile;
+ file.load_contents_async(cancellable, (f, res) => {
+ try {
+ const [, contents] = f.load_contents_finish(res);
+ const decoder = new TextDecoder('utf-8');
+ resolve(decoder.decode(contents));
+ } catch (err) {
+ reject(err);
+ }
+ });
+ } catch (err) {
+ reject(err);
+ }
+ });
+}
+
+export async function loadInterfaceXML(iface, cancellable = null) {
+ const readPromises = [
+ Gio.File.new_for_path(
+ `${extensionDirectory()}/dbus-interfaces/${iface}.xml`
+ ),
+ Gio.File.new_for_uri(
+ `resource:///org/gnome/shell/dbus-interfaces/${iface}.xml`
+ ),
+ ].map(async file => {
+ try {
+ return await readFileAsync(file, cancellable);
+ } catch (err) {
+ return '';
+ }
+ });
+ for await (const xml of readPromises) {
+ if (xml) return xml;
+ }
+ throw new Error(
+ `Failed to load D-Bus interface '${iface}'${
+ cancellable && cancellable.is_cancelled() ? ' (canceled)' : ''
+ }`
+ );
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules
new file mode 100644
index 0000000..05ec286
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules
@@ -0,0 +1,8 @@
+polkit.addRule(function(action, subject) {
+ if (action.id == "org.freedesktop.policykit.exec" &&
+ action.lookup("program") == "{{TOOL_OUT}}" &&
+ subject.isInGroup("{{TOOL_USER}}"))
+ {
+ return polkit.Result.YES;
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules.legacy b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules.legacy
new file mode 100644
index 0000000..7f00aa8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/10-dem.shutdowntimer.settimers.rules.legacy
@@ -0,0 +1,12 @@
+polkit.addRule(function (action, subject) {
+ var idx = action.id.lastIndexOf(".");
+ var username_stripped = action.id.substring(0, idx);
+ var username = action.id.substring(idx + 1);
+ if (username_stripped === "{{RULE_BASE}}") {
+ if (subject.user === username) {
+ return polkit.Result.YES;
+ } else {
+ return polkit.Result.NO;
+ }
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/dem.shutdowntimer.policy.in b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/dem.shutdowntimer.policy.in
new file mode 100644
index 0000000..6f93f7b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/polkit/dem.shutdowntimer.policy.in
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+<policyconfig>
+ <vendor>Shutdown Timer</vendor>
+ <vendor_url>https://github.com/Deminder/ShutdownTimer</vendor_url>
+
+ <action id="{{ACTION_ID}}">
+ <description>Control shutdown and rtc wake alarm schedule</description>
+ <description xml:lang="de">Steuerung des Ausschalt-Planers und des RTC-Weck-Alarms</description>
+ <message>No Authorization required to control shutdown or rtc wake alarm.</message>
+ <message xml:lang="de">Keine Autorisierung zur Steuerung des Ausschalt-Planers oder RTC-Weck-Alarms notwendig.</message>
+ <defaults>
+ <allow_any>yes</allow_any>
+ <allow_inactive>yes</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.exec.path">{{PATH}}</annotate>
+ <annotate key="{{ACTION_BASE}.polkit-rule.version">3.0.0</annotate>
+ </action>
+</policyconfig>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/prefs.js
new file mode 100644
index 0000000..9914fd2
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/prefs.js
@@ -0,0 +1,240 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import Gtk from 'gi://Gtk';
+import Gio from 'gi://Gio';
+
+import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import { Install } from './modules/install.js';
+import {
+ actionLabel,
+ ACTIONS,
+ mapLegacyAction,
+ supportedActions,
+} from './dbus-service/action.js';
+import { logDebug, Idle } from './modules/util.js';
+
+const templateComponents = {
+ shutdown: {
+ 'shutdown-mode': 'combo',
+ 'root-mode': 'switch',
+ 'show-end-session-dialog': 'switch',
+ 'shutdown-max-timer': 'adjustment',
+ 'shutdown-ref-timer': 'buffer',
+ 'shutdown-slider': 'adjustment',
+ 'nonlinear-shutdown-slider': 'adjustment',
+ },
+ wake: {
+ 'auto-wake': 'switch',
+ 'wake-max-timer': 'adjustment',
+ 'wake-ref-timer': 'buffer',
+ 'wake-slider': 'adjustment',
+ 'nonlinear-wake-slider': 'adjustment',
+ },
+ display: {
+ 'show-settings': 'switch',
+ 'show-shutdown-mode': 'buffer',
+ 'show-shutdown-slider': 'switch',
+ 'show-shutdown-indicator': 'switch',
+ 'show-shutdown-absolute-timer': 'switch',
+ 'show-textboxes': 'switch',
+ 'show-wake-slider': 'switch',
+ 'show-wake-items': 'switch',
+ 'show-wake-absolute-timer': 'switch',
+ },
+};
+
+export default class ShutdownTimerPreferences extends ExtensionPreferences {
+ /**
+ * Fill the preferences window with preferences.
+ *
+ * The default implementation adds the widget
+ * returned by getPreferencesWidget().
+ *
+ * @param {Adw.PreferencesWindow} window - the preferences window
+ */
+ fillPreferencesWindow(window) {
+ const builder = Gtk.Builder.new();
+ builder.add_from_file(
+ this.dir.get_child('ui').get_child('prefs.ui').get_path()
+ );
+
+ const settings = this.getSettings();
+ const handlers = [];
+ const pageNames = ['install', 'shutdown', 'wake', 'display'].map(
+ n => `shutdowntimer-prefs-${n}`
+ );
+ for (const name of pageNames) {
+ const pageId = name.replaceAll('-', '_');
+ const page = builder.get_object(pageId);
+ const pageName = pageId.split('_').at(-1);
+ if (!page) {
+ throw new Error(`${pageId} not found!`);
+ }
+ if (pageName === 'install') {
+ const idle = new Idle();
+ const install = new Install();
+ this.initInstallPage(
+ builder,
+ this.dir.get_child('tool').get_child('installer.sh').get_path(),
+ install,
+ idle
+ );
+ window.connect('destroy', () => {
+ idle.destroy();
+ install.destroy();
+ });
+ }
+ this.initPage(pageName, builder, settings);
+ window.add(page);
+ }
+ const selPageName =
+ pageNames[settings.get_int('preferences-selected-page-value')];
+ if (selPageName) {
+ window.set_visible_page_name(selPageName);
+ }
+ const pageVisHandlerId = window.connect('notify::visible-page-name', () => {
+ logDebug(window.get_visible_page_name());
+ settings.set_int(
+ 'preferences-selected-page-value',
+ pageNames.indexOf(window.get_visible_page_name())
+ );
+ });
+ handlers.push([window, pageVisHandlerId]);
+
+ window.connect('destroy', () => {
+ handlers.forEach(([comp, handlerId]) => {
+ comp.disconnect(handlerId);
+ });
+ });
+ }
+
+ async initShutdownModeCombo(settings, comp) {
+ const model = new Gtk.StringList();
+ const actionIds = [];
+ try {
+ for await (const action of supportedActions()) {
+ model.append(actionLabel(action));
+ actionIds.push(ACTIONS[action]);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ comp.model = model;
+ const updateComboRow = () => {
+ const actionId =
+ ACTIONS[mapLegacyAction(settings.get_string('shutdown-mode-value'))];
+ const index = actionIds.indexOf(actionId);
+ if (index >= 0) comp.selected = index;
+ };
+ comp.connect('notify::selected', () => {
+ const actionId = actionIds[comp.selected];
+ const action = Object.entries(ACTIONS).find(
+ ([_, id]) => id === actionId
+ )[0];
+ if (action) settings.set_string('shutdown-mode-value', action);
+ });
+ const comboHandlerId = settings.connect(
+ 'changed::shutdown-mode-value',
+ () => updateComboRow()
+ );
+ comp.connect('destroy', () => settings.disconnect(comboHandlerId));
+ updateComboRow();
+ }
+
+ initPage(pageName, builder, settings) {
+ if (pageName in templateComponents) {
+ for (const [baseName, component] of Object.entries(
+ templateComponents[pageName]
+ )) {
+ const baseId = baseName.replaceAll('-', '_');
+ const settingsName = `${baseName}-value`;
+ const compId = `${baseId}_${component}`;
+ const comp = builder.get_object(compId);
+ if (!comp) {
+ throw new Error(`Component not found in template: ${compId}`);
+ }
+ if (compId === 'shutdown_mode_combo') {
+ this.initShutdownModeCombo(settings, comp);
+ } else {
+ settings.bind(
+ settingsName,
+ comp,
+ {
+ adjustment: 'value',
+ switch: 'active',
+ textbuffer: 'text',
+ buffer: 'text',
+ }[component],
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ }
+
+ if (
+ [
+ 'show_wake_slider_switch',
+ 'show_wake_absolute_timer_switch',
+ ].includes(compId)
+ )
+ this.switchDependsOnSetting(comp, settings, 'show-wake-items-value');
+ }
+ }
+ }
+
+ switchDependsOnSetting(comp, settings, settingsName) {
+ const update = () => {
+ const active = settings.get_boolean(settingsName);
+ comp.sensitive = active;
+ const row = comp.get_parent().get_parent();
+ row.sensitive = active;
+ };
+ const handlerId = settings.connect(`changed::${settingsName}`, () =>
+ update()
+ );
+ comp.connect('destroy', () => settings.disconnect(handlerId));
+ update();
+ }
+
+ initInstallPage(builder, installerScriptPath, install, idle) {
+ // install log textbuffer updates
+ const logTextBuffer = builder.get_object('install_log_textbuffer');
+ const scrollAdj = builder.get_object('installer_scrollbar_adjustment');
+ const errorTag = new Gtk.TextTag({ foreground: 'red' });
+ const successTag = new Gtk.TextTag({ foreground: 'green' });
+ const table = logTextBuffer.get_tag_table();
+ table.add(errorTag);
+ table.add(successTag);
+
+ const installSwitch = builder.get_object('install_policy_switch');
+ installSwitch.set_active(install.checkInstalled());
+ installSwitch.connect('notify::active', () =>
+ install.installAction(
+ installerScriptPath,
+ installSwitch.get_active() ? 'install' : 'uninstall',
+ async message => {
+ await idle.guiIdle();
+ // Format log lines
+ message.split('\n').forEach(line => {
+ line = ['[', '#'].includes(line[0]) ? line : ` ${line}`;
+ const b = logTextBuffer;
+ b.insert(b.get_end_iter(), `${line}\n`, -1);
+ const end = b.get_end_iter();
+ const start = end.copy();
+ if (start.backward_line()) {
+ if (line.startsWith('# ')) {
+ b.apply_tag(errorTag, start, end);
+ } else if (line.endsWith('🟢')) {
+ b.apply_tag(successTag, start, end);
+ }
+ }
+ });
+ await idle.guiIdle();
+ scrollAdj.set_value(1000000);
+ }
+ )
+ );
+ // Clear install log
+ logTextBuffer.set_text('', -1);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/gschemas.compiled
new file mode 100644
index 0000000..4bba450
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/org.gnome.shell.extensions.shutdowntimer-deminder.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/org.gnome.shell.extensions.shutdowntimer-deminder.gschema.xml
new file mode 100644
index 0000000..c774c1e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/schemas/org.gnome.shell.extensions.shutdowntimer-deminder.gschema.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+
+<schemalist gettext-domain="ShutdownTimer">
+ <schema path="/org/gnome/shell/extensions/shutdowntimer-deminder/" id="org.gnome.shell.extensions.shutdowntimer-deminder">
+
+ <key type="i" name="shutdown-max-timer-value">
+ <default>180</default>
+ <summary>Maximum shutdown time (in minutes)</summary>
+ <description>Set maximum selectable shutdown time of the slider (in minutes). Use only values greater zero.</description>
+ </key>
+
+ <key type="i" name="wake-max-timer-value">
+ <default>1440</default>
+ <summary>Maximum wake time (in minutes)</summary>
+ <description>Set maximum selectable wake time of the slider (in minutes). Use only values greater zero.</description>
+ </key>
+
+ <key type="s" name="shutdown-ref-timer-value">
+ <default>"now"</default>
+ <summary>Reference start time for shutdown</summary>
+ <description>Reference start time for shutdown either now or HH:MM</description>
+ </key>
+
+ <key type="b" name="show-shutdown-absolute-timer-value">
+ <default>true</default>
+ <summary>Display shutdown time as absolute timestamp</summary>
+ <description>Display shutdown time as absolute timestamp "HH:MM"</description>
+ </key>
+
+ <key type="s" name="wake-ref-timer-value">
+ <default>"now"</default>
+ <summary>Reference start time for wake</summary>
+ <description>Reference start time for wake either now, shutdown, or HH:MM</description>
+ </key>
+
+ <key type="b" name="show-wake-absolute-timer-value">
+ <default>true</default>
+ <summary>Display wake time as absolute timestamp</summary>
+ <description>Display wake time as absolute timestamp "HH:MM"</description>
+ </key>
+
+ <key type="b" name="auto-wake-value">
+ <default>false</default>
+ <summary>Automatically start and stop wake on shutdown timer toggle</summary>
+ <description>Enable/Disable the wake alarm when the shutdown timer is started/stopped.</description>
+ </key>
+
+ <key type="i" name="shutdown-timestamp-value">
+ <default>-1</default>
+ <summary>Scheduled shutdown timestamp.</summary>
+ <description>Unix time in seconds of scheduled shutdown or -1 if disabled.</description>
+ </key>
+
+ <key type="d" name="wake-slider-value">
+ <default>70</default>
+ <summary>Wake slider position (in percent)</summary>
+ <description>Set wake slider position as percent of the maximum time. Must be in range 0 and 100.</description>
+ </key>
+
+ <key type="d" name="nonlinear-wake-slider-value">
+ <default>1.5</default>
+ <summary>Ramp-up of non-linear wake slider value</summary>
+ <description>Exponential ramp-up for wake time slider</description>
+ </key>
+
+ <key type="d" name="shutdown-slider-value">
+ <default>70</default>
+ <summary>Shutdown slider position (in percent)</summary>
+ <description>Set shutdown slider position as percent of the maximum time. Must be in range 0 and 100.</description>
+ </key>
+
+ <key type="d" name="nonlinear-shutdown-slider-value">
+ <default>0</default>
+ <summary>Ramp-up of non-linear shutdown slider value</summary>
+ <description>Exponential ramp-up for shutdown time slider</description>
+ </key>
+
+ <key type="b" name="show-settings-value">
+ <default>true</default>
+ <summary>Show settings button</summary>
+ <description>Show/hide settings button in widget.</description>
+ </key>
+
+ <key type="b" name="show-shutdown-slider-value">
+ <default>true</default>
+ <summary>Show shutdown slider</summary>
+ <description>Show/hide shutdown slider in widget.</description>
+ </key>
+
+ <key type="b" name="show-wake-slider-value">
+ <default>true</default>
+ <summary>Show wake slider</summary>
+ <description>Show/hide wake slider in widget.</description>
+ </key>
+
+ <key type="b" name="show-wake-items-value">
+ <default>false</default>
+ <summary>Show all wake items</summary>
+ <description>Show/hide all wake items in widget.</description>
+ </key>
+
+ <key type="b" name="show-textboxes-value">
+ <default>true</default>
+ <summary>Show notification text boxes</summary>
+ <description>Show/hide notification text boxes on screen.</description>
+ </key>
+
+ <key type="b" name="root-mode-value">
+ <default>false</default>
+ <summary>Root mode</summary>
+ <description>Set root mode on/off. In root mode powering off is done via 'pkexec' and 'shutdown' terminal command.</description>
+ </key>
+
+ <key type="b" name="show-end-session-dialog-value">
+ <default>true</default>
+ <summary>Show end-session dialog</summary>
+ <description>Show the end-session dialog for reboot and shutdown if screensaver is inactive.</description>
+ </key>
+
+ <key type="s" name="show-shutdown-mode-value">
+ <default>"p,s"</default>
+ <summary>Shown shutdown modes</summary>
+ <description>Comma-separated shutdown modes which are shown in the popup menu (p: poweroff, s: suspend, r: reboot)</description>
+ </key>
+
+ <key type="s" name="shutdown-mode-value">
+ <default>"poweroff"</default>
+ <summary>Use mode</summary>
+ <description>Mode to use for timer action</description>
+ </key>
+
+ <key type="b" name="show-shutdown-indicator-value">
+ <default>true</default>
+ <summary>Show shutdown indicator</summary>
+ <description>Shows the remaining time until shutdown action</description>
+ </key>
+
+ <key type="i" name="preferences-selected-page-value">
+ <default>0</default>
+ <summary>Last selected page in the preferences.</summary>
+ <description>Last selected page in the preferences.</description>
+ </key>
+
+ </schema>
+</schemalist>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/stylesheet.css b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/stylesheet.css
new file mode 100644
index 0000000..e73e2f6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/stylesheet.css
@@ -0,0 +1,29 @@
+/*
+ * SPDX-FileCopyrightText: Daniel Neumann
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+.textbox-label {
+ font-size: 2em;
+ font-weight: bold;
+ color: #ffffff;
+ background-color: rgba(10, 10, 10, 0.7);
+ border-radius: 0.5em;
+ padding: 0.5em;
+}
+
+.settings-button {
+ padding: 4px;
+ border-radius: 32px;
+ margin: 1px;
+}
+
+.settings-button:hover,
+.settings-button:focus {
+ padding: 5px;
+ margin: 0px;
+}
+
+.settings-button > StIcon {
+ icon-size: 15px;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/installer.sh b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/installer.sh
new file mode 100755
index 0000000..56d4167
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/installer.sh
@@ -0,0 +1,300 @@
+#!/bin/bash
+
+# SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# Adapted from cpupower extension by:
+# Martin Koppehel <psl.kontakt@gmail.com>, Fin Christensen <christensen.fin@gmail.com>
+
+# installer.sh - This script installs a policykit rule for the Shutdown Timer gnome-shell extension.
+#
+# This file is part of the gnome-shell extension ShutdownTimer@Deminder.
+
+
+set -e
+
+################################
+# EXTENSION SPECIFIC OPTIONS: #
+################################
+
+EXTENSION_NAME="Shutdown Timer"
+ACTION_BASE="dem.shutdowntimer"
+RULE_BASE="$ACTION_BASE.settimers"
+CFC_BASE="shutdowntimerctl"
+POLKIT_DIR="polkit"
+VERSION=1
+
+
+EXIT_SUCCESS=0
+EXIT_INVALID_ARG=1
+EXIT_FAILED=2
+EXIT_NEEDS_UPDATE=3
+EXIT_NEEDS_SECURITY_UPDATE=4
+EXIT_NOT_INSTALLED=5
+EXIT_MUST_BE_ROOT=6
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" #stackoverflow 59895
+
+export TEXTDOMAINDIR="$DIR/../locale"
+export TEXTDOMAIN="ShutdownTimer"
+function gtxt() {
+ gettext "$1"
+}
+
+function polkit_version_greater_than() {
+ printf -v versions '%s\n%s' "$(pkaction --version | cut -d' ' -f3)" "$1"
+ [[ $versions != "$(sort -V <<< "$versions")" ]]
+}
+
+function polkit_action_lookup_program_gets_realpath() {
+ polkit_version_greater_than "126"
+}
+
+function polkit_supports_standalonerule() {
+ polkit_version_greater_than "0.106"
+}
+
+function check_support() {
+ local polkit_info=", stand-alone polkit rules"
+ if polkit_supports_standalonerule; then
+ polkit_info+=" available"
+ if polkit_action_lookup_program_gets_realpath; then
+ polkit_info+=", with realpath program lookup"
+ fi
+ else
+ polkit_info+=" not avaliable"
+ fi
+ if which rtcwake >/dev/null 2>&1
+ then
+ echo "rtcwake supported${polkit_info}"
+ exit ${EXIT_SUCCESS}
+ else
+ echo "rtcwake unsupported${polkit_info}"
+ exit ${EXIT_FAILED}
+ fi
+}
+
+function require_root() {
+ if [ "${EUID}" -ne 0 ]; then
+ echo "This action must be run as root!"
+ exit ${EXIT_MUST_BE_ROOT}
+ fi
+}
+
+function fail() {
+ echo "$(gtxt "Failed")${1}" >&2 && exit ${EXIT_FAILED}
+}
+DEFAULT_SUCCESS_MSG=$(gtxt 'Success')
+
+function success() {
+ echo -n "${1:-$DEFAULT_SUCCESS_MSG}"
+ echo -e "\U1F7E2"
+}
+
+
+
+########################
+# GENERALIZED SCRIPT: #
+########################
+
+function usage() {
+ echo "Usage: installer.sh [options] {supported,install,check,update,uninstall}"
+ echo
+ echo "Available options:"
+ echo " --tool-user USER Set the user of the tool (default: \$USER)"
+ echo
+ exit ${EXIT_INVALID_ARG}
+}
+
+if [ $# -lt 1 ]
+then
+ usage
+fi
+
+ACTION=""
+TOOL_USER="$USER"
+while [[ $# -gt 0 ]]
+do
+ key="$1"
+
+ # we have to use command line arguments here as pkexec does not support
+ # setting environment variables
+ case $key in
+ --tool-user)
+ TOOL_USER="$2"
+ shift
+ shift
+ ;;
+ supported|install|check|update|uninstall)
+ if [ -z "$ACTION" ]
+ then
+ ACTION="$1"
+ else
+ echo "Too many actions specified. Please give at most 1."
+ usage
+ fi
+ shift
+ ;;
+ *)
+ echo "Unknown argument $key"
+ usage
+ ;;
+ esac
+done
+
+
+CFC_DIR="/usr/local/bin"
+RULE_DIR="/etc/polkit-1/rules.d"
+
+if polkit_supports_standalonerule; then
+ RULE_IN="${DIR}/../${POLKIT_DIR}/10-$RULE_BASE.rules"
+else
+ RULE_IN="${RULE_IN}.legacy"
+ ACTION_IN="${DIR}/../${POLKIT_DIR}/${ACTION_BASE}.policy.in"
+fi
+TOOL_IN="${DIR}/$CFC_BASE"
+
+TOOL_OUT="${CFC_DIR}/${CFC_BASE}-${TOOL_USER}"
+RULE_OUT="${RULE_DIR}/10-${RULE_BASE}-${TOOL_USER}.rules"
+ACTION_ID="${RULE_BASE}.${TOOL_USER}"
+ACTION_OUT="/usr/share/polkit-1/actions/${ACTION_ID}.policy"
+
+function print_policy_xml() {
+ sed -e "s:{{PATH}}:${TOOL_OUT}:g" \
+ -e "s:{{ACTION_BASE}}:${ACTION_BASE}:g" \
+ -e "s:{{ACTION_ID}}:${ACTION_ID}:g" "${ACTION_IN}"
+}
+
+function print_rules_javascript() {
+ if [[ "$RULE_IN" == *.legacy ]]; then
+ sed -e "s:{{RULE_BASE}}:${RULE_BASE}:g" "${RULE_IN}"
+ else
+ local tool_out=${TOOL_OUT}
+ if polkit_action_lookup_program_gets_realpath; then
+ tool_out="$(realpath "${TOOL_OUT}")"
+ fi
+ sed -e "s:{{TOOL_OUT}}:${tool_out}:g" \
+ -e "s:{{TOOL_USER}}:${TOOL_USER}:g" "${RULE_IN}"
+ fi
+
+}
+
+if [ "$ACTION" = "supported" ]
+then
+ check_support
+fi
+
+
+if [ "$ACTION" = "check" ]
+then
+ require_root
+ NOT_FOUND=0
+ NEEDS_UPDATE=0
+ function check_installed() {
+ if [ -f "$1" ]; then
+ echo -n "Found: $1"
+ if cmp --silent "$1" "$2"; then
+ echo " (OK)"
+ else
+ echo " (needs update)"
+ NEEDS_UPDATE=1
+ fi
+ else
+ echo "Not found: $1"
+ NOT_FOUND=1
+ fi
+ }
+ check_installed "${TOOL_OUT}" "${TOOL_IN}"
+ check_installed "${RULE_OUT}" <(print_rules_javascript)
+ if [ "$NEEDS_UPDATE" -gt 0 ]; then
+ echo "Your $EXTENSION_NAME installation needs updating!"
+ exit ${EXIT_NEEDS_UPDATE}
+ fi
+ if [ "$NOT_FOUND" -gt 0 ]; then
+ echo "Not installed!"
+ exit ${EXIT_NOT_INSTALLED}
+ fi
+
+ echo "OK"
+ exit ${EXIT_SUCCESS}
+fi
+
+TOOL_NAME=$(basename ${TOOL_OUT})
+
+if [ "$ACTION" = "install" ]
+then
+ require_root
+ echo -n "$(gtxt 'Installing') ${TOOL_NAME} $(gtxt 'tool')... "
+ mkdir -p "${CFC_DIR}"
+ install "${TOOL_IN}" "${TOOL_OUT}" || fail
+ success
+
+ if [ ! -z "$ACTION_IN" ];then
+ echo "$(gtxt 'Using legacy policykit install')..."
+ echo -n "$(gtxt 'Installing') $(gtxt 'policykit action')..."
+ (print_policy_xml > "${ACTION_OUT}" 2>/dev/null && chmod 0644 "${ACTION_OUT}") || fail
+ success
+ fi
+
+ echo -n "$(gtxt 'Installing') $(gtxt 'policykit rule')..."
+ mkdir -p "${RULE_DIR}"
+ (print_rules_javascript > "${RULE_OUT}" 2>/dev/null && chmod 0644 "${RULE_OUT}") || fail
+ success
+
+ exit ${EXIT_SUCCESS}
+fi
+
+if [ "$ACTION" = "update" ]
+then
+ require_root
+ "${BASH_SOURCE[0]}" --tool-user "${TOOL_USER}" uninstall || exit $?
+ "${BASH_SOURCE[0]}" --tool-user "${TOOL_USER}" install || exit $?
+
+ exit ${EXIT_SUCCESS}
+fi
+
+if [ "$ACTION" = "uninstall" ]
+then
+ require_root
+ LEG_CFG_OUT="/usr/bin/shutdowntimerctl-$TOOL_USER"
+ if [ -f "$LEG_CFG_OUT" ]
+ then
+ # remove legacy "tool" install
+ echo -n "$(gtxt 'Uninstalling') $(gtxt 'tool')..."
+ rm "${LEG_CFG_OUT}" || fail " - $(gtxt 'cannot remove') ${LEG_CFG_OUT}" && success
+ fi
+
+ if [ -f "$ACTION_OUT" ]
+ then
+ # remove legacy "policykit action" install
+ echo -n "$(gtxt 'Uninstalling') $(gtxt 'policykit action')..."
+ rm "${ACTION_OUT}" || fail " - $(gtxt 'cannot remove') ${ACTION_OUT}" && success
+ fi
+ LEG_RULE_OUT="/usr/share/polkit-1/rules.d/10-dem.shutdowntimer.settimers.rules"
+ if [ -f "$LEG_RULE_OUT" ]
+ then
+ # remove legacy "policykit action" install
+ echo -n "$(gtxt 'Uninstalling') $(gtxt 'policykit rule')..."
+ rm "${LEG_RULE_OUT}" || fail " - $(gtxt 'cannot remove') ${LEG_RULE_OUT}" && success
+ fi
+
+ function uninstallFile() {
+ echo -n "$(gtxt 'Uninstalling') $2... "
+ if [ -f "$1" ]
+ then
+ rm "$1" || fail " - $(gtxt 'cannot remove') $1" && success
+ else
+ echo "$2 $(gtxt 'not installed at') $1"
+ fi
+ }
+
+ uninstallFile "${TOOL_OUT}" "${TOOL_NAME} $(gtxt 'tool')"
+ uninstallFile "${RULE_OUT}" "$(gtxt 'policykit rule')"
+
+ exit ${EXIT_SUCCESS}
+fi
+
+echo "Unknown parameter."
+usage
+
+
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/shutdowntimerctl b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/shutdowntimerctl
new file mode 100644
index 0000000..c79c6b0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/tool/shutdowntimerctl
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# shutdowntimerctl - This script can configure the shutdown and rtc wake alarm schedule.
+#
+# This file is part of the gnome-shell extension ShutdownTimer@Deminder.
+
+SHUTDOWN_BIN=/usr/sbin/shutdown
+RTCWAKE_BIN=/usr/sbin/rtcwake
+
+SHUTDOWN_MODE="-P"
+if [ ! -z "$2" ] && [ "$2" -gt 0 ];then
+ POSITIVE_VALUE="$2"
+fi
+
+function print_help() {
+ echo "[help] (show this help)" >&2
+ echo "[shutdown|reboot|shutdown-cancel] {MINUTES}" >&2
+ echo "[wake|wake-cancel] {MINUTES} (default: 0)" >&2
+}
+
+if [ "$#" -lt 1 ]; then
+ print_help
+ exit
+fi
+
+case "$1" in
+ shutdown|reboot)
+ if [[ "$1" = "reboot" ]]; then
+ SHUTDOWN_MODE="-r"
+ fi
+ $SHUTDOWN_BIN "$SHUTDOWN_MODE" "$POSITIVE_VALUE"
+ ;;
+ shutdown-cancel)
+ $SHUTDOWN_BIN -c
+ ;;
+ wake)
+ $RTCWAKE_BIN --date +${POSITIVE_VALUE:-0}min --mode no
+ ;;
+ wake-cancel)
+ $RTCWAKE_BIN --mode disable
+ ;;
+ --version)
+ echo 1
+ ;;
+ -h|help)
+ print_help
+ ;;
+ *)
+ echo "Invalid argument: $1" >&2
+ print_help
+esac
+
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/ui/prefs.ui b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/ui/prefs.ui
new file mode 100644
index 0000000..8ce4730
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/ShutdownTimer@deminder/ui/prefs.ui
@@ -0,0 +1,482 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="ShutdownTimer">
+ <object class="GtkTextBuffer" id="check_command_textbuffer" />
+ <object class="GtkTextBuffer" id="install_log_textbuffer">
+ <property name="text">START test
+.
+.
+.
+.
+.
+.
+.
+.
+.
+.
+.
+.
+DONE test</property>
+ </object>
+ <object class="GtkEntryBuffer" id="install_policy_prefix_buffer" />
+ <object class="GtkAdjustment" id="installer_scrollbar_adjustment">
+ <property name="upper">1000000</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="nonlinear_shutdown_slider_adjustment">
+ <property name="lower">-5</property>
+ <property name="upper">5</property>
+ <property name="step-increment">0.001</property>
+ <property name="page-increment">0.10</property>
+ </object>
+ <object class="GtkAdjustment" id="nonlinear_wake_slider_adjustment">
+ <property name="lower">-5</property>
+ <property name="upper">5</property>
+ <property name="step-increment">0.001</property>
+ <property name="page-increment">0.10</property>
+ </object>
+ <object class="GtkEntryBuffer" id="show_shutdown_mode_buffer" />
+ <object class="GtkEntryBuffer" id="shutdown_ref_timer_buffer" />
+ <object class="GtkEntryBuffer" id="wake_ref_timer_buffer" />
+ <object class="GtkAdjustment" id="shutdown_max_timer_adjustment">
+ <property name="upper">10000</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="shutdown_slider_adjustment">
+ <property name="upper">100</property>
+ <property name="step-increment">0.1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="wake_max_timer_adjustment">
+ <property name="upper">10000</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="wake_slider_adjustment">
+ <property name="upper">100</property>
+ <property name="step-increment">0.1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <object class="AdwPreferencesPage" id="shutdowntimer_prefs_install">
+ <property name="name">shutdowntimer-prefs-install</property>
+ <property name="title" translatable="yes">Install</property>
+ <property name="icon-name">go-up-symbolic</property>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Install/Uninstall privileges for this user</property>
+ <property name="subtitle" translatable="yes">Setup a privileged script and give user access via polkit</property>
+ <property name="activatable-widget">install_policy_switch</property>
+ <child>
+ <object class="GtkSwitch" id="install_policy_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesRow">
+ <property name="title" translatable="yes">Install output</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="focusable">1</property>
+ <property name="vadjustment">installer_scrollbar_adjustment</property>
+ <property name="min-content-height">350</property>
+ <child>
+ <object class="GtkTextView">
+ <property name="focusable">1</property>
+ <property name="top-margin">5</property>
+ <property name="editable">0</property>
+ <property name="wrap-mode">char</property>
+ <property name="cursor-visible">0</property>
+ <property name="buffer">install_log_textbuffer</property>
+ <property name="accepts-tab">0</property>
+ <property name="monospace">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="AdwPreferencesPage" id="shutdowntimer_prefs_shutdown">
+ <property name="name">shutdowntimer-prefs-shutdown</property>
+ <property name="title" translatable="yes">Shutdown</property>
+ <property name="icon-name">preferences-system-time-symbolic</property>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Timer Action</property>
+ <child>
+ <object class="AdwComboRow" id="shutdown_mode_combo">
+ <property name="title" translatable="yes">Use mode</property>
+ <property name="subtitle" translatable="yes">Mode to use for timer action</property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Show end-session dialog</property>
+ <property name="subtitle" translatable="yes">Shown for reboot and shutdown if screensaver is inactive</property>
+ <property name="activatable-widget">show_end_session_dialog_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_end_session_dialog_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Toggle root shutdown with shutdown timer</property>
+ <property name="subtitle" translatable="yes">Runs extra &apos;shutdown -P/-r&apos; command for shutdown or reboot</property>
+ <property name="activatable-widget">root_mode_switch</property>
+ <child>
+ <object class="GtkSwitch" id="root_mode_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Timer Input</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Reference start time</property>
+ <property name="subtitle" translatable="yes">Options: now, HH:MM</property>
+ <property name="activatable-widget">shutdown_ref_timer_entry</property>
+ <child>
+ <object class="GtkEntry" id="shutdown_ref_timer_entry">
+ <property name="valign">center</property>
+ <property name="buffer">shutdown_ref_timer_buffer</property>
+ <property name="placeholder-text">23:59</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Shutdown slider position (in &amp;#37;)</property>
+ <property name="activatable-widget">shutdown_slider_spinbutton</property>
+ <child>
+ <object class="GtkSpinButton" id="shutdown_slider_spinbutton">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="adjustment">shutdown_slider_adjustment</property>
+ <property name="climb-rate">1</property>
+ <property name="snap-to-ticks">1</property>
+ <property name="numeric">1</property>
+ <property name="digits">1</property>
+ <property name="update-policy">if-valid</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesRow">
+ <property name="title" translatable="yes">Shutdown slider position (in &amp;#37;)</property>
+ <child>
+ <object class="GtkScale">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">shutdown_slider_adjustment</property>
+ <property name="show-fill-level">1</property>
+ <property name="round-digits">1</property>
+ <property name="digits">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Non-linear scaling or 0 to disable</property>
+ <property name="activatable-widget">nonlinear_shutdown_slider_spinbutton</property>
+ <child>
+ <object class="GtkSpinButton" id="nonlinear_shutdown_slider_spinbutton">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="adjustment">nonlinear_shutdown_slider_adjustment</property>
+ <property name="climb-rate">1</property>
+ <property name="digits">3</property>
+ <property name="snap-to-ticks">1</property>
+ <property name="numeric">1</property>
+ <property name="update-policy">if-valid</property>
+ <property name="value">120</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Maximum shutdown timer value (in minutes)</property>
+ <property name="activatable-widget">shutdown_max_timer_spinbutton</property>
+ <child>
+ <object class="GtkSpinButton" id="shutdown_max_timer_spinbutton">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="adjustment">shutdown_max_timer_adjustment</property>
+ <property name="climb-rate">1</property>
+ <property name="snap-to-ticks">1</property>
+ <property name="numeric">1</property>
+ <property name="update-policy">if-valid</property>
+ <property name="value">120</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="AdwPreferencesPage" id="shutdowntimer_prefs_wake">
+ <property name="name">shutdowntimer-prefs-wake</property>
+ <property name="title" translatable="yes">Wake</property>
+ <property name="icon-name">alarm-symbolic</property>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Timer Action</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Toggle wake with timer action</property>
+ <property name="activatable-widget">auto_wake_switch</property>
+ <child>
+ <object class="GtkSwitch" id="auto_wake_switch">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Timer Input</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Reference start time</property>
+ <property name="subtitle" translatable="yes">Options: now, shutdown, HH:MM</property>
+ <property name="activatable-widget">wake_ref_timer_entry</property>
+ <child>
+ <object class="GtkEntry" id="wake_ref_timer_entry">
+ <property name="valign">center</property>
+ <property name="buffer">wake_ref_timer_buffer</property>
+ <property name="placeholder-text">23:59</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Wake slider position (in &amp;#37;)</property>
+ <property name="activatable-widget">wake_slider_spinbutton</property>
+ <child>
+ <object class="GtkSpinButton" id="wake_slider_spinbutton">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="adjustment">wake_slider_adjustment</property>
+ <property name="climb-rate">1</property>
+ <property name="snap-to-ticks">1</property>
+ <property name="numeric">1</property>
+ <property name="digits">1</property>
+ <property name="update-policy">if-valid</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesRow">
+ <property name="title" translatable="yes">Wake slider position (in &amp;#37;)</property>
+ <child>
+ <object class="GtkScale">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">wake_slider_adjustment</property>
+ <property name="show-fill-level">1</property>
+ <property name="round-digits">1</property>
+ <property name="digits">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Non-linear scaling or 0 to disable</property>
+ <property name="activatable-widget">nonlinear_wake_slider_spinbutton</property>
+ <child>
+ <object class="GtkSpinButton" id="nonlinear_wake_slider_spinbutton">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="adjustment">nonlinear_wake_slider_adjustment</property>
+ <property name="climb-rate">1</property>
+ <property name="digits">3</property>
+ <property name="snap-to-ticks">1</property>
+ <property name="numeric">1</property>
+ <property name="update-policy">if-valid</property>
+ <property name="value">120</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Maximum wake timer value (in minutes)</property>
+ <property name="activatable-widget">wake_max_timer_spinbutton</property>
+ <child>
+ <object class="GtkSpinButton" id="wake_max_timer_spinbutton">
+ <property name="focusable">1</property>
+ <property name="valign">center</property>
+ <property name="adjustment">wake_max_timer_adjustment</property>
+ <property name="climb-rate">1</property>
+ <property name="snap-to-ticks">1</property>
+ <property name="numeric">1</property>
+ <property name="update-policy">if-valid</property>
+ <property name="value">120</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="AdwPreferencesPage" id="shutdowntimer_prefs_display">
+ <property name="name">shutdowntimer-prefs-display</property>
+ <property name="title" translatable="yes">Display</property>
+ <property name="icon-name">preferences-color-symbolic</property>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">General</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Show settings button</property>
+ <property name="activatable-widget">show_settings_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_settings_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Show notification text boxes</property>
+ <property name="activatable-widget">show_textboxes_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_textboxes_switch">
+ <property name="valign">center</property>
+ <property name="focusable">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Shutdown</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Show shutdown items</property>
+ <property name="subtitle" translatable="yes">Comma-separated shutdown modes which are shown in the popup menu (p: poweroff, s: suspend, r: reboot)</property>
+ <property name="activatable-widget">show_shutdown_mode_entry</property>
+ <child>
+ <object class="GtkEntry" id="show_shutdown_mode_entry">
+ <property name="valign">center</property>
+ <property name="buffer">show_shutdown_mode_buffer</property>
+ <property name="placeholder-text">p,r,s</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Show shutdown slider</property>
+ <property name="activatable-widget">show_shutdown_slider_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_shutdown_slider_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Show shutdown indicator</property>
+ <property name="subtitle" translatable="yes">Shows the remaining time until shutdown action</property>
+ <property name="activatable-widget">show_shutdown_indicator_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_shutdown_indicator_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Absolute shutdown time selection</property>
+ <property name="activatable-widget">show_shutdown_absolute_timer_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_shutdown_absolute_timer_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Wake</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Show wake items</property>
+ <property name="activatable-widget">show_wake_items_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_wake_items_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Show wake slider</property>
+ <property name="activatable-widget">show_wake_slider_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_wake_slider_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="focusable">1</property>
+ <property name="title" translatable="yes">Absolute wake time selection</property>
+ <property name="activatable-widget">show_wake_absolute_timer_switch</property>
+ <child>
+ <object class="GtkSwitch" id="show_wake_absolute_timer_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE
new file mode 100644
index 0000000..d7f1051
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/LICENSE
@@ -0,0 +1,339 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js
new file mode 100644
index 0000000..642c4d7
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/extension.js
@@ -0,0 +1,656 @@
+import Clutter from 'gi://Clutter';
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import St from 'gi://St'
+
+import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Util from 'resource:///org/gnome/shell/misc/util.js';
+
+import * as Sensors from './sensors.js';
+
+import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
+import * as Values from './values.js';
+import * as Config from 'resource:///org/gnome/shell/misc/config.js';
+import * as MenuItem from './menuItem.js';
+
+let vitalsMenu;
+
+var VitalsMenuButton = GObject.registerClass({
+ GTypeName: 'VitalsMenuButton',
+}, class VitalsMenuButton extends PanelMenu.Button {
+ _init(extensionObject) {
+ super._init(Clutter.ActorAlign.FILL);
+
+ this._extensionObject = extensionObject;
+ this._settings = extensionObject.getSettings();
+
+ this._sensorIcons = {
+ 'temperature' : { 'icon': 'temperature-symbolic.svg' },
+ 'voltage' : { 'icon': 'voltage-symbolic.svg' },
+ 'fan' : { 'icon': 'fan-symbolic.svg' },
+ 'memory' : { 'icon': 'memory-symbolic.svg' },
+ 'processor' : { 'icon': 'cpu-symbolic.svg' },
+ 'system' : { 'icon': 'system-symbolic.svg' },
+ 'network' : { 'icon': 'network-symbolic.svg',
+ 'icon-rx': 'network-download-symbolic.svg',
+ 'icon-tx': 'network-upload-symbolic.svg' },
+ 'storage' : { 'icon': 'storage-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._hotItems = {};
+ 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);
+ this._values = new Values.Values(this._settings, this._sensorIcons);
+ this._menuLayout = new St.BoxLayout({
+ vertical: false,
+ clip_to_allocation: true,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.CENTER,
+ reactive: true,
+ x_expand: true,
+ style_class: 'vitals-panel-menu'
+ });
+
+ this._drawMenu();
+ this.add_child(this._menuLayout);
+ this._settingChangedSignals = [];
+ this._refreshTimeoutId = null;
+
+ this._addSettingChangedSignal('update-time', this._updateTimeSettingChanged.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', 'include-static-gpu-info' ];
+ for (let setting of Object.values(settings))
+ this._addSettingChangedSignal(setting, this._redrawMenu.bind(this));
+
+ // add signals for show- preference based categories
+ for (let sensor in this._sensorIcons)
+ this._addSettingChangedSignal('show-' + sensor, this._showHideSensorsChanged.bind(this));
+
+ this._initializeMenu();
+
+ // start off with fresh sensors
+ this._querySensors();
+
+ // start monitoring sensors
+ this._initializeTimer();
+ }
+
+ _initializeMenu() {
+ // display sensor categories
+ for (let sensor in this._sensorIcons) {
+ // groups associated sensors under accordion menu
+ if (sensor in this._groups) continue;
+
+ //handle gpus separately.
+ if (sensor === 'gpu') continue;
+
+ 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());
+
+ let item = new PopupMenu.PopupBaseMenuItem({
+ reactive: false,
+ style_class: 'vitals-menu-button-container'
+ });
+
+ let customButtonBox = new St.BoxLayout({
+ style_class: 'vitals-button-box',
+ vertical: false,
+ clip_to_allocation: true,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ reactive: true,
+ x_expand: true
+ });
+
+ // custom round refresh button
+ let refreshButton = this._createRoundButton('view-refresh-symbolic', _('Refresh'));
+ refreshButton.connect('clicked', (self) => {
+ // force refresh by clearing history
+ this._sensors.resetHistory();
+ this._values.resetHistory(this._numGpus);
+
+ // make sure timer fires at next full interval
+ this._updateTimeChanged();
+
+ // refresh sensors now
+ this._querySensors();
+ });
+ customButtonBox.add_child(refreshButton);
+
+ // custom round monitor button
+ let monitorButton = this._createRoundButton('org.gnome.SystemMonitor-symbolic', _('System Monitor'));
+ monitorButton.connect('clicked', (self) => {
+ this.menu._getTopMenu().close();
+ Util.spawn(this._settings.get_string('monitor-cmd').split(" "));
+ });
+ customButtonBox.add_child(monitorButton);
+
+ // custom round preferences button
+ let prefsButton = this._createRoundButton('preferences-system-symbolic', _('Preferences'));
+ prefsButton.connect('clicked', (self) => {
+ this.menu._getTopMenu().close();
+ this._extensionObject.openPreferences();
+ });
+ customButtonBox.add_child(prefsButton);
+
+ // now add the buttons to the top bar
+ item.actor.add_child(customButtonBox);
+
+ // add buttons
+ this.menu.addMenuItem(item);
+
+ // query sensors on menu open
+ this._menuStateChangeId = this.menu.connect('open-state-changed', (self, isMenuOpen) => {
+ if (isMenuOpen) {
+ // make sure timer fires at next full interval
+ this._updateTimeChanged();
+
+ // refresh sensors now
+ this._querySensors();
+ }
+ });
+ }
+
+ _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'
+ });
+
+ button.child = new St.Icon({
+ icon_name: iconName
+ });
+
+ return button;
+ }
+
+ _removeMissingHotSensors(hotSensors) {
+ for (let i = hotSensors.length - 1; i >= 0; i--) {
+ let sensor = hotSensors[i];
+
+ // make sure default icon (if any) stays visible
+ if (sensor == '_default_icon_') continue;
+
+ // removes sensors that are no longer available
+ if (!this._sensorMenuItems[sensor]) {
+ hotSensors.splice(i, 1);
+ this._removeHotItem(sensor);
+ }
+ }
+
+ return hotSensors;
+ }
+
+ _saveHotSensors(hotSensors) {
+ // removes any sensors that may not currently be available
+ hotSensors = this._removeMissingHotSensors(hotSensors);
+
+ this._settings.set_strv('hot-sensors', hotSensors.filter(
+ function(item, pos) {
+ return hotSensors.indexOf(item) == pos;
+ }
+ ));
+ }
+
+ _initializeTimer() {
+ // used to query sensors and update display
+ let update_time = this._settings.get_int('update-time');
+ this._refreshTimeoutId = GLib.timeout_add_seconds(
+ GLib.PRIORITY_DEFAULT,
+ update_time,
+ (self) => {
+ // always query sensors (for panel display when hot)
+ this._querySensors();
+ return GLib.SOURCE_CONTINUE;
+ }
+ );
+ }
+
+ _createHotItem(key, value) {
+ let item = new St.BoxLayout({
+ style_class: 'vitals-panel-item',
+ });
+ this._hotItems[key] = item;
+ this._menuLayout.add_child(item);
+
+ if (!this._settings.get_boolean('hide-icons') || key == '_default_icon_') {
+ let icon = this._defaultIcon(key);
+ item.add_child(icon);
+ }
+
+ // don't add a label when no sensors are in the panel
+ if (key == '_default_icon_') return;
+
+ let label = new St.Label({
+ style_class: 'vitals-panel-label',
+ text: (value)?value:'\u2026', // ...
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER
+ });
+ // attempt to prevent ellipsizes
+ label.get_clutter_text().ellipsize = 0;
+ // keep track of label for removal later
+ this._hotLabels[key] = label;
+ // prevent "called on the widget" "which is not in the stage" errors by adding before width below
+ item.add_child(label);
+
+ // support for fixed widths #55, save label (text) width
+ this._widths[key] = label.get_clutter_text().width;
+ }
+
+ _showHideSensorsChanged(self, sensor) {
+ this._sensors.resetHistory();
+
+ 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_child(this.container);
+ let position = this._positionInPanel();
+
+ // allows easily addressable boxes
+ let boxes = {
+ left: Main.panel._leftBox,
+ center: Main.panel._centerBox,
+ right: Main.panel._rightBox
+ };
+
+ // update position when changed from preferences
+ 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();
+ }
+
+ _removeHotItems(){
+ for (let key in this._hotItems) {
+ this._removeHotItem(key);
+ }
+ }
+
+ _removeHotItem(key) {
+ if (key in this._hotItems) {
+ this._hotItems[key].destroy();
+ delete this._hotItems[key];
+ delete this._hotLabels[key];
+ delete this._widths[key];
+ }
+ }
+
+ _redrawMenu() {
+ this._removeHotItems();
+
+ for (let key in this._sensorMenuItems) {
+ if (key.includes('-group')) continue;
+ this._sensorMenuItems[key].destroy();
+ delete this._sensorMenuItems[key];
+ }
+
+ this._drawMenu();
+ this._sensors.resetHistory();
+ this._values.resetHistory(this._numGpus);
+ this._querySensors();
+ }
+
+ _drawMenu() {
+ // grab list of selected menubar icons
+ let hotSensors = this._settings.get_strv('hot-sensors');
+ for (let key of Object.values(hotSensors)) {
+ // fixes issue #225 which started when _max_ was moved to the end
+ if (key == '__max_network-download__') key = '__network-rx_max__';
+ if (key == '__max_network-upload__') key = '__network-tx_max__';
+
+ this._createHotItem(key);
+ }
+ }
+
+ _destroyTimer() {
+ // invalidate and reinitialize timer
+ if (this._refreshTimeoutId != null) {
+ GLib.Source.remove(this._refreshTimeoutId);
+ this._refreshTimeoutId = null;
+ }
+ }
+
+ _updateTimeSettingChanged() {
+ this._destroyTimer();
+ this._initializeTimer();
+ }
+
+ _updateTimeChanged() {
+ this._destroyTimer();
+ this._initializeTimer();
+ }
+
+ _addSettingChangedSignal(key, callback) {
+ this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback));
+ }
+
+ _updateDisplay(label, value, type, key) {
+ // update sensor value in menubar
+ if (this._hotLabels[key]) {
+ this._hotLabels[key].set_text(value);
+
+ // support for fixed widths #55
+ if (this._settings.get_boolean('fixed-widths')) {
+ // grab text box width and see if new text is wider than old text
+ let width2 = this._hotLabels[key].get_clutter_text().width;
+ if (width2 > this._widths[key]) {
+ this._hotLabels[key].set_width(width2);
+ this._widths[key] = width2;
+ }
+ }
+ }
+
+ // have we added this sensor before?
+ let item = this._sensorMenuItems[key];
+ if (item) {
+ // update sensor value in the group
+ item.value = value;
+ } else if (type.includes('-group')) {
+ // update text next to group header
+ let group = type.split('-')[0];
+ if (this._groups[group]) {
+ this._groups[group].status.text = value;
+ this._sensorMenuItems[type] = this._groups[group];
+ }
+ } else {
+ // add item to group for the first time
+ let sensor = { 'label': label, 'value': value, 'type': type }
+ this._appendMenuItem(sensor, key);
+ }
+ }
+
+ _appendMenuItem(sensor, key) {
+ 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._sensorIconPath(type, icon));
+
+ let item = new MenuItem.MenuItem(gicon, key, sensor.label, sensor.value, this._hotLabels[key]);
+ item.connect('toggle', (self) => {
+ let hotSensors = this._settings.get_strv('hot-sensors');
+
+ if (self.checked) {
+ // add selected sensor to panel
+ hotSensors.push(self.key);
+ this._createHotItem(self.key, self.value);
+ } else {
+ // remove selected sensor from panel
+ hotSensors.splice(hotSensors.indexOf(self.key), 1);
+ this._removeHotItem(self.key);
+ }
+
+ if (hotSensors.length <= 0) {
+ // add generic icon to panel when no sensors are selected
+ hotSensors.push('_default_icon_');
+ this._createHotItem('_default_icon_');
+ } else {
+ let defIconPos = hotSensors.indexOf('_default_icon_');
+ if (defIconPos >= 0) {
+ // remove generic icon from panel when sensors are selected
+ hotSensors.splice(defIconPos, 1);
+ this._removeHotItem('_default_icon_');
+ }
+ }
+
+ // this code is called asynchronously - make sure to save it for next round
+ this._saveHotSensors(hotSensors);
+ });
+
+ this._sensorMenuItems[key] = item;
+ let i = Object.keys(this._sensorMenuItems[key]).length;
+
+ // alphabetize the sensors for these categories
+ if (this._settings.get_boolean('alphabetize')) {
+ let menuItems = this._groups[type].menu._getMenuItems();
+ for (i = 0; i < menuItems.length; i++)
+ // use natural sort order for system load, etc
+ if (menuItems[i].label.localeCompare(item.label, undefined, { numeric: true, sensitivity: 'base' }) > 0)
+ break;
+ }
+
+ this._groups[type].menu.addMenuItem(item, i);
+ }
+
+ _defaultLabel() {
+ return new St.Label({
+ y_expand: true,
+ y_align: Clutter.ActorAlign.CENTER
+ });
+ }
+
+ _defaultIcon(key) {
+ let split = key.replaceAll('_', ' ').trim().split(' ')[0].split('-');
+ let type = split[0];
+
+ let icon = new St.Icon({
+ style_class: 'system-status-icon vitals-panel-icon-' + type,
+ reactive: true
+ });
+
+ // second condition prevents crash due to issue #225, which started when _max_ was moved to the end
+ // 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 { // support for hide icons #80
+ let iconObj = (split.length == 2)?'icon-' + split[1]:'icon';
+ 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);
+ }
+
+ _positionInPanel() {
+ let alignment = '';
+ let gravity = 0;
+ let arrow_pos = 0;
+
+ switch (this._settings.get_int('position-in-panel')) {
+ case 0: // left
+ alignment = 'left';
+ gravity = -1;
+ arrow_pos = 1;
+ break;
+ case 1: // center
+ alignment = 'center';
+ gravity = -1;
+ arrow_pos = 0.5;
+ break;
+ case 2: // right
+ alignment = 'right';
+ gravity = 0;
+ arrow_pos = 0;
+ break;
+ case 3: // far left
+ alignment = 'left';
+ gravity = 0;
+ arrow_pos = 1;
+ break;
+ case 4: // far right
+ alignment = 'right';
+ gravity = -1;
+ arrow_pos = 0;
+ break;
+ }
+
+ let centered = this._settings.get_boolean('menu-centered')
+ if (centered) arrow_pos = 0.5;
+
+ // set arrow position when initializing and moving vitals
+ this.menu._arrowAlignment = arrow_pos;
+
+ return [alignment, gravity];
+ }
+
+ _querySensors() {
+ // figure out last run time
+ let now = new Date().getTime();
+ let dwell = (now - this._last_query) / 1000;
+ this._last_query = now;
+
+ this._sensors.query((label, value, type, format) => {
+ const typeKey = type.replace('-group', '');
+ let key = '_' + typeKey + '_' + label.replace(' ', '_').toLowerCase() + '_';
+
+ // if a sensor is disabled, gray it out
+ if (key in this._sensorMenuItems) {
+ this._sensorMenuItems[key].setSensitive((value!='disabled'));
+
+ // don't continue below, last known value is shown
+ 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 = [];
+ }
+ }
+
+ _notify(msg, details, icon) {
+ let source = new MessageTray.Source('MyApp Information', icon);
+ Main.messageTray.add(source);
+ let notification = new MessageTray.Notification(source, msg, details);
+ notification.setTransient(true);
+ source.notify(notification);
+ }
+
+ destroy() {
+ this._destroyTimer();
+ this._sensors.destroy();
+
+ for (let signal of Object.values(this._settingChangedSignals))
+ this._settings.disconnect(signal);
+
+ super.destroy();
+ }
+});
+
+export default class VitalsExtension extends Extension {
+ enable() {
+ vitalsMenu = new VitalsMenuButton(this);
+ let position = vitalsMenu._positionInPanel();
+ Main.panel.addToStatusArea('vitalsMenu', vitalsMenu, position[1], position[0]);
+ }
+
+ disable() {
+ vitalsMenu.destroy();
+ vitalsMenu = null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js
new file mode 100644
index 0000000..a7211b9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/file.js
@@ -0,0 +1,82 @@
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib'
+
+// convert Uint8Array into a literal string
+function convertUint8ArrayToString(contents) {
+ const decoder = new TextDecoder('utf-8');
+ return decoder.decode(contents).trim();
+}
+
+export function File(path) {
+ if (path.indexOf('https://') == -1)
+ this.file = Gio.File.new_for_path(path);
+ else
+ this.file = Gio.File.new_for_uri(path);
+}
+
+File.prototype.read = function(delimiter = '', strip_header = false) {
+ return new Promise((resolve, reject) => {
+ try {
+ this.file.load_contents_async(null, function(file, res) {
+ try {
+ // grab contents of file or website
+ let contents = file.load_contents_finish(res)[1];
+
+ // convert contents to string
+ contents = convertUint8ArrayToString(contents);
+
+ // split contents by delimiter if passed in
+ if (delimiter) contents = contents.split(delimiter);
+
+ // optionally strip header when converting to a list
+ if (strip_header) contents.shift();
+
+ // return results
+ resolve(contents);
+ } catch (e) {
+ reject(e.message);
+ }
+ });
+ } catch (e) {
+ reject(e.message);
+ }
+ });
+};
+
+File.prototype.list = function() {
+ return new Promise((resolve, reject) => {
+ let max_items = 125, results = [];
+
+ try {
+ this.file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME, Gio.FileQueryInfoFlags.NONE, GLib.PRIORITY_LOW, null, function(file, res) {
+ try {
+ let enumerator = file.enumerate_children_finish(res);
+
+ let callback = function(enumerator, res) {
+ try {
+ let files = enumerator.next_files_finish(res);
+ for (let i = 0; i < files.length; i++) {
+ results.push(files[i].get_attribute_as_string(Gio.FILE_ATTRIBUTE_STANDARD_NAME));
+ }
+
+ if (files.length == 0) {
+ enumerator.close_async(GLib.PRIORITY_LOW, null, function(){});
+ resolve(results);
+ } else {
+ enumerator.next_files_async(max_items, GLib.PRIORITY_LOW, null, callback);
+ }
+ } catch (e) {
+ reject(e.message);
+ }
+ };
+
+ enumerator.next_files_async(max_items, GLib.PRIORITY_LOW, null, callback);
+ } catch (e) {
+ reject(e.message);
+ }
+ });
+ } catch (e) {
+ reject(e.message);
+ }
+ });
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/subprocess.js b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/helpers/subprocess.js
new file mode 100644
index 0000000..df0bf49
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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;
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg
new file mode 100644
index 0000000..28a9887
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/battery-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#222" d="M7 0C6 0 6 1 6 1v1H5s-.707-.016-1.45.355C2.814 2.727 2 3.668 2 5v8s-.016.707.355 1.45C2.727 15.187 3.668 16 5 16h6s.707.016 1.45-.355C13.187 15.273 14 14.332 14 13V5c0-1.332-.812-2.273-1.55-2.645C11.706 1.985 11 2 11 2h-1V1c0-1-1-1-1-1zM5 4h6c.555 0 1 .445 1 1v8c0 .555-.445 1-1 1H5c-.555 0-1-.445-1-1V5c0-.555.445-1 1-1m0 1v2h6V5zm0 3v2h6V8zm0 3v2h6v-2zm0 0"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg
new file mode 100644
index 0000000..31c397b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/cpu-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#222"><path d="M5 5h6v6H5zm8 0h3v1h-3zm0 2h3v1h-3zm0 2h3v1h-3zM0 6h3v1H0zm0 2h3v1H0zm0 2h3v1H0zM5 0h1v3H5zm2 0h1v3H7zm2 0h1v3H9zm1 13h1v3h-1zm-2 0h1v3H8zm-2 0h1v3H6zm0 0"/><path d="M5 2C3.355 2 2 3.355 2 5v6c0 1.645 1.355 3 3 3h6c1.645 0 3-1.355 3-3V5c0-1.645-1.355-3-3-3zm0 2h6c.57 0 1 .43 1 1v6c0 .57-.43 1-1 1H5c-.57 0-1-.43-1-1V5c0-.57.43-1 1-1m0 0"/></g></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/fan-symbolic.svg
new file mode 100644
index 0000000..234f797
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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="#222"><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.96 7.96 0 0 0 8 0M4 1.094a8 8 0 0 0-3.219 3.5c.12 2.215 1.974 3.23 4.225 3.472L5 8a3 3 0 0 1 .646-1.85C4.248 4.28 3.912 1.787 4 1.094m9.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.96 7.96 0 0 0-1.438-4.563 3.4 3.4 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.056a8 8 0 0 0 3.219-3.5c-.12-2.214-1.972-3.228-4.221-3.472M0 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 8m9.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.96 7.96 0 0 0 4.655 1.03c1.833-1.215 1.772-3.334.86-5.404"/><circle cx="8" cy="8" r="2"/></g></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg
new file mode 100644
index 0000000..b59beda
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/gpu-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#222" d="M0 0v2h.086c.5 0 .914.414.914.914V16h2v-2.008h1c0 1.11.89 2 2 2h5c1.11 0 2-.89 2-2 1.66 0 3-1.34 3-3v-6c0-1.66-1.34-3-3-3H2.848A2.9 2.9 0 0 0 .086 0zm3 3.992h10c.547 0 1 .453 1 1v6c0 .547-.453 1-1 1h-2v2h-1v-2H9v2H8v-2H7v2H6v-2H3zm3 1v1H5v3h1v1h1v-1h1v1h1v-1h1v1h1v-1h1v-3h-1v-1h-1v1H9v-1H8v1H7v-1zm0 0"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/memory-symbolic.svg
new file mode 100644
index 0000000..7fb2200
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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="#222"><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-1"/><path fill-opacity=".5" d="M4 5h2v4H4zm3 0h2v4H7zm3 0h2v4h-2z"/><path d="M2 10h12v4H2z"/></g></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg
new file mode 100644
index 0000000..c95cc12
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-download-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222" fill-rule="evenodd"><path d="m 1 3.914062 c 0.003906 -0.257812 0.105469 -0.511718 0.304688 -0.703124 l 3 -2.917969 c 0.386718 -0.3789065 1.003906 -0.3789065 1.394531 0 l 3 2.917969 c 0.394531 0.382812 0.402343 1.015624 0.019531 1.414062 c -0.386719 0.394531 -1.019531 0.402344 -1.417969 0.015625 l -1.300781 -1.265625 v 6.550781 c 0 1.332031 -2 1.332031 -2 0 v -6.550781 l -1.300781 1.269531 c -0.398438 0.382813 -1.03125 0.375 -1.414063 -0.019531 c -0.195312 -0.199219 -0.289062 -0.457031 -0.285156 -0.710938 z m 0 0" fill-opacity="0.34902"/><path d="m 7 11.941406 c 0.003906 0.253906 0.105469 0.507813 0.304688 0.703125 l 3 2.917969 c 0.386718 0.375 1.003906 0.375 1.394531 0 l 3 -2.917969 c 0.394531 -0.386719 0.402343 -1.019531 0.019531 -1.414062 c -0.386719 -0.398438 -1.019531 -0.40625 -1.417969 -0.019531 l -1.300781 1.265624 v -6.550781 c 0 -1.332031 -2 -1.332031 -2 0 v 6.550781 l -1.300781 -1.265624 c -0.398438 -0.386719 -1.03125 -0.378907 -1.414063 0.015624 c -0.195312 0.199219 -0.289062 0.457032 -0.285156 0.710938 z m 0 0"/></g></svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg
new file mode 100644
index 0000000..ac7a6ee
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222" fill-rule="evenodd"><path d="m 1 3.914062 c 0.003906 -0.257812 0.105469 -0.511718 0.304688 -0.703124 l 3 -2.917969 c 0.386718 -0.3789065 1.003906 -0.3789065 1.394531 0 l 3 2.917969 c 0.394531 0.382812 0.402343 1.015624 0.019531 1.414062 c -0.386719 0.394531 -1.019531 0.402344 -1.417969 0.015625 l -1.300781 -1.265625 v 6.550781 c 0 1.332031 -2 1.332031 -2 0 v -6.550781 l -1.300781 1.269531 c -0.398438 0.382813 -1.03125 0.375 -1.414063 -0.019531 c -0.195312 -0.199219 -0.289062 -0.457031 -0.285156 -0.710938 z m 0 0"/><path d="m 7 11.941406 c 0.003906 0.253906 0.105469 0.507813 0.304688 0.703125 l 3 2.917969 c 0.386718 0.375 1.003906 0.375 1.394531 0 l 3 -2.917969 c 0.394531 -0.386719 0.402343 -1.019531 0.019531 -1.414062 c -0.386719 -0.398438 -1.019531 -0.40625 -1.417969 -0.019531 l -1.300781 1.265624 v -6.550781 c 0 -1.332031 -2 -1.332031 -2 0 v 6.550781 l -1.300781 -1.265624 c -0.398438 -0.386719 -1.03125 -0.378907 -1.414063 0.015624 c -0.195312 0.199219 -0.289062 0.457032 -0.285156 0.710938 z m 0 0"/></g></svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg
new file mode 100644
index 0000000..9ca1f68
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/network-upload-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><g fill="#222222" fill-rule="evenodd"><path d="m 1 3.914062 c 0.003906 -0.257812 0.105469 -0.511718 0.304688 -0.703124 l 3 -2.917969 c 0.386718 -0.3789065 1.003906 -0.3789065 1.394531 0 l 3 2.917969 c 0.394531 0.382812 0.402343 1.015624 0.019531 1.414062 c -0.386719 0.394531 -1.019531 0.402344 -1.417969 0.015625 l -1.300781 -1.265625 v 6.550781 c 0 1.332031 -2 1.332031 -2 0 v -6.550781 l -1.300781 1.269531 c -0.398438 0.382813 -1.03125 0.375 -1.414063 -0.019531 c -0.195312 -0.199219 -0.289062 -0.457031 -0.285156 -0.710938 z m 0 0"/><path d="m 7 11.941406 c 0.003906 0.253906 0.105469 0.507813 0.304688 0.703125 l 3 2.917969 c 0.386718 0.375 1.003906 0.375 1.394531 0 l 3 -2.917969 c 0.394531 -0.386719 0.402343 -1.019531 0.019531 -1.414062 c -0.386719 -0.398438 -1.019531 -0.40625 -1.417969 -0.019531 l -1.300781 1.265624 v -6.550781 c 0 -1.332031 -2 -1.332031 -2 0 v 6.550781 l -1.300781 -1.265624 c -0.398438 -0.386719 -1.03125 -0.378907 -1.414063 0.015624 c -0.195312 0.199219 -0.289062 0.457032 -0.285156 0.710938 z m 0 0" fill-opacity="0.34902"/></g></svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/storage-symbolic.svg
new file mode 100644
index 0000000..b0d4753
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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="#222" 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-1m4 1C5.79 3 4 4.79 4 7v4h4c2.5 0 4-1.79 4-4s-1.79-4-4-4m0 2a2 2 0 1 1-2 2 2 2 0 0 1 2-2m0 0"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/system-symbolic.svg
new file mode 100644
index 0000000..a041518
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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="#222" 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-1m2 12a2 2 0 0 0-2 2h10a2 2 0 0 0-2-2zm0 0"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/temperature-symbolic.svg
new file mode 100644
index 0000000..ed976bf
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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="#222" d="M8 0C6.338 0 5 1.338 5 3v6q0 .174.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 3 0 0 0 11 9V3c0-1.662-1.338-3-3-3m0 2c.554 0 1 .446 1 1v1H8v1h1v1H8v1h1v1H8v1h1v1.27a2 2 0 0 1 .316.23l.082.082a2 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-.219l.06-.06A2 2 0 0 1 7 10.277V3c0-.554.446-1 1-1m0 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-1"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg
new file mode 100644
index 0000000..c786442
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/gnome/voltage-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#222" d="M4 .004v3H2.977A1.97 1.97 0 0 0 1 4.977v2.05q0 .194.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.602A2 2 0 0 0 13 7.027v-2.05a1.97 1.97 0 0 0-1.977-1.973H10v-3H8v3H6v-3Zm0 0"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/battery-symbolic.svg
new file mode 100644
index 0000000..70df9ee
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg
new file mode 100644
index 0000000..19969e4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/cpu-symbolic.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg enable-background="new 0 0 52 52" id="Layer_1" version="1.1" viewBox="0 0 52 52" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M38.9799805,2H2v48h48V13.0200195L38.9799805,2z M25.0005493,6.0021362h3v2h-3V6.0021362z M25.0005493,10.0021362h3v2h-3 V10.0021362z M25.0005493,14.0021362h3v2h-3V14.0021362z M19.0005493,6.0021362h3v2h-3V6.0021362z M19.0005493,10.0021362h3v2h-3 V10.0021362z M19.0005493,14.0021362h3v2h-3V14.0021362z M10.0005493,46.0021362h-3v-2h3V46.0021362z M10.0005493,42.0021362h-3v-2 h3V42.0021362z M10.0005493,38.0021362h-3v-2h3V38.0021362z M10.0005493,34.0021362h-3v-2h3V34.0021362z M10.0005493,30.0021362h-3 v-2h3V30.0021362z M10.0005493,25.0021362h-3v-2h3V25.0021362z M10.0005493,21.0021362h-3v-2h3V21.0021362z M10.0005493,16.0021362 h-3v-2h3V16.0021362z M10.0005493,12.0021362h-3v-2h3V12.0021362z M10.0005493,8.0021362h-3v-2h3V8.0021362z M16.0005493,46.0021362 h-3v-2h3V46.0021362z M16.0005493,42.0021362h-3v-2h3V42.0021362z M16.0005493,38.0021362h-3v-2h3V38.0021362z M16.0005493,34.0021362h-3v-2h3V34.0021362z M16.0005493,30.0021362h-3v-2h3V30.0021362z M16.0005493,25.0021362h-3v-2h3 V25.0021362z M16.0005493,21.0021362h-3v-2h3V21.0021362z M16.0005493,16.0021362h-3v-2h3V16.0021362z M16.0005493,12.0021362h-3v-2 h3V12.0021362z M16.0005493,8.0021362h-3v-2h3V8.0021362z M19,19h14v14H19V19z M22.0005493,46.0021362h-3v-2h3V46.0021362z M22.0005493,42.0021362h-3v-2h3V42.0021362z M22.0005493,38.0021362h-3v-2h3V38.0021362z M28.0005493,46.0021362h-3v-2h3 V46.0021362z M28.0005493,42.0021362h-3v-2h3V42.0021362z M28.0005493,38.0021362h-3v-2h3V38.0021362z M34.0005493,46.0021362h-3v-2 h3V46.0021362z M34.0005493,42.0021362h-3v-2h3V42.0021362z M34.0005493,38.0021362h-3v-2h3V38.0021362z M34.0005493,16.0021362h-3 v-2h3V16.0021362z M34.0005493,12.0021362h-3v-2h3V12.0021362z M34.0005493,8.0021362h-3v-2h3V8.0021362z M40.0005493,46.0021362h-3 v-2h3V46.0021362z M40.0005493,42.0021362h-3v-2h3V42.0021362z M40.0005493,38.0021362h-3v-2h3V38.0021362z M40.0005493,34.0021362 h-3v-2h3V34.0021362z M40.0005493,30.0021362h-3v-2h3V30.0021362z M40.0005493,25.0021362h-3v-2h3V25.0021362z M40.0005493,21.0021362h-3v-2h3V21.0021362z M40.0005493,16.0021362h-3v-2h3V16.0021362z M40.0005493,12.0021362h-3v-2h3 V12.0021362z M46.0005493,46.0021362h-3v-2h3V46.0021362z M46.0005493,42.0021362h-3v-2h3V42.0021362z M46.0005493,38.0021362h-3v-2 h3V38.0021362z M46.0005493,34.0021362h-3v-2h3V34.0021362z M46.0005493,30.0021362h-3v-2h3V30.0021362z M46.0005493,25.0021362h-3 v-2h3V25.0021362z M46.0005493,21.0021362h-3v-2h3V21.0021362z M46.0005493,16.0021362h-3v-2h3V16.0021362z"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg
new file mode 100644
index 0000000..a584b74
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/fan-symbolic.svg
@@ -0,0 +1,133 @@
+<?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"
+ id="svg7384"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ height="16"
+ sodipodi:docname="fan.svg"
+ width="16">
+ <metadata
+ id="metadata90">
+ <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>Gnome Symbolic Icon Theme</dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ inkscape:object-paths="true"
+ inkscape:cy="7.9523753"
+ inkscape:current-layer="svg7384"
+ inkscape:window-width="1680"
+ pagecolor="#555753"
+ showborder="false"
+ showguides="true"
+ inkscape:snap-nodes="false"
+ objecttolerance="10"
+ showgrid="true"
+ inkscape:object-nodes="true"
+ inkscape:pageshadow="2"
+ inkscape:guide-bbox="true"
+ inkscape:window-x="0"
+ inkscape:snap-bbox="true"
+ bordercolor="#666666"
+ id="namedview88"
+ inkscape:window-maximized="1"
+ inkscape:snap-global="false"
+ inkscape:window-y="27"
+ gridtolerance="10"
+ inkscape:zoom="45.254834"
+ inkscape:window-height="991"
+ borderopacity="1"
+ guidetolerance="10"
+ inkscape:snap-bbox-midpoints="false"
+ inkscape:cx="6.4701314"
+ inkscape:bbox-paths="false"
+ inkscape:snap-grids="true"
+ inkscape:pageopacity="1"
+ inkscape:snap-to-guides="true">
+ <inkscape:grid
+ visible="true"
+ spacingx="1px"
+ type="xygrid"
+ spacingy="1px"
+ id="grid4866"
+ empspacing="2"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <title
+ id="title9167">Gnome Symbolic Icon Theme</title>
+ <defs
+ id="defs7386" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer9"
+ inkscape:label="status"
+ style="display:inline" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer10"
+ inkscape:label="devices" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer11"
+ inkscape:label="apps" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer12"
+ inkscape:label="actions" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer13"
+ inkscape:label="places" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer14"
+ inkscape:label="mimetypes" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer15"
+ inkscape:label="emblems"
+ style="display:inline" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="g4953"
+ inkscape:label="categories"
+ style="display:inline" />
+ <path
+ style="fill:#bebebe;fill-opacity:1;stroke:none"
+ d="M 8 0 C 7.7662783 -3.1322393e-17 7.5411335 0.011610998 7.3125 0.03125 C 5.2102943 1.4256475 5.5987411 4.0108346 6.90625 6.34375 C 7.2209706 6.1372503 7.5954602 6 8 6 C 8.0316752 6 8.062435 5.9985435 8.09375 6 C 8.7335699 3.3508722 11.235427 1.3679906 11.96875 1.0625 C 10.798244 0.39154001 9.4458996 -7.7509341e-16 8 0 z M 4 1.09375 C 2.5986515 1.9071018 1.4765974 3.1207274 0.78125 4.59375 C 0.9182133 7.1283275 3.3250439 8.0908827 6 8.125 C 5.9974202 8.0834163 6 8.0422335 6 8 C 6 7.6007368 6.1109237 7.2184502 6.3125 6.90625 C 4.3514736 5.0277432 3.8996391 1.8856213 4 1.09375 z M 13.15625 3.0625 C 11.454675 3.0097524 9.9536158 4.4003424 8.875 6.21875 C 9.2647725 6.4100014 9.5848814 6.7069783 9.78125 7.09375 C 12.408696 6.3344077 15.40554 7.5273725 16 8 C 16 6.3023731 15.464331 4.7324062 14.5625 3.4375 C 14.086383 3.1993071 13.609414 3.0765477 13.15625 3.0625 z M 10 7.875 C 10.00258 7.9165837 10 7.9577665 10 8 C 10 8.3992632 9.8890763 8.7815498 9.6875 9.09375 C 11.648527 10.972257 12.100361 14.114379 12 14.90625 C 13.401349 14.092898 14.523403 12.879273 15.21875 11.40625 C 15.081787 8.8716722 12.674956 7.9091173 10 7.875 z M 0 8 C -4.5501758e-16 9.6976269 0.53566854 11.267594 1.4375 12.5625 C 3.7013779 13.695079 5.7591272 12.083936 7.125 9.78125 C 6.7352275 9.5899986 6.4151186 9.2930217 6.21875 8.90625 C 3.5913037 9.6655923 0.5944604 8.4726275 0 8 z M 9.09375 9.65625 C 8.7790294 9.8627497 8.4045398 10 8 10 C 7.9683248 10 7.937565 10.001457 7.90625 10 C 7.26643 12.649128 4.7645729 14.632009 4.03125 14.9375 C 5.2017564 15.60846 6.5541004 16 8 16 C 8.2337217 16 8.4588665 15.988389 8.6875 15.96875 C 10.789706 14.574352 10.401259 11.989165 9.09375 9.65625 z "
+ id="path3830" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#bebebe;fill-opacity:1;stroke:none"
+ id="path3896"
+ sodipodi:cx="8.0433397"
+ sodipodi:cy="8.0008545"
+ sodipodi:rx="0.9722718"
+ sodipodi:ry="0.95017475"
+ d="m 9.0156115,8.0008545 a 0.9722718,0.95017475 0 1 1 -1.9445436,0 0.9722718,0.95017475 0 1 1 1.9445436,0 z"
+ transform="matrix(1.028519,0,0,1.052438,-0.27272757,-0.42040319)" />
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/gpu-symbolic.svg
new file mode 100644
index 0000000..d5cf157
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/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>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg
new file mode 100644
index 0000000..13ca4bc
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/memory-symbolic.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg enable-background="new 0 0 52 52" id="Layer_1" version="1.1" viewBox="0 0 52 52" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M50,41.0999756V11.4000244h-6.9978638V7.7766113C44.164978,7.364502,45,6.2706909,45,4.9799805 C45,3.3400269,43.6600342,2,42,2c-1.6499634,0-3,1.3400269-3,2.9799805c0,1.2908936,0.8413696,2.3848267,2.0021362,2.796814v3.62323 h-8V7.7766113C34.164978,7.364502,35,6.2706909,35,4.9799805C35,3.3400269,33.6600342,2,32,2c-1.6499634,0-3,1.3400269-3,2.9799805 c0,1.2908936,0.8413696,2.3848267,2.0021362,2.796814v3.62323h-8V7.7766113C24.164978,7.364502,25,6.2706909,25,4.9799805 C25,3.3400269,23.6600342,2,22,2c-1.6499634,0-3,1.3400269-3,2.9799805c0,1.2908936,0.8413696,2.3848267,2.0021362,2.796814v3.62323 h-8V7.7766113C14.164978,7.364502,15,6.2706909,15,4.9799805C15,3.3400269,13.6600342,2,12,2c-1.6499634,0-3,1.3400269-3,2.9799805 c0,1.2908936,0.8413696,2.3848267,2.0021362,2.796814v3.62323H2v29.6999512h9.0021362v3.1321411 C9.8413696,44.6417847,9,45.7312622,9,47.0299683C9,48.6699829,10.3500366,50,12,50c1.6600342,0,3-1.3300171,3-2.9700317 c0-1.298584-0.835022-2.3878784-1.9978638-2.7977295v-3.1322632h8v3.1321411C19.8413696,44.6417847,19,45.7312622,19,47.0299683 C19,48.6699829,20.3500366,50,22,50c1.6600342,0,3-1.3300171,3-2.9700317c0-1.298584-0.835022-2.3878784-1.9978638-2.7977295 v-3.1322632h8v3.1321411C29.8413696,44.6417847,29,45.7312622,29,47.0299683C29,48.6699829,30.3500366,50,32,50 c1.6600342,0,3-1.3300171,3-2.9700317c0-1.298584-0.835022-2.3878784-1.9978638-2.7977295v-3.1322632h8v3.1321411 C39.8413696,44.6417847,39,45.7312622,39,47.0299683C39,48.6699829,40.3500366,50,42,50c1.6600342,0,3-1.3300171,3-2.9700317 c0-1.298584-0.835022-2.3878784-1.9978638-2.7977295v-3.1322632H50z M7,16.3500366h38v19.7999878H7V16.3500366z"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg
new file mode 100644
index 0000000..5cab840
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-download-symbolic.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><defs/><g fill="none" fill-rule="evenodd" id="Icons with numbers" stroke="none" stroke-width="1"><g fill="#000000" id="Group" transform="translate(-672.000000, -288.000000)"><path d="M679,293 L679,296 L677,296 L680,300 L683,296 L681,296 L681,293 Z M672,298 C672,296.651721 672.895887,295.507545 674.127761,295.131093 C674.500314,293.897932 675.645295,293 677,293 C677.174013,293 677.344566,293.014816 677.510466,293.043254 C678.195719,291.823839 679.50165,291 681,291 C683.209139,291 685,292.790861 685,295 C686.668415,295.005076 688,296.346276 688,298 C688,299.653483 686.652611,301 684.990522,301 L675.009478,301 C673.336631,301 672,299.656854 672,298 Z M672,298" id="Rectangle 169 copy 2"/></g></g></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg
new file mode 100644
index 0000000..0483417
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-symbolic.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><defs/><g fill="none" fill-rule="evenodd" id="Icons with numbers" stroke="none" stroke-width="1"><g fill="#000000" id="Group" transform="translate(-576.000000, -288.000000)"><path d="M576,298 C576,296.651721 576.895887,295.507545 578.127761,295.131093 C578.500314,293.897932 579.645295,293 581,293 C581.174013,293 581.344566,293.014816 581.510466,293.043254 C582.195719,291.823839 583.50165,291 585,291 C587.209139,291 589,292.790861 589,295 C590.668415,295.005076 592,296.346276 592,298 C592,299.653483 590.652611,301 588.990522,301 L579.009478,301 C577.336631,301 576,299.656854 576,298 Z M576,298" id="Rectangle 169"/></g></g></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg
new file mode 100644
index 0000000..e2c5a08
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/network-upload-symbolic.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><svg height="16px" version="1.1" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" xmlns:xlink="http://www.w3.org/1999/xlink"><title/><defs/><g fill="none" fill-rule="evenodd" id="Icons with numbers" stroke="none" stroke-width="1"><g fill="#000000" id="Group" transform="translate(-624.000000, -288.000000)"><path d="M631,297 L631,300 L633,300 L633,297 L635,297 L632,293 L629,297 Z M624,298 C624,296.651721 624.895887,295.507545 626.127761,295.131093 C626.500314,293.897932 627.645295,293 629,293 C629.174013,293 629.344566,293.014816 629.510466,293.043254 C630.195719,291.823839 631.50165,291 633,291 C635.209139,291 637,292.790861 637,295 C638.668415,295.005076 640,296.346276 640,298 C640,299.653483 638.652611,301 636.990522,301 L627.009478,301 C625.336631,301 624,299.656854 624,298 Z M624,298" id="Rectangle 169 copy"/></g></g></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg
new file mode 100644
index 0000000..9832999
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/storage-symbolic.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
+ <path style="fill:#bebebe" d="M 3,1 C 2,1 2,2 2,2 V 14 C 2,14 2,15 3,15 H 13 C 13,15 14,15 14,14 V 2 C 14,2 14,1 13,1 Z M 8,3 C 10.21,3 12,4.79 12,7 12,9.21 10.21,11 8,11 H 4 V 7 C 4,4.79 5.79,3 8,3 Z M 8,5 C 6.9,5 6,5.9 6,7 6,8.1 6.9,9 8,9 9.1,9 10,8.1 10,7 10,5.9 9.1,5 8,5 Z"/>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg
new file mode 100644
index 0000000..909f530
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/system-symbolic.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" version="1.1">
+ <path style="fill:#bebebe" d="M 15,1 C 16,1 16,2 16,2 V 12 C 16,12 16,13 15,13 H 1 C 1,13 0,13 0,12 V 2 C 0,2 0,1 1,1 Z M 14,3 H 2 V 11 H 14 Z M 11,14 V 15 H 5 V 14 C 5,13 6,13 6,13 H 10 C 10,13 11,13 11,14 Z"/>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.svg
new file mode 100644
index 0000000..c58beca
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/temperature-symbolic.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.2 0 18 30"
+ height="30"
+ version="1.1"
+ viewBox="-0.2 0 30 30"
+ width="30"
+ xml:space="preserve"
+ id="svg6"
+ sodipodi:docname="if_Vector-icons_49_1041647.svg"
+ inkscape:version="0.92.3 (3ce5693, 2018-03-11)"><metadata
+ id="metadata10"><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><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1070"
+ inkscape:window-height="638"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="7.8666667"
+ inkscape:cx="5.2520864"
+ inkscape:cy="14.6"
+ inkscape:window-x="330"
+ inkscape:window-y="226"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" /><defs
+ id="defs2" /><path
+ d="M 14.6,29.8 C 9.8,29.8 6,25.9 6,21.2 6,18.1 7.6,15.4 10,13.9 V 4.5 C 10,2 12.1,0 14.5,0 16.9,0 19,2 19,4.5 v 9.2 c 2.5,1.5 4.2,4.3 4.2,7.4 0.1,4.8 -3.8,8.7 -8.6,8.7 z M 17.3,15.4 17.1,5 C 17.1,3.8 15.7,2.9 14.5,2.9 13.3,2.9 12,3.8 12,5 v 10.5 c -2.1,1 -3.6,3.2 -3.6,5.7 0,3.5 2.8,6.3 6.3,6.3 3.5,0 6.3,-2.8 6.3,-6.3 0,-2.6 -1.5,-4.8 -3.7,-5.8 z m -2.7,10.5 c -2.6,0 -4.7,-2.1 -4.7,-4.7 0,-2 1.3,-3.7 3.1,-4.4 V 8 h 3 v 8.8 c 1.9,0.6 3.3,2.3 3.3,4.4 0,2.6 -2.1,4.7 -4.7,4.7 z"
+ id="path4"
+ inkscape:connector-curvature="0"
+ style="clip-rule:evenodd;fill:#0d0d0d;fill-rule:evenodd" /></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg
new file mode 100644
index 0000000..b2e7eff
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/icons/original/voltage-symbolic.svg
@@ -0,0 +1,125 @@
+<?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"
+ id="svg7384"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ height="16"
+ sodipodi:docname="voltage.svg"
+ width="16">
+ <metadata
+ id="metadata90">
+ <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>Gnome Symbolic Icon Theme</dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ inkscape:object-paths="true"
+ inkscape:cy="8.2822539"
+ inkscape:current-layer="svg7384"
+ inkscape:window-width="1680"
+ pagecolor="#555753"
+ showborder="false"
+ showguides="true"
+ inkscape:snap-nodes="false"
+ objecttolerance="10"
+ showgrid="true"
+ inkscape:object-nodes="true"
+ inkscape:pageshadow="2"
+ inkscape:guide-bbox="true"
+ inkscape:window-x="0"
+ inkscape:snap-bbox="true"
+ bordercolor="#666666"
+ id="namedview88"
+ inkscape:window-maximized="1"
+ inkscape:snap-global="false"
+ inkscape:window-y="27"
+ gridtolerance="10"
+ inkscape:zoom="68.593502"
+ inkscape:window-height="991"
+ borderopacity="1"
+ guidetolerance="10"
+ inkscape:snap-bbox-midpoints="false"
+ inkscape:cx="6.2925429"
+ inkscape:bbox-paths="false"
+ inkscape:snap-grids="true"
+ inkscape:pageopacity="1"
+ inkscape:snap-to-guides="true">
+ <inkscape:grid
+ visible="true"
+ spacingx="1px"
+ type="xygrid"
+ spacingy="1px"
+ id="grid4866"
+ empspacing="2"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <title
+ id="title9167">Gnome Symbolic Icon Theme</title>
+ <defs
+ id="defs7386" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer9"
+ inkscape:label="status"
+ style="display:inline" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer10"
+ inkscape:label="devices" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer11"
+ inkscape:label="apps" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer12"
+ inkscape:label="actions" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer13"
+ inkscape:label="places" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer14"
+ inkscape:label="mimetypes" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="layer15"
+ inkscape:label="emblems"
+ style="display:inline" />
+ <g
+ transform="translate(-261,-277)"
+ inkscape:groupmode="layer"
+ id="g4953"
+ inkscape:label="categories"
+ style="display:inline" />
+ <path
+ style="fill:#bebebe;fill-opacity:1;stroke:none"
+ d="m 11.40625,1 c -0.455108,0.038189 -0.61564,0.1791748 -1.25,0.625 L 2.84375,7.34375 C 1.9959525,8.0221961 2,8.0144269 2,9 l 0,1 4,0 -3.1875,3.1875 C 1.98932,14.004249 2.00091,13.998722 2,15 l 0,1 1,0.03125 c 0.986227,-0.01956 0.997937,-0.03056 1.84375,-0.625 L 12.15625,9.6875 C 13.004048,9.0090539 13,8.9855731 13,8 L 13,7 9,7 12.1875,3.8125 C 13.01068,2.9957516 12.99909,3.0012778 13,2 l 0,-1 -1,0 c -0.246557,0.00489 -0.442047,-0.0127296 -0.59375,0 z"
+ id="path3898"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sccsccccccccsccccccs" />
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..5d1d5ac
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ar/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..1001254
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/be/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..5a18109
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ca/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..689742c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/cs/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..f374b84
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/de/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..fbdb72a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/es/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..38454e3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fi_FI/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..1260f3d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/fr/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..f37507d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/it/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..e194797
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/my_MM/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..2a90349
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/nl/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..ef097f7
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/oc/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..b42daa4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pl/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..ebf4c11
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..986cf6d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/pt_BR/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..1f58435
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/ru/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..73a62ea
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/sk/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..eee9edd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/tr/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..9f11b14
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/uk/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mo b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mo
new file mode 100644
index 0000000..3618dc3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/locale/zh_CN/LC_MESSAGES/vitals.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js
new file mode 100644
index 0000000..cf112ab
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/menuItem.js
@@ -0,0 +1,77 @@
+import Clutter from 'gi://Clutter';
+import GObject from 'gi://GObject';
+import St from 'gi://St'
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+
+export const MenuItem = GObject.registerClass({
+
+ Signals: {
+ 'toggle': { param_types: [Clutter.Event.$gtype] },
+ },
+
+}, class MenuItem extends PopupMenu.PopupBaseMenuItem {
+
+ _init(icon, key, label, value, checked) {
+ super._init({ reactive: true });
+
+ this._checked = checked;
+ this._updateOrnament();
+
+ this._key = key;
+ this._gIcon = icon;
+
+ // add icon
+ 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_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_child(this._valueLabel);
+
+ this.actor._delegate = this;
+ }
+
+ get checked() {
+ return this._checked;
+ }
+
+ get key() {
+ return this._key;
+ }
+
+ get gicon() {
+ return this._gIcon;
+ }
+
+ set value(value) {
+ this._valueLabel.text = value;
+ }
+
+ get value() {
+ return this._valueLabel.text;
+ }
+
+ // prevents menu from being closed
+ activate(event) {
+ this._checked = !this._checked;
+ this._updateOrnament();
+ this.emit('toggle', event);
+ }
+
+ _updateOrnament() {
+ if (this._checked)
+ this.setOrnament(PopupMenu.Ornament.CHECK);
+ else
+ this.setOrnament(PopupMenu.Ornament.NONE);
+ }
+
+ get label() {
+ return this._labelActor.text;
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json
new file mode 100644
index 0000000..bb2e767
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/metadata.json
@@ -0,0 +1,21 @@
+{
+ "_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",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/corecoding/Vitals",
+ "uuid": "Vitals@CoreCoding.com",
+ "version": 75
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js
new file mode 100644
index 0000000..07916bd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.js
@@ -0,0 +1,138 @@
+import Adw from 'gi://Adw';
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk';
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+/*
+ if (sensor == 'show-storage' && this._settings.get_boolean(sensor)) {
+
+ let val = true;
+
+ try {
+ let GTop = imports.gi.GTop;
+ } catch (e) {
+ val = false;
+ }
+
+ let now = new Date().getTime();
+ this._notify("Vitals", "Please run sudo apt install gir1.2-gtop-2.0", 'folder-symbolic');
+
+ }
+*/
+
+const Settings = new GObject.Class({
+ Name: 'Vitals.Settings',
+
+ _init: function(extensionObject, params) {
+ this._extensionObject = extensionObject
+ this.parent(params);
+
+ this._settings = extensionObject.getSettings();
+
+ this.builder = new Gtk.Builder();
+ this.builder.set_translation_domain(this._extensionObject.metadata['gettext-domain']);
+ this.builder.add_from_file(this._extensionObject.path + '/prefs.ui');
+ this.widget = this.builder.get_object('prefs-container');
+
+ this._bind_settings();
+ },
+
+ // Bind the gtk window to the schema settings
+ _bind_settings: function() {
+ let widget;
+
+ // process sensor toggles
+ let sensors = [ 'show-temperature', 'show-voltage', 'show-fan',
+ 'show-memory', 'show-processor', 'show-system',
+ 'show-network', 'show-storage', 'use-higher-precision',
+ 'alphabetize', 'hide-zeros', 'include-public-ip',
+ 'show-battery', 'fixed-widths', 'hide-icons',
+ 'menu-centered', 'include-static-info',
+ 'show-gpu', 'include-static-gpu-info' ];
+
+ for (let key in sensors) {
+ let sensor = sensors[key];
+
+ widget = this.builder.get_object(sensor);
+ widget.set_active(this._settings.get_boolean(sensor));
+ widget.connect('state-set', (_, val) => {
+ this._settings.set_boolean(sensor, val);
+ });
+ }
+
+ // process individual drop down sensor preferences
+ 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];
+
+ widget = this.builder.get_object(sensor);
+ widget.set_active(this._settings.get_int(sensor));
+ widget.connect('changed', (widget) => {
+ this._settings.set_int(sensor, widget.get_active());
+ });
+ }
+
+ this._settings.bind('update-time', this.builder.get_object('update-time'), 'value', Gio.SettingsBindFlags.DEFAULT);
+
+ // process individual text entry sensor preferences
+ sensors = [ 'storage-path', 'monitor-cmd' ];
+ for (let key in sensors) {
+ let sensor = sensors[key];
+
+ widget = this.builder.get_object(sensor);
+ widget.set_text(this._settings.get_string(sensor));
+
+ widget.connect('changed', (widget) => {
+ let text = widget.get_text();
+ if (!text) text = widget.get_placeholder_text();
+ this._settings.set_string(sensor, text);
+ });
+ }
+
+ // makes individual sensor preference boxes appear
+ sensors = [ 'temperature', 'network', 'storage', 'memory', 'battery', 'system', 'processor', 'gpu' ];
+ for (let key in sensors) {
+ let sensor = sensors[key];
+
+ // create dialog for intelligent autohide advanced settings
+ this.builder.get_object(sensor + '-prefs').connect('clicked', () => {
+ let transientObj = this.widget.get_root();
+ let title = sensor.charAt(0).toUpperCase() + sensor.slice(1);
+ let dialog = new Gtk.Dialog({ title: _(title) + ' ' + _('Preferences'),
+ transient_for: transientObj,
+ use_header_bar: false,
+ modal: true });
+
+ let box = this.builder.get_object(sensor + '_prefs');
+ dialog.get_content_area().append(box);
+ dialog.connect('response', (dialog, id) => {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ return;
+ });
+
+ dialog.show();
+ });
+ }
+ }
+});
+
+
+export default class VitalsPrefs extends ExtensionPreferences {
+ fillPreferencesWindow(window) {
+ window._settings = this.getSettings();
+
+ let settings = new Settings(this);
+ let widget = settings.widget;
+
+ const page = new Adw.PreferencesPage();
+ const group = new Adw.PreferencesGroup({});
+ group.add(widget);
+ page.add(group);
+ window.add(page);
+ window.set_default_size(widget.width, widget.height);
+ widget.show();
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui
new file mode 100644
index 0000000..950c1b0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/prefs.ui
@@ -0,0 +1,1394 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <object class="GtkAdjustment" id="update-time-params">
+ <property name="lower">1</property>
+ <property name="upper">60</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkBox" id="network_prefs">
+ <property name="can_focus">0</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Include public IP address</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="include-public-ip">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Format</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="network-speed-format">
+ <property name="can_focus">0</property>
+ <property name="active">0</property>
+ <items>
+ <item translatable="yes">Bytes</item>
+ <item translatable="yes">Bits</item>
+ </items>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="prefs-container">
+ <property name="visible">0</property>
+ <property name="can_focus">0</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="homogeneous">1</property>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">9</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="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="valign">baseline</property>
+ <property name="label" translatable="yes">General</property>
+ <attributes>
+ <attribute name="weight" value="bold"></attribute>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Seconds between updates</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="update-time">
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="digits">0</property>
+ <property name="max_width_chars">2</property>
+ <property name="adjustment">update-time-params</property>
+ <property name="climb_rate">1</property>
+ <property name="snap_to_ticks">1</property>
+ <property name="numeric">1</property>
+ <property name="update_policy">if-valid</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="activatable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Position in panel</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="position-in-panel">
+ <property name="can_focus">0</property>
+ <property name="margin-end">5</property>
+ <property name="active">0</property>
+ <items>
+ <item translatable="yes">Left</item>
+ <item translatable="yes">Center</item>
+ <item translatable="yes">Right</item>
+ <item translatable="yes">Far Left</item>
+ <item translatable="yes">Far Right</item>
+ </items>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Use higher precision</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="use-higher-precision">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Alphabetize sensors</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="alphabetize">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Hide zero values</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="hide-zeros">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Use fixed widths</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="fixed-widths">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Hide icons in top bar</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="hide-icons">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Menu always centered</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="menu-centered">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="activatable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Icon style</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="icon-style">
+ <property name="can_focus">0</property>
+ <property name="margin-end">5</property>
+ <property name="active">0</property>
+ <items>
+ <item translatable="yes">Original</item>
+ <item translatable="yes">GNOME</item>
+ </items>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">9</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="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="valign">baseline</property>
+ <property name="label" translatable="yes">Sensors</property>
+ <attributes>
+ <attribute name="weight" value="bold"></attribute>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <child>
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor temperature</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="temperature-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-temperature">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor voltage</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-voltage">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor fan</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-fan">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor memory</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="memory-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-memory">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor processor</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="processor-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-processor">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor system</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="system-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-system">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor network</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="network-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-network">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor storage</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="storage-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-storage">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor battery</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="battery-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-battery">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Monitor GPU (beta)</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="gpu-prefs">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show-gpu">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="halign">start</property>
+ <property name="can_focus">0</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">9</property>
+ <property name="margin_bottom">12</property>
+ <property name="label" translatable="yes">About</property>
+ <attributes>
+ <attribute name="weight" value="bold"></attribute>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <property name="child">
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="valign">baseline</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="margin-start">5</property>
+ <property name="margin-end">5</property>
+ <property name="label" translatable="yes">Feature requests or bugs? Please visit &lt;a href=&quot;https://github.com/corecoding/Vitals/issues&quot;&gt;GitHub&lt;/a&gt;. No warranty, expressed or implied. &lt;a href=&quot;https://corecoding.com/donate.php&quot;&gt;Donate&lt;/a&gt; if you found this useful.</property>
+ <property name="use_markup">1</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="storage_prefs">
+ <property name="can_focus">1</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">1</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">1</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">1</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Path</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="storage-path">
+ <property name="placeholder_text" translatable="no">/</property>
+ <property name="can_focus">1</property>
+ <property name="editable">1</property>
+ <property name="width_chars">24</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Measurement</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="storage-measurement">
+ <property name="can_focus">0</property>
+ <property name="active">0</property>
+ <items>
+ <item translatable="yes">Binary</item>
+ <item translatable="yes">Decimal</item>
+ </items>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="temperature_prefs">
+ <property name="can_focus">0</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Unit</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="unit">
+ <property name="can_focus">0</property>
+ <property name="active">0</property>
+ <property name="halign">end</property>
+ <items>
+ <item translatable="yes">°C</item>
+ <item translatable="yes">°F</item>
+ </items>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="memory_prefs">
+ <property name="can_focus">0</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Measurement</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="memory-measurement">
+ <property name="can_focus">0</property>
+ <property name="active">0</property>
+ <items>
+ <item translatable="yes">Binary</item>
+ <item translatable="yes">Decimal</item>
+ </items>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="battery_prefs">
+ <property name="can_focus">0</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Display Battery</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="battery-slot">
+ <property name="can_focus">0</property>
+ <property name="active">0</property>
+ <items>
+ <item translatable="yes">BAT0</item>
+ <item translatable="yes">BAT1</item>
+ <item translatable="yes">BAT2</item>
+ <item translatable="yes">BATT</item>
+ <item translatable="yes">CMB0</item>
+ <item translatable="yes">CMB1</item>
+ <item translatable="yes">CMB2</item>
+ <item translatable="yes">macsmc-battery</item>
+ </items>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="system_prefs">
+ <property name="can_focus">1</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">1</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">1</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">1</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Monitor command</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="monitor-cmd">
+ <property name="placeholder_text" translatable="no">gnome-system-monitor</property>
+ <property name="can_focus">1</property>
+ <property name="editable">1</property>
+ <property name="width_chars">24</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="processor_prefs">
+ <property name="can_focus">0</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Include static info</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="include-static-info">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ <object class="GtkBox" id="gpu_prefs">
+ <property name="can_focus">0</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="hexpand">1</property>
+ <property name="selectable">0</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Include static GPU info</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="include-static-gpu-info">
+ <property name="halign">end</property>
+ <property name="margin-end">5</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiled
new file mode 100644
index 0000000..936fdb3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml
new file mode 100644
index 0000000..7a0decd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/schemas/org.gnome.shell.extensions.vitals.gschema.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist gettext-domain="gnome-shell-extensions">
+ <schema id="org.gnome.shell.extensions.vitals" path="/org/gnome/shell/extensions/vitals/">
+ <key name="hot-sensors" type="as">
+ <default>['_memory_usage_', '_system_load_1m_', '__network-rx_max__']</default>
+ <summary>Sensors to show in panel</summary>
+ <description>List of sensors to be shown in the panel</description>
+ </key>
+ <key type="i" name="update-time">
+ <default>5</default>
+ <summary>Seconds between updates</summary>
+ <description>Delay between sensor polling</description>
+ </key>
+ <key type="i" name="position-in-panel">
+ <default>2</default>
+ <summary>Position in panel</summary>
+ <description>Position in Panel ('left', 'center', 'right')</description>
+ </key>
+ <key type="b" name="use-higher-precision">
+ <default>false</default>
+ <summary>Use higher precision</summary>
+ <description>Show one extra digit after decimal</description>
+ </key>
+ <key type="b" name="alphabetize">
+ <default>true</default>
+ <summary>Alphabetize sensors</summary>
+ <description>Display sensors in alphabetical order</description>
+ </key>
+ <key type="b" name="hide-zeros">
+ <default>false</default>
+ <summary>Hide zero values</summary>
+ <description>Hide data from sensors that are invalid</description>
+ </key>
+ <key type="b" name="show-temperature">
+ <default>true</default>
+ <summary>Monitor temperature</summary>
+ <description>Display temperature of various components</description>
+ </key>
+ <key type="i" name="unit">
+ <default>0</default>
+ <summary>Temperature unit</summary>
+ <description>The unit ('centigrade' or 'fahrenheit') the extension should display the temperature in</description>
+ </key>
+ <key type="b" name="show-voltage">
+ <default>true</default>
+ <summary>Monitor voltage</summary>
+ <description>Display voltage of various components</description>
+ </key>
+ <key type="b" name="show-fan">
+ <default>true</default>
+ <summary>Monitor fan</summary>
+ <description>Display fan rotation per minute</description>
+ </key>
+ <key type="b" name="show-memory">
+ <default>true</default>
+ <summary>Monitor memory</summary>
+ <description>Display memory information</description>
+ </key>
+ <key type="b" name="show-processor">
+ <default>true</default>
+ <summary>Monitor processor</summary>
+ <description>Display processor information</description>
+ </key>
+ <key type="b" name="show-system">
+ <default>true</default>
+ <summary>Monitor system</summary>
+ <description>Display system information</description>
+ </key>
+ <key type="b" name="show-storage">
+ <default>true</default>
+ <summary>Monitor storage</summary>
+ <description>Display storage information</description>
+ </key>
+ <key type="b" name="show-network">
+ <default>true</default>
+ <summary>Monitor network</summary>
+ <description>Display network information</description>
+ </key>
+ <key type="b" name="include-public-ip">
+ <default>true</default>
+ <summary>Include public IP address</summary>
+ <description>Display public IP address of internet connection</description>
+ </key>
+ <key type="i" name="network-speed-format">
+ <default>0</default>
+ <summary>Network speed format</summary>
+ <description>Should speed display in bits or bytes?</description>
+ </key>
+ <key type="s" name="storage-path">
+ <default>"/"</default>
+ <summary>Storage path</summary>
+ <description>Storage path for monitoring</description>
+ </key>
+ <key type="b" name="show-battery">
+ <default>false</default>
+ <summary>Monitor battery</summary>
+ <description>Monitor battery health</description>
+ </key>
+ <key type="i" name="memory-measurement">
+ <default>1</default>
+ <summary>Memory measurement</summary>
+ <description>Can use gigabyte or gibibyte for memory</description>
+ </key>
+ <key type="i" name="storage-measurement">
+ <default>1</default>
+ <summary>Storage measurement</summary>
+ <description>Can use gigabyte or gibibyte for storage</description>
+ </key>
+ <key type="i" name="battery-slot">
+ <default>0</default>
+ <summary>Battery slot to monitor</summary>
+ <description>Which numerical battery slot should vitals monitor</description>
+ </key>
+ <key type="b" name="fixed-widths">
+ <default>true</default>
+ <summary>Use fixed widths in top bar</summary>
+ <description>Keep sensors in top bar from jumping around</description>
+ </key>
+ <key type="b" name="hide-icons">
+ <default>false</default>
+ <summary>Hide icons in top bar</summary>
+ <description>Keep top bar clean by only showing sensor values</description>
+ </key>
+ <key type="b" name="menu-centered">
+ <default>false</default>
+ <summary>Make the menu centered</summary>
+ <description>Center the menu to the icon regardless of the position in the panel</description>
+ </key>
+ <key type="s" name="monitor-cmd">
+ <default>"gnome-system-monitor"</default>
+ <summary>System Monitor command</summary>
+ <description>The command run when system monitor button is clicked</description>
+ </key>
+ <key type="b" name="include-static-info">
+ <default>false</default>
+ <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>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js
new file mode 100644
index 0000000..be8df5d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/sensors.js
@@ -0,0 +1,1072 @@
+/*
+ Copyright (c) 2018, Chris Monahan <chris@corecoding.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.
+*/
+
+import GLib from 'gi://GLib';
+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';
+
+let GTop, hasGTop = true;
+try {
+ ({default: GTop} = await import('gi://GTop'));
+} catch (err) {
+ log(err);
+ hasGTop = false;
+};
+
+export const Sensors = GObject.registerClass({
+ GTypeName: 'Sensors',
+}, class Sensors extends GObject.Object {
+ _init(settings, sensorIcons) {
+ this._settings = settings;
+ this._sensorIcons = sensorIcons;
+
+ this.resetHistory();
+
+ 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._gpu_drm_vendors = null;
+ this._gpu_drm_indices = null;
+ this._nvidia_smi_process = null;
+ this._nvidia_labels = [];
+ this._bad_split_count = 0;
+
+ this._frameMonitorSignalId = 0;
+ this._frameMonitorLastTime = 0;
+ this._frameMonitorFrameCount = 0;
+ this._frameMonitorAccTime = 0;
+ this._frameMonitorCurrentHz = 0;
+
+ if (hasGTop) {
+ this.storage = new GTop.glibtop_fsusage();
+ this._storageDevice = '';
+ this._findStorageDevice();
+
+ this._lastRead = 0;
+ this._lastWrite = 0;
+ }
+ }
+
+ _addSettingChangedSignal(key, callback) {
+ this._settingChangedSignals.push(this._settings.connect('changed::' + key, callback));
+ }
+
+ _refreshIPAddress(callback) {
+ // check IP address
+ new FileModule.File('https://ipv4.corecoding.com').read().then(contents => {
+ let obj = JSON.parse(contents);
+ this._returnValue(callback, 'Public IP', obj['IPv4'], 'network', 'string');
+ }).catch(err => { });
+ }
+
+ _findStorageDevice() {
+ new FileModule.File('/proc/mounts').read("\n").then(lines => {
+ for (let line of lines) {
+ let loadArray = line.trim().split(/\s+/);
+ if (loadArray[1] == this._settings.get_string('storage-path')) {
+ this._storageDevice = loadArray[0];
+ break;
+ }
+ }
+ }).catch(err => { });
+ }
+
+ query(callback, dwell) {
+ if (!this._hardware_detected) {
+ // we could set _hardware_detected in discoverHardwareMonitors, but by
+ // doing it here, we guarantee avoidance of race conditions
+ this._hardware_detected = true;
+ this._discoverHardwareMonitors(callback);
+ }
+
+ for (let sensor in this._sensorIcons) {
+ if (this._settings.get_boolean('show-' + sensor)) {
+ if (sensor == 'temperature' || sensor == 'voltage' || sensor == 'fan') {
+ // for temp, volt, fan, we have a shared handler
+ this._queryTempVoltFan(callback, sensor);
+ } else {
+ // directly call queryFunction below
+ let method = '_query' + sensor[0].toUpperCase() + sensor.slice(1);
+ this[method](callback, dwell);
+ }
+ }
+ }
+ }
+
+ _queryTempVoltFan(callback, type) {
+ for (let label in this._tempVoltFanSensors[type]) {
+ let sensor = this._tempVoltFanSensors[type][label];
+
+ new FileModule.File(sensor['path']).read().then(value => {
+ this._returnValue(callback, label, value, type, sensor['format']);
+ }).catch(err => {
+ this._returnValue(callback, label, 'disabled', type, sensor['format']);
+ });
+ }
+ }
+
+ _queryMemory(callback) {
+ // check memory info
+ new FileModule.File('/proc/meminfo').read().then(lines => {
+ let values = '', total = 0, avail = 0, swapTotal = 0, swapFree = 0, cached = 0, memFree = 0;
+
+ if (values = lines.match(/MemTotal:(\s+)(\d+) kB/)) total = values[2];
+ if (values = lines.match(/MemAvailable:(\s+)(\d+) kB/)) avail = values[2];
+ if (values = lines.match(/SwapTotal:(\s+)(\d+) kB/)) swapTotal = values[2];
+ if (values = lines.match(/SwapFree:(\s+)(\d+) kB/)) swapFree = values[2];
+ if (values = lines.match(/Cached:(\s+)(\d+) kB/)) cached = values[2];
+ if (values = lines.match(/MemFree:(\s+)(\d+) kB/)) memFree = values[2];
+
+ let used = total - avail
+ let utilized = used / total;
+ let swapUsed = swapTotal - swapFree
+ let swapUtilized = swapUsed / swapTotal;
+
+ this._returnValue(callback, 'Usage', utilized, 'memory', 'percent');
+ this._returnValue(callback, 'memory', utilized, 'memory-group', 'percent');
+ this._returnValue(callback, 'Physical', total, 'memory', 'memory');
+ this._returnValue(callback, 'Available', avail, 'memory', 'memory');
+ this._returnValue(callback, 'Allocated', used, 'memory', 'memory');
+ this._returnValue(callback, 'Cached', cached, 'memory', 'memory');
+ this._returnValue(callback, 'Free', memFree, 'memory', 'memory');
+ this._returnValue(callback, 'Swap Total', swapTotal, 'memory', 'memory');
+ this._returnValue(callback, 'Swap Free', swapFree, 'memory', 'memory');
+ this._returnValue(callback, 'Swap Used', swapUsed, 'memory', 'memory');
+ this._returnValue(callback, 'Swap Usage', swapUtilized, 'memory', 'percent');
+ }).catch(err => { });
+ }
+
+ _queryProcessor(callback, dwell) {
+ let columns = ['user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq', 'steal', 'guest', 'guest_nice'];
+
+ // check processor usage
+ new FileModule.File('/proc/stat').read("\n").then(lines => {
+ let statistics = {};
+
+ for (let line of lines) {
+ let reverse_data = line.match(/^(cpu\d*\s)(.+)/);
+ if (reverse_data) {
+ let cpu = reverse_data[1].trim();
+
+ if (!(cpu in statistics))
+ statistics[cpu] = {};
+
+ if (!(cpu in this._last_processor['core']))
+ this._last_processor['core'][cpu] = 0;
+
+ let stats = reverse_data[2].trim().split(' ').reverse();
+ for (let column of columns)
+ statistics[cpu][column] = parseInt(stats.pop());
+ }
+ }
+
+ let cores = Object.keys(statistics).length - 1;
+
+ for (let cpu in statistics) {
+ let total = statistics[cpu]['user'] + statistics[cpu]['nice'] + statistics[cpu]['system'];
+
+ // make sure we have data to report
+ if (this._last_processor['core'][cpu] > 0) {
+ let delta = (total - this._last_processor['core'][cpu]) / dwell;
+
+ // /proc/stat provides overall usage for us under the 'cpu' heading
+ if (cpu == 'cpu') {
+ delta = delta / cores;
+ this._returnValue(callback, 'processor', delta / 100, 'processor-group', 'percent');
+ this._returnValue(callback, 'Usage', delta / 100, 'processor', 'percent');
+ } else {
+ this._returnValue(callback, _('Core %d').format(cpu.substr(3)), delta / 100, 'processor', 'percent');
+ }
+ }
+
+ this._last_processor['core'][cpu] = total;
+ }
+
+ // if frequency scaling is enabled, gather cpu-freq values
+ if (!this._processor_uses_cpu_info) {
+ for (let core = 0; core <= cores; core++) {
+ new FileModule.File('/sys/devices/system/cpu/cpu' + core + '/cpufreq/scaling_cur_freq').read().then(value => {
+ this._last_processor['speed'][core] = parseInt(value);
+ }).catch(err => { });
+ }
+ }
+ }).catch(err => { });
+
+ // if frequency scaling is disabled, use cpuinfo for speed
+ if (this._processor_uses_cpu_info) {
+ // grab CPU frequency
+ new FileModule.File('/proc/cpuinfo').read("\n").then(lines => {
+ let freqs = [];
+ for (let line of lines) {
+ // grab megahertz
+ let value = line.match(/^cpu MHz(\s+): ([+-]?\d+(\.\d+)?)/);
+ if (value) freqs.push(parseFloat(value[2]));
+ }
+
+ let sum = freqs.reduce((a, b) => a + b);
+ let hertz = (sum / freqs.length) * 1000 * 1000;
+ this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz');
+
+ let max_hertz = freqs.reduce((a, b) => Math.max(a, b)) * 1000 * 1000;
+ this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz');
+ let min_hertz = freqs.reduce((a, b) => Math.min(a, b)) * 1000 * 1000;
+ this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz');
+ }).catch(err => { });
+ // if frequency scaling is enabled, cpu-freq reports
+ } else if (Object.values(this._last_processor['speed']).length > 0) {
+ let sum = this._last_processor['speed'].reduce((a, b) => a + b);
+ let hertz = (sum / this._last_processor['speed'].length) * 1000;
+ this._returnValue(callback, 'Frequency', hertz, 'processor', 'hertz');
+ let max_hertz = this._last_processor['speed'].reduce((a, b) => Math.max(a, b)) * 1000;
+ this._returnValue(callback, 'Max frequency', max_hertz, 'processor', 'hertz');
+ let min_hertz = this._last_processor['speed'].reduce((a, b) => Math.min(a, b)) * 1000;
+ this._returnValue(callback, 'Min frequency', min_hertz, 'processor', 'hertz');
+ }
+ }
+
+ _querySystem(callback) {
+ // check load average
+ new FileModule.File('/proc/sys/fs/file-nr').read("\t").then(loadArray => {
+ this._returnValue(callback, 'Open Files', loadArray[0], 'system', 'string');
+ }).catch(err => { });
+
+ // check load average
+ new FileModule.File('/proc/loadavg').read(' ').then(loadArray => {
+ let proc = loadArray[3].split('/');
+
+ this._returnValue(callback, 'Load 1m', loadArray[0], 'system', 'load');
+ this._returnValue(callback, 'system', loadArray[0], 'system-group', 'load');
+ this._returnValue(callback, 'Load 5m', loadArray[1], 'system', 'load');
+ this._returnValue(callback, 'Load 15m', loadArray[2], 'system', 'load');
+ this._returnValue(callback, 'Threads Active', proc[0], 'system', 'string');
+ this._returnValue(callback, 'Threads Total', proc[1], 'system', 'string');
+ }).catch(err => { });
+
+ // check uptime
+ new FileModule.File('/proc/uptime').read(' ').then(upArray => {
+ this._returnValue(callback, 'Uptime', upArray[0], 'system', 'uptime');
+
+ let cores = Object.keys(this._last_processor['core']).length - 1;
+ if (cores > 0)
+ this._returnValue(callback, 'Process Time', upArray[0] - upArray[1] / cores, 'processor', 'uptime');
+ }).catch(err => { });
+ }
+
+ _queryNetwork(callback, dwell) {
+ // check network speed
+ let directions = ['tx', 'rx'];
+ let netbase = '/sys/class/net/';
+
+ new FileModule.File(netbase).list().then(interfaces => {
+ for (let iface of interfaces) {
+ for (let direction of directions) {
+ // lo tx and rx are the same
+ if (iface == 'lo' && direction == 'rx') continue;
+
+ new FileModule.File(netbase + iface + '/statistics/' + direction + '_bytes').read().then(value => {
+ // issue #217 - don't include 'lo' traffic in Maximum calculations in values.js
+ // by not using network-rx or network-tx
+ let name = iface + ((iface == 'lo')?'':' ' + direction);
+
+ let type = 'network' + ((iface=='lo')?'':'-' + direction);
+ this._returnValue(callback, name, value, type, 'storage');
+ }).catch(err => { });
+ }
+ }
+ }).catch(err => { });
+
+ // some may not want public ip checking
+ if (this._settings.get_boolean('include-public-ip')) {
+ // check the public ip every hour or when waking from sleep
+ if (this._next_public_ip_check <= 0) {
+ this._next_public_ip_check = 3600;
+
+ this._refreshIPAddress(callback);
+ }
+
+ this._next_public_ip_check -= dwell;
+ }
+
+ // wireless interface statistics
+ new FileModule.File('/proc/net/wireless').read("\n", true).then(lines => {
+ // wireless has two headers - first is stripped in helper function
+ lines.shift();
+
+ // if multiple wireless device, we use the last one
+ for (let line of lines) {
+ let netArray = line.trim().split(/\s+/);
+ let quality_pct = netArray[2].substr(0, netArray[2].length-1) / 70;
+ let signal = netArray[3].substr(0, netArray[3].length-1);
+
+ this._returnValue(callback, 'WiFi Link Quality', quality_pct, 'network', 'percent');
+ this._returnValue(callback, 'WiFi Signal Level', signal, 'network', 'string');
+ }
+ }).catch(err => { });
+ }
+
+ _queryStorage(callback, dwell) {
+ // display zfs arc status, if available
+ new FileModule.File('/proc/spl/kstat/zfs/arcstats').read().then(lines => {
+ let values = '', target = 0, maximum = 0, current = 0;
+
+ if (values = lines.match(/c(\s+)(\d+)(\s+)(\d+)/)) target = values[4];
+ if (values = lines.match(/c_max(\s+)(\d+)(\s+)(\d+)/)) maximum = values[4];
+ if (values = lines.match(/size(\s+)(\d+)(\s+)(\d+)/)) current = values[4];
+
+ // ZFS statistics
+ this._returnValue(callback, 'ARC Target', target, 'storage', 'storage');
+ this._returnValue(callback, 'ARC Maximum', maximum, 'storage', 'storage');
+ this._returnValue(callback, 'ARC Current', current, 'storage', 'storage');
+ }).catch(err => { });
+
+ // check disk performance stats
+ new FileModule.File('/proc/diskstats').read("\n").then(lines => {
+ for (let line of lines) {
+ let loadArray = line.trim().split(/\s+/);
+ if ('/dev/' + loadArray[2] == this._storageDevice) {
+ var read = (loadArray[5] * 512);
+ var write = (loadArray[9] * 512);
+ this._returnValue(callback, 'Read total', read, 'storage', 'storage');
+ this._returnValue(callback, 'Write total', write, 'storage', 'storage');
+ this._returnValue(callback, 'Read rate', (read - this._lastRead) / dwell, 'storage', 'storage');
+ this._returnValue(callback, 'Write rate', (write - this._lastWrite) / dwell, 'storage', 'storage');
+ this._lastRead = read;
+ this._lastWrite = write;
+ break;
+ }
+ }
+ }).catch(err => { });
+
+ // skip rest of stats if gtop not available
+ if (!hasGTop) return;
+
+ GTop.glibtop_get_fsusage(this.storage, this._settings.get_string('storage-path'));
+
+ let total = this.storage.blocks * this.storage.block_size;
+ let avail = this.storage.bavail * this.storage.block_size;
+ let free = this.storage.bfree * this.storage.block_size;
+ let used = total - free;
+ let reserved = (total - avail) - used;
+ let freePercent = 0;
+ let usedPercent = 0;
+ if (total > 0) {
+ freePercent = Math.round((free / total) * 100);
+ usedPercent = Math.round((used / total) * 100);
+ }
+
+ this._returnValue(callback, 'Total', total, 'storage', 'storage');
+ this._returnValue(callback, 'Used', used, 'storage', 'storage');
+ this._returnValue(callback, 'Reserved', reserved, 'storage', 'storage');
+ this._returnValue(callback, 'Free', avail, 'storage', 'storage');
+ this._returnValue(callback, 'Used %', usedPercent + '%', 'storage', 'string');
+ this._returnValue(callback, 'Free %', freePercent + '%', 'storage', 'string');
+ this._returnValue(callback, 'storage', avail, 'storage-group', 'storage');
+ }
+
+ _queryBattery(callback) {
+ let battery_slot = this._settings.get_int('battery-slot');
+
+ // create a mapping of indices to battery paths (from prefs.ui)
+ const BATTERY_PATHS = {
+ 0: 'BAT0',
+ 1: 'BAT1',
+ 2: 'BAT2',
+ 3: 'BATT',
+ 4: 'CMB0',
+ 5: 'CMB1',
+ 6: 'CMB2',
+ 7: 'macsmc-battery'
+ };
+
+ // uevent has all necessary fields, no need to read individual files
+ let battery_path = '/sys/class/power_supply/' + BATTERY_PATHS[battery_slot] + '/uevent';
+ new FileModule.File(battery_path).read("\n").then(lines => {
+ let output = {};
+ for (let line of lines) {
+ let split = line.split('=');
+ output[split[0].replace('POWER_SUPPLY_', '')] = split[1];
+ }
+
+ if ('STATUS' in output) {
+ this._returnValue(callback, 'State', output['STATUS'], 'battery', '');
+ }
+
+ if ('CYCLE_COUNT' in output) {
+ this._returnValue(callback, 'Cycles', output['CYCLE_COUNT'], 'battery', '');
+ }
+
+ if ('VOLTAGE_NOW' in output) {
+ this._returnValue(callback, 'Voltage', output['VOLTAGE_NOW'] / 1000, 'battery', 'in');
+ }
+
+ if ('CAPACITY_LEVEL' in output) {
+ this._returnValue(callback, 'Level', output['CAPACITY_LEVEL'], 'battery', '');
+ }
+
+ if ('CAPACITY' in output) {
+ this._returnValue(callback, 'Percentage', output['CAPACITY'] / 100, 'battery', 'percent');
+ }
+
+ if ('VOLTAGE_NOW' in output && 'CURRENT_NOW' in output && (!('POWER_NOW' in output))) {
+ output['POWER_NOW'] = (output['VOLTAGE_NOW'] * output['CURRENT_NOW']) / 1000000;
+ }
+
+ if ('POWER_NOW' in output) {
+ const powerValue = (
+ parseFloat(output['POWER_NOW']) * (output['STATUS'] === 'Discharging' ? -1 : 1)
+ );
+ this._returnValue(callback, 'Power Rate', powerValue, 'battery', 'watt');
+ this._returnValue(callback, 'battery', powerValue, 'battery-group', 'watt');
+ }
+
+ if ('CHARGE_FULL' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL' in output))) {
+ output['ENERGY_FULL'] = (output['CHARGE_FULL'] * output['VOLTAGE_MIN_DESIGN']) / 1000000;
+ }
+
+ if ('ENERGY_FULL' in output) {
+ this._returnValue(callback, 'Energy (full)', output['ENERGY_FULL'], 'battery', 'watt-hour');
+ }
+
+ if ('CHARGE_FULL_DESIGN' in output && 'VOLTAGE_MIN_DESIGN' in output && (!('ENERGY_FULL_DESIGN' in output))) {
+ output['ENERGY_FULL_DESIGN'] = (output['CHARGE_FULL_DESIGN'] * output['VOLTAGE_MIN_DESIGN']) / 1000000;
+ }
+
+ if ('ENERGY_FULL_DESIGN' in output) {
+ this._returnValue(callback, 'Energy (design)', output['ENERGY_FULL_DESIGN'], 'battery', 'watt-hour');
+
+ if ('ENERGY_FULL' in output) {
+ this._returnValue(callback, 'Capacity', (output['ENERGY_FULL'] / output['ENERGY_FULL_DESIGN']), 'battery', 'percent');
+ }
+ }
+
+ if ('VOLTAGE_MIN_DESIGN' in output && 'CHARGE_NOW' in output && (!('ENERGY_NOW' in output))) {
+ output['ENERGY_NOW'] = (output['VOLTAGE_MIN_DESIGN'] * output['CHARGE_NOW']) / 1000000;
+ }
+
+ if ('ENERGY_NOW' in output) {
+ this._returnValue(callback, 'Energy (now)', output['ENERGY_NOW'], 'battery', 'watt-hour');
+ }
+
+ if ('ENERGY_FULL' in output && 'ENERGY_NOW' in output && 'POWER_NOW' in output && output['POWER_NOW'] !== 0 && 'STATUS' in output && (output['STATUS'] == 'Charging' || output['STATUS'] == 'Discharging')) {
+
+ let timeLeft = 0;
+
+ // two different formulas depending on if we are charging or discharging
+ if (output['STATUS'] == 'Charging') {
+ timeLeft = ((output['ENERGY_FULL'] - output['ENERGY_NOW']) / output['POWER_NOW']);
+ } else {
+ timeLeft = (output['ENERGY_NOW'] / Math.abs(output['POWER_NOW']));
+ }
+
+ // don't process Infinity values
+ if (timeLeft !== Infinity) {
+ if (this._battery_charge_status != output['STATUS']) {
+ // clears history due to state change
+ this._battery_time_left_history = [];
+
+ // clear time left history when laptop goes in and out of charging
+ this._battery_charge_status = output['STATUS'];
+ }
+
+ // add latest time left estimate to our history
+ this._battery_time_left_history.push(parseInt(timeLeft * 3600));
+
+ // keep track of last 15 time left estimates by erasing the first
+ if (this._battery_time_left_history.length > 10)
+ this._battery_time_left_history.shift();
+
+ // sum up and create average of our time left history
+ let sum = this._battery_time_left_history.reduce((a, b) => a + b);
+ let avg = sum / this._battery_time_left_history.length;
+
+ // use time left history to update screen
+ this._returnValue(callback, 'Time left', parseInt(avg), 'battery', 'runtime');
+ }
+ } else {
+ this._returnValue(callback, 'Time left', output['STATUS'], 'battery', '');
+ }
+ }).catch(err => { });
+ }
+
+ _initFrameMonitor() {
+ if (this._frameMonitorSignalId) return;
+ this._frameMonitorLastTime = 0;
+ this._frameMonitorFrameCount = 0;
+ this._frameMonitorAccTime = 0;
+ this._frameMonitorCurrentHz = 0;
+ this._frameMonitorSignalId = global.stage.connect('after-paint', () => {
+ this._onAfterPaint();
+ });
+ }
+
+ _destroyFrameMonitor() {
+ if (this._frameMonitorSignalId) {
+ global.stage.disconnect(this._frameMonitorSignalId);
+ this._frameMonitorSignalId = 0;
+ }
+ this._frameMonitorLastTime = 0;
+ this._frameMonitorCurrentHz = 0;
+ }
+
+ _onAfterPaint() {
+ const now = GLib.get_monotonic_time();
+
+ if (this._frameMonitorLastTime === 0) {
+ this._frameMonitorLastTime = now;
+ return;
+ }
+
+ const delta = now - this._frameMonitorLastTime;
+ this._frameMonitorLastTime = now;
+
+ this._frameMonitorFrameCount++;
+ this._frameMonitorAccTime += delta;
+
+ if (this._frameMonitorAccTime >= 500000) {
+ this._frameMonitorCurrentHz = this._frameMonitorFrameCount / (this._frameMonitorAccTime / 1000000);
+ this._frameMonitorFrameCount = 0;
+ this._frameMonitorAccTime = 0;
+ }
+ }
+
+ _queryGpu(callback) {
+ if (this._frameMonitorCurrentHz > 0)
+ this._returnValue(callback, 'Refresh Rate', this._frameMonitorCurrentHz, 'gpu#1', 'hertz');
+
+ if (!this._nvidia_smi_process) {
+ // no nvidia-smi, so we use sysfs DRM if any cards was discovered
+ if (!this._gpu_drm_indices){
+ if (this._frameMonitorCurrentHz > 0)
+ this._returnValue(callback, 'Refresh Rate', this._frameMonitorCurrentHz, 'gpu#1-group', 'hertz');
+ this._disableGpuLabels(callback);
+ return;
+ } else {
+ this._readGpuDrm(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');
+ }
+
+ _readGpuDrm(callback){
+ const multiGpu = this._gpu_drm_indices.length > 1;
+ const unit = this._settings.get_int('memory-measurement') ? 1000 : 1024;
+ for (let z = 0; z < this._gpu_drm_indices.length; z++ ) {
+ let i = this._gpu_drm_indices[z];
+ const typeName = 'gpu#' + i;
+ const vendor = this._gpu_drm_vendors[z];
+
+ // AMD
+ if(vendor === "0x1002") {
+ // read GPU usage and create group lebel for card
+ new FileModule.File('/sys/class/drm/card'+i+'/device/gpu_busy_percent').read().then(value => {
+ // create group
+ this._returnGpuValue(callback, 'Graphics', parseInt(value) * 0.01, typeName + '-group', 'percent');
+ this._returnGpuValue(callback, 'Vendor', "AMD", typeName, 'string');
+ this._returnGpuValue(callback, 'Usage', parseInt(value) * 0.01, typeName, 'percent');
+ }).catch(err => {
+ // nothing to do, keep old value displayed
+ });
+ new FileModule.File('/sys/class/drm/card'+i+'/device/mem_info_vram_used').read().then(value => {
+ this._returnGpuValue(callback, 'Memory Used', parseInt(value) / unit, typeName, 'memory');
+ }).catch(err => {
+ // nothing to do, keep old value displayed
+ });
+ new FileModule.File('/sys/class/drm/card'+i+'/device/mem_info_vram_total').read().then(value => {
+ this._returnGpuValue(callback, 'Memory Total', parseInt(value) / unit, typeName, 'memory');
+ }).catch(err => {
+ // nothing to do, keep old value displayed
+ });
+ } else {
+ // for other vendors only show basic card info
+ let vendorName = null;
+ switch (vendor){
+ case '0x10DE': vendorName = 'NVIDIA'; break; // should be never used as nvidia-smi should be preferred
+ case '0x13B5': vendorName = 'ARM'; break;
+ case '0x5143': vendorName = 'Qualcomm'; break;
+ case '0x8086': vendorName = 'Intel'; break;
+ default: vendorName = "Unknown " + vendor;
+ }
+ this._returnGpuValue(callback, 'Graphics', vendorName, typeName + '-group', '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(format !== "string" && (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;
+ callback(label, value, type, format);
+ }
+
+ _discoverHardwareMonitors(callback) {
+ this._tempVoltFanSensors = { 'temperature': {}, 'voltage': {}, 'fan': {} };
+
+ let hwbase = '/sys/class/hwmon/';
+
+ // process sensor_types now so it is not called multiple times below
+ let sensor_types = {};
+
+ if (this._settings.get_boolean('show-temperature'))
+ sensor_types['temp'] = 'temperature';
+
+ if (this._settings.get_boolean('show-voltage'))
+ sensor_types['in'] = 'voltage';
+
+ if (this._settings.get_boolean('show-fan'))
+ sensor_types['fan'] = 'fan';
+
+ // a little informal, but this code has zero I/O block
+ new FileModule.File(hwbase).list().then(files => {
+ for (let file of files) {
+ // grab name of sensor
+ new FileModule.File(hwbase + file + '/name').read().then(name => {
+ // are we dealing with a CPU?
+ if (name == 'coretemp') {
+ // determine which processor (socket) we are dealing with
+ new FileModule.File(hwbase + file + '/temp1_label').read().then(prefix => {
+ this._processTempVoltFan(callback, sensor_types, prefix, hwbase + file, file);
+ }).catch(err => {
+ // this shouldn't be necessary, but just in case temp1_label doesn't exist
+ // attempt to fix #266
+ this._processTempVoltFan(callback, sensor_types, name, hwbase + file, file);
+ });
+ } else {
+ // not a CPU, process all other sensors
+ this._processTempVoltFan(callback, sensor_types, name, hwbase + file, file);
+ }
+ }).catch(err => {
+ new FileModule.File(hwbase + file + '/device/name').read().then(name => {
+ this._processTempVoltFan(callback, sensor_types, name, hwbase + file + '/device', file);
+ }).catch(err => { });
+ });
+ }
+ }).catch(err => { });
+
+ // does this system support cpu scaling? if so we will use it to grab Frequency and Boost below
+ new FileModule.File('/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq').read().then(value => {
+ this._processor_uses_cpu_info = false;
+ }).catch(err => { });
+
+ // is static CPU information enabled?
+ if (this._settings.get_boolean('include-static-info')) {
+ // grab static CPU information
+ new FileModule.File('/proc/cpuinfo').read("\n").then(lines => {
+ let vendor_id = '';
+ let bogomips = '';
+ let sockets = {};
+ let cache = '';
+
+ for (let line of lines) {
+ let value = '';
+
+ // grab cpu vendor
+ if (value = line.match(/^vendor_id(\s+): (\w+.*)/)) vendor_id = value[2];
+
+ // grab bogomips
+ if (value = line.match(/^bogomips(\s+): (\d*\.?\d*)$/)) bogomips = value[2];
+
+ // grab processor count
+ if (value = line.match(/^physical id(\s+): (\d+)$/)) sockets[value[2]] = 1;
+
+ // grab cache
+ if (value = line.match(/^cache size(\s+): (\d+) KB$/)) cache = value[2];
+ }
+
+ this._returnValue(callback, 'Vendor', vendor_id, 'processor', 'string');
+ this._returnValue(callback, 'Bogomips', bogomips, 'processor', 'string');
+ this._returnValue(callback, 'Sockets', Object.keys(sockets).length, 'processor', 'string');
+ this._returnValue(callback, 'Cache', cache, 'processor', 'memory');
+ }).catch(err => { });
+
+ // grab static CPU information
+ new FileModule.File('/proc/version').read(' ').then(kernelArray => {
+ this._returnValue(callback, 'Kernel', kernelArray[2], 'system', 'string');
+ }).catch(err => { });
+ }
+
+ // Launch nvidia-smi subprocess if nvidia querying is enabled
+ this._reconfigureNvidiaSmiProcess();
+ this._discoverGpuDrm();
+ this._initFrameMonitor();
+ }
+
+ _discoverGpuDrm() {
+ // use DRM only if nvidia-smi is not used
+ if (this._settings.get_boolean('show-gpu') && this._nvidia_smi_process == null) {
+ // try to discover up to 10 cards starting from index 0
+ for(let i = 0; i < 10 ; i++){
+ new FileModule.File('/sys/class/drm/card'+i+'/device/vendor').read().then(value => {
+ if(!this._gpu_drm_indices){
+ this._gpu_drm_indices = [];
+ this._gpu_drm_vendors = [];
+ }
+ this._gpu_drm_indices.push(i);
+ this._gpu_drm_vendors.push(value);
+ }).catch(err => { });
+ }
+ } else {
+ this._gpu_drm_vendors = null;
+ this._gpu_drm_indices = null;
+ }
+ }
+
+ // 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) {
+ let sensor_files = [ 'input', 'label' ];
+
+ // grab files from directory
+ new FileModule.File(path).list().then(files2 => {
+ let trisensors = {};
+
+ // loop over files from directory
+ for (let file2 of Object.values(files2)) {
+ // simple way of processing input and label (from above)
+ for (let key of Object.values(sensor_files)) {
+ // process toggled on sensors from extension preferences
+ for (let sensor_type in sensor_types) {
+ if (file2.substr(0, sensor_type.length) == sensor_type && file2.substr(-(key.length+1)) == '_' + key) {
+ let key2 = file + file2.substr(0, file2.indexOf('_'));
+
+ if (!(key2 in trisensors)) {
+ trisensors[key2] = {
+ 'type': sensor_types[sensor_type],
+ 'format': sensor_type,
+ 'label': path + '/name'
+ };
+ }
+
+ trisensors[key2][key] = path + '/' + file2;
+ }
+ }
+ }
+ }
+
+ for (let obj of Object.values(trisensors)) {
+ if (!('input' in obj))
+ continue;
+
+ new FileModule.File(obj['input']).read().then(value => {
+ let extra = (obj['label'].indexOf('_label')==-1) ? ' ' + obj['input'].substr(obj['input'].lastIndexOf('/')+1).split('_')[0] : '';
+
+ if (value > 0 || !this._settings.get_boolean('hide-zeros') || obj['type'] == 'fan') {
+ new FileModule.File(obj['label']).read().then(label => {
+ this._addTempVoltFan(callback, obj, name, label, extra, value);
+ }).catch(err => {
+ let tmpFile = obj['label'].substr(0, obj['label'].lastIndexOf('/')) + '/name';
+ new FileModule.File(tmpFile).read().then(label => {
+ this._addTempVoltFan(callback, obj, name, label, extra, value);
+ }).catch(err => { });
+ });
+ }
+ }).catch(err => { });
+ }
+ }).catch(err => { });
+ }
+
+ _addTempVoltFan(callback, obj, name, label, extra, value) {
+ // prepend module that provided sensor data
+ if (name != label) label = name + ' ' + label;
+
+ //if (label == 'nvme Composite') label = 'NVMe';
+ //if (label == 'nouveau') label = 'Nvidia';
+
+ label = label + extra;
+
+ // in the future we will read /etc/sensors3.conf
+ if (label == 'acpitz temp1') label = 'ACPI Thermal Zone';
+ if (label == 'pch_cannonlake temp1') label = 'Platform Controller Hub';
+ if (label == 'iwlwifi_1 temp1') label = 'Wireless Adapter';
+ if (label == 'Package id 0') label = 'Processor 0';
+ if (label == 'Package id 1') label = 'Processor 1';
+ label = label.replace('Package id', 'CPU');
+
+ let types = [ 'temperature', 'voltage', 'fan' ];
+ for (let type of types) {
+ // check if this label already exists
+ if (label in this._tempVoltFanSensors[type]) {
+ for (let i = 2; i <= 9; i++) {
+ // append an incremented number to end
+ let new_label = label + ' ' + i;
+
+ // if new label is available, use it
+ if (!(new_label in this._tempVoltFanSensors[type])) {
+ label = new_label;
+ break;
+ }
+ }
+ }
+ }
+
+ // update screen on initial build to prevent delay on update
+ this._returnValue(callback, label, value, obj['type'], obj['format']);
+
+ this._tempVoltFanSensors[obj['type']][label] = {
+ 'format': obj['format'],
+ 'path': obj['input']
+ };
+ }
+
+ 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;
+ this._frameMonitorLastTime = 0;
+ this._frameMonitorFrameCount = 0;
+ this._frameMonitorAccTime = 0;
+ }
+
+ destroy() {
+ this._destroyFrameMonitor();
+ this._terminateNvidiaSmiProcess();
+
+ for (let signal of Object.values(this._settingChangedSignals))
+ this._settings.disconnect(signal);
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css
new file mode 100644
index 0000000..4fcd985
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/stylesheet.css
@@ -0,0 +1,19 @@
+.vitals-icon { icon-size: 16px; }
+.vitals-menu-button-container {}
+.vitals-panel-item{spacing: 0;}
+.vitals-panel-menu{spacing: 11px; padding: 3px; }
+.vitals-panel-icon-default {}
+.vitals-panel-icon-temperature { margin: 0 1px 0 0; padding: 0; }
+.vitals-panel-icon-voltage { margin: 0 0 0 0; padding: 0; }
+.vitals-panel-icon-fan { margin: 0 4px 0 0; padding: 0; }
+.vitals-panel-icon-memory { margin: 0 2px 0 0; padding: 0; }
+.vitals-panel-icon-processor { margin: 0 3px 0 0; padding: 0; }
+.vitals-panel-icon-system { margin: 0 3px 0 0; padding: 0; }
+.vitals-panel-icon-network { margin: 0 3px 0 0; padding: 0; }
+.vitals-panel-icon-storage { margin: 0 2px 0 0; padding: 0; }
+.vitals-panel-icon-battery { margin: 0 4px 0 0; padding: 0; }
+.vitals-panel-label {}
+.vitals-button-action { -st-icon-style: symbolic; border-radius: 32px; margin: 0px; min-height: 22px; min-width: 22px; padding: 10px; font-size: 100%; border: 1px solid transparent; }
+.vitals-button-action:hover, .vitals-button-action:focus { border-color: #777; }
+.vitals-button-action > StIcon { icon-size: 16px; }
+.vitals-button-box { padding: 0px; spacing: 22px; }
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js
new file mode 100644
index 0000000..f16778c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/Vitals@CoreCoding.com/values.js
@@ -0,0 +1,358 @@
+/*
+ Copyright (c) 2018, Chris Monahan <chris@corecoding.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.
+*/
+
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+
+const cbFun = (d, c) => {
+ let bb = d[1] % c[0],
+ aa = (d[1] - bb) / c[0];
+ aa = aa > 0 ? aa + c[1] : '';
+
+ return [d[0] + aa, bb];
+};
+
+export const Values = GObject.registerClass({
+ GTypeName: 'Values',
+}, class Values extends GObject.Object {
+
+ _init(settings, sensorIcons) {
+ this._settings = settings;
+ this._sensorIcons = sensorIcons;
+
+ this._networkSpeedOffset = {};
+ this._networkSpeeds = {};
+
+ this._history = {};
+ //this._history2 = {};
+ this.resetHistory();
+ }
+
+ _legible(value, sensorClass) {
+ let unit = 1000;
+ if (value === null) return 'N/A';
+ let use_higher_precision = this._settings.get_boolean('use-higher-precision');
+ let memory_measurement = this._settings.get_int('memory-measurement')
+ let storage_measurement = this._settings.get_int('storage-measurement')
+ let use_bps = (this._settings.get_int('network-speed-format') == 1);
+
+ let format = '';
+ let ending = '';
+ let exp = 0;
+
+ var decimal = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ];
+ var binary = [ 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' ];
+ var hertz = [ 'Hz', 'KHz', 'MHz', 'GHz', 'THz', 'PHz', 'EHz', 'ZHz' ];
+
+ switch (sensorClass) {
+ case 'percent':
+ format = (use_higher_precision)?'%.1f%s':'%d%s';
+ value = value * 100;
+ if (value > 100) value = 100;
+ ending = '%';
+ break;
+ case 'temp':
+ value = value / 1000;
+ ending = '°C';
+
+ // are we converting to fahrenheit?
+ if (this._settings.get_int('unit') == 1) {
+ value = ((9 / 5) * value + 32);
+ ending = '°F';
+ }
+
+ format = (use_higher_precision)?'%.1f%s':'%d%s';
+ break;
+ case 'fan':
+ format = '%d %s';
+ ending = 'RPM';
+ break;
+ case 'in': // voltage
+ value = value / 1000;
+ format = ((value >= 0) ? '+' : '-') + ((use_higher_precision)?'%.2f %s':'%.1f %s');
+ ending = 'V';
+ break;
+ case 'hertz':
+ if (value > 0) {
+ exp = Math.floor(Math.log(value) / Math.log(unit));
+ if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++;
+ value = parseFloat((value / Math.pow(unit, exp)));
+ }
+
+ format = (use_higher_precision)?'%.2f %s':'%.1f %s';
+ ending = hertz[exp];
+ break;
+ case 'memory':
+ unit = (memory_measurement)?1000:1024;
+
+ if (value > 0) {
+ value *= unit;
+ exp = Math.floor(Math.log(value) / Math.log(unit));
+ if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++;
+ value = parseFloat((value / Math.pow(unit, exp)));
+ }
+
+ format = (use_higher_precision)?'%.2f %s':'%.1f %s';
+
+ if (memory_measurement)
+ ending = decimal[exp];
+ else
+ ending = binary[exp];
+
+ break;
+ case 'storage':
+ unit = (storage_measurement)?1000:1024;
+
+ if (value > 0) {
+ exp = Math.floor(Math.log(value) / Math.log(unit));
+ if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++;
+ value = parseFloat((value / Math.pow(unit, exp)));
+ }
+
+ format = (use_higher_precision)?'%.2f %s':'%.1f %s';
+
+ if (storage_measurement)
+ ending = decimal[exp];
+ else
+ ending = binary[exp];
+
+ break;
+ case 'speed':
+ if (value > 0) {
+ if (use_bps) value *= 8;
+ exp = Math.floor(Math.log(value) / Math.log(unit));
+ if (value >= Math.pow(unit, exp) * (unit - 0.05)) exp++;
+ value = parseFloat((value / Math.pow(unit, exp)));
+ }
+
+ format = (use_higher_precision)?'%.1f %s':'%.0f %s';
+
+ if (use_bps) {
+ ending = decimal[exp].replace('B', 'bps');
+ } else {
+ ending = decimal[exp] + '/s';
+ }
+
+ break;
+ case 'runtime':
+ case 'uptime':
+ let scale = [24, 60, 60];
+ let units = ['d ', 'h ', 'm '];
+
+ // show seconds on higher precision or if value under a minute
+ if (sensorClass != 'runtime' && (use_higher_precision || value < 60)) {
+ scale.push(1);
+ units.push('s ');
+ }
+
+ let rslt = scale.map((d, i, a) => a.slice(i).reduce((d, c) => d * c))
+ .map((d, i) => ([d, units[i]]))
+ .reduce(cbFun, ['', value]);
+
+ value = rslt[0].trim();
+
+ format = '%s';
+ break;
+ case 'milliamp':
+ format = (use_higher_precision)?'%.1f %s':'%d %s';
+ value = value / 1000;
+ ending = 'mA';
+ break;
+ case 'milliamp-hour':
+ format = (use_higher_precision)?'%.1f %s':'%d %s';
+ value = value / 1000;
+ ending = 'mAh';
+ break;
+ case 'watt':
+ format = (
+ ((value > 0) ? '+' : '') + ((use_higher_precision)?'%.2f %s':'%.1f %s')
+ );
+ 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;
+ ending = 'Wh';
+ break;
+ 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;
+ }
+
+ return format.format(value, ending).trim();
+ }
+
+ returnIfDifferent(dwell, label, value, type, format, key) {
+ let output = [];
+
+ // make sure the keys exist
+ if (!(type in this._history)) this._history[type] = {};
+
+ // no sense in continuing when the raw value has not changed
+ if (type != 'network-rx' && type != 'network-tx' &&
+ key in this._history[type] && this._history[type][key][1] == value)
+ return output;
+
+ // is the value different from last time?
+ let legible = this._legible(value, format);
+
+ // don't return early when dealing with network traffic
+ if (type != 'network-rx' && type != 'network-tx') {
+ // only update when we are coming through for the first time, or if a value has changed
+ if (key in this._history[type] && this._history[type][key][0] == legible)
+ return output;
+
+ // add label as it was sent from sensors class
+ output.push([label, legible, type, key]);
+ }
+
+ // save previous values to update screen on changes only
+ let previousValue = this._history[type][key];
+ this._history[type][key] = [legible, value];
+
+ // process average, min and max values
+ if (type == 'temperature' || type == 'voltage' || type == 'fan') {
+ let vals = Object.values(this._history[type]).map(x => parseFloat(x[1]));
+
+ // show value in group even if there is one value present
+ let sum = vals.reduce((a, b) => a + b);
+ let avg = this._legible(sum / vals.length, format);
+ output.push([type, avg, type + '-group', '']);
+
+ // If only one value is present, don't display avg, min and max
+ if (vals.length > 1) {
+ output.push(['Average', avg, type, '__' + type + '_avg__']);
+
+ // calculate Minimum value
+ let min = Math.min(...vals);
+ min = this._legible(min, format);
+ output.push(['Minimum', min, type, '__' + type + '_min__']);
+
+ // calculate Maximum value
+ let max = Math.max(...vals);
+ max = this._legible(max, format);
+ output.push(['Maximum', max, type, '__' + type + '_max__']);
+ }
+ } else if (type == 'network-rx' || type == 'network-tx') {
+ let direction = type.split('-')[1];
+
+ // appends total upload and download for all interfaces for #216
+ let vals = Object.values(this._history[type]).map(x => parseFloat(x[1]));
+ let sum = vals.reduce((partialSum, a) => partialSum + a, 0);
+ const memUnit = this._settings.get_int('memory-measurement') ? 1000 : 1024;
+ output.push(['Boot ' + direction, this._legible(sum, format), type, '__' + type + '_boot__']);
+
+ // keeps track of session start point
+ if (!(key in this._networkSpeedOffset) || this._networkSpeedOffset[key] <= 0)
+ this._networkSpeedOffset[key] = sum;
+
+ // outputs session upload and download for all interfaces for #234
+ output.push(['Session ' + direction, this._legible(sum - this._networkSpeedOffset[key], format), type, '__' + type + '_ses__']);
+
+ // calculate speed for this interface
+ let speed = (value - previousValue[1]) / dwell;
+ output.push([label, this._legible(speed, 'speed'), type, key]);
+
+ // store speed for Device report
+ if (!(direction in this._networkSpeeds)) this._networkSpeeds[direction] = {};
+ if (!(label in this._networkSpeeds[direction])) this._networkSpeeds[direction][label] = 0;
+
+ // store value for next go around
+ if (value > 0 || (value == 0 && !this._settings.get_boolean('hide-zeros')))
+ this._networkSpeeds[direction][label] = speed;
+
+ // calculate total upload and download device speed
+ for (let direction in this._networkSpeeds) {
+ let sumNum = 0;
+ for (let iface in this._networkSpeeds[direction])
+ sumNum += parseFloat(this._networkSpeeds[direction][iface]);
+
+ let sum = this._legible(sumNum, 'speed');
+ output.push(['Device ' + direction, sum, 'network-' + direction, '__network-' + direction + '_max__']);
+ // append download speed to group itself
+ if (direction == 'rx') output.push([type, sum, type + '-group', '']);
+ }
+ }
+
+/*
+ global.log('before', JSON.stringify(output));
+ for (let i = output.length - 1; i >= 0; i--) {
+ let sensor = output[i];
+ // sensor[0]=label, sensor[1]=value, sensor[2]=type, sensor[3]=key)
+
+ //["CPU Core 5","46°C","temperature","_temperature_hwmon8temp7_"]
+
+ // make sure the keys exist
+ if (!(sensor[2] in this._history2)) this._history2[sensor[2]] = {};
+
+ if (sensor[3] in this._history2[sensor[2]]) {
+ if (this._history2[sensor[2]][sensor[3]] == sensor[1]) {
+ output.splice(i, 1);
+ }
+ }
+
+ this._history2[sensor[2]][sensor[3]] = sensor[1];
+ }
+
+ global.log(' after', JSON.stringify(output));
+ global.log('***************************');
+*/
+
+ return output;
+ }
+
+ 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'] = {};
+ }
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/appIndicator.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/appIndicator.js
new file mode 100644
index 0000000..7efded2
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/appIndicator.js
@@ -0,0 +1,1607 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import Clutter from 'gi://Clutter';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import GdkPixbuf from 'gi://GdkPixbuf';
+import Gio from 'gi://Gio';
+import St from 'gi://St';
+
+import * as Params from 'resource:///org/gnome/shell/misc/params.js';
+import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
+
+import * as DBusUtils from './dbusUtils.js';
+import * as IconCache from './iconCache.js';
+import * as Util from './util.js';
+import * as Interfaces from './interfaces.js';
+import * as PixmapsUtils from './pixmapsUtils.js';
+import * as PromiseUtils from './promiseUtils.js';
+import * as SettingsManager from './settingsManager.js';
+import {DBusProxy} from './dbusProxy.js';
+
+Gio._promisify(Gio.File.prototype, 'read_async');
+Gio._promisify(GdkPixbuf.Pixbuf, 'get_file_info_async');
+Gio._promisify(GdkPixbuf.Pixbuf, 'new_from_stream_at_scale_async',
+ 'new_from_stream_finish');
+Gio._promisify(St.IconInfo.prototype, 'load_symbolic_async');
+Gio._promisify(Gio.DBusConnection.prototype, 'call');
+
+const MAX_UPDATE_FREQUENCY = 30; // In ms
+const FALLBACK_ICON_NAME = 'image-loading-symbolic';
+const PIXMAPS_FORMAT = imports.gi.Cogl.PixelFormat.ARGB_8888;
+
+export const SNICategory = Object.freeze({
+ APPLICATION: 'ApplicationStatus',
+ COMMUNICATIONS: 'Communications',
+ SYSTEM: 'SystemServices',
+ HARDWARE: 'Hardware',
+});
+
+export const SNIStatus = Object.freeze({
+ PASSIVE: 'Passive',
+ ACTIVE: 'Active',
+ NEEDS_ATTENTION: 'NeedsAttention',
+});
+
+const SNIconType = Object.freeze({
+ NORMAL: 0,
+ ATTENTION: 1,
+ OVERLAY: 2,
+
+ toPropertyName: (iconType, params = {isPixbuf: false}) => {
+ let propertyName = 'Icon';
+
+ if (iconType === SNIconType.OVERLAY)
+ propertyName = 'OverlayIcon';
+ else if (iconType === SNIconType.ATTENTION)
+ propertyName = 'AttentionIcon';
+
+ return `${propertyName}${params.isPixbuf ? 'Pixmap' : 'Name'}`;
+ },
+});
+
+export const AppIndicatorProxy = GObject.registerClass(
+class AppIndicatorProxy extends DBusProxy {
+ static get interfaceInfo() {
+ if (!this._interfaceInfo) {
+ this._interfaceInfo = Gio.DBusInterfaceInfo.new_for_xml(
+ Interfaces.StatusNotifierItem);
+ }
+ return this._interfaceInfo;
+ }
+
+ static get OPTIONAL_PROPERTIES() {
+ return [
+ 'XAyatanaLabel',
+ 'XAyatanaLabelGuide',
+ 'XAyatanaOrderingIndex',
+ 'IconAccessibleDesc',
+ 'AttentionAccessibleDesc',
+ ];
+ }
+
+ static get TUPLE_TYPE() {
+ if (!this._tupleType)
+ this._tupleType = new GLib.VariantType('()');
+
+ return this._tupleType;
+ }
+
+ static destroy() {
+ delete this._interfaceInfo;
+ delete this._tupleType;
+ }
+
+ _init(busName, objectPath) {
+ const {interfaceInfo} = AppIndicatorProxy;
+
+ super._init(busName, objectPath, interfaceInfo,
+ Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
+
+ this.set_cached_property('Status',
+ new GLib.Variant('s', SNIStatus.PASSIVE));
+
+
+ this._accumulatedProperties = new Set();
+ this._cancellables = new Map();
+ this._changedProperties = Object.create(null);
+ }
+
+ async initAsync(cancellable) {
+ await super.initAsync(cancellable);
+
+ this._setupProxyPropertyList();
+ }
+
+ destroy() {
+ const cachedProperties = this.get_cached_property_names();
+ if (cachedProperties) {
+ cachedProperties.forEach(propertyName =>
+ this.set_cached_property(propertyName, null));
+ }
+
+ super.destroy();
+ }
+
+ _onNameOwnerChanged() {
+ this._resetNeededProperties();
+
+ if (!this.gNameOwner)
+ this._cancelRefreshProperties();
+ else
+ this._setupProxyPropertyList();
+ }
+
+ _setupProxyPropertyList() {
+ this._propertiesList =
+ (this.get_cached_property_names() || []).filter(p =>
+ this.gInterfaceInfo.properties.some(pInfo => pInfo.name === p));
+
+ if (this._propertiesList.length) {
+ AppIndicatorProxy.OPTIONAL_PROPERTIES.forEach(
+ p => this._addExtraProperty(p));
+ }
+ }
+
+ _addExtraProperty(name) {
+ if (this._propertiesList.includes(name))
+ return;
+
+ if (!(name in this)) {
+ Object.defineProperty(this, name, {
+ configurable: false,
+ enumerable: true,
+ get: () => {
+ const v = this.get_cached_property(name);
+ return v ? v.deep_unpack() : null;
+ },
+ });
+ }
+
+ this._propertiesList.push(name);
+ }
+
+ _signalToPropertyName(signal) {
+ if (signal.startsWith('New'))
+ return signal.substr(3);
+ else if (signal.startsWith('XAyatanaNew'))
+ return `XAyatana${signal.substr(11)}`;
+
+ return null;
+ }
+
+ // The Author of the spec didn't like the PropertiesChanged signal, so he invented his own
+ async _refreshOwnProperties(prop) {
+ await Promise.all(
+ [prop, `${prop}Name`, `${prop}Pixmap`, `${prop}AccessibleDesc`].filter(p =>
+ this._propertiesList.includes(p)).map(async p => {
+ try {
+ await this.refreshProperty(p, {
+ skipEqualityCheck: p.endsWith('Pixmap'),
+ });
+ } catch (e) {
+ if (!AppIndicatorProxy.OPTIONAL_PROPERTIES.includes(p) ||
+ !(e instanceof Gio.DBusError))
+ logError(e);
+ }
+ }));
+ }
+
+ _onSignal(sender, signal, ...args) {
+ this._onSignalAsync(sender, signal, ...args).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, `Error while processing signal '${signal}'`);
+ });
+ }
+
+ async _onSignalAsync(_sender, signal, params) {
+ const property = this._signalToPropertyName(signal);
+ if (!property)
+ return;
+
+ if (this.status === SNIStatus.PASSIVE &&
+ ![...AppIndicator.NEEDED_PROPERTIES, 'Status'].includes(property)) {
+ this._accumulatedProperties.add(property);
+ return;
+ }
+
+ const cancellable = this._cancellable;
+
+ if (!params.get_type().equal(AppIndicatorProxy.TUPLE_TYPE)) {
+ // If the property includes arguments, we can just queue the signal emission
+ const [value] = params.unpack();
+ try {
+ await this._queuePropertyUpdate(property, value);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ throw e;
+ }
+
+ if (!this._accumulatedProperties.size)
+ return;
+ } else {
+ this._accumulatedProperties.add(property);
+ }
+
+ if (this._signalsAccumulator)
+ return;
+
+ this._signalsAccumulator = new PromiseUtils.TimeoutPromise(
+ MAX_UPDATE_FREQUENCY, GLib.PRIORITY_DEFAULT_IDLE, cancellable);
+ try {
+ await this._signalsAccumulator;
+ const refreshPropertiesPromises =
+ [...this._accumulatedProperties].map(p =>
+ this._refreshOwnProperties(p));
+ this._accumulatedProperties.clear();
+ await Promise.all(refreshPropertiesPromises);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ throw e;
+ } finally {
+ delete this._signalsAccumulator;
+ }
+ }
+
+ _resetNeededProperties() {
+ AppIndicator.NEEDED_PROPERTIES.forEach(p =>
+ this.set_cached_property(p, null));
+ }
+
+ async refreshAllProperties() {
+ const cancellableName = 'org.freedesktop.DBus.Properties.GetAll';
+ const cancellable = this._cancelRefreshProperties({
+ propertyName: cancellableName,
+ addNew: true,
+ });
+
+ try {
+ const [valuesVariant] = (await this.getProperties(
+ cancellable)).deep_unpack();
+
+ this._cancellables.delete(cancellableName);
+
+ await Promise.all(
+ Object.entries(valuesVariant).map(([propertyName, valueVariant]) =>
+ this._queuePropertyUpdate(propertyName, valueVariant, {
+ skipEqualityCheck: true,
+ cancellable,
+ })));
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ // the property may not even exist, silently ignore it
+ Util.Logger.debug(`While refreshing all properties: ${e}`);
+
+ this.get_cached_property_names().forEach(propertyName =>
+ this.set_cached_property(propertyName, null));
+
+ this._cancellables.delete(cancellableName);
+ throw e;
+ }
+ }
+ }
+
+ async refreshProperty(propertyName, params) {
+ params = Params.parse(params, {
+ skipEqualityCheck: false,
+ });
+
+ const cancellable = this._cancelRefreshProperties({
+ propertyName,
+ addNew: true,
+ });
+
+ try {
+ const [valueVariant] = (await this.getProperty(
+ propertyName, cancellable)).deep_unpack();
+
+ this._cancellables.delete(propertyName);
+ await this._queuePropertyUpdate(propertyName, valueVariant,
+ Object.assign(params, {cancellable}));
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ // the property may not even exist, silently ignore it
+ Util.Logger.debug(`Error when calling 'Get(${propertyName})' ` +
+ `in ${this.gName}, ${this.gObjectPath}, ` +
+ `org.freedesktop.DBus.Properties, ${this.gInterfaceName} ` +
+ `while refreshing property ${propertyName}: ${e}\n` +
+ `${e.stack}`);
+ this.set_cached_property(propertyName, null);
+ this._cancellables.delete(propertyName);
+ delete this._changedProperties[propertyName];
+ throw e;
+ }
+ }
+ }
+
+ async _queuePropertyUpdate(propertyName, value, params) {
+ params = Params.parse(params, {
+ skipEqualityCheck: false,
+ cancellable: null,
+ });
+
+ if (!params.skipEqualityCheck) {
+ const cachedProperty = this.get_cached_property(propertyName);
+
+ if (value && cachedProperty &&
+ value.equal(this.get_cached_property(propertyName)))
+ return;
+ }
+
+ this.set_cached_property(propertyName, value);
+
+ // synthesize a batched property changed event
+ this._changedProperties[propertyName] = value;
+
+ if (!this._propertiesEmitTimeout || !this._propertiesEmitTimeout.pending()) {
+ if (!params.cancellable) {
+ params.cancellable = this._cancelRefreshProperties({
+ propertyName,
+ addNew: true,
+ });
+ }
+ this._propertiesEmitTimeout = new PromiseUtils.TimeoutPromise(
+ MAX_UPDATE_FREQUENCY * 2, GLib.PRIORITY_DEFAULT_IDLE, params.cancellable);
+ await this._propertiesEmitTimeout;
+
+ if (Object.keys(this._changedProperties).length) {
+ this.emit('g-properties-changed', GLib.Variant.new('a{sv}',
+ this._changedProperties), []);
+ this._changedProperties = Object.create(null);
+ }
+ }
+ }
+
+ _cancelRefreshProperties(params) {
+ params = Params.parse(params, {
+ propertyName: undefined,
+ addNew: false,
+ });
+
+ if (!this._cancellables.size && !params.addNew)
+ return null;
+
+ if (params.propertyName !== undefined) {
+ let cancellable = this._cancellables.get(params.propertyName);
+ if (cancellable) {
+ cancellable.cancel();
+
+ if (!params.addNew)
+ this._cancellables.delete(params.propertyName);
+ }
+
+ if (params.addNew) {
+ cancellable = new Util.CancellableChild(this._cancellable);
+ this._cancellables.set(params.propertyName, cancellable);
+ return cancellable;
+ }
+ } else {
+ this._cancellables.forEach(c => c.cancel());
+ this._cancellables.clear();
+ this._changedProperties = Object.create(null);
+ }
+
+ return null;
+ }
+});
+
+/**
+ * the AppIndicator class serves as a generic container for indicator information and functions common
+ * for every displaying implementation (IndicatorMessageSource and IndicatorStatusIcon)
+ */
+export class AppIndicator extends Signals.EventEmitter {
+ static get NEEDED_PROPERTIES() {
+ return ['Id', 'Menu'];
+ }
+
+ constructor(service, busName, object) {
+ super();
+
+ this.isReady = false;
+ this.busName = busName;
+ this._uniqueId = Util.indicatorId(service, busName, object);
+
+ this._cancellable = new Gio.Cancellable();
+ this._proxy = new AppIndicatorProxy(busName, object);
+ this._invalidatedPixmapsIcons = new Set();
+
+ this._setupProxy().catch(logError);
+ Util.connectSmart(this._proxy, 'g-properties-changed', this, this._onPropertiesChanged);
+ Util.connectSmart(this._proxy, 'notify::g-name-owner', this, this._nameOwnerChanged);
+
+ if (this.uniqueId === service) {
+ this._nameWatcher = new Util.NameWatcher(service);
+ Util.connectSmart(this._nameWatcher, 'changed', this, this._nameOwnerChanged);
+ }
+ }
+
+ async _setupProxy() {
+ const cancellable = this._cancellable;
+
+ try {
+ await this._proxy.initAsync(cancellable);
+ this._checkIfReady();
+ await this._checkNeededProperties();
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e, `While initalizing proxy for ${this._uniqueId}`);
+ this.destroy();
+ }
+ }
+
+ // We try to lookup the activate method to see if the app supports it
+ try {
+ const introspectionVariant = await this._proxy.gConnection.call(
+ this._proxy.gNameOwner, this._proxy.gObjectPath,
+ 'org.freedesktop.DBus.Introspectable', 'Introspect', null, null,
+ Gio.DBusCallFlags.NONE, -1, cancellable);
+ const [introspectionXml] = introspectionVariant.deep_unpack();
+ const nodeInfo = Gio.DBusNodeInfo.new_for_xml(introspectionXml);
+ const interfaceInfo = nodeInfo.lookup_interface(this._proxy.gInterfaceName);
+ this.supportsActivation = !!interfaceInfo.lookup_method('Activate');
+ this._hasAyatanaSecondaryActivate =
+ !!interfaceInfo.lookup_method('XAyatanaSecondaryActivate');
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ Util.Logger.debug(
+ `${this.uniqueId}, check for Activation support: ${e.message}`);
+ }
+ }
+
+ try {
+ this._commandLine = await DBusUtils.getProcessName(this.busName,
+ cancellable, GLib.PRIORITY_LOW);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ Util.Logger.debug(
+ `${this.uniqueId}, failed getting command line: ${e.message}`);
+ }
+ }
+ }
+
+ _checkIfReady() {
+ const wasReady = this.isReady;
+ let isReady = false;
+
+ if (this.hasNameOwner && this.id && this.menuPath)
+ isReady = true;
+
+ this.isReady = isReady;
+
+ if (this.isReady && !wasReady) {
+ if (this._delayCheck) {
+ this._delayCheck.cancel();
+ delete this._delayCheck;
+ }
+
+ this.emit('ready');
+ return true;
+ }
+
+ return false;
+ }
+
+ async _checkNeededProperties() {
+ if (this.id && this.menuPath)
+ return true;
+
+ const MAX_RETRIES = 3;
+ const cancellable = this._cancellable;
+ for (let checks = 0; checks < MAX_RETRIES; ++checks) {
+ this._delayCheck = new PromiseUtils.TimeoutSecondsPromise(1,
+ GLib.PRIORITY_DEFAULT_IDLE, cancellable);
+ // eslint-disable-next-line no-await-in-loop
+ await this._delayCheck;
+
+ try {
+ // eslint-disable-next-line no-await-in-loop
+ await Promise.all(AppIndicator.NEEDED_PROPERTIES.map(p =>
+ this._proxy.refreshProperty(p)));
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ throw e;
+
+ if (checks < MAX_RETRIES - 1)
+ continue;
+
+ throw e;
+ }
+
+ if (this.id && this.menuPath)
+ break;
+ }
+
+ return this.id && this.menuPath;
+ }
+
+ async _nameOwnerChanged() {
+ if (!this.hasNameOwner) {
+ this._checkIfReady();
+ } else {
+ try {
+ await this._checkNeededProperties();
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ Util.Logger.warn(`${this.uniqueId}, Impossible to get basic properties: ${e}`);
+ this.checkAlive();
+ }
+ }
+ }
+
+ this.emit('name-owner-changed');
+ }
+
+ // public property getters
+ get title() {
+ return this._proxy.Title;
+ }
+
+ get id() {
+ return this._proxy.Id;
+ }
+
+ get uniqueId() {
+ return this._uniqueId;
+ }
+
+ get status() {
+ return this._proxy.Status;
+ }
+
+ get label() {
+ return this._proxy.XAyatanaLabel || null;
+ }
+
+ get accessibleName() {
+ const accessibleDesc = this.status === SNIStatus.NEEDS_ATTENTION
+ ? this._proxy.AccessibleDesc : this._proxy.IconAccessibleDesc;
+
+ return accessibleDesc || this.title;
+ }
+
+ get menuPath() {
+ if (this._proxy.Menu === '/NO_DBUSMENU')
+ return null;
+
+ return this._proxy.Menu;
+ }
+
+ get attentionIcon() {
+ return {
+ theme: this._proxy.IconThemePath,
+ name: this._proxy.AttentionIconName,
+ pixmap: this._getPixmapProperty(SNIconType.ATTENTION),
+ };
+ }
+
+ get icon() {
+ return {
+ theme: this._proxy.IconThemePath,
+ name: this._proxy.IconName,
+ pixmap: this._getPixmapProperty(SNIconType.NORMAL),
+ };
+ }
+
+ get overlayIcon() {
+ return {
+ theme: this._proxy.IconThemePath,
+ name: this._proxy.OverlayIconName,
+ pixmap: this._getPixmapProperty(SNIconType.OVERLAY),
+ };
+ }
+
+ get hasOverlayIcon() {
+ const {name, pixmap} = this.overlayIcon;
+
+ return name || (pixmap && pixmap.n_children());
+ }
+
+ get hasNameOwner() {
+ if (this._nameWatcher && !this._nameWatcher.nameOnBus)
+ return false;
+ return !!this._proxy.g_name_owner;
+ }
+
+ get cancellable() {
+ return this._cancellable;
+ }
+
+ async checkAlive() {
+ // Some applications (hey electron!) just remove the indicator object
+ // from bus after hiding it, without closing its bus name, so we are
+ // not able to understand whe they're gone.
+ // Thus we just kill it when an expected well-known method is failing.
+ if (this.status !== SNIStatus.PASSIVE && this._checkIfReady()) {
+ if (this._checkAliveTimeout) {
+ this._checkAliveTimeout.cancel();
+ delete this._checkAliveTimeout;
+ }
+ return;
+ }
+
+ if (this._checkAliveTimeout)
+ return;
+
+ try {
+ const cancellable = this._cancellable;
+ this._checkAliveTimeout = new PromiseUtils.TimeoutSecondsPromise(10,
+ GLib.PRIORITY_DEFAULT_IDLE, cancellable);
+ Util.Logger.debug(`${this.uniqueId}: may not respond, checking...`);
+ await this._checkAliveTimeout;
+
+ // We should call the Ping method instead but in some containers
+ // such as snaps that's not accessible, so let's just use our own
+ await this._proxy.getProperty('Status', cancellable);
+ } catch (e) {
+ if (e.matches(Gio.DBusError, Gio.DBusError.NAME_HAS_NO_OWNER) ||
+ e.matches(Gio.DBusError, Gio.DBusError.SERVICE_UNKNOWN) ||
+ e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_OBJECT) ||
+ e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_INTERFACE) ||
+ e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD) ||
+ e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_PROPERTY)) {
+ Util.Logger.warn(`${this.uniqueId}: not on bus anymore, removing it`);
+ this.destroy();
+ return;
+ }
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ } finally {
+ delete this._checkAliveTimeout;
+ }
+ }
+
+ _onPropertiesChanged(_proxy, changed, _invalidated) {
+ const props = Object.keys(changed.unpack());
+ const signalsToEmit = new Set();
+ const checkIfReadyChanged = () => {
+ if (checkIfReadyChanged.value === undefined)
+ checkIfReadyChanged.value = this._checkIfReady();
+ return checkIfReadyChanged.value;
+ };
+
+ props.forEach(property => {
+ // some property changes require updates on our part,
+ // a few need to be passed down to the displaying code
+ if (property === 'Id')
+ checkIfReadyChanged();
+
+ // all these can mean that the icon has to be changed
+ if (property.startsWith('Icon') ||
+ property.startsWith('AttentionIcon'))
+ signalsToEmit.add('icon');
+
+ // same for overlays
+ if (property.startsWith('OverlayIcon'))
+ signalsToEmit.add('overlay-icon');
+
+ // this may make all of our icons invalid
+ if (property === 'IconThemePath') {
+ signalsToEmit.add('icon');
+ signalsToEmit.add('overlay-icon');
+ }
+
+ // the label will be handled elsewhere
+ if (property === 'XAyatanaLabel')
+ signalsToEmit.add('label');
+
+ if (property === 'Menu') {
+ if (!checkIfReadyChanged() && this.isReady)
+ signalsToEmit.add('menu');
+ }
+
+ if (property === 'IconAccessibleDesc' ||
+ property === 'AttentionAccessibleDesc' ||
+ property === 'Title')
+ signalsToEmit.add('accessible-name');
+
+ // status updates may cause the indicator to be hidden
+ if (property === 'Status') {
+ signalsToEmit.add('icon');
+ signalsToEmit.add('overlay-icon');
+ signalsToEmit.add('status');
+ signalsToEmit.add('accessible-name');
+ }
+ });
+
+ signalsToEmit.forEach(s => this.emit(s));
+ }
+
+ reset() {
+ this.emit('reset');
+ }
+
+ destroy() {
+ this.emit('destroy');
+
+ this.disconnectAll();
+ this._proxy.destroy();
+ this._cancellable.cancel();
+ this._invalidatedPixmapsIcons.clear();
+
+ if (this._nameWatcher)
+ this._nameWatcher.destroy();
+ delete this._cancellable;
+ delete this._proxy;
+ delete this._nameWatcher;
+ }
+
+ _getPixmapProperty(iconType) {
+ const propertyName = SNIconType.toPropertyName(iconType,
+ {isPixbuf: true});
+ const pixmap = this._proxy.get_cached_property(propertyName);
+ const wasInvalidated = this._invalidatedPixmapsIcons.delete(iconType);
+
+ if (!pixmap && wasInvalidated) {
+ this._proxy.refreshProperty(propertyName, {
+ skipEqualityCheck: true,
+ }).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ });
+ }
+
+ return pixmap;
+ }
+
+ invalidatePixmapProperty(iconType) {
+ this._invalidatedPixmapsIcons.add(iconType);
+ this._proxy.set_cached_property(
+ SNIconType.toPropertyName(iconType, {isPixbuf: true}), null);
+ }
+
+ _getActivationToken(timestamp) {
+ const launchContext = global.create_app_launch_context(timestamp, -1);
+ const fakeAppInfo = Gio.AppInfo.create_from_commandline(
+ this._commandLine || 'true', this.id,
+ Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
+ return [launchContext, launchContext.get_startup_notify_id(fakeAppInfo, [])];
+ }
+
+ async provideActivationToken(timestamp) {
+ if (this._hasProvideXdgActivationToken === false)
+ return;
+
+ const [launchContext, activationToken] = this._getActivationToken(timestamp);
+ try {
+ await this._proxy.ProvideXdgActivationTokenAsync(activationToken,
+ this._cancellable);
+ this._hasProvideXdgActivationToken = true;
+ } catch (e) {
+ launchContext.launch_failed(activationToken);
+
+ if (e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD))
+ this._hasProvideXdgActivationToken = false;
+ else
+ Util.Logger.warn(`${this.id}, failed to provide activation token: ${e.message}`);
+ }
+ }
+
+ async open(x, y, timestamp) {
+ const cancellable = this._cancellable;
+ // we can't use WindowID because we're not able to get the x11 window id from a MetaWindow
+ // nor can we call any X11 functions. Luckily, the Activate method usually works fine.
+ // parameters are "an hint to the item where to show eventual windows" [sic]
+ // ... and don't seem to have any effect.
+
+ try {
+ await this.provideActivationToken(timestamp);
+ await this._proxy.ActivateAsync(x, y, cancellable);
+ this.supportsActivation = true;
+ } catch (e) {
+ if (e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD)) {
+ this.supportsActivation = false;
+ Util.Logger.warn(`${this.id}, does not support activation: ${e.message}`);
+ return;
+ }
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ Util.Logger.critical(`${this.id}, failed to activate: ${e.message}`);
+ }
+ }
+
+ async secondaryActivate(timestamp, x, y) {
+ const cancellable = this._cancellable;
+
+ try {
+ await this.provideActivationToken(timestamp);
+
+ if (this._hasAyatanaSecondaryActivate !== false) {
+ try {
+ await this._proxy.XAyatanaSecondaryActivateAsync(timestamp, cancellable);
+ this._hasAyatanaSecondaryActivate = true;
+ } catch (e) {
+ if (e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD))
+ this._hasAyatanaSecondaryActivate = false;
+ else
+ throw e;
+ }
+ }
+
+ if (!this._hasAyatanaSecondaryActivate)
+ await this._proxy.SecondaryActivateAsync(x, y, cancellable);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ Util.Logger.critical(`${this.id}, failed to secondary activate: ${e.message}`);
+ }
+ }
+
+ async scroll(dx, dy) {
+ const cancellable = this._cancellable;
+
+ try {
+ const actions = [];
+
+ if (dx !== 0) {
+ actions.push(this._proxy.ScrollAsync(Math.floor(dx),
+ 'horizontal', cancellable));
+ }
+
+ if (dy !== 0) {
+ actions.push(this._proxy.ScrollAsync(Math.floor(dy),
+ 'vertical', cancellable));
+ }
+
+ await Promise.all(actions);
+ } catch (e) {
+ Util.Logger.critical(`${this.id}, failed to scroll: ${e.message}`);
+ }
+ }
+}
+
+const StTextureCacheSkippingFileIcon = GObject.registerClass({
+ Implements: [Gio.Icon],
+}, class StTextureCacheSkippingFileIconImpl extends Gio.EmblemedIcon {
+ _init(params) {
+ // FIXME: We can't just inherit from Gio.FileIcon for some reason
+ super._init({gicon: new Gio.FileIcon(params)});
+ }
+
+ vfunc_to_tokens() {
+ // Disables the to_tokens() vfunc so that the icon to_string()
+ // method won't work and thus can't be kept forever around by
+ // StTextureCache, see the awesome debugging session in this thread:
+ // https://twitter.com/mild_sunrise/status/1458739604098621443
+ // upstream bug is at:
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4944
+ return [false, [], 0];
+ }
+});
+
+export const IconActor = GObject.registerClass(
+class AppIndicatorsIconActor extends St.Icon {
+ static get DEFAULT_STYLE() {
+ const settings = SettingsManager.getDefaultGSettings();
+ if (!settings.get_boolean('compact-mode-enabled'))
+ return 'padding: 0';
+ else
+ return 'padding: 0; margin: 0';
+ }
+
+ static get USER_WRITABLE_PATHS() {
+ if (!this._userWritablePaths) {
+ this._userWritablePaths = [
+ GLib.get_user_cache_dir(),
+ GLib.get_user_data_dir(),
+ GLib.get_user_config_dir(),
+ GLib.get_user_runtime_dir(),
+ GLib.get_home_dir(),
+ GLib.get_tmp_dir(),
+ ];
+
+ this._userWritablePaths.push(Object.values(GLib.UserDirectory).slice(
+ 0, -1).map(dirId => GLib.get_user_special_dir(dirId)));
+ }
+
+ return this._userWritablePaths;
+ }
+
+ _init(indicator, iconSize) {
+ super._init({
+ reactive: true,
+ style_class: 'system-status-icon',
+ fallbackIconName: FALLBACK_ICON_NAME,
+ });
+
+ this.name = this.constructor.name;
+ this.add_style_class_name('appindicator-icon');
+ this.add_style_class_name('status-notifier-icon');
+ this.set_style(AppIndicatorsIconActor.DEFAULT_STYLE);
+
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+ this.height = iconSize * themeContext.scale_factor;
+
+ this._indicator = indicator;
+ this._customIcons = new Map();
+ this._iconSize = iconSize;
+ this._iconCache = new IconCache.IconCache();
+ this._cancellable = new Gio.Cancellable();
+ this._loadingIcons = Object.create(null);
+
+ Object.values(SNIconType).forEach(t => (this._loadingIcons[t] = new Map()));
+
+ Util.connectSmart(this._indicator, 'icon', this, () => {
+ if (this.is_mapped())
+ this._updateIcon();
+ });
+ Util.connectSmart(this._indicator, 'overlay-icon', this, () => {
+ if (this.is_mapped())
+ this._updateIcon();
+ });
+ Util.connectSmart(this._indicator, 'reset', this,
+ () => this._invalidateIconWhenFullyReady());
+
+ const settings = SettingsManager.getDefaultGSettings();
+ Util.connectSmart(settings, 'changed::icon-size', this, () =>
+ this._updateWhenFullyReady());
+ Util.connectSmart(settings, 'changed::custom-icons', this, () => {
+ this._updateCustomIcons();
+ this._invalidateIconWhenFullyReady();
+ });
+
+ if (GObject.signal_lookup('resource-scale-changed', this))
+ this.connect('resource-scale-changed', () => this._invalidateIcon());
+ else
+ this.connect('notify::resource-scale', () => this._invalidateIcon());
+
+ Util.connectSmart(themeContext, 'notify::scale-factor', this, tc => {
+ this._updateIconSize();
+ this.height = this._iconSize * tc.scale_factor;
+ this.width = -1;
+ this._invalidateIcon();
+ });
+
+ Util.connectSmart(Util.getDefaultTheme(), 'changed', this,
+ () => this._invalidateIconWhenFullyReady());
+
+ this.connect('notify::mapped', () => {
+ if (!this.is_mapped())
+ this._updateWhenFullyReady();
+ });
+
+ this._updateWhenFullyReady();
+
+ this.connect('destroy', () => {
+ this._iconCache.destroy();
+ this._cancellable.cancel();
+ this._cancellable = null;
+ this._indicator = null;
+ this._loadingIcons = null;
+ this._iconTheme = null;
+ });
+ }
+
+ get debugId() {
+ return this._indicator ? this._indicator.id : this.toString();
+ }
+
+ async _waitForFullyReady() {
+ const waitConditions = [];
+
+ if (!this.is_mapped()) {
+ waitConditions.push(new PromiseUtils.SignalConnectionPromise(
+ this, 'notify::mapped', this._cancellable));
+ }
+
+ if (!this._indicator.isReady) {
+ waitConditions.push(new PromiseUtils.SignalConnectionPromise(
+ this._indicator, 'ready', this._cancellable));
+ }
+
+ if (!waitConditions.length)
+ return true;
+
+ await Promise.all(waitConditions);
+ return this._waitForFullyReady();
+ }
+
+ async _updateWhenFullyReady() {
+ if (this._waitingReady)
+ return;
+
+ try {
+ this._waitingReady = true;
+ await this._waitForFullyReady();
+
+ this._updateIconSize();
+ this._updateIconClass();
+ this._updateCustomIcons();
+ this._invalidateIcon();
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ } finally {
+ delete this._waitingReady;
+ }
+ }
+
+ _updateIconClass() {
+ if (!this._indicator)
+ return;
+
+ this.add_style_class_name(
+ `appindicator-icon-${this._indicator.id.toLowerCase().replace(/_|\s/g, '-')}`);
+ }
+
+ _cancelLoadingByType(iconType) {
+ this._loadingIcons[iconType].forEach(c => c.cancel());
+ this._loadingIcons[iconType].clear();
+ }
+
+ _ensureNoIconIsLoading(iconType, id) {
+ if (this._loadingIcons[iconType].has(id)) {
+ Util.Logger.debug(`${this.debugId}, Icon ${id} Is still loading, ignoring the request`);
+ throw new GLib.Error(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING,
+ 'Already in progress');
+ } else if (this._loadingIcons[iconType].size > 0) {
+ throw new GLib.Error(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS,
+ 'Another icon is already loading');
+ }
+ }
+
+ _getIconLoadingCancellable(iconType, loadingId) {
+ try {
+ this._ensureNoIconIsLoading(iconType, loadingId);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
+ throw e;
+ this._cancelLoadingByType(iconType);
+ }
+
+ const cancellable = new Util.CancellableChild(this._cancellable);
+ this._loadingIcons[iconType].set(loadingId, cancellable);
+
+ return cancellable;
+ }
+
+ _cleanupIconLoadingCancellable(iconType, loadingId) {
+ if (this._loadingIcons)
+ this._loadingIcons[iconType].delete(loadingId);
+ }
+
+ _getResourceScale() {
+ // Remove this when we remove support for versions earlier than 3.38
+ const resourceScale = this.get_resource_scale();
+ if (Array.isArray(resourceScale))
+ return resourceScale[0] ? resourceScale[1] : 1.0;
+
+ return resourceScale;
+ }
+
+ // Will look the icon up in the cache, if it's found
+ // it will return it. Otherwise, it will create it and cache it.
+ async _cacheOrCreateIconByName(iconType, iconSize, iconScaling, iconName, themePath) {
+ const id = `${iconType}:${iconName}@${iconSize * iconScaling}:${themePath || ''}`;
+ let gicon = this._iconCache.get(id);
+
+ if (gicon)
+ return gicon;
+
+ const iconData = this._getIconData(iconName, themePath, iconSize, iconScaling);
+ const loadingId = iconData.file ? iconData.file.get_path() : id;
+
+ const cancellable = await this._getIconLoadingCancellable(iconType, id);
+ try {
+ gicon = await this._createIconByIconData(iconData, iconSize,
+ iconScaling, cancellable);
+ } finally {
+ this._cleanupIconLoadingCancellable(iconType, loadingId);
+ }
+ if (gicon)
+ gicon = this._iconCache.add(id, gicon);
+ return gicon;
+ }
+
+ _getIconLookupFlags(themeNode) {
+ let lookupFlags = 0;
+
+ if (!themeNode)
+ return lookupFlags;
+
+ const lookupFlagsEnum = St.IconLookupFlags;
+ const iconStyle = themeNode.get_icon_style();
+ if (iconStyle === St.IconStyle.REGULAR)
+ lookupFlags |= lookupFlagsEnum.FORCE_REGULAR;
+ else if (iconStyle === St.IconStyle.SYMBOLIC)
+ lookupFlags |= lookupFlagsEnum.FORCE_SYMBOLIC;
+
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ lookupFlags |= lookupFlagsEnum.DIR_RTL;
+ else
+ lookupFlags |= lookupFlagsEnum.DIR_LTR;
+
+ return lookupFlags;
+ }
+
+ async _createIconByIconData(iconData, iconSize, iconScaling, cancellable) {
+ const {file, name} = iconData;
+
+ if (!file && !name) {
+ if (this._createIconIdle) {
+ throw new GLib.Error(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING,
+ 'Already in progress');
+ }
+
+ try {
+ this._createIconIdle = new PromiseUtils.IdlePromise(GLib.PRIORITY_DEFAULT_IDLE,
+ cancellable);
+ await this._createIconIdle;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ throw e;
+ } finally {
+ delete this._createIconIdle;
+ }
+ return this.gicon;
+ } else if (this._createIconIdle) {
+ this._createIconIdle.cancel();
+ delete this._createIconIdle;
+ }
+
+ if (name)
+ return new Gio.ThemedIcon({name});
+
+ if (!file)
+ throw new Error('Neither file or name are set');
+
+ if (!this._isFileInWritableArea(file))
+ return new Gio.FileIcon({file});
+
+ try {
+ const [format, width, height] = await GdkPixbuf.Pixbuf.get_file_info_async(
+ file.get_path(), cancellable);
+
+ if (!format) {
+ Util.Logger.critical(`${this.debugId}, Invalid image format: ${file.get_path()}`);
+ return null;
+ }
+
+ if (width >= height * 1.5) {
+ /* Hello indicator-multiload! */
+ await this._loadCustomImage(file,
+ width, height, iconSize, iconScaling, cancellable);
+ return null;
+ } else {
+ /* We'll wrap the icon so that it won't be cached forever by the shell */
+ return new StTextureCacheSkippingFileIcon({file});
+ }
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ Util.Logger.warn(
+ `${this.debugId}, Impossible to read image info from ` +
+ `path '${file ? file.get_path() : null}' or name '${name}': ${e}`);
+ }
+ throw e;
+ }
+ }
+
+ async _loadCustomImage(file, width, height, iconSize, iconScaling, cancellable) {
+ const textureCache = St.TextureCache.get_default();
+ const customImage = textureCache.load_file_async(file, -1,
+ height, 1, iconScaling);
+
+ const setCustomImageActor = imageActor => {
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const {content} = imageActor;
+ imageActor.content = null;
+ imageActor.destroy();
+
+ this._setImageContent(content,
+ width * scaleFactor, height * scaleFactor);
+ };
+
+ if (customImage.content) {
+ setCustomImageActor(customImage);
+ return;
+ }
+
+ const imageContentPromise = new PromiseUtils.SignalConnectionPromise(
+ customImage, 'notify::content', cancellable);
+ const waitPromise = new PromiseUtils.TimeoutSecondsPromise(
+ 1, GLib.PRIORITY_DEFAULT, cancellable);
+
+ const racingPromises = [imageContentPromise, waitPromise];
+
+ try {
+ await Promise.race(racingPromises);
+ if (!waitPromise.resolved())
+ setCustomImageActor(customImage);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ throw e;
+ } finally {
+ racingPromises.forEach(p => p.cancel());
+ }
+ }
+
+ _isFileInWritableArea(file) {
+ // No need to use IO here, we can just do some assumptions
+ // print('Writable paths', IconActor.USER_WRITABLE_PATHS)
+ const path = file.get_path();
+ return IconActor.USER_WRITABLE_PATHS.some(writablePath =>
+ path.startsWith(writablePath));
+ }
+
+ _createIconTheme(searchPath = []) {
+ const iconTheme = new St.IconTheme();
+ iconTheme.set_search_path(searchPath);
+
+ return iconTheme;
+ }
+
+ _getIconData(name, themePath, size, scale) {
+ const emptyIconData = {iconInfo: null, file: null, name: null};
+
+ if (!name) {
+ delete this._iconTheme;
+ return emptyIconData;
+ }
+
+ // HACK: icon is a path name. This is not specified by the API,
+ // but at least indicator-sensors uses it.
+ if (name[0] === '/') {
+ delete this._iconTheme;
+
+ const file = Gio.File.new_for_path(name);
+ return {file, iconInfo: null, name: null};
+ }
+
+ if (name.includes('.')) {
+ const splits = name.split('.');
+
+ if (['svg', 'png'].includes(splits[splits.length - 1]))
+ name = splits.slice(0, -1).join('');
+ }
+
+ if (themePath && Util.getDefaultTheme().get_search_path().includes(themePath))
+ themePath = null;
+
+ if (themePath) {
+ // If a theme path is provided, we need to lookup the icon ourself
+ // as St won't be able to do it unless we mess with default theme
+ // that is something we prefer not to do, as it would imply lots of
+ // St.TextureCache cleanups.
+
+ const newSearchPath = [themePath];
+ if (!this._iconTheme) {
+ this._iconTheme = this._createIconTheme(newSearchPath);
+ } else {
+ const currentSearchPath = this._iconTheme.get_search_path();
+
+ if (!currentSearchPath.includes(newSearchPath))
+ this._iconTheme.set_search_path(newSearchPath);
+ }
+
+ // try to look up the icon in the icon theme
+ const iconInfo = this._iconTheme.lookup_icon_for_scale(`${name}`,
+ size, scale, this._getIconLookupFlags(this.get_theme_node()) |
+ St.IconLookupFlags.GENERIC_FALLBACK);
+
+ if (iconInfo) {
+ return {
+ iconInfo,
+ file: Gio.File.new_for_path(iconInfo.get_filename()),
+ name: null,
+ };
+ }
+
+ const logger = this.gicon ? Util.Logger.debug : Util.Logger.warn;
+ logger(`${this.debugId}, Impossible to lookup icon ` +
+ `for '${name}' in ${themePath}`);
+
+ return emptyIconData;
+ }
+
+ delete this._iconTheme;
+ return {name, iconInfo: null, file: null};
+ }
+
+ _setImageContent(content, width, height) {
+ this.set({
+ content,
+ width,
+ height,
+ contentGravity: Clutter.ContentGravity.RESIZE_ASPECT,
+ fallbackIconName: null,
+ });
+ }
+
+ async _createIconFromPixmap(iconType, iconSize, iconScaling, scaleFactor, pixmapsVariant) {
+ const {pixmapVariant, width, height, rowStride} =
+ PixmapsUtils.getBestPixmap(pixmapsVariant, iconSize * iconScaling);
+
+ const id = `__PIXMAP_ICON_${width}x${height}`;
+
+ const imageContent = new St.ImageContent({
+ preferredWidth: width,
+ preferredHeight: height,
+ });
+
+ // Remove this dynamic check when we depend on GNOME 48.
+ const coglContext = [];
+ const mutterBackend = global.stage?.context?.get_backend?.();
+ if (imageContent.set_bytes.length === 6 && mutterBackend?.get_cogl_context)
+ coglContext.push(mutterBackend.get_cogl_context());
+ imageContent.set_bytes(...coglContext, pixmapVariant.get_data_as_bytes(),
+ PIXMAPS_FORMAT, width, height, rowStride);
+
+ if (iconType !== SNIconType.OVERLAY && !this._indicator.hasOverlayIcon) {
+ const scaledSize = iconSize * scaleFactor;
+ this._setImageContent(imageContent, scaledSize, scaledSize);
+ return null;
+ }
+
+ const cancellable = this._getIconLoadingCancellable(iconType, id);
+ try {
+ // FIXME: async API results in a gray icon for some reason
+ const [inputStream] = imageContent.load(iconSize, cancellable);
+ return await GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(
+ inputStream, -1, iconSize * iconScaling, true, cancellable);
+ } catch (e) {
+ // the image data was probably bogus. We don't really know why, but it _does_ happen.
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ Util.Logger.warn(`${this.debugId}, Impossible to create image from data: ${e}`);
+ throw e;
+ } finally {
+ this._cleanupIconLoadingCancellable(iconType, id);
+ }
+ }
+
+ // The icon cache Active flag will be set to true if the used gicon matches
+ // the cached one (as in some cases it may be equal, but not the same object).
+ // So when it's not need anymore we make sure to check the active state
+ // and set it to false so that it can be picked up by the garbage collector.
+ _setGicon(iconType, gicon) {
+ if (iconType !== SNIconType.OVERLAY) {
+ if (gicon) {
+ if (this.gicon === gicon ||
+ (this.gicon && this.gicon.get_icon() === gicon))
+ return;
+
+ if (gicon instanceof Gio.EmblemedIcon)
+ this.gicon = gicon;
+ else
+ this.gicon = new Gio.EmblemedIcon({gicon});
+
+ this._iconCache.updateActive(SNIconType.NORMAL, gicon,
+ this.gicon.get_icon() === gicon);
+ } else {
+ this.gicon = null;
+ }
+ } else if (gicon) {
+ this._emblem = new Gio.Emblem({icon: gicon});
+ this._iconCache.updateActive(iconType, gicon, true);
+ } else {
+ this._emblem = null;
+ }
+
+ if (this.gicon) {
+ if (!this.gicon.get_emblems().some(e => e.equal(this._emblem))) {
+ this.gicon.clear_emblems();
+ if (this._emblem)
+ this.gicon.add_emblem(this._emblem);
+ }
+ }
+ }
+
+ async _updateIconByType(iconType, iconSize) {
+ let icon;
+ switch (iconType) {
+ case SNIconType.ATTENTION:
+ icon = this._indicator.attentionIcon;
+ break;
+ case SNIconType.NORMAL:
+ ({icon} = this._indicator);
+ break;
+ case SNIconType.OVERLAY:
+ icon = this._indicator.overlayIcon;
+ break;
+ }
+
+ const {theme, name, pixmap} = icon;
+ const commonArgs = [theme, iconType, iconSize];
+
+ if (this._customIcons.size) {
+ let customIcon = this._customIcons.get(iconType);
+ if (!await this._createAndSetIcon(customIcon, null, ...commonArgs)) {
+ if (iconType !== SNIconType.OVERLAY) {
+ customIcon = this._customIcons.get(SNIconType.NORMAL);
+ await this._createAndSetIcon(customIcon, null, ...commonArgs);
+ }
+ }
+ } else {
+ await this._createAndSetIcon(name, pixmap, ...commonArgs);
+ }
+ }
+
+ async _createAndSetIcon(name, pixmap, theme, iconType, iconSize) {
+ let gicon = null;
+
+ try {
+ gicon = await this._createIcon(name, pixmap, theme, iconType, iconSize);
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED) ||
+ e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING))
+ return null;
+
+ if (iconType === SNIconType.OVERLAY) {
+ logError(e, `${this.debugId} unable to update icon emblem`);
+ } else {
+ this.fallbackIconName = FALLBACK_ICON_NAME;
+ logError(e, `${this.debugId} unable to update icon`);
+ }
+ }
+
+ try {
+ this._setGicon(iconType, gicon);
+
+ if (pixmap && this.gicon) {
+ // The pixmap has been saved, we can free the variants memory
+ this._indicator.invalidatePixmapProperty(iconType);
+ }
+
+ return gicon;
+ } catch (e) {
+ logError(e, 'Setting GIcon failed');
+ return null;
+ }
+ }
+
+ // updates the base icon
+ async _createIcon(name, pixmap, theme, iconType, iconSize) {
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const resourceScale = this._getResourceScale();
+ const iconScaling = Math.ceil(resourceScale * scaleFactor);
+
+ // From now on we consider them the same thing, as one replaces the other
+ if (iconType === SNIconType.ATTENTION)
+ iconType = SNIconType.NORMAL;
+
+ if (name) {
+ const gicon = await this._cacheOrCreateIconByName(
+ iconType, iconSize, iconScaling, name, theme);
+ if (gicon)
+ return gicon;
+ }
+
+ if (pixmap && pixmap.n_children()) {
+ return this._createIconFromPixmap(iconType,
+ iconSize, iconScaling, scaleFactor, pixmap);
+ }
+
+ return null;
+ }
+
+ // updates the base icon
+ async _updateIcon() {
+ if (this._indicator.status === SNIStatus.PASSIVE)
+ return;
+
+ if (this.gicon instanceof Gio.EmblemedIcon) {
+ const {gicon} = this.gicon;
+ this._iconCache.updateActive(SNIconType.NORMAL, gicon, false);
+ }
+
+ // we might need to use the AttentionIcon*, which have precedence over the normal icons
+ const iconType = this._indicator.status === SNIStatus.NEEDS_ATTENTION
+ ? SNIconType.ATTENTION : SNIconType.NORMAL;
+
+ try {
+ await this._updateIconByType(iconType, this._iconSize);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED) &&
+ !e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING))
+ logError(e, `${this.debugId}: Updating icon type ${iconType} failed`);
+ }
+ }
+
+ async _updateOverlayIcon() {
+ if (this._indicator.status === SNIStatus.PASSIVE)
+ return;
+
+ if (this._emblem) {
+ const {icon} = this._emblem;
+ this._iconCache.updateActive(SNIconType.OVERLAY, icon, false);
+ }
+
+ // KDE hardcodes the overlay icon size to 10px (normal icon size 16px)
+ // we approximate that ratio for other sizes, too.
+ // our algorithms will always pick a smaller one instead of stretching it.
+ const iconSize = Math.floor(this._iconSize / 1.6);
+
+ try {
+ await this._updateIconByType(SNIconType.OVERLAY, iconSize);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED) &&
+ !e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.PENDING))
+ logError(e, `${this.debugId}: Updating overlay icon failed`);
+ }
+ }
+
+ async _invalidateIconWhenFullyReady() {
+ if (this._waitingInvalidation)
+ return;
+
+ try {
+ this._waitingInvalidation = true;
+ await this._waitForFullyReady();
+ this._invalidateIcon();
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ } finally {
+ delete this._waitingInvalidation;
+ }
+ }
+
+ // called when the icon theme changes
+ _invalidateIcon() {
+ this._iconCache.clear();
+ this._cancellable.cancel();
+ this._cancellable = new Gio.Cancellable();
+ Object.values(SNIconType).forEach(iconType =>
+ this._loadingIcons[iconType].clear());
+
+ this._updateIcon().catch(e => logError(e));
+ this._updateOverlayIcon().catch(e => logError(e));
+ }
+
+ _updateIconSize() {
+ const settings = SettingsManager.getDefaultGSettings();
+ const sizeValue = settings.get_int('icon-size');
+
+ if (sizeValue > 0) {
+ if (!this._defaultIconSize)
+ this._defaultIconSize = this._iconSize;
+
+ this._iconSize = sizeValue;
+ } else if (this._defaultIconSize) {
+ this._iconSize = this._defaultIconSize;
+ delete this._defaultIconSize;
+ }
+
+ const themeIconSize = Math.round(
+ this.get_theme_node().get_length('icon-size'));
+ let iconStyle = AppIndicatorsIconActor.DEFAULT_STYLE;
+
+ if (themeIconSize > 0) {
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+
+ if (themeIconSize / scaleFactor !== this._iconSize) {
+ iconStyle = `${AppIndicatorsIconActor.DEFAULT_STYLE};` +
+ 'icon-size: 0';
+ }
+ }
+
+ this.set_style(iconStyle);
+ this.set_icon_size(this._iconSize);
+ }
+
+ _updateCustomIcons() {
+ const settings = SettingsManager.getDefaultGSettings();
+ this._customIcons.clear();
+
+ settings.get_value('custom-icons').deep_unpack().forEach(customIcons => {
+ const [indicatorId, normalIcon, attentionIcon] = customIcons;
+ if (this._indicator.id === indicatorId) {
+ this._customIcons.set(SNIconType.NORMAL, normalIcon);
+ this._customIcons.set(SNIconType.ATTENTION, attentionIcon);
+ }
+ });
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusMenu.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusMenu.js
new file mode 100644
index 0000000..52c812d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusMenu.js
@@ -0,0 +1,990 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import Clutter from 'gi://Clutter';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import GdkPixbuf from 'gi://GdkPixbuf';
+import Gio from 'gi://Gio';
+import St from 'gi://St';
+
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
+
+import * as DBusInterfaces from './interfaces.js';
+import * as PromiseUtils from './promiseUtils.js';
+import * as Util from './util.js';
+import {DBusProxy} from './dbusProxy.js';
+
+Gio._promisify(GdkPixbuf.Pixbuf, 'new_from_stream_async');
+
+// ////////////////////////////////////////////////////////////////////////
+// PART ONE: "ViewModel" backend implementation.
+// Both code and design are inspired by libdbusmenu
+// ////////////////////////////////////////////////////////////////////////
+
+/**
+ * Saves menu property values and handles type checking and defaults
+ */
+export class PropertyStore {
+ constructor(initialProperties) {
+ this._props = new Map();
+
+ if (initialProperties) {
+ for (const [prop, value] of Object.entries(initialProperties))
+ this.set(prop, value);
+ }
+ }
+
+ set(name, value) {
+ if (name in PropertyStore.MandatedTypes && value &&
+ !value.is_of_type(PropertyStore.MandatedTypes[name]))
+ Util.Logger.warn(`Cannot set property ${name}: type mismatch!`);
+ else if (value)
+ this._props.set(name, value);
+ else
+ this._props.delete(name);
+ }
+
+ get(name) {
+ const prop = this._props.get(name);
+ if (prop)
+ return prop;
+ else if (name in PropertyStore.DefaultValues)
+ return PropertyStore.DefaultValues[name];
+ else
+ return null;
+ }
+}
+
+// we list all the properties we know and use here, so we won' have to deal with unexpected type mismatches
+PropertyStore.MandatedTypes = {
+ 'visible': GLib.VariantType.new('b'),
+ 'enabled': GLib.VariantType.new('b'),
+ 'label': GLib.VariantType.new('s'),
+ 'type': GLib.VariantType.new('s'),
+ 'children-display': GLib.VariantType.new('s'),
+ 'icon-name': GLib.VariantType.new('s'),
+ 'icon-data': GLib.VariantType.new('ay'),
+ 'toggle-type': GLib.VariantType.new('s'),
+ 'toggle-state': GLib.VariantType.new('i'),
+};
+
+PropertyStore.DefaultValues = {
+ 'visible': GLib.Variant.new_boolean(true),
+ 'enabled': GLib.Variant.new_boolean(true),
+ 'label': GLib.Variant.new_string(''),
+ 'type': GLib.Variant.new_string('standard'),
+ // elements not in here must return null
+};
+
+/**
+ * Represents a single menu item
+ */
+export class DbusMenuItem extends Signals.EventEmitter {
+ // will steal the properties object
+ constructor(client, id, properties, childrenIds) {
+ super();
+
+ this._client = client;
+ this._id = id;
+ this._propStore = new PropertyStore(properties);
+ this._children_ids = childrenIds;
+ }
+
+ propertyGet(propName) {
+ const prop = this.propertyGetVariant(propName);
+ return prop ? prop.get_string()[0] : null;
+ }
+
+ propertyGetVariant(propName) {
+ return this._propStore.get(propName);
+ }
+
+ propertyGetBool(propName) {
+ const prop = this.propertyGetVariant(propName);
+ return prop ? prop.get_boolean() : false;
+ }
+
+ propertyGetInt(propName) {
+ const prop = this.propertyGetVariant(propName);
+ return prop ? prop.get_int32() : 0;
+ }
+
+ propertySet(prop, value) {
+ this._propStore.set(prop, value);
+
+ this.emit('property-changed', prop, this.propertyGetVariant(prop));
+ }
+
+ resetProperties() {
+ Object.entries(PropertyStore.DefaultValues).forEach(([prop, value]) =>
+ this.propertySet(prop, value));
+ }
+
+ getChildrenIds() {
+ return this._children_ids.concat(); // clone it!
+ }
+
+ addChild(pos, childId) {
+ this._children_ids.splice(pos, 0, childId);
+ this.emit('child-added', this._client.getItem(childId), pos);
+ }
+
+ removeChild(childId) {
+ // find it
+ let pos = -1;
+ for (let i = 0; i < this._children_ids.length; ++i) {
+ if (this._children_ids[i] === childId) {
+ pos = i;
+ break;
+ }
+ }
+
+ if (pos < 0) {
+ Util.Logger.critical("Trying to remove child which doesn't exist");
+ } else {
+ this._children_ids.splice(pos, 1);
+ this.emit('child-removed', this._client.getItem(childId));
+ }
+ }
+
+ moveChild(childId, newPos) {
+ // find the old position
+ let oldPos = -1;
+ for (let i = 0; i < this._children_ids.length; ++i) {
+ if (this._children_ids[i] === childId) {
+ oldPos = i;
+ break;
+ }
+ }
+
+ if (oldPos < 0) {
+ Util.Logger.critical("tried to move child which wasn't in the list");
+ return;
+ }
+
+ if (oldPos !== newPos) {
+ this._children_ids.splice(oldPos, 1);
+ this._children_ids.splice(newPos, 0, childId);
+ this.emit('child-moved', oldPos, newPos, this._client.getItem(childId));
+ }
+ }
+
+ getChildren() {
+ return this._children_ids.map(el => this._client.getItem(el));
+ }
+
+ hasChildren() {
+ return this._children_ids.length > 0;
+ }
+
+ handleEvent(event, data, timestamp) {
+ if (!data)
+ data = GLib.Variant.new_int32(0);
+
+ return this._client.sendEvent(this._id, event, data, timestamp);
+ }
+
+ getId() {
+ return this._id;
+ }
+
+ sendAboutToShow() {
+ this._client.sendAboutToShow(this._id);
+ }
+}
+
+
+/**
+ * The client does the heavy lifting of actually reading layouts and distributing events
+ */
+
+export const DBusClient = GObject.registerClass({
+ Signals: {'ready-changed': {}},
+}, class AppIndicatorsDBusClient extends DBusProxy {
+ static get interfaceInfo() {
+ if (!this._interfaceInfo) {
+ this._interfaceInfo = Gio.DBusInterfaceInfo.new_for_xml(
+ DBusInterfaces.DBusMenu);
+ }
+ return this._interfaceInfo;
+ }
+
+ static get baseItems() {
+ if (!this._baseItems) {
+ this._baseItems = {
+ 'children-display': GLib.Variant.new_string('submenu'),
+ };
+ }
+ return this._baseItems;
+ }
+
+ static destroy() {
+ delete this._interfaceInfo;
+ }
+
+ _init(busName, objectPath) {
+ const {interfaceInfo} = AppIndicatorsDBusClient;
+
+ super._init(busName, objectPath, interfaceInfo,
+ Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
+
+ this._items = new Map();
+ this._items.set(0, new DbusMenuItem(this, 0, DBusClient.baseItems, []));
+ this._flagItemsUpdateRequired = false;
+
+ // will be set to true if a layout update is needed once active
+ this._flagLayoutUpdateRequired = false;
+
+ // property requests are queued
+ this._propertiesRequestedFor = new Set(/* ids */);
+
+ this._layoutUpdated = false;
+ this._active = false;
+ }
+
+ async initAsync(cancellable) {
+ await super.initAsync(cancellable);
+
+ this._requestLayoutUpdate();
+ }
+
+ _onNameOwnerChanged() {
+ if (this.isReady)
+ this._requestLayoutUpdate();
+ }
+
+ get isReady() {
+ return this._layoutUpdated && !!this.gNameOwner;
+ }
+
+ get cancellable() {
+ return this._cancellable;
+ }
+
+ getRoot() {
+ return this._items.get(0);
+ }
+
+ _requestLayoutUpdate() {
+ const cancellable = new Util.CancellableChild(this._cancellable);
+ this._beginLayoutUpdate(cancellable);
+ }
+
+ async _requestProperties(propertyId, cancellable) {
+ this._propertiesRequestedFor.add(propertyId);
+
+ if (this._propertiesRequest && this._propertiesRequest.pending())
+ return;
+
+ // if we don't have any requests queued, we'll need to add one
+ this._propertiesRequest = new PromiseUtils.IdlePromise(
+ GLib.PRIORITY_DEFAULT_IDLE, cancellable);
+ await this._propertiesRequest;
+
+ const requestedProperties = Array.from(this._propertiesRequestedFor);
+ this._propertiesRequestedFor.clear();
+ const [result] = await this.GetGroupPropertiesAsync(requestedProperties,
+ [], cancellable);
+
+ result.forEach(([id, properties]) => {
+ const item = this._items.get(id);
+ if (!item)
+ return;
+
+ item.resetProperties();
+
+ for (const [prop, value] of Object.entries(properties))
+ item.propertySet(prop, value);
+ });
+ }
+
+ // Traverses the list of cached menu items and removes everyone that is not in the list
+ // so we don't keep alive unused items
+ _gcItems() {
+ const tag = new Date().getTime();
+
+ const toTraverse = [0];
+ while (toTraverse.length > 0) {
+ const item = this.getItem(toTraverse.shift());
+ item._dbusClientGcTag = tag;
+ Array.prototype.push.apply(toTraverse, item.getChildrenIds());
+ }
+
+ this._items.forEach((i, id) => {
+ if (i._dbusClientGcTag !== tag)
+ this._items.delete(id);
+ });
+ }
+
+ // the original implementation will only request partial layouts if somehow possible
+ // we try to save us from multiple kinds of race conditions by always requesting a full layout
+ _beginLayoutUpdate(cancellable) {
+ this._layoutUpdateUpdateAsync(cancellable).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ });
+ }
+
+ // the original implementation will only request partial layouts if somehow possible
+ // we try to save us from multiple kinds of race conditions by always requesting a full layout
+ async _layoutUpdateUpdateAsync(cancellable) {
+ // we only read the type property, because if the type changes after reading all properties,
+ // the view would have to replace the item completely which we try to avoid
+ if (this._layoutUpdateCancellable)
+ this._layoutUpdateCancellable.cancel();
+
+ this._layoutUpdateCancellable = cancellable;
+
+ try {
+ const [revision_, root] = await this.GetLayoutAsync(0, -1,
+ ['type', 'children-display'], cancellable);
+
+ this._updateLayoutState(true);
+ this._doLayoutUpdate(root, cancellable);
+ this._gcItems();
+ this._flagLayoutUpdateRequired = false;
+ this._flagItemsUpdateRequired = false;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ this._updateLayoutState(false);
+ throw e;
+ } finally {
+ if (this._layoutUpdateCancellable === cancellable)
+ this._layoutUpdateCancellable = null;
+ }
+ }
+
+ _updateLayoutState(state) {
+ const wasReady = this.isReady;
+ this._layoutUpdated = state;
+ if (this.isReady !== wasReady)
+ this.emit('ready-changed');
+ }
+
+ _doLayoutUpdate(item, cancellable) {
+ const [id, properties, children] = item;
+
+ const childrenUnpacked = children.map(c => c.deep_unpack());
+ const childrenIds = childrenUnpacked.map(([c]) => c);
+
+ // make sure all our children exist
+ childrenUnpacked.forEach(c => this._doLayoutUpdate(c, cancellable));
+
+ // make sure we exist
+ const menuItem = this._items.get(id);
+
+ if (menuItem) {
+ // we do, update our properties if necessary
+ for (const [prop, value] of Object.entries(properties))
+ menuItem.propertySet(prop, value);
+
+ // make sure our children are all at the right place, and exist
+ const oldChildrenIds = menuItem.getChildrenIds();
+ for (let i = 0; i < childrenIds.length; ++i) {
+ // try to recycle an old child
+ let oldChild = -1;
+ for (let j = 0; j < oldChildrenIds.length; ++j) {
+ if (oldChildrenIds[j] === childrenIds[i]) {
+ [oldChild] = oldChildrenIds.splice(j, 1);
+ break;
+ }
+ }
+
+ if (oldChild < 0) {
+ // no old child found, so create a new one!
+ menuItem.addChild(i, childrenIds[i]);
+ } else {
+ // old child found, reuse it!
+ menuItem.moveChild(childrenIds[i], i);
+ }
+ }
+
+ // remove any old children that weren't reused
+ oldChildrenIds.forEach(c => menuItem.removeChild(c));
+
+ if (!this._flagItemsUpdateRequired)
+ return id;
+ }
+
+ // we don't, so let's create us
+ let newMenuItem = menuItem;
+
+ if (!newMenuItem) {
+ newMenuItem = new DbusMenuItem(this, id, properties, childrenIds);
+ this._items.set(id, newMenuItem);
+ }
+
+ this._requestProperties(id, cancellable).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ Util.Logger.warn(`Could not get menu properties menu proxy: ${e}`);
+ });
+
+ return id;
+ }
+
+ async _doPropertiesUpdateAsync(cancellable) {
+ if (this._propertiesUpdateCancellable)
+ this._propertiesUpdateCancellable.cancel();
+
+ this._propertiesUpdateCancellable = cancellable;
+
+ try {
+ const requests = [];
+
+ this._items.forEach((_, id) =>
+ requests.push(this._requestProperties(id, cancellable)));
+
+ await Promise.all(requests);
+ } finally {
+ if (this._propertiesUpdateCancellable === cancellable)
+ this._propertiesUpdateCancellable = null;
+ }
+ }
+
+ _doPropertiesUpdate() {
+ const cancellable = new Util.CancellableChild(this._cancellable);
+ this._doPropertiesUpdateAsync(cancellable).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ Util.Logger.warn(`Could not get menu properties menu proxy: ${e}`);
+ });
+ }
+
+
+ set active(active) {
+ const wasActive = this._active;
+ this._active = active;
+
+ if (active && wasActive !== active) {
+ if (this._flagLayoutUpdateRequired) {
+ this._requestLayoutUpdate();
+ } else if (this._flagItemsUpdateRequired) {
+ this._doPropertiesUpdate();
+ this._flagItemsUpdateRequired = false;
+ }
+ }
+ }
+
+ _onSignal(_sender, signal, params) {
+ if (signal === 'LayoutUpdated') {
+ if (!this._active && this.getRoot()?.hasChildren()) {
+ this._flagLayoutUpdateRequired = true;
+ return;
+ }
+
+ this._requestLayoutUpdate();
+ } else if (signal === 'ItemsPropertiesUpdated') {
+ if (!this._active && this.getRoot()?.hasChildren()) {
+ this._flagItemsUpdateRequired = true;
+ return;
+ }
+
+ this._onPropertiesUpdated(params.deep_unpack());
+ }
+ }
+
+ getItem(id) {
+ const item = this._items.get(id);
+ if (!item)
+ Util.Logger.warn(`trying to retrieve item for non-existing id ${id} !?`);
+ return item || null;
+ }
+
+ // we don't need to cache and burst-send that since it will not happen that frequently
+ async sendAboutToShow(id) {
+ if (this._hasAboutToShow === false)
+ return;
+
+ /* Some indicators (you, dropbox!) don't use the right signature
+ * and don't return a boolean, so we need to support both cases */
+ try {
+ const ret = await this.gConnection.call(this.gName, this.gObjectPath,
+ this.gInterfaceName, 'AboutToShow', new GLib.Variant('(i)', [id]),
+ null, Gio.DBusCallFlags.NONE, -1, this._cancellable);
+
+ if ((ret.is_of_type(new GLib.VariantType('(b)')) &&
+ ret.get_child_value(0).get_boolean()) ||
+ ret.is_of_type(new GLib.VariantType('()')))
+ this._requestLayoutUpdate();
+ } catch (e) {
+ Util.Logger.debug('Error when calling \'AboutToShow()\' in ' +
+ `${this.gName}, ${this.gObjectPath}, ${this.gInterfaceName}`);
+ if (e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_METHOD) ||
+ e.matches(Gio.DBusError, Gio.DBusError.FAILED)) {
+ this._hasAboutToShow = false;
+ return;
+ }
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }
+ }
+
+ async sendEvent(id, event, params, timestamp) {
+ if (!this.gNameOwner)
+ return;
+
+ try {
+ await this.EventAsync(id, event, params, timestamp, this._cancellable);
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ return;
+
+ throw e;
+ }
+ }
+
+ _onPropertiesUpdated([changed, removed]) {
+ changed.forEach(([id, props]) => {
+ const item = this._items.get(id);
+ if (!item)
+ return;
+
+ for (const [prop, value] of Object.entries(props))
+ item.propertySet(prop, value);
+ });
+ removed.forEach(([id, propNames]) => {
+ const item = this._items.get(id);
+ if (!item)
+ return;
+
+ propNames.forEach(propName => item.propertySet(propName, null));
+ });
+ }
+});
+
+// ////////////////////////////////////////////////////////////////////////
+// PART TWO: "View" frontend implementation.
+// ////////////////////////////////////////////////////////////////////////
+
+// https://bugzilla.gnome.org/show_bug.cgi?id=731514
+// GNOME 3.10 and 3.12 can't open a nested submenu.
+// Patches have been written, but it's not clear when (if?) they will be applied.
+// We also don't know whether they will be backported to 3.10, so we will work around
+// it in the meantime. Offending versions can be clearly identified:
+const NEED_NESTED_SUBMENU_FIX = '_setOpenedSubMenu' in PopupMenu.PopupMenu.prototype;
+
+/**
+ * Creates new wrapper menu items and injects methods for managing them at runtime.
+ *
+ * Many functions in this object will be bound to the created item and executed as event
+ * handlers, so any `this` will refer to a menu item create in createItem
+ */
+const MenuItemFactory = {
+ createItem(client, dbusItem) {
+ // first, decide whether it's a submenu or not
+ let shellItem;
+ if (dbusItem.propertyGet('children-display') === 'submenu')
+ shellItem = new PopupMenu.PopupSubMenuMenuItem('FIXME');
+ else if (dbusItem.propertyGet('type') === 'separator')
+ shellItem = new PopupMenu.PopupSeparatorMenuItem('');
+ else
+ shellItem = new PopupMenu.PopupMenuItem('FIXME');
+
+ shellItem._dbusItem = dbusItem;
+ shellItem._dbusClient = client;
+
+ if (shellItem instanceof PopupMenu.PopupMenuItem) {
+ shellItem._icon = new St.Icon({
+ style_class: 'popup-menu-icon',
+ xAlign: Clutter.ActorAlign.END,
+ });
+ shellItem.add_child(shellItem._icon);
+ shellItem.label.x_expand = true;
+ }
+
+ // initialize our state
+ MenuItemFactory._updateLabel.call(shellItem);
+ MenuItemFactory._updateOrnament.call(shellItem);
+ MenuItemFactory._updateImage.call(shellItem);
+ MenuItemFactory._updateVisible.call(shellItem);
+ MenuItemFactory._updateSensitive.call(shellItem);
+
+ // initially create children
+ if (shellItem instanceof PopupMenu.PopupSubMenuMenuItem) {
+ dbusItem.getChildren().forEach(c =>
+ shellItem.menu.addMenuItem(MenuItemFactory.createItem(client, c)));
+ }
+
+ // now, connect various events
+ Util.connectSmart(dbusItem, 'property-changed',
+ shellItem, MenuItemFactory._onPropertyChanged);
+ Util.connectSmart(dbusItem, 'child-added',
+ shellItem, MenuItemFactory._onChildAdded);
+ Util.connectSmart(dbusItem, 'child-removed',
+ shellItem, MenuItemFactory._onChildRemoved);
+ Util.connectSmart(dbusItem, 'child-moved',
+ shellItem, MenuItemFactory._onChildMoved);
+ Util.connectSmart(shellItem, 'activate',
+ shellItem, MenuItemFactory._onActivate);
+
+ shellItem.connect('destroy', () => {
+ shellItem._dbusItemCancellable?.cancel();
+ shellItem._dbusItemCancellable = null;
+ shellItem._dbusItem = null;
+ shellItem._dbusClient = null;
+ shellItem._icon = null;
+ });
+
+ if (shellItem.menu) {
+ Util.connectSmart(shellItem.menu, 'open-state-changed',
+ shellItem, MenuItemFactory._onOpenStateChanged);
+ }
+
+ return shellItem;
+ },
+
+ _onOpenStateChanged(menu, open) {
+ if (open) {
+ if (NEED_NESTED_SUBMENU_FIX) {
+ // close our own submenus
+ if (menu._openedSubMenu)
+ menu._openedSubMenu.close(false);
+
+ // register ourselves and close sibling submenus
+ if (menu._parent._openedSubMenu && menu._parent._openedSubMenu !== menu)
+ menu._parent._openedSubMenu.close(true);
+
+ menu._parent._openedSubMenu = menu;
+ }
+
+ this._dbusItem.handleEvent('opened', null, 0).catch(logError);
+ this._dbusItem.sendAboutToShow();
+ } else {
+ if (NEED_NESTED_SUBMENU_FIX) {
+ // close our own submenus
+ if (menu._openedSubMenu)
+ menu._openedSubMenu.close(false);
+ }
+
+ this._dbusItem.handleEvent('closed', null, 0).catch(logError);
+ }
+ },
+
+ _onActivate(_item, event) {
+ const timestamp = event.get_time();
+ if (timestamp && this._dbusClient.indicator)
+ this._dbusClient.indicator.provideActivationToken(timestamp);
+
+ this._dbusItem.handleEvent('clicked', GLib.Variant.new('i', 0),
+ timestamp).catch(logError);
+ },
+
+ _onPropertyChanged(dbusItem, prop, _value) {
+ if (prop === 'toggle-type' || prop === 'toggle-state')
+ MenuItemFactory._updateOrnament.call(this);
+ else if (prop === 'label')
+ MenuItemFactory._updateLabel.call(this);
+ else if (prop === 'enabled')
+ MenuItemFactory._updateSensitive.call(this);
+ else if (prop === 'visible')
+ MenuItemFactory._updateVisible.call(this);
+ else if (prop === 'icon-name' || prop === 'icon-data')
+ MenuItemFactory._updateImage.call(this);
+ else if (prop === 'type' || prop === 'children-display')
+ MenuItemFactory._replaceSelf.call(this);
+ else
+ Util.Logger.debug(`Unhandled property change: ${prop}`);
+ },
+
+ _onChildAdded(dbusItem, child, position) {
+ if (!(this instanceof PopupMenu.PopupSubMenuMenuItem)) {
+ Util.Logger.warn('Tried to add a child to non-submenu item. Better recreate it as whole');
+ MenuItemFactory._replaceSelf.call(this);
+ } else {
+ this.menu.addMenuItem(MenuItemFactory.createItem(this._dbusClient, child), position);
+ }
+ },
+
+ _onChildRemoved(dbusItem, child) {
+ if (!(this instanceof PopupMenu.PopupSubMenuMenuItem)) {
+ Util.Logger.warn('Tried to remove a child from non-submenu item. Better recreate it as whole');
+ MenuItemFactory._replaceSelf.call(this);
+ } else {
+ // find it!
+ this.menu._getMenuItems().forEach(item => {
+ if (item._dbusItem === child)
+ item.destroy();
+ });
+ }
+ },
+
+ _onChildMoved(dbusItem, child, oldpos, newpos) {
+ if (!(this instanceof PopupMenu.PopupSubMenuMenuItem)) {
+ Util.Logger.warn('Tried to move a child in non-submenu item. Better recreate it as whole');
+ MenuItemFactory._replaceSelf.call(this);
+ } else {
+ MenuUtils.moveItemInMenu(this.menu, child, newpos);
+ }
+ },
+
+ _updateLabel() {
+ const label = this._dbusItem.propertyGet('label').replace(/_([^_])/, '$1');
+
+ if (this.label) // especially on GS3.8, the separator item might not even have a hidden label
+ this.label.set_text(label);
+ },
+
+ _updateOrnament() {
+ if (!this.setOrnament)
+ return; // separators and alike might not have gotten the polyfill
+
+ if (this._dbusItem.propertyGet('toggle-type') === 'checkmark' &&
+ this._dbusItem.propertyGetInt('toggle-state'))
+ this.setOrnament(PopupMenu.Ornament.CHECK);
+ else if (this._dbusItem.propertyGet('toggle-type') === 'radio' &&
+ this._dbusItem.propertyGetInt('toggle-state'))
+ this.setOrnament(PopupMenu.Ornament.DOT);
+ else
+ this.setOrnament(PopupMenu.Ornament.NONE);
+ },
+
+ async _updateImage() {
+ if (!this._icon)
+ return; // might be missing on submenus / separators
+
+ const iconName = this._dbusItem.propertyGet('icon-name');
+ const iconData = this._dbusItem.propertyGetVariant('icon-data');
+ if (iconName) {
+ this._icon.icon_name = iconName;
+ } else if (iconData) {
+ try {
+ if (!this._dbusItemCancellable) {
+ this._dbusItemCancellable = new Util.CancellableChild(
+ this._dbusClient.cancellable);
+ }
+
+ const inputStream = Gio.MemoryInputStream.new_from_bytes(
+ iconData.get_data_as_bytes());
+ this._icon.gicon = await GdkPixbuf.Pixbuf.new_from_stream_async(
+ inputStream, this._dbusItemCancellable);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }
+ }
+ },
+
+ _updateVisible() {
+ this.visible = this._dbusItem.propertyGetBool('visible');
+ },
+
+ _updateSensitive() {
+ this.setSensitive(this._dbusItem.propertyGetBool('enabled'));
+ },
+
+ _replaceSelf(newSelf) {
+ // create our new self if needed
+ if (!newSelf)
+ newSelf = MenuItemFactory.createItem(this._dbusClient, this._dbusItem);
+
+ // first, we need to find our old position
+ let pos = -1;
+ const family = this._parent._getMenuItems();
+ for (let i = 0; i < family.length; ++i) {
+ if (family[i] === this)
+ pos = i;
+ }
+
+ if (pos < 0)
+ throw new Error("DBusMenu: can't replace non existing menu item");
+
+
+ // add our new self while we're still alive
+ this._parent.addMenuItem(newSelf, pos);
+
+ // now destroy our old self
+ this.destroy();
+ },
+};
+
+/**
+ * Utility functions not necessarily belonging into the item factory
+ */
+const MenuUtils = {
+ moveItemInMenu(menu, dbusItem, newpos) {
+ // HACK: we're really getting into the internals of the PopupMenu implementation
+
+ // First, find our wrapper. Children tend to lie. We do not trust the old positioning.
+ const family = menu._getMenuItems();
+ for (let i = 0; i < family.length; ++i) {
+ if (family[i]._dbusItem === dbusItem) {
+ // now, remove it
+ menu.box.remove_child(family[i]);
+
+ // and add it again somewhere else
+ if (newpos < family.length && family[newpos] !== family[i])
+ menu.box.insert_child_below(family[i], family[newpos]);
+ else
+ menu.box.add(family[i]);
+
+ // skip the rest
+ return;
+ }
+ }
+ },
+};
+
+
+/**
+ * Processes DBus events, creates the menu items and handles the actions
+ *
+ * Something like a mini-god-object
+ */
+export class Client extends Signals.EventEmitter {
+ constructor(busName, path, indicator) {
+ super();
+
+ this._busName = busName;
+ this._busPath = path;
+ this._client = new DBusClient(busName, path);
+ this._rootMenu = null; // the shell menu
+ this._rootItem = null; // the DbusMenuItem for the root
+ this.indicator = indicator;
+ this.cancellable = new Util.CancellableChild(this.indicator.cancellable);
+
+ this._client.initAsync(this.cancellable).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ });
+
+ Util.connectSmart(this._client, 'ready-changed', this,
+ () => this.emit('ready-changed'));
+ }
+
+ get isReady() {
+ return this._client.isReady;
+ }
+
+ // this will attach the client to an already existing menu that will be used as the root menu.
+ // it will also connect the client to be automatically destroyed when the menu dies.
+ attachToMenu(menu) {
+ this._rootMenu = menu;
+ this._rootItem = this._client.getRoot();
+ this._itemsBeingAdded = new Set();
+
+ // cleanup: remove existing children (just in case)
+ this._rootMenu.removeAll();
+
+ if (NEED_NESTED_SUBMENU_FIX)
+ menu._setOpenedSubMenu = this._setOpenedSubmenu.bind(this);
+
+ // connect handlers
+ Util.connectSmart(menu, 'open-state-changed', this, this._onMenuOpenStateChanged);
+ Util.connectSmart(menu, 'destroy', this, this.destroy);
+
+ Util.connectSmart(this._rootItem, 'child-added', this, this._onRootChildAdded);
+ Util.connectSmart(this._rootItem, 'child-removed', this, this._onRootChildRemoved);
+ Util.connectSmart(this._rootItem, 'child-moved', this, this._onRootChildMoved);
+
+ // Dropbox requires us to call AboutToShow(0) first
+ this._rootItem.sendAboutToShow();
+
+ // fill the menu for the first time
+ const children = this._rootItem.getChildren();
+ children.forEach(child =>
+ this._onRootChildAdded(this._rootItem, child));
+ }
+
+ _setOpenedSubmenu(submenu) {
+ if (!submenu)
+ return;
+
+ if (submenu._parent !== this._rootMenu)
+ return;
+
+ if (submenu === this._openedSubMenu)
+ return;
+
+ if (this._openedSubMenu && this._openedSubMenu.isOpen)
+ this._openedSubMenu.close(true);
+
+ this._openedSubMenu = submenu;
+ }
+
+ _onRootChildAdded(dbusItem, child, position) {
+ // Menu additions can be expensive, so let's do it in different chunks
+ const basePriority = this.isOpen ? GLib.PRIORITY_DEFAULT : GLib.PRIORITY_LOW;
+ const idlePromise = new PromiseUtils.IdlePromise(
+ basePriority + this._itemsBeingAdded.size, this.cancellable);
+ this._itemsBeingAdded.add(child);
+
+ idlePromise.then(() => {
+ if (!this._itemsBeingAdded.has(child))
+ return;
+
+ this._rootMenu.addMenuItem(
+ MenuItemFactory.createItem(this, child), position);
+ }).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }).finally(() => this._itemsBeingAdded.delete(child));
+ }
+
+ _onRootChildRemoved(dbusItem, child) {
+ // children like to play hide and seek
+ // but we know how to find it for sure!
+ const item = this._rootMenu._getMenuItems().find(it =>
+ it._dbusItem === child);
+
+ if (item)
+ item.destroy();
+ else
+ this._itemsBeingAdded.delete(child);
+ }
+
+ _onRootChildMoved(dbusItem, child, oldpos, newpos) {
+ MenuUtils.moveItemInMenu(this._rootMenu, dbusItem, newpos);
+ }
+
+ _onMenuOpenStateChanged(menu, state) {
+ if (!this._rootItem)
+ return;
+
+ this._client.active = state;
+
+ if (state) {
+ if (this._openedSubMenu && this._openedSubMenu.isOpen)
+ this._openedSubMenu.close();
+
+ this._rootItem.handleEvent('opened', null, 0).catch(logError);
+ this._rootItem.sendAboutToShow();
+ } else {
+ this._rootItem.handleEvent('closed', null, 0).catch(e => {
+ if (e.matches(Gio.DBusError, Gio.DBusError.UNKNOWN_OBJECT)) {
+ // The menu hay have been removed at this point, thus do not
+ // spam the users about this if it happens.
+ return;
+ }
+
+ logError(e);
+ });
+ }
+ }
+
+ destroy() {
+ this.emit('destroy');
+
+ if (this._client)
+ this._client.destroy();
+
+ this._client = null;
+ this._rootItem = null;
+ this._rootMenu = null;
+ this.indicator = null;
+ this._itemsBeingAdded = null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusProxy.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusProxy.js
new file mode 100644
index 0000000..047f2d2
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusProxy.js
@@ -0,0 +1,108 @@
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+
+import {CancellableChild, Logger} from './util.js';
+
+Gio._promisify(Gio.DBusProxy.prototype, 'init_async');
+
+export const DBusProxy = GObject.registerClass({
+ Signals: {'destroy': {}},
+}, class DBusProxy extends Gio.DBusProxy {
+ static get TUPLE_VARIANT_TYPE() {
+ if (!this._tupleVariantType)
+ this._tupleVariantType = new GLib.VariantType('(v)');
+
+ return this._tupleVariantType;
+ }
+
+ static destroy() {
+ delete this._tupleType;
+ }
+
+ _init(busName, objectPath, interfaceInfo, flags = Gio.DBusProxyFlags.NONE) {
+ if (interfaceInfo.signals.length)
+ Logger.warn('Avoid exposing signals to gjs!');
+
+ super._init({
+ gConnection: Gio.DBus.session,
+ gInterfaceName: interfaceInfo.name,
+ gInterfaceInfo: interfaceInfo,
+ gName: busName,
+ gObjectPath: objectPath,
+ gFlags: flags,
+ });
+
+ this._signalIds = [];
+
+ if (!(flags & Gio.DBusProxyFlags.DO_NOT_CONNECT_SIGNALS)) {
+ this._signalIds.push(this.connect('g-signal',
+ (_proxy, ...args) => this._onSignal(...args)));
+ }
+
+ this._signalIds.push(this.connect('notify::g-name-owner', () => {
+ if (!this.gNameOwner) {
+ this._cancellable.cancel();
+ this._cancellable = new Gio.Cancellable();
+ }
+ this._onNameOwnerChanged();
+ }));
+ }
+
+ async initAsync(cancellable) {
+ cancellable = new CancellableChild(cancellable);
+ await this.init_async(GLib.PRIORITY_DEFAULT, cancellable);
+ this._cancellable = cancellable;
+
+ this.gInterfaceInfo.methods.map(m => m.name).forEach(method =>
+ this._ensureAsyncMethod(method));
+ }
+
+ destroy() {
+ this.emit('destroy');
+
+ this._signalIds.forEach(id => this.disconnect(id));
+
+ if (this._cancellable)
+ this._cancellable.cancel();
+ }
+
+ // This can be removed when we will have GNOME 43 as minimum version
+ _ensureAsyncMethod(method) {
+ if (this[`${method}Async`])
+ return;
+
+ if (!this[`${method}Remote`])
+ throw new Error(`Missing remote method '${method}'`);
+
+ this[`${method}Async`] = function (...args) {
+ return new Promise((resolve, reject) => {
+ this[`${method}Remote`](...args, (ret, e) => {
+ if (e)
+ reject(e);
+ else
+ resolve(ret);
+ });
+ });
+ };
+ }
+
+ _onSignal() {
+ }
+
+ getProperty(propertyName, cancellable) {
+ return this.gConnection.call(this.gName,
+ this.gObjectPath, 'org.freedesktop.DBus.Properties', 'Get',
+ GLib.Variant.new('(ss)', [this.gInterfaceName, propertyName]),
+ DBusProxy.TUPLE_VARIANT_TYPE, Gio.DBusCallFlags.NONE, -1,
+ cancellable);
+ }
+
+ getProperties(cancellable) {
+ return this.gConnection.call(this.gName,
+ this.gObjectPath, 'org.freedesktop.DBus.Properties', 'GetAll',
+ GLib.Variant.new('(s)', [this.gInterfaceName]),
+ GLib.VariantType.new('(a{sv})'), Gio.DBusCallFlags.NONE, -1,
+ cancellable);
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusUtils.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusUtils.js
new file mode 100644
index 0000000..d4d7a96
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/dbusUtils.js
@@ -0,0 +1,121 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+
+import {Logger} from './logger.js';
+
+export const BUS_ADDRESS_REGEX = /([a-zA-Z0-9._-]+\.[a-zA-Z0-9.-]+)|(:[0-9]+\.[0-9]+)$/;
+
+Gio._promisify(Gio.DBusConnection.prototype, 'call');
+
+export async function getUniqueBusName(bus, name, cancellable) {
+ if (name[0] === ':')
+ return name;
+
+ if (!bus)
+ bus = Gio.DBus.session;
+
+ const variantName = new GLib.Variant('(s)', [name]);
+ const [unique] = (await bus.call('org.freedesktop.DBus', '/', 'org.freedesktop.DBus',
+ 'GetNameOwner', variantName, new GLib.VariantType('(s)'),
+ Gio.DBusCallFlags.NONE, -1, cancellable)).deep_unpack();
+
+ return unique;
+}
+
+export async function getBusNames(bus, cancellable) {
+ if (!bus)
+ bus = Gio.DBus.session;
+
+ const [names] = (await bus.call('org.freedesktop.DBus', '/', 'org.freedesktop.DBus',
+ 'ListNames', null, new GLib.VariantType('(as)'), Gio.DBusCallFlags.NONE,
+ -1, cancellable)).deep_unpack();
+
+ const uniqueNames = new Map();
+ const requests = names.map(name => getUniqueBusName(bus, name, cancellable));
+ const results = await Promise.allSettled(requests);
+
+ for (let i = 0; i < results.length; i++) {
+ const result = results[i];
+ if (result.status === 'fulfilled') {
+ let namesForBus = uniqueNames.get(result.value);
+ if (!namesForBus) {
+ namesForBus = new Set();
+ uniqueNames.set(result.value, namesForBus);
+ }
+ if (result.value !== names[i])
+ namesForBus.add(names[i]);
+ } else if (!result.reason.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ Logger.debug(`Impossible to get the unique name of ${names[i]}: ${result.reason}`);
+ }
+ }
+
+ return uniqueNames;
+}
+
+async function getProcessId(connectionName, cancellable = null, bus = Gio.DBus.session) {
+ const res = await bus.call('org.freedesktop.DBus', '/',
+ 'org.freedesktop.DBus', 'GetConnectionUnixProcessID',
+ new GLib.Variant('(s)', [connectionName]),
+ new GLib.VariantType('(u)'),
+ Gio.DBusCallFlags.NONE,
+ -1,
+ cancellable);
+ const [pid] = res.deepUnpack();
+ return pid;
+}
+
+export async function getProcessName(connectionName, cancellable = null,
+ priority = GLib.PRIORITY_DEFAULT, bus = Gio.DBus.session) {
+ const pid = await getProcessId(connectionName, cancellable, bus);
+ const cmdFile = Gio.File.new_for_path(`/proc/${pid}/cmdline`);
+ const inputStream = await cmdFile.read_async(priority, cancellable);
+ const bytes = await inputStream.read_bytes_async(2048, priority, cancellable);
+ const textDecoder = new TextDecoder();
+ return textDecoder.decode(bytes.toArray().map(v => !v ? 0x20 : v));
+}
+
+export async function* introspectBusObject(bus, name, cancellable,
+ interfaces = undefined, path = undefined) {
+ if (!path)
+ path = '/';
+
+ const [introspection] = (await bus.call(name, path, 'org.freedesktop.DBus.Introspectable',
+ 'Introspect', null, new GLib.VariantType('(s)'), Gio.DBusCallFlags.NONE,
+ 5000, cancellable)).deep_unpack();
+
+ const nodeInfo = Gio.DBusNodeInfo.new_for_xml(introspection);
+
+ if (!interfaces || dbusNodeImplementsInterfaces(nodeInfo, interfaces))
+ yield {nodeInfo, path};
+
+ if (path === '/')
+ path = '';
+
+ for (const subNodeInfo of nodeInfo.nodes) {
+ const subPath = `${path}/${subNodeInfo.path}`;
+ yield* introspectBusObject(bus, name, cancellable, interfaces, subPath);
+ }
+}
+
+function dbusNodeImplementsInterfaces(nodeInfo, interfaces) {
+ if (!(nodeInfo instanceof Gio.DBusNodeInfo) || !Array.isArray(interfaces))
+ return false;
+
+ return interfaces.some(iface => nodeInfo.lookup_interface(iface));
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/extension.js
new file mode 100644
index 0000000..6321077
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/extension.js
@@ -0,0 +1,90 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import * as Extension from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import * as StatusNotifierWatcher from './statusNotifierWatcher.js';
+import * as Interfaces from './interfaces.js';
+import * as TrayIconsManager from './trayIconsManager.js';
+import * as Util from './util.js';
+import {Logger} from './logger.js';
+import {SettingsManager} from './settingsManager.js';
+
+export default class AppIndicatorExtension extends Extension.Extension {
+ constructor(...args) {
+ super(...args);
+
+ Logger.init(this);
+ Interfaces.initialize(this);
+
+ this._isEnabled = false;
+ this._statusNotifierWatcher = null;
+ this._watchDog = new Util.NameWatcher(StatusNotifierWatcher.WATCHER_BUS_NAME);
+ this._watchDog.connect('vanished', () => this._maybeEnableAfterNameAvailable());
+
+ // HACK: we want to leave the watchdog alive when disabling the extension,
+ // but if we are being reloaded, we destroy it since it could be considered
+ // a leak and spams our log, too.
+ /* eslint-disable no-undef */
+ if (typeof global['--appindicator-extension-on-reload'] === 'function')
+ global['--appindicator-extension-on-reload']();
+
+ global['--appindicator-extension-on-reload'] = () => {
+ Logger.debug('Reload detected, destroying old watchdog');
+ this._watchDog.destroy();
+ this._watchDog = null;
+ };
+ /* eslint-enable no-undef */
+ }
+
+ enable() {
+ this._isEnabled = true;
+ SettingsManager.initialize(this);
+ Util.tryCleanupOldIndicators();
+ this._maybeEnableAfterNameAvailable();
+ TrayIconsManager.TrayIconsManager.initialize();
+ }
+
+ disable() {
+ this._isEnabled = false;
+ TrayIconsManager.TrayIconsManager.destroy();
+
+ if (this._statusNotifierWatcher !== null) {
+ this._statusNotifierWatcher.destroy();
+ this._statusNotifierWatcher = null;
+ }
+
+ SettingsManager.destroy();
+ }
+
+ // FIXME: when entering/leaving the lock screen, the extension might be
+ // enabled/disabled rapidly.
+ // This will create very bad side effects in case we were not done unowning
+ // the name while trying to own it again. Since g_bus_unown_name doesn't
+ // fire any callback when it's done, we need to monitor the bus manually
+ // to find out when the name vanished so we can reclaim it again.
+ _maybeEnableAfterNameAvailable() {
+ // by the time we get called whe might not be enabled
+ if (!this._isEnabled || this._statusNotifierWatcher)
+ return;
+
+ if (this._watchDog.nameAcquired && this._watchDog.nameOnBus)
+ return;
+
+ this._statusNotifierWatcher = new StatusNotifierWatcher.StatusNotifierWatcher(
+ this, this._watchDog);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconCache.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconCache.js
new file mode 100644
index 0000000..3610184
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconCache.js
@@ -0,0 +1,179 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+
+import * as PromiseUtils from './promiseUtils.js';
+import * as Util from './util.js';
+
+// The icon cache caches icon objects in case they're reused shortly aftwerwards.
+// This is necessary for some indicators like skype which rapidly switch between serveral icons.
+// Without caching, the garbage collection would never be able to handle the amount of new icon data.
+// If the lifetime of an icon is over, the cache will destroy the icon. (!)
+// The presence of active icons will extend the lifetime.
+
+const GC_INTERVAL = 100; // seconds
+const LIFETIME_TIMESPAN = 120; // seconds
+
+// how to use: see IconCache.add, IconCache.get
+export class IconCache {
+ constructor() {
+ this._cache = new Map();
+ this._activeIcons = Object.create(null);
+ this._lifetime = new Map(); // we don't want to attach lifetime to the object
+ }
+
+ add(id, icon) {
+ if (!(icon instanceof Gio.Icon)) {
+ Util.Logger.critical('IconCache: Only Gio.Icons are supported');
+ return null;
+ }
+
+ if (!id) {
+ Util.Logger.critical('IconCache: Invalid ID provided');
+ return null;
+ }
+
+ const oldIcon = this._cache.get(id);
+ if (!oldIcon || !oldIcon.equal(icon)) {
+ Util.Logger.debug(`IconCache: adding ${id}: ${icon}`);
+ this._cache.set(id, icon);
+ } else {
+ icon = oldIcon;
+ }
+
+ this._renewLifetime(id);
+ this._checkGC();
+
+ return icon;
+ }
+
+ updateActive(iconType, gicon, isActive) {
+ if (!gicon)
+ return;
+
+ const previousActive = this._activeIcons[iconType];
+
+ if (isActive && [...this._cache.values()].some(icon => icon === gicon))
+ this._activeIcons[iconType] = gicon;
+ else if (previousActive === gicon)
+ delete this._activeIcons[iconType];
+ else
+ return;
+
+ if (previousActive) {
+ this._cache.forEach((icon, id) => {
+ if (icon === previousActive)
+ this._renewLifetime(id);
+ });
+ }
+ }
+
+ _remove(id) {
+ Util.Logger.debug(`IconCache: removing ${id}`);
+
+ this._cache.delete(id);
+ this._lifetime.delete(id);
+ }
+
+ _renewLifetime(id) {
+ this._lifetime.set(id, new Date().getTime() + LIFETIME_TIMESPAN * 1000);
+ }
+
+ forceDestroy(id) {
+ const gicon = this._cache.has(id);
+ if (gicon) {
+ Object.keys(this._activeIcons).forEach(iconType =>
+ this.updateActive(iconType, gicon, false));
+ this._remove(id);
+ this._checkGC();
+ }
+ }
+
+ // marks all the icons as removable, if something doesn't claim them before
+ weakClear() {
+ this._activeIcons = Object.create(null);
+ this._checkGC();
+ }
+
+ // removes everything from the cache
+ clear() {
+ this._activeIcons = Object.create(null);
+ this._cache.forEach((_icon, id) => this._remove(id));
+ this._checkGC();
+ }
+
+ // returns an object from the cache, or null if it can't be found.
+ get(id) {
+ const icon = this._cache.get(id);
+ if (icon) {
+ Util.Logger.debug(`IconCache: retrieving ${id}: ${icon}`);
+ this._renewLifetime(id);
+ return icon;
+ }
+
+ return null;
+ }
+
+ async _checkGC() {
+ const cacheIsEmpty = this._cache.size === 0;
+
+ if (!cacheIsEmpty && !this._gcTimeout) {
+ Util.Logger.debug('IconCache: garbage collector started');
+ let anyUnusedInCache = false;
+ this._gcTimeout = new PromiseUtils.TimeoutSecondsPromise(GC_INTERVAL,
+ GLib.PRIORITY_LOW);
+ try {
+ await this._gcTimeout;
+ anyUnusedInCache = this._gc();
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'IconCache: garbage collector');
+ } finally {
+ delete this._gcTimeout;
+ }
+
+ if (anyUnusedInCache)
+ this._checkGC();
+ } else if (cacheIsEmpty && this._gcTimeout) {
+ Util.Logger.debug('IconCache: garbage collector stopped');
+ this._gcTimeout.cancel();
+ }
+ }
+
+ _gc() {
+ const time = new Date().getTime();
+ let anyUnused = false;
+
+ this._cache.forEach((icon, id) => {
+ if (Object.values(this._activeIcons).includes(icon)) {
+ Util.Logger.debug(`IconCache: ${id} is in use.`);
+ } else if (this._lifetime.get(id) < time) {
+ this._remove(id);
+ } else {
+ anyUnused = true;
+ Util.Logger.debug(`IconCache: ${id} survived this round.`);
+ }
+ });
+
+ return anyUnused;
+ }
+
+ destroy() {
+ this.clear();
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconTheme.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconTheme.js
new file mode 100644
index 0000000..cdd6540
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/iconTheme.js
@@ -0,0 +1,12 @@
+let _defaultTheme;
+export function getDefaultTheme() {
+ if (_defaultTheme)
+ return _defaultTheme;
+
+ _defaultTheme = new St.IconTheme();
+ return _defaultTheme;
+}
+
+export function destroyDefaultTheme() {
+ _defaultTheme = null;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/custom-icons-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/custom-icons-symbolic.svg
new file mode 100644
index 0000000..bfc3698
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/custom-icons-symbolic.svg
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ viewBox="0 0 16 16"
+ version="1.1"
+ id="svg2"
+ sodipodi:docname="custom-icons-symbolic.svg"
+ inkscape:version="1.4 (1:1.4+202410161351+e7c3feb100)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="namedview2"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="31.404053"
+ inkscape:cx="7.4671891"
+ inkscape:cy="8.9001252"
+ inkscape:window-width="1860"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <g
+ fill="#222222"
+ id="g3"
+ transform="translate(-0.16009403)">
+ <path
+ d="m 5.207031,1.292969 c -0.390625,-0.390625 -1.023437,-0.390625 -1.414062,0 l -2.5,2.5 c -0.390625,0.390625 -0.390625,1.023437 0,1.414062 l 2.5,2.5 c 0.390625,0.390625 1.023437,0.390625 1.414062,0 l 2.496094,-2.5 c 0.390625,-0.390625 0.390625,-1.023437 0,-1.414062 z m 0,0"
+ id="path1-3" />
+ <path
+ d="m 10,12 c 0,1.65625 -1.34375,3 -3,3 -1.660156,0 -3,-1.34375 -3,-3 0,-1.65625 1.339844,-3 3,-3 1.65625,0 3,1.34375 3,3 z m 0,0"
+ id="path2-6" />
+ <path
+ d="M 11.945312,2.003906 C 11.605469,2.019531 11.300781,2.207031 11.132812,2.5 L 8.8125,6.5 C 8.425781,7.164062 8.90625,8 9.675781,8 h 4.644531 c 0.769532,0 1.25,-0.835938 0.863282,-1.5 l -2.320313,-4 C 12.675781,2.171875 12.320312,1.980469 11.945312,2.003906 Z m 0,0"
+ id="path3" />
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/general-preferences-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/general-preferences-symbolic.svg
new file mode 100644
index 0000000..6334430
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/icons/hicolor/scalable/actions/general-preferences-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M12.5 2A2.5 2.5 0 0 0 10 4.5 2.5 2.5 0 0 0 12.5 7 2.5 2.5 0 0 0 15 4.5 2.5 2.5 0 0 0 12.5 2zm0 1A1.5 1.5 0 0 1 14 4.5 1.5 1.5 0 0 1 12.5 6 1.5 1.5 0 0 1 11 4.5 1.5 1.5 0 0 1 12.5 3zm-9 5A2.5 2.5 0 0 0 1 10.5 2.5 2.5 0 0 0 3.5 13a2.5 2.5 0 0 0 2.443-2H15v-1H5.95A2.5 2.5 0 0 0 3.5 8zm0 1A1.5 1.5 0 0 1 5 10.5 1.5 1.5 0 0 1 3.5 12 1.5 1.5 0 0 1 2 10.5 1.5 1.5 0 0 1 3.5 9z" style="fill:gray;stroke-width:.70228"/><path d="M-9 4h8v1h-8z" style="fill:gray;fill-opacity:1;stroke-width:1.05153;opacity:.5" transform="scale(-1 1)"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/indicatorStatusIcon.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/indicatorStatusIcon.js
new file mode 100644
index 0000000..533789e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/indicatorStatusIcon.js
@@ -0,0 +1,613 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import Clutter from 'gi://Clutter';
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import St from 'gi://St';
+
+import * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Panel from 'resource:///org/gnome/shell/ui/panel.js';
+import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
+
+import * as AppIndicator from './appIndicator.js';
+import * as PromiseUtils from './promiseUtils.js';
+import * as SettingsManager from './settingsManager.js';
+import * as Util from './util.js';
+import * as DBusMenu from './dbusMenu.js';
+
+const DEFAULT_ICON_SIZE = Panel.PANEL_ICON_SIZE || 16;
+
+export function addIconToPanel(statusIcon) {
+ if (!(statusIcon instanceof BaseStatusIcon))
+ throw TypeError(`Unexpected icon type: ${statusIcon}`);
+
+ const settings = SettingsManager.getDefaultGSettings();
+ const indicatorId = `appindicator-${statusIcon.uniqueId}`;
+
+ const currentIcon = Main.panel.statusArea[indicatorId];
+ if (currentIcon) {
+ if (currentIcon !== statusIcon)
+ currentIcon.destroy();
+
+ Main.panel.statusArea[indicatorId] = null;
+ }
+
+ Main.panel.addToStatusArea(indicatorId, statusIcon, 1,
+ settings.get_string('tray-pos'));
+
+ Util.connectSmart(settings, 'changed::tray-pos', statusIcon, () =>
+ addIconToPanel(statusIcon));
+}
+
+export function getTrayIcons() {
+ return Object.values(Main.panel.statusArea).filter(
+ i => i instanceof IndicatorStatusTrayIcon);
+}
+
+export function getAppIndicatorIcons() {
+ return Object.values(Main.panel.statusArea).filter(
+ i => i instanceof IndicatorStatusIcon);
+}
+
+export const BaseStatusIcon = GObject.registerClass(
+class IndicatorBaseStatusIcon extends PanelMenu.Button {
+ _init(menuAlignment, nameText, iconActor, dontCreateMenu) {
+ super._init(menuAlignment, nameText, dontCreateMenu);
+
+ const settings = SettingsManager.getDefaultGSettings();
+ Util.connectSmart(settings, 'changed::icon-opacity', this, this._updateOpacity);
+ this.connect('notify::hover', () => this._onHoverChanged());
+
+ if (!super._onDestroy)
+ this.connect('destroy', () => this._onDestroy());
+
+ this._box = new St.BoxLayout({style_class: 'panel-status-indicators-box'});
+ this.add_child(this._box);
+
+ this._setIconActor(iconActor);
+ this._showIfReady();
+
+ this.set_style(IndicatorBaseStatusIcon.DEFAULT_STYLE);
+ }
+
+ _setIconActor(icon) {
+ if (!(icon instanceof Clutter.Actor))
+ throw new Error(`${icon} is not a valid actor`);
+
+ if (this._icon && this._icon !== icon)
+ this._icon.destroy();
+
+ this._icon = icon;
+ this._updateEffects();
+ this._monitorIconEffects();
+
+ if (this._icon) {
+ this._box.add_child(this._icon);
+ const id = this._icon.connect('destroy', () => {
+ this._icon.disconnect(id);
+ this._icon = null;
+ this._monitorIconEffects();
+ });
+ }
+ }
+
+ _onDestroy() {
+ if (this._icon)
+ this._icon.destroy();
+
+ if (super._onDestroy)
+ super._onDestroy();
+ }
+
+ isReady() {
+ throw new GObject.NotImplementedError('isReady() in %s'.format(this.constructor.name));
+ }
+
+ get icon() {
+ return this._icon;
+ }
+
+ get uniqueId() {
+ throw new GObject.NotImplementedError('uniqueId in %s'.format(this.constructor.name));
+ }
+
+ _showIfReady() {
+ this.visible = this.isReady();
+ }
+
+ _onHoverChanged() {
+ if (this.hover) {
+ this.opacity = 255;
+ if (this._icon)
+ this._icon.remove_effect_by_name('desaturate');
+ } else {
+ this._updateEffects();
+ }
+ }
+
+ _updateOpacity() {
+ const settings = SettingsManager.getDefaultGSettings();
+ const userValue = settings.get_user_value('icon-opacity');
+ if (userValue)
+ this.opacity = userValue.unpack();
+ else
+ this.opacity = 255;
+ }
+
+ _updateEffects() {
+ this._updateOpacity();
+
+ if (this._icon) {
+ this._updateSaturation();
+ this._updateBrightnessContrast();
+ }
+ }
+
+ _monitorIconEffects() {
+ const settings = SettingsManager.getDefaultGSettings();
+ const monitoring = !!this._iconSaturationIds;
+
+ if (!this._icon && monitoring) {
+ Util.disconnectSmart(settings, this, this._iconSaturationIds);
+ delete this._iconSaturationIds;
+
+ Util.disconnectSmart(settings, this, this._iconBrightnessIds);
+ delete this._iconBrightnessIds;
+
+ Util.disconnectSmart(settings, this, this._iconContrastIds);
+ delete this._iconContrastIds;
+
+ Util.disconnectSmart(settings, this, this._compactModeEnabledIds);
+ delete this._compactModeEnabledIds;
+ } else if (this._icon && !monitoring) {
+ this._iconSaturationIds =
+ Util.connectSmart(settings, 'changed::icon-saturation', this,
+ this._updateSaturation);
+ this._iconBrightnessIds =
+ Util.connectSmart(settings, 'changed::icon-brightness', this,
+ this._updateBrightnessContrast);
+ this._iconContrastIds =
+ Util.connectSmart(settings, 'changed::icon-contrast', this,
+ this._updateBrightnessContrast);
+ this._compactModeEnabledIds =
+ Util.connectSmart(settings, 'changed::compact-mode-enabled', this,
+ this._updateCompactMode);
+ }
+ }
+
+ static get DEFAULT_STYLE() {
+ const settings = SettingsManager.getDefaultGSettings();
+ if (!settings.get_boolean('compact-mode-enabled'))
+ return null; // drop to default -natural-hpadding.
+
+ return '-natural-hpadding: 10px';
+ }
+
+ _updateCompactMode() {
+ this._icon.set_style(AppIndicator.IconActor.DEFAULT_STYLE);
+ this.set_style(IndicatorBaseStatusIcon.DEFAULT_STYLE);
+ }
+
+ _updateSaturation() {
+ const settings = SettingsManager.getDefaultGSettings();
+ const desaturationValue = settings.get_double('icon-saturation');
+ let desaturateEffect = this._icon.get_effect('desaturate');
+
+ if (desaturationValue > 0) {
+ if (!desaturateEffect) {
+ desaturateEffect = new Clutter.DesaturateEffect();
+ this._icon.add_effect_with_name('desaturate', desaturateEffect);
+ }
+ desaturateEffect.set_factor(desaturationValue);
+ } else if (desaturateEffect) {
+ this._icon.remove_effect(desaturateEffect);
+ }
+ }
+
+ _updateBrightnessContrast() {
+ const settings = SettingsManager.getDefaultGSettings();
+ const brightnessValue = settings.get_double('icon-brightness');
+ const contrastValue = settings.get_double('icon-contrast');
+ let brightnessContrastEffect = this._icon.get_effect('brightness-contrast');
+
+ if (brightnessValue !== 0 | contrastValue !== 0) {
+ if (!brightnessContrastEffect) {
+ brightnessContrastEffect = new Clutter.BrightnessContrastEffect();
+ this._icon.add_effect_with_name('brightness-contrast', brightnessContrastEffect);
+ }
+ brightnessContrastEffect.set_brightness(brightnessValue);
+ brightnessContrastEffect.set_contrast(contrastValue);
+ } else if (brightnessContrastEffect) {
+ this._icon.remove_effect(brightnessContrastEffect);
+ }
+ }
+});
+
+/*
+ * IndicatorStatusIcon implements an icon in the system status area
+ */
+export const IndicatorStatusIcon = GObject.registerClass(
+class IndicatorStatusIcon extends BaseStatusIcon {
+ _init(indicator) {
+ super._init(0.5, indicator.accessibleName,
+ new AppIndicator.IconActor(indicator, DEFAULT_ICON_SIZE));
+
+ // Disable upstream's click gesture and fall back to vfunc_button_press_event etc.
+ this._clickGesture?.set_enabled(false);
+
+ this._indicator = indicator;
+
+ this._lastClickTime = -1;
+ this._lastClickX = -1;
+ this._lastClickY = -1;
+
+ this._box.add_style_class_name('appindicator-box');
+
+ Util.connectSmart(this._indicator, 'ready', this, this._showIfReady);
+ Util.connectSmart(this._indicator, 'menu', this, this._updateMenu);
+ Util.connectSmart(this._indicator, 'label', this, this._updateLabel);
+ Util.connectSmart(this._indicator, 'status', this, this._updateStatus);
+ Util.connectSmart(this._indicator, 'reset', this, () => {
+ this._updateStatus();
+ this._updateLabel();
+ });
+ Util.connectSmart(this._indicator, 'accessible-name', this, () =>
+ this.set_accessible_name(this._indicator.accessibleName));
+ Util.connectSmart(this._indicator, 'destroy', this, () => this.destroy());
+
+ this.connect('notify::visible', () => this._updateMenu());
+
+ this._showIfReady();
+ }
+
+ _onDestroy() {
+ if (this._menuClient) {
+ this._menuClient.disconnect(this._menuReadyId);
+ this._menuClient.destroy();
+ this._menuClient = null;
+ }
+
+ super._onDestroy();
+ }
+
+ get uniqueId() {
+ return this._indicator.uniqueId;
+ }
+
+ isReady() {
+ return this._indicator && this._indicator.isReady;
+ }
+
+ _updateLabel() {
+ const {label} = this._indicator;
+ if (label) {
+ if (!this._label || !this._labelBin) {
+ this._labelBin = new St.Bin({
+ yAlign: Clutter.ActorAlign.CENTER,
+ });
+ this._label = new St.Label();
+ Util.addActor(this._labelBin, this._label);
+ Util.addActor(this._box, this._labelBin);
+ }
+ this._label.set_text(label);
+ if (!this._box.contains(this._labelBin))
+ Util.addActor(this._box, this._labelBin); // FIXME: why is it suddenly necessary?
+ } else if (this._label) {
+ this._labelBin.destroy_all_children();
+ Util.removeActor(this._box, this._labelBin);
+ this._labelBin.destroy();
+ delete this._labelBin;
+ delete this._label;
+ }
+ }
+
+ _updateStatus() {
+ const wasVisible = this.visible;
+ this.visible = this._indicator.status !== AppIndicator.SNIStatus.PASSIVE;
+
+ if (this.visible !== wasVisible)
+ this._indicator.checkAlive().catch(logError);
+ }
+
+ _updateMenu() {
+ if (this._menuClient) {
+ this._menuClient.disconnect(this._menuReadyId);
+ this._menuClient.destroy();
+ this._menuClient = null;
+ this.menu.removeAll();
+ }
+
+ if (this.visible && this._indicator.menuPath) {
+ this._menuClient = new DBusMenu.Client(this._indicator.busName,
+ this._indicator.menuPath, this._indicator);
+
+ if (this._menuClient.isReady)
+ this._menuClient.attachToMenu(this.menu);
+
+ this._menuReadyId = this._menuClient.connect('ready-changed', () => {
+ if (this._menuClient.isReady)
+ this._menuClient.attachToMenu(this.menu);
+ else
+ this._updateMenu();
+ });
+ }
+ }
+
+ _showIfReady() {
+ if (!this.isReady())
+ return;
+
+ this._updateLabel();
+ this._updateStatus();
+ this._updateMenu();
+ }
+
+ _updateClickCount(event) {
+ const [x, y] = event.get_coords();
+ const time = event.get_time();
+ const {doubleClickDistance, doubleClickTime} =
+ Clutter.Settings.get_default();
+
+ if (time > (this._lastClickTime + doubleClickTime) ||
+ (Math.abs(x - this._lastClickX) > doubleClickDistance) ||
+ (Math.abs(y - this._lastClickY) > doubleClickDistance))
+ this._clickCount = 0;
+
+ this._lastClickTime = time;
+ this._lastClickX = x;
+ this._lastClickY = y;
+
+ this._clickCount = (this._clickCount % 2) + 1;
+
+ return this._clickCount;
+ }
+
+ _maybeHandleDoubleClick(event) {
+ if (this._indicator.supportsActivation === false)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (event.get_button() !== Clutter.BUTTON_PRIMARY)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._updateClickCount(event) === 2) {
+ this._indicator.open(...event.get_coords(), event.get_time());
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ async _waitForDoubleClick() {
+ const {doubleClickTime} = Clutter.Settings.get_default();
+ this._waitDoubleClickPromise = new PromiseUtils.TimeoutPromise(
+ doubleClickTime);
+
+ try {
+ await this._waitDoubleClickPromise;
+ this.menu.toggle();
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ throw e;
+ } finally {
+ delete this._waitDoubleClickPromise;
+ }
+ }
+
+ vfunc_event(event) {
+ if (this.menu.numMenuItems && event.type() === Clutter.EventType.TOUCH_BEGIN)
+ this.menu.toggle();
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_button_press_event(event) {
+ if (this._waitDoubleClickPromise)
+ this._waitDoubleClickPromise.cancel();
+
+ // if middle mouse button clicked send SecondaryActivate dbus event and do not show appindicator menu
+ if (event.get_button() === Clutter.BUTTON_MIDDLE) {
+ if (Main.panel.menuManager.activeMenu)
+ Main.panel.menuManager._closeMenu(true, Main.panel.menuManager.activeMenu);
+ this._indicator.secondaryActivate(event.get_time(), ...event.get_coords());
+ return Clutter.EVENT_STOP;
+ }
+
+ if (event.get_button() === Clutter.BUTTON_SECONDARY) {
+ this.menu.toggle();
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ const doubleClickHandled = this._maybeHandleDoubleClick(event);
+ if (doubleClickHandled === Clutter.EVENT_PROPAGATE &&
+ event.get_button() === Clutter.BUTTON_PRIMARY &&
+ this.menu.numMenuItems) {
+ if (this._indicator.supportsActivation !== false)
+ this._waitForDoubleClick().catch(logError);
+ else
+ this.menu.toggle();
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_scroll_event(event) {
+ // Since Clutter 1.10, clutter will always send a smooth scrolling event
+ // with explicit deltas, no matter what input device is used
+ // In fact, for every scroll there will be a smooth and non-smooth scroll
+ // event, and we can choose which one we interpret.
+ if (event.get_scroll_direction() === Clutter.ScrollDirection.SMOOTH) {
+ const [dx, dy] = event.get_scroll_delta();
+
+ this._indicator.scroll(dx, dy);
+ return Clutter.EVENT_STOP;
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+});
+
+export const IndicatorStatusTrayIcon = GObject.registerClass(
+class IndicatorTrayIcon extends BaseStatusIcon {
+ _init(icon) {
+ super._init(0.5, icon.wm_class, icon, {dontCreateMenu: true});
+ Util.Logger.debug(`Adding legacy tray icon ${this.uniqueId}`);
+ this._box.add_style_class_name('appindicator-trayicons-box');
+ this.add_style_class_name('appindicator-icon');
+ this.add_style_class_name('tray-icon');
+
+ this.connect('button-press-event', (_actor, _event) => {
+ this.add_style_pseudo_class('active');
+ return Clutter.EVENT_PROPAGATE;
+ });
+ this.connect('button-release-event', (_actor, event) => {
+ this._icon.click(event);
+ this.remove_style_pseudo_class('active');
+ return Clutter.EVENT_PROPAGATE;
+ });
+ this.connect('key-press-event', (_actor, event) => {
+ this.add_style_pseudo_class('active');
+ this._icon.click(event);
+ return Clutter.EVENT_PROPAGATE;
+ });
+ this.connect('key-release-event', (_actor, event) => {
+ this._icon.click(event);
+ this.remove_style_pseudo_class('active');
+ return Clutter.EVENT_PROPAGATE;
+ });
+
+ Util.connectSmart(this._icon, 'destroy', this, () => {
+ icon.clear_effects();
+ this.destroy();
+ });
+
+ const settings = SettingsManager.getDefaultGSettings();
+ Util.connectSmart(settings, 'changed::icon-size', this, this._updateIconSize);
+
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+ Util.connectSmart(themeContext, 'notify::scale-factor', this, () =>
+ this._updateIconSize());
+
+ this._updateIconSize();
+ }
+
+ _onDestroy() {
+ Util.Logger.debug(`Destroying legacy tray icon ${this.uniqueId}`);
+
+ if (this._waitDoubleClickPromise)
+ this._waitDoubleClickPromise.cancel();
+
+ super._onDestroy();
+ }
+
+ isReady() {
+ return !!this._icon;
+ }
+
+ get uniqueId() {
+ return `legacy:${this._icon.wm_class}:${this._icon.pid}`;
+ }
+
+ vfunc_navigate_focus(from, direction) {
+ this.grab_key_focus();
+ return super.vfunc_navigate_focus(from, direction);
+ }
+
+ _getSimulatedButtonEvent(touchEvent) {
+ const event = Clutter.Event.new(Clutter.EventType.BUTTON_RELEASE);
+ event.set_button(1);
+ event.set_time(touchEvent.get_time());
+ event.set_flags(touchEvent.get_flags());
+ event.set_stage(global.stage);
+ event.set_source(touchEvent.get_source());
+ event.set_coords(...touchEvent.get_coords());
+ event.set_state(touchEvent.get_state());
+ return event;
+ }
+
+ vfunc_touch_event(event) {
+ // Under X11 we rely on emulated pointer events
+ const isWayland = typeof imports.gi.Meta.is_wayland_compositor === 'function'
+ ? imports.gi.Meta.is_wayland_compositor()
+ : global.display?.is_wayland_compositor?.() ?? true;
+ if (!isWayland)
+ return Clutter.EVENT_PROPAGATE;
+
+ const slot = event.get_event_sequence().get_slot();
+
+ if (!this._touchPressSlot &&
+ event.get_type() === Clutter.EventType.TOUCH_BEGIN) {
+ this.add_style_pseudo_class('active');
+ this._touchButtonEvent = this._getSimulatedButtonEvent(event);
+ this._touchPressSlot = slot;
+ this._touchDelayPromise = new PromiseUtils.TimeoutPromise(
+ AppDisplay.MENU_POPUP_TIMEOUT);
+ this._touchDelayPromise.then(() => {
+ delete this._touchDelayPromise;
+ delete this._touchPressSlot;
+ this._touchButtonEvent.set_button(3);
+ this._icon.click(this._touchButtonEvent);
+ this.remove_style_pseudo_class('active');
+ });
+ } else if (event.get_type() === Clutter.EventType.TOUCH_END &&
+ this._touchPressSlot === slot) {
+ delete this._touchPressSlot;
+ delete this._touchButtonEvent;
+ if (this._touchDelayPromise) {
+ this._touchDelayPromise.cancel();
+ delete this._touchDelayPromise;
+ }
+
+ this._icon.click(this._getSimulatedButtonEvent(event));
+ this.remove_style_pseudo_class('active');
+ } else if (event.get_type() === Clutter.EventType.TOUCH_UPDATE &&
+ this._touchPressSlot === slot) {
+ this.add_style_pseudo_class('active');
+ this._touchButtonEvent = this._getSimulatedButtonEvent(event);
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ vfunc_leave_event(event) {
+ this.remove_style_pseudo_class('active');
+
+ if (this._touchDelayPromise) {
+ this._touchDelayPromise.cancel();
+ delete this._touchDelayPromise;
+ }
+
+ return super.vfunc_leave_event(event);
+ }
+
+ _updateIconSize() {
+ const settings = SettingsManager.getDefaultGSettings();
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ let iconSize = settings.get_int('icon-size');
+
+ if (iconSize <= 0)
+ iconSize = DEFAULT_ICON_SIZE;
+
+ this.height = -1;
+ this._icon.set({
+ width: iconSize * scaleFactor,
+ height: iconSize * scaleFactor,
+ xAlign: Clutter.ActorAlign.CENTER,
+ yAlign: Clutter.ActorAlign.CENTER,
+ });
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/DBusMenu.xml b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/DBusMenu.xml
new file mode 100644
index 0000000..e98e04e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/DBusMenu.xml
@@ -0,0 +1,66 @@
+<interface name="com.canonical.dbusmenu">
+ <!-- Properties -->
+ <property name="Version" type="u" access="read" />
+ <property name="TextDirection" type="s" access="read" />
+ <property name="Status" type="s" access="read" />
+ <property name="IconThemePath" type="as" access="read" />
+
+ <!-- Functions -->
+ <method name="GetLayout">
+ <arg type="i" name="parentId" direction="in" />
+ <arg type="i" name="recursionDepth" direction="in" />
+ <arg type="as" name="propertyNames" direction="in" />
+ <arg type="u" name="revision" direction="out" />
+ <arg type="(ia{sv}av)" name="layout" direction="out" />
+ </method>
+
+ <method name="GetGroupProperties">
+ <arg type="ai" name="ids" direction="in" />
+ <arg type="as" name="propertyNames" direction="in" />
+ <arg type="a(ia{sv})" name="properties" direction="out" />
+ </method>
+
+ <method name="GetProperty">
+ <arg type="i" name="id" direction="in" />
+ <arg type="s" name="name" direction="in" />
+ <arg type="v" name="value" direction="out" />
+ </method>
+
+ <method name="Event">
+ <arg type="i" name="id" direction="in" />
+ <arg type="s" name="eventId" direction="in" />
+ <arg type="v" name="data" direction="in" />
+ <arg type="u" name="timestamp" direction="in" />
+ </method>
+
+ <method name="EventGroup">
+ <arg type="a(isvu)" name="events" direction="in" />
+ <arg type="ai" name="idErrors" direction="out" />
+ </method>
+
+ <method name="AboutToShow">
+ <arg type="i" name="id" direction="in" />
+ <arg type="b" name="needUpdate" direction="out" />
+ </method>
+
+ <method name="AboutToShowGroup">
+ <arg type="ai" name="ids" direction="in" />
+ <arg type="ai" name="updatesNeeded" direction="out" />
+ <arg type="ai" name="idErrors" direction="out" />
+ </method>
+
+ <!-- Signals
+ <signal name="ItemsPropertiesUpdated">
+ <arg type="a(ia{sv})" name="updatedProps" direction="out" />
+ <arg type="a(ias)" name="removedProps" direction="out" />
+ </signal>
+ <signal name="LayoutUpdated">
+ <arg type="u" name="revision" direction="out" />
+ <arg type="i" name="parent" direction="out" />
+ </signal>
+ <signal name="ItemActivationRequested">
+ <arg type="i" name="id" direction="out" />
+ <arg type="u" name="timestamp" direction="out" />
+ </signal>
+ -->
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierItem.xml b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierItem.xml
new file mode 100644
index 0000000..8c36294
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierItem.xml
@@ -0,0 +1,130 @@
+<!-- Based on:
+ https://invent.kde.org/frameworks/knotifications/-/blob/master/src/org.kde.StatusNotifierItem.xml
+-->
+
+<interface name="org.kde.StatusNotifierItem">
+
+ <property name="Category" type="s" access="read"/>
+ <property name="Id" type="s" access="read"/>
+ <property name="Title" type="s" access="read"/>
+ <property name="Status" type="s" access="read"/>
+ <property name="WindowId" type="i" access="read"/>
+
+ <!-- An additional path to add to the theme search path to find the icons specified above. -->
+ <property name="IconThemePath" type="s" access="read"/>
+ <property name="Menu" type="o" access="read"/>
+ <property name="ItemIsMenu" type="b" access="read"/>
+
+
+ <!-- main icon -->
+ <!-- names are preferred over pixmaps -->
+ <property name="IconName" type="s" access="read"/>
+
+ <!--struct containing width, height and image data-->
+ <property name="IconPixmap" type="a(iiay)" access="read">
+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/>
+ </property>
+
+ <property name="OverlayIconName" type="s" access="read"/>
+
+ <property name="OverlayIconPixmap" type="a(iiay)" access="read">
+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/>
+ </property>
+
+
+ <!-- Requesting attention icon -->
+ <property name="AttentionIconName" type="s" access="read"/>
+
+ <!--same definition as image-->
+ <property name="AttentionIconPixmap" type="a(iiay)" access="read">
+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/>
+ </property>
+
+ <property name="AttentionMovieName" type="s" access="read"/>
+
+
+
+ <!-- tooltip data -->
+
+ <!--(iiay) is an image-->
+ <!-- We disable this as we don't support tooltip, so no need to go through it
+ <property name="ToolTip" type="(sa(iiay)ss)" access="read">
+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusToolTipStruct"/>
+ </property>
+ -->
+
+ <!-- interaction: the systemtray wants the application to do something -->
+ <method name="ContextMenu">
+ <!-- we're passing the coordinates of the icon, so the app knows where to put the popup window -->
+ <arg name="x" type="i" direction="in"/>
+ <arg name="y" type="i" direction="in"/>
+ </method>
+
+ <method name="Activate">
+ <arg name="x" type="i" direction="in"/>
+ <arg name="y" type="i" direction="in"/>
+ </method>
+
+ <method name="ProvideXdgActivationToken">
+ <arg name="token" type="s" direction="in"/>
+ </method>
+
+ <method name="SecondaryActivate">
+ <arg name="x" type="i" direction="in"/>
+ <arg name="y" type="i" direction="in"/>
+ </method>
+
+ <method name="XAyatanaSecondaryActivate">
+ <arg name="timestamp" type="u" direction="in"/>
+ </method>
+
+ <method name="Scroll">
+ <arg name="delta" type="i" direction="in"/>
+ <arg name="orientation" type="s" direction="in"/>
+ </method>
+
+ <!-- Signals: the client wants to change something in the status
+ <signal name="NewTitle">
+ </signal>
+
+ <signal name="NewIcon">
+ </signal>
+
+ <signal name="NewAttentionIcon">
+ </signal>
+
+ <signal name="NewOverlayIcon">
+ </signal>
+ -->
+ <!-- We disable this as we don't support tooltip, so no need to go through it
+ <signal name="NewToolTip">
+ </signal>
+ -->
+
+ <!--
+ <signal name="NewStatus">
+ <arg name="status" type="s"/>
+ </signal>
+ -->
+
+
+ <!-- The following items are not supported by specs, but widely used
+ <signal name="NewIconThemePath">
+ <arg type="s" name="icon_theme_path" direction="out" />
+ </signal>
+
+ <signal name="NewMenu"></signal>
+ -->
+
+ <!-- ayatana labels -->
+ <!-- These are commented out because GDBusProxy would otherwise require them,
+ but they are not available for KDE indicators
+ -->
+ <!--<signal name="XAyatanaNewLabel">
+ <arg type="s" name="label" direction="out" />
+ <arg type="s" name="guide" direction="out" />
+ </signal>
+ <property name="XAyatanaLabel" type="s" access="read" />
+ <property name="XAyatanaLabelGuide" type="s" access="read" />-->
+
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierWatcher.xml b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierWatcher.xml
new file mode 100644
index 0000000..e450d04
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces-xml/StatusNotifierWatcher.xml
@@ -0,0 +1,38 @@
+<interface name="org.kde.StatusNotifierWatcher">
+
+ <!-- methods -->
+ <method name="RegisterStatusNotifierItem">
+ <arg name="service" type="s" direction="in"/>
+ </method>
+
+ <method name="RegisterStatusNotifierHost">
+ <arg name="service" type="s" direction="in"/>
+ </method>
+
+
+ <!-- properties -->
+
+ <property name="RegisteredStatusNotifierItems" type="as" access="read">
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QStringList"/>
+ </property>
+
+ <property name="IsStatusNotifierHostRegistered" type="b" access="read"/>
+
+ <property name="ProtocolVersion" type="i" access="read"/>
+
+
+ <!-- signals -->
+ <signal name="StatusNotifierItemRegistered">
+ <arg type="s"/>
+ </signal>
+
+ <signal name="StatusNotifierItemUnregistered">
+ <arg type="s"/>
+ </signal>
+
+ <signal name="StatusNotifierHostRegistered">
+ </signal>
+
+ <signal name="StatusNotifierHostUnregistered">
+ </signal>
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces.js
new file mode 100644
index 0000000..0a97907
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/interfaces.js
@@ -0,0 +1,52 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+export let StatusNotifierItem = null;
+export let StatusNotifierWatcher = null;
+export let DBusMenu = null;
+
+// loads a xml file into an in-memory string
+function loadInterfaceXml(extension, filename) {
+ const interfacesDir = extension.dir.get_child('interfaces-xml');
+ const file = interfacesDir.get_child(filename);
+ const [result, contents] = imports.gi.GLib.file_get_contents(file.get_path());
+
+ if (result) {
+ // HACK: The "" + trick is important as hell because file_get_contents returns
+ // an object (WTF?) but Gio.makeProxyWrapper requires `typeof() === "string"`
+ // Otherwise, it will try to check `instanceof XML` and fail miserably because there
+ // is no `XML` on very recent SpiderMonkey releases (or, if SpiderMonkey is old enough,
+ // will spit out a TypeError soon).
+ let nodeContents = contents;
+ if (contents instanceof Uint8Array)
+ nodeContents = imports.byteArray.toString(contents);
+ return `<node>${nodeContents}</node>`;
+ } else {
+ throw new Error(`AppIndicatorSupport: Could not load file: ${filename}`);
+ }
+}
+
+export function initialize(extension) {
+ StatusNotifierItem = loadInterfaceXml(extension, 'StatusNotifierItem.xml');
+ StatusNotifierWatcher = loadInterfaceXml(extension, 'StatusNotifierWatcher.xml');
+ DBusMenu = loadInterfaceXml(extension, 'DBusMenu.xml');
+}
+
+export function destroy() {
+ StatusNotifierItem = null;
+ StatusNotifierWatcher = null;
+ DBusMenu = null;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ar/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ar/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..6c73616
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ar/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/bg/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/bg/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..2289e6d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/bg/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/cs/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/cs/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..4cedb41
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/cs/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/de/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/de/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..6edb419
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/de/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/fr/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/fr/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..ba0d667
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/fr/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/hu/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/hu/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..ffbe4ed
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/hu/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/it/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/it/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..8b4b367
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/it/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ja/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ja/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..d22309b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ja/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ka/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ka/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..0773c28
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ka/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ko/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ko/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..6d33af8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ko/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/nl/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/nl/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..9d81088
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/nl/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/oc/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/oc/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..fe31bca
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/oc/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pl/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pl/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..4536d6e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pl/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pt_BR/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pt_BR/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..0bb1b44
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/pt_BR/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ru/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ru/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..b4dfbe5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ru/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sk/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sk/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..060d9c3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sk/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..a7770c5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr@latin/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr@latin/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..af2179f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/sr@latin/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ta/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ta/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..9b6e5a6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/ta/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/tr/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/tr/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..d31e5f2
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/tr/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/uk/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/uk/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..88c7fbc
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/uk/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_CN/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_CN/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..df28844
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_CN/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_TW/LC_MESSAGES/AppIndicatorExtension.mo b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_TW/LC_MESSAGES/AppIndicatorExtension.mo
new file mode 100644
index 0000000..f66fd67
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/locale/zh_TW/LC_MESSAGES/AppIndicatorExtension.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/logger.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/logger.js
new file mode 100644
index 0000000..3c60400
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/logger.js
@@ -0,0 +1,105 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import GLib from 'gi://GLib';
+
+/**
+ * Helper class for logging stuff
+ */
+export class Logger {
+ static _logStructured(logLevel, message, extraFields = {}) {
+ if (!Object.values(GLib.LogLevelFlags).includes(logLevel)) {
+ Logger._logStructured(GLib.LogLevelFlags.LEVEL_WARNING,
+ 'logLevel is not a valid GLib.LogLevelFlags');
+ return;
+ }
+
+ if (!Logger._levels.includes(logLevel))
+ return;
+
+ let fields = {
+ 'SYSLOG_IDENTIFIER': Logger._uuid,
+ 'MESSAGE': `${message}`,
+ };
+
+ let thisFile = null;
+ const {stack} = new Error();
+ for (let stackLine of stack.split('\n')) {
+ stackLine = stackLine.replace('resource:///org/gnome/Shell/', '');
+ const [code, line] = stackLine.split(':');
+ const [func, file] = code.split(/@(.+)/);
+
+ if (!thisFile || thisFile === file) {
+ thisFile = file;
+ continue;
+ }
+
+ fields = Object.assign(fields, {
+ 'CODE_FILE': file || '',
+ 'CODE_LINE': line || '',
+ 'CODE_FUNC': func || '',
+ });
+
+ break;
+ }
+
+ GLib.log_structured(Logger._domain, logLevel, Object.assign(fields, extraFields));
+ }
+
+ static init(extension) {
+ if (Logger._domain)
+ return;
+
+ const allLevels = Object.values(GLib.LogLevelFlags);
+ const domains = GLib.getenv('G_MESSAGES_DEBUG');
+ const {name: domain} = extension.metadata;
+ Logger._uuid = extension.metadata.uuid;
+ Logger._domain = domain.replaceAll(' ', '-');
+
+ if (domains === 'all' || (domains && domains.split(' ').includes(Logger._domain))) {
+ Logger._levels = allLevels;
+ } else {
+ Logger._levels = allLevels.filter(
+ l => l <= GLib.LogLevelFlags.LEVEL_WARNING);
+ }
+ }
+
+ static destroy() {
+ delete Logger._domain;
+ delete Logger._uuid;
+ delete Logger._levels;
+ }
+
+ static debug(message) {
+ Logger._logStructured(GLib.LogLevelFlags.LEVEL_DEBUG, message);
+ }
+
+ static message(message) {
+ Logger._logStructured(GLib.LogLevelFlags.LEVEL_MESSAGE, message);
+ }
+
+ static warn(message) {
+ Logger._logStructured(GLib.LogLevelFlags.LEVEL_WARNING, message);
+ }
+
+ static error(message) {
+ Logger._logStructured(GLib.LogLevelFlags.LEVEL_ERROR, message);
+ }
+
+ static critical(message) {
+ Logger._logStructured(GLib.LogLevelFlags.LEVEL_CRITICAL, message);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/metadata.json
new file mode 100644
index 0000000..96e3e2d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/metadata.json
@@ -0,0 +1,18 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Adds AppIndicator, KStatusNotifierItem and legacy Tray icons support to the Shell",
+ "gettext-domain": "AppIndicatorExtension",
+ "name": "AppIndicator and KStatusNotifierItem Support",
+ "settings-schema": "org.gnome.shell.extensions.appindicator",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/ubuntu/gnome-shell-extension-appindicator",
+ "uuid": "appindicatorsupport@rgcjonas.gmail.com",
+ "version": 64
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/pixmapsUtils.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/pixmapsUtils.js
new file mode 100644
index 0000000..ce4c3f3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/pixmapsUtils.js
@@ -0,0 +1,68 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+export function argbToRgba(src) {
+ const dest = new Uint8Array(src.length);
+
+ for (let j = 0; j < src.length; j += 4) {
+ const srcAlpha = src[j];
+
+ dest[j] = src[j + 1]; /* red */
+ dest[j + 1] = src[j + 2]; /* green */
+ dest[j + 2] = src[j + 3]; /* blue */
+ dest[j + 3] = srcAlpha; /* alpha */
+ }
+
+ return dest;
+}
+
+export function getBestPixmap(pixmapsVariant, preferredSize) {
+ if (!pixmapsVariant)
+ throw new TypeError('null pixmapsVariant');
+
+ const pixmapsVariantsArray = new Array(pixmapsVariant.n_children());
+ if (!pixmapsVariantsArray.length)
+ throw TypeError('Empty Icon found');
+
+ for (let i = 0; i < pixmapsVariantsArray.length; ++i)
+ pixmapsVariantsArray[i] = pixmapsVariant.get_child_value(i);
+
+ const pixmapsSizedArray = pixmapsVariantsArray.map((pixmapVariant, index) => ({
+ width: pixmapVariant.get_child_value(0).unpack(),
+ height: pixmapVariant.get_child_value(1).unpack(),
+ index,
+ }));
+
+ const sortedIconPixmapArray = pixmapsSizedArray.sort(
+ ({width: widthA, height: heightA}, {width: widthB, height: heightB}) => {
+ const areaA = widthA * heightA;
+ const areaB = widthB * heightB;
+
+ return areaA - areaB;
+ });
+
+ // we prefer any pixmap that is equal or bigger than our requested size
+ const qualifiedIconPixmapArray = sortedIconPixmapArray.filter(({width, height}) =>
+ width >= preferredSize && height >= preferredSize);
+
+ const {width, height, index} = qualifiedIconPixmapArray.length > 0
+ ? qualifiedIconPixmapArray[0] : sortedIconPixmapArray.pop();
+
+ const pixmapVariant = pixmapsVariantsArray[index].get_child_value(2);
+ const rowStride = width * 4; // hopefully this is correct
+
+ return {pixmapVariant, width, height, rowStride};
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/customIconPage.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/customIconPage.js
new file mode 100644
index 0000000..b132562
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/customIconPage.js
@@ -0,0 +1,240 @@
+/* exported GeneralPage*/
+
+import Adw from 'gi://Adw';
+import Gtk from 'gi://Gtk';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+const IconData = GObject.registerClass({
+ GTypeName: 'IconData',
+ Properties: {
+ 'id': GObject.ParamSpec.string(
+ 'id',
+ 'Id',
+ 'A read and write string property',
+ GObject.ParamFlags.READWRITE,
+ ''
+ ),
+ 'name': GObject.ParamSpec.string(
+ 'name',
+ 'Name',
+ 'A read and write string property',
+ GObject.ParamFlags.READWRITE,
+ ''
+ ),
+ 'at-name': GObject.ParamSpec.string(
+ 'at-name',
+ 'At Name',
+ 'A read and write string property',
+ GObject.ParamFlags.READWRITE,
+ ''
+ ),
+
+ },
+}, class IconData extends GObject.Object {
+ constructor(props = {}) {
+ super(props);
+ }
+
+ get id() {
+ if (this._id === 'undefined')
+ this._id = null;
+ return this._id;
+ }
+
+ set id(value) {
+ if (this.id === value)
+ return;
+
+ this._id = value;
+ this.notify('id');
+ }
+
+ get name() {
+ if (this._name === 'undefined')
+ this._name = null;
+ return this._name;
+ }
+
+ set name(value) {
+ if (this.name === value)
+ return;
+
+ this._name = value;
+ this.notify('name');
+ }
+
+ get atName() {
+ if (this._atName === 'undefined')
+ this._atName = null;
+ return this._atName;
+ }
+
+ set atName(value) {
+ if (this.atName === value)
+ return;
+
+ this._atName = value;
+ this.notify('at-name');
+ }
+});
+
+export var CustomIconPage = GObject.registerClass(
+class AppIndicatorCustomIconPage extends Adw.PreferencesPage {
+ _init(settings, settingsKey) {
+ super._init({
+ title: _('Custom Icons'),
+ icon_name: 'custom-icons-symbolic',
+ name: 'Custom Icons Page',
+ });
+ this._settings = settings;
+ this._settingsKey = settingsKey;
+
+ this.group = new Adw.PreferencesGroup();
+
+ this.container = new Gtk.Box({
+ halign: Gtk.Align.FILL,
+ hexpand: true,
+ orientation: Gtk.Orientation.VERTICAL,
+ });
+
+ this.listStore = new Gio.ListStore({
+ item_type: IconData,
+ });
+
+ const initList = this._settings.get_value(this._settingsKey.CUSTOM_ICONS).deep_unpack();
+ initList.forEach(pair => {
+ let data = new IconData({
+ id: pair[0],
+ name: pair[1],
+ atName: pair[2],
+ });
+ this.listStore.append(data);
+ });
+
+ this.listStore.append(new IconData({id: '', name: '', atName: ''}));
+
+ this.selectionModel = new Gtk.SingleSelection({
+ model: this.listStore,
+ });
+
+ this.columnView = new Gtk.ColumnView({
+ hexpand: true,
+ model: this.selectionModel,
+ reorderable: false,
+ });
+
+ const columnTitles = [
+ _('Indicator ID'),
+ _('Icon Name'),
+ _('Attention Icon Name'),
+ ];
+ const indicatorIdColumn = new Gtk.ColumnViewColumn({
+ title: columnTitles[0],
+ expand: true,
+ });
+ const iconNameColumn = new Gtk.ColumnViewColumn({
+ title: columnTitles[1],
+ expand: true,
+ });
+ const attentionIconNameColumn = new Gtk.ColumnViewColumn({
+ title: columnTitles[2],
+ expand: true,
+ });
+
+
+ const factoryId = new Gtk.SignalListItemFactory();
+ factoryId.connect('setup', (_widget, item) => {
+ const label = new Gtk.EditableLabel({text: ''});
+ item.set_child(label);
+ });
+ factoryId.connect('bind', (_widget, item) => {
+ const label = item.get_child();
+ const data = item.get_item();
+ label.set_text(data.id);
+ label.bind_property('text', data, 'id', GObject.BindingFlags.SYNC_CREATE);
+ label.connect('changed', () => this._autoSave(item));
+ });
+
+ const factoryName = new Gtk.SignalListItemFactory();
+ factoryName.connect('setup', (_widget, item) => {
+ const label = new Gtk.EditableLabel({text: ''});
+ item.set_child(label);
+ });
+ factoryName.connect('bind', (_widget, item) => {
+ const label = item.get_child();
+ const data = item.get_item();
+ label.set_text(data.name);
+ label.bind_property('text', data, 'name', GObject.BindingFlags.SYNC_CREATE);
+ label.connect('changed', () => this._autoSave(item));
+ });
+
+ const factoryItName = new Gtk.SignalListItemFactory();
+ factoryItName.connect('setup', (_widget, item) => {
+ const label = new Gtk.EditableLabel({text: ''});
+ item.set_child(label);
+ });
+ factoryItName.connect('bind', (_widget, item) => {
+ const label = item.get_child();
+ const data = item.get_item();
+ label.set_text(data.atName);
+ label.bind_property('text', data, 'at-name', GObject.BindingFlags.SYNC_CREATE);
+ label.connect('changed', () => this._autoSave(item));
+ });
+
+ indicatorIdColumn.set_factory(factoryId);
+ iconNameColumn.set_factory(factoryName);
+ attentionIconNameColumn.set_factory(factoryItName);
+
+ this.columnView.append_column(indicatorIdColumn);
+ this.columnView.append_column(iconNameColumn);
+ this.columnView.append_column(attentionIconNameColumn);
+
+ this.container.append(this.columnView);
+
+ this.group.add(this.container);
+ this.add(this.group);
+ }
+
+ _autoSave(item) {
+ const storeLength = this.listStore.get_n_items();
+ const newStore = [];
+
+ for (let i = 0; i < storeLength; i++) {
+ const currentItem = this.listStore.get_item(i);
+ const id = currentItem.id;
+ const name = currentItem.name;
+ const atName = currentItem.atName;
+ if (id && name)
+ newStore.push([id, name, atName || '']);
+ }
+
+ this._settings.set_value(this._settingsKey.CUSTOM_ICONS,
+ new GLib.Variant('a(sss)', newStore));
+
+ const id = item.get_item().id;
+ const name = item.get_item().name;
+
+ /* dynamic new entry*/
+ if (storeLength === 1 && (id || name))
+ this.listStore.append(new IconData({id: '', name: '', atName: ''}));
+
+ if (storeLength > 1) {
+ if ((id || name) && item.get_position() >= storeLength - 1)
+ this.listStore.append(new IconData({id: '', name: '', atName: ''}));
+ }
+ }
+
+ _autoDelete(item) {
+ const storeLength = this.listStore.get_n_items();
+ const id = item.get_item().id;
+ const name = item.get_item().name;
+ if (storeLength > 1 && !id && !name &&
+ item.get_position() < storeLength - 1)
+ this.listStore.remove(item.get_position());
+ }
+});
+
+
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/generalPage.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/generalPage.js
new file mode 100644
index 0000000..7a2575b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/preferences/generalPage.js
@@ -0,0 +1,159 @@
+/* exported GeneralPage*/
+
+import Adw from 'gi://Adw';
+import Gtk from 'gi://Gtk';
+import GObject from 'gi://GObject';
+import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+
+export var GeneralPage = GObject.registerClass(
+class AppIndicatorGeneralPage extends Adw.PreferencesPage {
+ _init(settings, settingsKey) {
+ super._init({
+ title: _('General'),
+ icon_name: 'general-preferences-symbolic',
+ name: 'General Page',
+ });
+
+ this._settings = settings;
+ this._settingsKey = settingsKey;
+
+ this.group = new Adw.PreferencesGroup(); // no title since only single group
+
+ const legacyTraySwitch = new Adw.SwitchRow({
+ title: _('Enable Legacy Tray Icons support'),
+ subtitle: _('Add X11 legacy tray icons to the panel area'),
+ active: this._settings.get_boolean(this._settingsKey.LEGACY_TRAY_ENABLED),
+ });
+
+ legacyTraySwitch.connect('notify::active', widget =>
+ this._settings.set_boolean(
+ this._settingsKey.LEGACY_TRAY_ENABLED,
+ widget.get_active()
+ )
+ );
+
+ this.group.add(legacyTraySwitch);
+
+ const compactModeSwitch = new Adw.SwitchRow({
+ title: _('Compact Mode'),
+ subtitle: _('Puts tray indicators closer together'),
+ active: this._settings.get_boolean(this._settingsKey.COMPACT_MODE_ENABLED),
+ });
+
+ compactModeSwitch.connect('notify::active', widget =>
+ this._settings.set_boolean(
+ this._settingsKey.COMPACT_MODE_ENABLED,
+ widget.get_active()
+ )
+ );
+
+ this.group.add(compactModeSwitch);
+
+ this._createSpinRow({
+ title: _('Opacity'),
+ settingsKey: this._settingsKey.ICON_OPACITY,
+ from: 0,
+ to: 255,
+ step: 1,
+ round: true,
+ });
+
+ this._createSpinRow({
+ title: _('Desaturation'),
+ settingsKey: this._settingsKey.ICON_SATURATION,
+ from: 0.0,
+ to: 1.0,
+ step: 0.1,
+ });
+
+ this._createSpinRow({
+ title: _('Brightness'),
+ settingsKey: this._settingsKey.ICON_BRIGHTNESS,
+ from: -1.0,
+ to: 1.0,
+ step: 0.1,
+ });
+
+ this._createSpinRow({
+ title: _('Contrast'),
+ settingsKey: this._settingsKey.ICON_CONTRAST,
+ from: -1.0,
+ to: 1.0,
+ step: 0.1,
+ });
+
+ this._createSpinRow({
+ title: _('Icon Size'),
+ settingsKey: this._settingsKey.ICON_SIZE,
+ from: 0,
+ to: 96,
+ step: 2,
+ round: true,
+ });
+
+ const alignmentList = new Gtk.StringList();
+ const comboItems = [
+ {pos: 'center', label: _('Center')},
+ {pos: 'left', label: _('Left')},
+ {pos: 'right', label: _('Right')},
+ ];
+
+ comboItems.forEach(item => alignmentList.append(item.label));
+
+ const combo = new Adw.ComboRow({
+ title: _('Tray Horizontal Alignment'),
+ model: alignmentList,
+ });
+ const trayPos = this._settings.get_string(this._settingsKey.TRAY_POS);
+ combo.set_selected(comboItems.findIndex(item => trayPos === item.pos));
+
+ combo.connect('notify::selected', widget => this._settings.set_string(
+ this._settingsKey.TRAY_POS, comboItems[widget.get_selected()].pos));
+
+ this.group.add(combo);
+ this.add(this.group);
+ }
+
+ _createSpinRow(args) {
+ let title = args.title || 'Default Title';
+ let subtitle = args.subtitle || null;
+ let settingsKey = args.settingsKey || '';
+ let from = args.from || 0;
+ let to = args.to || 100;
+ let step = args.step || 1;
+ let round = args.round || false;
+
+ let spin = Adw.SpinRow.new_with_range(from, to, step);
+ spin.title = title;
+ if (subtitle !== null)
+ spin.subtitle = subtitle;
+
+ if (round)
+ spin.set_value(this._settings.get_int(settingsKey));
+ else
+ spin.set_value(this._settings.get_int(settingsKey));
+
+
+ spin.connect('input', widget => {
+ if (round)
+ this._settings.set_int(settingsKey, parseInt(widget.get_value(), 10));
+ else
+ this._settings.set_double(settingsKey, widget.get_value());
+
+ return false;
+ });
+ spin.connect('output', widget => {
+ if (round)
+ this._settings.set_int(settingsKey, parseInt(widget.get_value(), 10));
+ else
+ this._settings.set_double(settingsKey, widget.get_value());
+
+ return false;
+ });
+
+ this.group.add(spin);
+ }
+});
+
+
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/prefs.js
new file mode 100644
index 0000000..344ba0a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/prefs.js
@@ -0,0 +1,45 @@
+// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
+
+/* exported init, buildPrefsWidget */
+
+import Gtk from 'gi://Gtk'; // will be removed
+import Gdk from 'gi://Gdk';
+import * as GeneralPreferences from './preferences/generalPage.js';
+import * as CustomIconPreferences from './preferences/customIconPage.js';
+
+import {
+ ExtensionPreferences,
+ gettext as _
+} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+const SettingsKey = {
+ LEGACY_TRAY_ENABLED: 'legacy-tray-enabled',
+ COMPACT_MODE_ENABLED: 'compact-mode-enabled',
+ ICON_SIZE: 'icon-size',
+ ICON_OPACITY: 'icon-opacity',
+ ICON_SATURATION: 'icon-saturation',
+ ICON_BRIGHTNESS: 'icon-brightness',
+ ICON_CONTRAST: 'icon-contrast',
+ TRAY_POS: 'tray-pos',
+ CUSTOM_ICONS: 'custom-icons',
+};
+
+export default class DockPreferences extends ExtensionPreferences {
+ fillPreferencesWindow(window) {
+ const iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default());
+ if (!iconTheme.get_search_path().includes(`${this.path}/icons`))
+ iconTheme.add_search_path(`${this.path}/icons`);
+
+
+ const settings = this.getSettings();
+ const generalPage = new GeneralPreferences.GeneralPage(settings, SettingsKey);
+ const customIconPage = new CustomIconPreferences.CustomIconPage(settings, SettingsKey);
+
+ window.add(generalPage);
+ window.add(customIconPage);
+
+ window.connect('close-request', () => {
+ window.destroy();
+ });
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/promiseUtils.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/promiseUtils.js
new file mode 100644
index 0000000..2749b03
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/promiseUtils.js
@@ -0,0 +1,324 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Meta from 'gi://GdkPixbuf';
+
+import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
+
+export class CancellablePromise extends Promise {
+ constructor(executor, cancellable) {
+ if (!(executor instanceof Function))
+ throw TypeError('executor is not a function');
+
+ if (cancellable && !(cancellable instanceof Gio.Cancellable))
+ throw TypeError('cancellable parameter is not a Gio.Cancellable');
+
+ let rejector;
+ let resolver;
+ super((resolve, reject) => {
+ resolver = resolve;
+ rejector = reject;
+ });
+
+ const {stack: promiseStack} = new Error();
+ this._promiseStack = promiseStack;
+
+ this._resolver = (...args) => {
+ resolver(...args);
+ this._resolved = true;
+ this._cleanup();
+ };
+ this._rejector = (...args) => {
+ rejector(...args);
+ this._rejected = true;
+ this._cleanup();
+ };
+
+ if (!cancellable) {
+ executor(this._resolver, this._rejector);
+ return;
+ }
+
+ this._cancellable = cancellable;
+ this._cancelled = cancellable.is_cancelled();
+ if (this._cancelled) {
+ this._rejector(new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.CANCELLED, 'Promise cancelled'));
+ return;
+ }
+
+ this._cancellationId = cancellable.connect(() => {
+ const id = this._cancellationId;
+ this._cancellationId = 0;
+ GLib.idle_add(GLib.PRIORITY_DEFAULT, () => cancellable.disconnect(id));
+ this.cancel();
+ });
+
+ executor(this._resolver, this._rejector);
+ }
+
+ _cleanup() {
+ if (this._cancellationId)
+ this._cancellable.disconnect(this._cancellationId);
+ }
+
+ get cancellable() {
+ return this._chainRoot._cancellable || null;
+ }
+
+ get _chainRoot() {
+ return this._root ? this._root : this;
+ }
+
+ then(...args) {
+ const ret = super.then(...args);
+
+ /* Every time we call then() on this promise we'd get a new
+ * CancellablePromise however that won't have the properties that the
+ * root one has set, and then it won't be possible to cancel a promise
+ * chain from the last one.
+ * To allow this we keep track of the root promise, make sure that
+ * the same method on the root object is called during cancellation
+ * or any destruction method if you want this to work. */
+ if (ret instanceof CancellablePromise)
+ ret._root = this._chainRoot;
+
+ return ret;
+ }
+
+ resolved() {
+ return !!this._chainRoot._resolved;
+ }
+
+ rejected() {
+ return !!this._chainRoot._rejected;
+ }
+
+ cancelled() {
+ return !!this._chainRoot._cancelled;
+ }
+
+ pending() {
+ return !this.resolved() && !this.rejected();
+ }
+
+ cancel() {
+ if (this._root) {
+ this._root.cancel();
+ return this;
+ }
+
+ if (!this.pending())
+ return this;
+
+ this._cancelled = true;
+ const error = new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.CANCELLED, 'Promise cancelled');
+ error.stack += `## Promise created at:\n${this._promiseStack}`;
+ this._rejector(error);
+
+ return this;
+ }
+}
+
+export class SignalConnectionPromise extends CancellablePromise {
+ constructor(object, signal, cancellable) {
+ if (arguments.length === 1 && object instanceof Function) {
+ super(object);
+ return;
+ }
+
+ if (!(object.connect instanceof Function))
+ throw new TypeError('Not a valid object');
+
+ if (object instanceof GObject.Object &&
+ !GObject.signal_lookup(signal.split(':')[0], object.constructor.$gtype))
+ throw new TypeError(`Signal ${signal} not found on object ${object}`);
+
+ let id;
+ let destroyId;
+ super(resolve => {
+ let connectSignal;
+ if (object instanceof GObject.Object)
+ connectSignal = (sig, cb) => GObject.signal_connect(object, sig, cb);
+ else
+ connectSignal = (sig, cb) => object.connect(sig, cb);
+
+ id = connectSignal(signal, (_obj, ...args) => {
+ if (!args.length)
+ resolve();
+ else
+ resolve(args.length === 1 ? args[0] : args);
+ });
+
+ if (signal !== 'destroy' &&
+ (!(object instanceof GObject.Object) ||
+ GObject.signal_lookup('destroy', object.constructor.$gtype)))
+ destroyId = connectSignal('destroy', () => this.cancel());
+ }, cancellable);
+
+ this._object = object;
+ this._id = id;
+ this._destroyId = destroyId;
+ }
+
+ _cleanup() {
+ if (this._id) {
+ let disconnectSignal;
+
+ if (this._object instanceof GObject.Object)
+ disconnectSignal = id => GObject.signal_handler_disconnect(this._object, id);
+ else
+ disconnectSignal = id => this._object.disconnect(id);
+
+ disconnectSignal(this._id);
+ if (this._destroyId) {
+ disconnectSignal(this._destroyId);
+ this._destroyId = 0;
+ }
+ this._object = null;
+ this._id = 0;
+ }
+
+ super._cleanup();
+ }
+
+ get object() {
+ return this._chainRoot._object;
+ }
+}
+
+export class GSourcePromise extends CancellablePromise {
+ constructor(gsource, priority, cancellable) {
+ if (arguments.length === 1 && gsource instanceof Function) {
+ super(gsource);
+ return;
+ }
+
+ if (gsource.constructor.$gtype !== GLib.Source.$gtype)
+ throw new TypeError(`gsource ${gsource} is not of type GLib.Source`);
+
+ if (priority === undefined)
+ priority = GLib.PRIORITY_DEFAULT;
+ else if (!Number.isInteger(priority))
+ throw TypeError('Invalid priority');
+
+ super(resolve => {
+ gsource.set_priority(priority);
+ gsource.set_callback(() => {
+ resolve();
+ return GLib.SOURCE_REMOVE;
+ });
+ gsource.attach(null);
+ }, cancellable);
+
+ this._gsource = gsource;
+ this._gsource.set_name(`[gnome-shell] ${this.constructor.name} ${
+ new Error().stack.split('\n').filter(line =>
+ !line.match(/misc\/promiseUtils\.js/))[0]}`);
+
+ if (this.rejected())
+ this._gsource.destroy();
+ }
+
+ get gsource() {
+ return this._chainRoot._gsource;
+ }
+
+ _cleanup() {
+ if (this._gsource) {
+ this._gsource.destroy();
+ this._gsource = null;
+ }
+ super._cleanup();
+ }
+}
+
+export class IdlePromise extends GSourcePromise {
+ constructor(priority, cancellable) {
+ if (arguments.length === 1 && priority instanceof Function) {
+ super(priority);
+ return;
+ }
+
+ if (priority === undefined)
+ priority = GLib.PRIORITY_DEFAULT_IDLE;
+
+ super(GLib.idle_source_new(), priority, cancellable);
+ }
+}
+
+export class TimeoutPromise extends GSourcePromise {
+ constructor(interval, priority, cancellable) {
+ if (arguments.length === 1 && interval instanceof Function) {
+ super(interval);
+ return;
+ }
+
+ if (!Number.isInteger(interval) || interval < 0)
+ throw TypeError('Invalid interval');
+
+ super(GLib.timeout_source_new(interval), priority, cancellable);
+ }
+}
+
+export class TimeoutSecondsPromise extends GSourcePromise {
+ constructor(interval, priority, cancellable) {
+ if (arguments.length === 1 && interval instanceof Function) {
+ super(interval);
+ return;
+ }
+
+ if (!Number.isInteger(interval) || interval < 0)
+ throw TypeError('Invalid interval');
+
+ super(GLib.timeout_source_new_seconds(interval), priority, cancellable);
+ }
+}
+
+export class MetaLaterPromise extends CancellablePromise {
+ constructor(laterType, cancellable) {
+ if (arguments.length === 1 && laterType instanceof Function) {
+ super(laterType);
+ return;
+ }
+
+ if (laterType && laterType.constructor.$gtype !== Meta.LaterType.$gtype)
+ throw new TypeError(`laterType ${laterType} is not of type Meta.LaterType`);
+ else if (!laterType)
+ laterType = Meta.LaterType.BEFORE_REDRAW;
+
+ let id;
+ super(resolve => {
+ id = Meta.later_add(laterType, () => {
+ this.remove();
+ resolve();
+ return GLib.SOURCE_REMOVE;
+ });
+ }, cancellable);
+
+ this._id = id;
+ }
+
+ _cleanup() {
+ if (this._id) {
+ Meta.later_remove(this._id);
+ this._id = 0;
+ }
+ super._cleanup();
+ }
+}
+
+export function _promisifySignals(proto) {
+ if (proto.connect_once)
+ return;
+
+ proto.connect_once = function (signal, cancellable) {
+ return new SignalConnectionPromise(this, signal, cancellable);
+ };
+}
+
+_promisifySignals(GObject.Object.prototype);
+_promisifySignals(Signals.EventEmitter.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/gschemas.compiled
new file mode 100644
index 0000000..1a6beff
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/org.gnome.shell.extensions.appindicator.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/org.gnome.shell.extensions.appindicator.gschema.xml
new file mode 100644
index 0000000..c303489
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/schemas/org.gnome.shell.extensions.appindicator.gschema.xml
@@ -0,0 +1,49 @@
+<schemalist gettext-domain="AppIndicatorExtension">
+ <schema id="org.gnome.shell.extensions.appindicator" path="/org/gnome/shell/extensions/appindicator/">
+ <key name="legacy-tray-enabled" type="b">
+ <default>true</default>
+ <summary>Enable legacy tray icons support</summary>
+ </key>
+ <key name="icon-saturation" type="d">
+ <default>0.0</default>
+ <summary>Saturation</summary>
+ </key>
+ <key name="icon-brightness" type="d">
+ <default>0.0</default>
+ <summary>Brightness</summary>
+ </key>
+ <key name="icon-contrast" type="d">
+ <default>0.0</default>
+ <summary>Contrast</summary>
+ </key>
+ <key name="icon-opacity" type="i">
+ <default>240</default>
+ <summary>Opacity</summary>
+ </key>
+ <key name="icon-size" type="i">
+ <default>0</default>
+ <summary>Icon size</summary>
+ <description>Icon size in pixel</description>
+ </key>
+ <key name="compact-mode-enabled" type="b">
+ <default>true</default>
+ <summary>Compact Mode</summary>
+ <description>Reduces the Spacing Between Indicators</description>
+ </key>
+ <key name="tray-pos" type="s">
+ <default>"right"</default>
+ <summary>Position in tray</summary>
+ <description>Set where the Icon tray should appear in Gnome tray</description>
+ </key>
+ <key name="tray-order" type="i">
+ <default>1</default>
+ <summary>Order in tray</summary>
+ <description>Set where the Icon tray should appear among other trays</description>
+ </key>
+ <key name="custom-icons" type="a(sss)">
+ <default>[]</default>
+ <summary>Custom icons</summary>
+ <description>Replace any icons with custom icons from themes</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/settingsManager.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/settingsManager.js
new file mode 100644
index 0000000..6d943b9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/settingsManager.js
@@ -0,0 +1,55 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+let settingsManager;
+
+export class SettingsManager {
+ static initialize(extension) {
+ SettingsManager._settingsManager = new SettingsManager(extension);
+ }
+
+ static destroy() {
+ SettingsManager._settingsManager.destroy();
+ SettingsManager._settingsManager = null;
+ }
+
+ static getDefault() {
+ return this._settingsManager;
+ }
+
+ get gsettings() {
+ return this._gsettings;
+ }
+
+ constructor(extension) {
+ if (settingsManager)
+ throw new Error('SettingsManager is already constructed');
+
+ this._gsettings = extension.getSettings();
+ }
+
+ destroy() {
+ this._gsettings = null;
+ }
+}
+
+export function getDefault() {
+ return SettingsManager.getDefault();
+}
+
+export function getDefaultGSettings() {
+ return SettingsManager.getDefault().gsettings;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/statusNotifierWatcher.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/statusNotifierWatcher.js
new file mode 100644
index 0000000..2c5e54f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/statusNotifierWatcher.js
@@ -0,0 +1,320 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+
+import * as AppIndicator from './appIndicator.js';
+import * as IndicatorStatusIcon from './indicatorStatusIcon.js';
+import * as Interfaces from './interfaces.js';
+import * as PromiseUtils from './promiseUtils.js';
+import * as Util from './util.js';
+import * as DBusUtils from './dbusUtils.js';
+import * as DBusMenu from './dbusMenu.js';
+
+import {DBusProxy} from './dbusProxy.js';
+
+Gio._promisify(Gio.Subprocess.prototype, 'wait_async');
+Gio._promisify(Gio.Subprocess.prototype, 'communicate_async');
+Gio._promisify(Gio.DataInputStream.prototype, 'read_line_async', 'read_line_finish_utf8');
+
+
+// TODO: replace with org.freedesktop and /org/freedesktop when approved
+const KDE_PREFIX = 'org.kde';
+
+export const WATCHER_BUS_NAME = `${KDE_PREFIX}.StatusNotifierWatcher`;
+const WATCHER_OBJECT = '/StatusNotifierWatcher';
+
+const DEFAULT_ITEM_OBJECT_PATH = '/StatusNotifierItem';
+
+/*
+ * The StatusNotifierWatcher class implements the StatusNotifierWatcher dbus object
+ */
+export class StatusNotifierWatcher {
+ constructor(extension, watchDog) {
+ this._watchDog = watchDog;
+ this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(Interfaces.StatusNotifierWatcher, this);
+ try {
+ this._dbusImpl.export(Gio.DBus.session, WATCHER_OBJECT);
+ } catch (e) {
+ Util.Logger.warn(`Failed to export ${WATCHER_OBJECT}`);
+ logError(e);
+ }
+ this._cancellable = new Gio.Cancellable();
+ this._everAcquiredName = false;
+ this._ownName = Gio.DBus.session.own_name(WATCHER_BUS_NAME,
+ Gio.BusNameOwnerFlags.NONE,
+ this._acquiredName.bind(this),
+ this._lostName.bind(this));
+ this._items = new Map();
+
+ try {
+ this._dbusImpl.emit_signal('StatusNotifierHostRegistered', null);
+ } catch (e) {
+ Util.Logger.warn(`Failed to notify registered host ${WATCHER_OBJECT}`);
+ }
+
+ this._seekStatusNotifierItems(extension).catch(e => {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'Looking for StatusNotifierItem\'s');
+ });
+ }
+
+ _acquiredName() {
+ this._everAcquiredName = true;
+ this._watchDog.nameAcquired = true;
+ }
+
+ _lostName() {
+ if (this._everAcquiredName)
+ Util.Logger.debug(`Lost name${WATCHER_BUS_NAME}`);
+ else
+ Util.Logger.warn(`Failed to acquire ${WATCHER_BUS_NAME}`);
+ this._watchDog.nameAcquired = false;
+ }
+
+ async _registerItem(service, busName, objPath) {
+ const id = Util.indicatorId(service, busName, objPath);
+
+ if (this._items.has(id)) {
+ Util.Logger.warn(`Item ${id} is already registered`);
+ return;
+ }
+
+ Util.Logger.debug(`Registering StatusNotifierItem ${id}`);
+
+ try {
+ const indicator = new AppIndicator.AppIndicator(service, busName, objPath);
+ this._items.set(id, indicator);
+ indicator.connect('destroy', () => this._onIndicatorDestroyed(indicator));
+
+ indicator.connect('name-owner-changed', async () => {
+ if (!indicator.hasNameOwner) {
+ try {
+ await new PromiseUtils.TimeoutPromise(500,
+ GLib.PRIORITY_DEFAULT, this._cancellable);
+ if (this._items.has(id) && !indicator.hasNameOwner)
+ indicator.destroy();
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ }
+ }
+ });
+
+ // if the desktop is not ready delay the icon creation and signal emissions
+ await Util.waitForStartupCompletion(indicator.cancellable);
+ const statusIcon = new IndicatorStatusIcon.IndicatorStatusIcon(indicator);
+ IndicatorStatusIcon.addIconToPanel(statusIcon);
+
+ this._dbusImpl.emit_signal('StatusNotifierItemRegistered',
+ GLib.Variant.new('(s)', [indicator.uniqueId]));
+ this._dbusImpl.emit_property_changed('RegisteredStatusNotifierItems',
+ GLib.Variant.new('as', this.RegisteredStatusNotifierItems));
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ throw e;
+ }
+ }
+
+ async _ensureItemRegistered(service, busName, objPath) {
+ const id = Util.indicatorId(service, busName, objPath);
+ const item = this._items.get(id);
+
+ if (item) {
+ // delete the old one and add the new indicator
+ Util.Logger.debug(`Attempting to re-register ${id}; resetting instead`);
+ item.reset();
+ return;
+ }
+
+ await this._registerItem(service, busName, objPath);
+ }
+
+ async _seekStatusNotifierItems(extension) {
+ // Some indicators (*coff*, dropbox, *coff*) do not re-register again
+ // when the plugin is enabled/disabled, thus we need to manually look
+ // for the objects in the session bus that implements the
+ // StatusNotifierItem interface... However let's do it after a low
+ // priority timeout, and using an external process so that it won't
+ // affect startup or memory (as it seems that gjs is not great at
+ // handling the memory of the bus analyzer async code).
+ const cancellable = this._cancellable;
+ await new PromiseUtils.TimeoutSecondsPromise(2, GLib.PRIORITY_LOW, cancellable);
+ const busAnalyzer = GLib.build_filenamev([
+ extension.path, 'tools', 'busAnalyzer.js',
+ ]);
+
+ const subProcess = Gio.Subprocess.new(['gjs', '-m', busAnalyzer],
+ Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE);
+
+ const stdOut = subProcess.get_stdout_pipe();
+ const dataInputStream = new Gio.DataInputStream({base_stream: stdOut});
+ const textDecoder = new TextDecoder();
+
+ while (true) {
+ // eslint-disable-next-line no-await-in-loop
+ const [line] = await dataInputStream.read_line_async(GLib.PRIORITY_DEFAULT,
+ cancellable);
+ if (!line)
+ break;
+
+ try {
+ const {services, name, path} = JSON.parse(textDecoder.decode(line));
+ const ids = [null, ...services].map(s => Util.indicatorId(s, name, path));
+
+ if (ids.every(id => !this._items.has(id))) {
+ const service = services.find(s =>
+ s && s.startsWith('org.kde.StatusNotifierItem')) || services[0];
+ const id = Util.indicatorId(
+ path === DEFAULT_ITEM_OBJECT_PATH ? service : null,
+ name, path);
+ Util.Logger.warn(`Using Brute-force mode for StatusNotifierItem ${id}`);
+ // eslint-disable-next-line no-await-in-loop
+ await this._registerItem(service, name, path);
+ }
+ } catch (e) {
+ logError(e);
+ }
+ }
+
+ const [, stdErr] = await subProcess.communicate_async(null, cancellable);
+ await subProcess.wait_async(cancellable);
+
+ if (subProcess.get_exit_status() !== 0) {
+ const errorLines = textDecoder.decode(stdErr.toArray()).split('\n');
+ const error = new GLib.Error(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED,
+ errorLines[0]);
+ error.stack = `${errorLines.slice(3).join('\n')}${error.stack}`;
+ throw error;
+ }
+ }
+
+ async RegisterStatusNotifierItemAsync(params, invocation) {
+ // it would be too easy if all application behaved the same
+ // instead, ayatana patched gnome apps to send a path
+ // while kde apps send a bus name
+ const [service] = params;
+ let busName, objPath;
+
+ if (service.charAt(0) === '/') { // looks like a path
+ busName = invocation.get_sender();
+ objPath = service;
+ } else if (service.match(DBusUtils.BUS_ADDRESS_REGEX)) {
+ try {
+ busName = await DBusUtils.getUniqueBusName(invocation.get_connection(),
+ service, this._cancellable);
+ } catch (e) {
+ logError(e);
+ }
+ objPath = DEFAULT_ITEM_OBJECT_PATH;
+ }
+
+ if (!busName || !objPath) {
+ const error = `Impossible to register an indicator for parameters '${
+ service.toString()}'`;
+ Util.Logger.warn(error);
+
+ invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError',
+ error);
+ return;
+ }
+
+ try {
+ await this._ensureItemRegistered(service, busName, objPath);
+ invocation.return_value(null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e);
+ invocation.return_dbus_error('org.gnome.gjs.JSError.ValueError',
+ e.message);
+ }
+ }
+
+ _onIndicatorDestroyed(indicator) {
+ const {uniqueId} = indicator;
+ this._items.delete(uniqueId);
+
+ try {
+ this._dbusImpl.emit_signal('StatusNotifierItemUnregistered',
+ GLib.Variant.new('(s)', [uniqueId]));
+ this._dbusImpl.emit_property_changed('RegisteredStatusNotifierItems',
+ GLib.Variant.new('as', this.RegisteredStatusNotifierItems));
+ } catch (e) {
+ Util.Logger.warn(`Failed to emit signals: ${e}`);
+ }
+ }
+
+ RegisterStatusNotifierHostAsync(_service, invocation) {
+ invocation.return_error_literal(
+ Gio.DBusError,
+ Gio.DBusError.NOT_SUPPORTED,
+ 'Registering additional notification hosts is not supported');
+ }
+
+ IsNotificationHostRegistered() {
+ return true;
+ }
+
+ get RegisteredStatusNotifierItems() {
+ return Array.from(this._items.values()).map(i => i.uniqueId);
+ }
+
+ get IsStatusNotifierHostRegistered() {
+ return true;
+ }
+
+ get ProtocolVersion() {
+ return 0;
+ }
+
+ destroy() {
+ if (this._isDestroyed)
+ return;
+
+ // this doesn't do any sync operation and doesn't allow us to hook up
+ // the event of being finished which results in our unholy debounce hack
+ // (see extension.js)
+ this._items.forEach(indicator => indicator.destroy());
+ this._cancellable.cancel();
+
+ try {
+ this._dbusImpl.emit_signal('StatusNotifierHostUnregistered', null);
+ } catch (e) {
+ Util.Logger.warn(`Failed to emit uinregistered signal: ${e}`);
+ }
+
+ Gio.DBus.session.unown_name(this._ownName);
+
+ try {
+ this._dbusImpl.unexport();
+ } catch (e) {
+ Util.Logger.warn(`Failed to unexport watcher object: ${e}`);
+ }
+
+ DBusMenu.DBusClient.destroy();
+ AppIndicator.AppIndicatorProxy.destroy();
+ DBusProxy.destroy();
+ Util.destroyDefaultTheme();
+
+ this._dbusImpl.run_dispose();
+ delete this._dbusImpl;
+
+ delete this._items;
+ this._isDestroyed = true;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/tools/busAnalyzer.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/tools/busAnalyzer.js
new file mode 100644
index 0000000..969f4e6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/tools/busAnalyzer.js
@@ -0,0 +1,46 @@
+#!/usr/bin/env gjs -m
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import GioUnix from 'gi://GioUnix';
+
+import * as DBusUtils from '../dbusUtils.js';
+
+async function seekStatusNotifierItems() {
+ // Some indicators (*coff*, dropbox, *coff*) do not re-register again
+ // when the plugin is enabled/disabled, thus we need to manually look
+ // for the objects in the session bus that implements the StatusNotifierItem
+ // interface...
+ const cancellable = null;
+ const bus = Gio.DBus.session;
+ const uniqueNames = await DBusUtils.getBusNames(bus, cancellable);
+
+ const stdErrOutputStream = new GioUnix.OutputStream({fd: 1, closeFd: true});
+ const introspectName = async name => {
+ const nodes = DBusUtils.introspectBusObject(bus, name, cancellable,
+ ['org.kde.StatusNotifierItem']);
+ const services = [...uniqueNames.get(name)];
+
+ for await (const node of nodes) {
+ const {path} = node;
+ stdErrOutputStream.write(`${JSON.stringify({services, name, path})}\n`,
+ cancellable);
+ }
+ };
+ await Promise.allSettled([...uniqueNames.keys()].map(n => introspectName(n)));
+}
+
+function main(_argv) {
+ const loop = new GLib.MainLoop(null, false);
+
+ let exitCode = 0;
+ seekStatusNotifierItems().catch(e => {
+ logError(e);
+ exitCode = 1;
+ }).finally(() => loop.quit());
+ loop.run();
+
+ return exitCode;
+}
+
+imports.system.exit(main(ARGV));
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/trayIconsManager.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/trayIconsManager.js
new file mode 100644
index 0000000..6ec7ccd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/trayIconsManager.js
@@ -0,0 +1,103 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import Shell from 'gi://Shell';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
+
+import * as IndicatorStatusIcon from './indicatorStatusIcon.js';
+import * as Util from './util.js';
+import * as SettingsManager from './settingsManager.js';
+
+let trayIconsManager;
+
+export class TrayIconsManager extends Signals.EventEmitter {
+ static initialize() {
+ if (!trayIconsManager)
+ trayIconsManager = new TrayIconsManager();
+ return trayIconsManager;
+ }
+
+ static destroy() {
+ trayIconsManager.destroy();
+ }
+
+ constructor() {
+ super();
+
+ if (trayIconsManager)
+ throw new Error('TrayIconsManager is already constructed');
+
+ this._changedId = SettingsManager.getDefaultGSettings().connect(
+ 'changed::legacy-tray-enabled', () => this._toggle());
+
+ this._toggle();
+ }
+
+ _toggle() {
+ if (SettingsManager.getDefaultGSettings().get_boolean('legacy-tray-enabled'))
+ this._enable();
+ else
+ this._disable();
+ }
+
+ _getPanelBgColor() {
+ return Main.panel?.get_parent()
+ ? Main.panel.get_theme_node()?.get_background_color() : null;
+ }
+
+ _enable() {
+ if (this._tray)
+ return;
+
+ this._tray = new Shell.TrayManager({bgColor: this._getPanelBgColor()});
+ Util.connectSmart(this._tray, 'tray-icon-added', this, this.onTrayIconAdded);
+ Util.connectSmart(this._tray, 'tray-icon-removed', this, this.onTrayIconRemoved);
+
+ this._tray.manage_screen(Main.panel);
+ }
+
+ _disable() {
+ if (!this._tray)
+ return;
+
+ IndicatorStatusIcon.getTrayIcons().forEach(i => i.destroy());
+ this._tray.unmanage_screen();
+ this._tray = null;
+ }
+
+ onTrayIconAdded(_tray, icon) {
+ const trayIcon = new IndicatorStatusIcon.IndicatorStatusTrayIcon(icon);
+ IndicatorStatusIcon.addIconToPanel(trayIcon);
+ }
+
+ onTrayIconRemoved(_tray, icon) {
+ try {
+ const [trayIcon] = IndicatorStatusIcon.getTrayIcons().filter(i => i.icon === icon);
+ trayIcon.destroy();
+ } catch (e) {
+ Util.Logger.warning(`No icon container found for ${icon.title} (${icon})`);
+ }
+ }
+
+ destroy() {
+ this.emit('destroy');
+ SettingsManager.getDefaultGSettings().disconnect(this._changedId);
+ this._disable();
+ trayIconsManager = null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/util.js b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/util.js
new file mode 100644
index 0000000..7ec25b5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/appindicatorsupport@rgcjonas.gmail.com/util.js
@@ -0,0 +1,271 @@
+// This file is part of the AppIndicator/KStatusNotifierItem GNOME Shell extension
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import St from 'gi://St';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Config from 'resource:///org/gnome/shell/misc/config.js';
+import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
+
+import {Logger} from './logger.js';
+import {BaseStatusIcon} from './indicatorStatusIcon.js';
+import {BUS_ADDRESS_REGEX} from './dbusUtils.js';
+
+Gio._promisify(Gio._LocalFilePrototype, 'read');
+Gio._promisify(Gio.InputStream.prototype, 'read_bytes_async');
+
+export function indicatorId(service, busName, objectPath) {
+ if (service !== busName && service?.match(BUS_ADDRESS_REGEX))
+ return service;
+
+ return `${busName}@${objectPath}`;
+}
+
+
+export class NameWatcher extends Signals.EventEmitter {
+ constructor(name) {
+ super();
+
+ this._watcherId = Gio.DBus.session.watch_name(name,
+ Gio.BusNameWatcherFlags.NONE, () => {
+ this._nameOnBus = true;
+ Logger.debug(`Name ${name} appeared`);
+ this.emit('changed');
+ this.emit('appeared');
+ }, () => {
+ this._nameOnBus = false;
+ Logger.debug(`Name ${name} vanished`);
+ this.emit('changed');
+ this.emit('vanished');
+ });
+ }
+
+ destroy() {
+ this.emit('destroy');
+
+ Gio.DBus.session.unwatch_name(this._watcherId);
+ delete this._watcherId;
+ }
+
+ get nameOnBus() {
+ return !!this._nameOnBus;
+ }
+}
+
+function connectSmart3A(src, signal, handler) {
+ const id = src.connect(signal, handler);
+ let destroyId = 0;
+
+ if (src.connect && (!(src instanceof GObject.Object) || GObject.signal_lookup('destroy', src))) {
+ destroyId = src.connect('destroy', () => {
+ src.disconnect(id);
+ src.disconnect(destroyId);
+ });
+ }
+
+ return [id, destroyId];
+}
+
+function connectSmart4A(src, signal, target, method) {
+ if (typeof method !== 'function')
+ throw new TypeError('Unsupported function');
+
+ method = method.bind(target);
+ const signalId = src.connect(signal, method);
+ const onDestroy = () => {
+ src.disconnect(signalId);
+ if (srcDestroyId)
+ src.disconnect(srcDestroyId);
+ if (tgtDestroyId)
+ target.disconnect(tgtDestroyId);
+ };
+
+ // GObject classes might or might not have a destroy signal
+ // JS Classes will not complain when connecting to non-existent signals
+ const srcDestroyId = src.connect && (!(src instanceof GObject.Object) ||
+ GObject.signal_lookup('destroy', src)) ? src.connect('destroy', onDestroy) : 0;
+ const tgtDestroyId = target.connect && (!(target instanceof GObject.Object) ||
+ GObject.signal_lookup('destroy', target)) ? target.connect('destroy', onDestroy) : 0;
+
+ return [signalId, srcDestroyId, tgtDestroyId];
+}
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * Connect signals to slots, and remove the connection when either source or
+ * target are destroyed
+ *
+ * Usage:
+ * Util.connectSmart(srcOb, 'signal', tgtObj, 'handler')
+ * or
+ * Util.connectSmart(srcOb, 'signal', () => { ... })
+ */
+export function connectSmart(...args) {
+ if (arguments.length === 4)
+ return connectSmart4A(...args);
+ else
+ return connectSmart3A(...args);
+}
+
+function disconnectSmart3A(src, signalIds) {
+ const [id, destroyId] = signalIds;
+ src.disconnect(id);
+
+ if (destroyId)
+ src.disconnect(destroyId);
+}
+
+function disconnectSmart4A(src, tgt, signalIds) {
+ const [signalId, srcDestroyId, tgtDestroyId] = signalIds;
+
+ disconnectSmart3A(src, [signalId, srcDestroyId]);
+
+ if (tgtDestroyId)
+ tgt.disconnect(tgtDestroyId);
+}
+
+export function disconnectSmart(...args) {
+ if (arguments.length === 2)
+ return disconnectSmart3A(...args);
+ else if (arguments.length === 3)
+ return disconnectSmart4A(...args);
+
+ throw new TypeError('Unexpected number of arguments');
+}
+
+let _defaultTheme;
+export function getDefaultTheme() {
+ if (_defaultTheme)
+ return _defaultTheme;
+
+ _defaultTheme = new St.IconTheme();
+ return _defaultTheme;
+}
+
+export function destroyDefaultTheme() {
+ _defaultTheme = null;
+}
+
+// eslint-disable-next-line valid-jsdoc
+/**
+ * Helper function to wait for the system startup to be completed.
+ * Adding widgets before the desktop is ready to accept them can result in errors.
+ */
+export async function waitForStartupCompletion(cancellable) {
+ if (Main.layoutManager._startingUp)
+ await Main.layoutManager.connect_once('startup-complete', cancellable);
+}
+
+export {Logger};
+
+export function versionCheck(required) {
+ const current = Config.PACKAGE_VERSION;
+ const currentArray = current.split('.');
+ const [major] = currentArray;
+ return major >= required;
+}
+
+export function tryCleanupOldIndicators() {
+ const indicatorType = BaseStatusIcon;
+ const indicators = Object.values(Main.panel.statusArea).filter(i => i instanceof indicatorType);
+
+ try {
+ const panelBoxes = [
+ Main.panel._leftBox, Main.panel._centerBox, Main.panel._rightBox,
+ ];
+
+ panelBoxes.forEach(box =>
+ indicators.push(...box.get_children().filter(i => i instanceof indicatorType)));
+ } catch (e) {
+ logError(e);
+ }
+
+ new Set(indicators).forEach(i => i.destroy());
+}
+
+export function addActor(obj, actor) {
+ if (obj.add_actor)
+ obj.add_actor(actor);
+ else
+ obj.add_child(actor);
+}
+
+export function removeActor(obj, actor) {
+ if (obj.remove_actor)
+ obj.remove_actor(actor);
+ else
+ obj.remove_child(actor);
+}
+
+export const CancellableChild = GObject.registerClass({
+ Properties: {
+ 'parent': GObject.ParamSpec.object(
+ 'parent', 'parent', 'parent',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Gio.Cancellable.$gtype),
+ },
+},
+class CancellableChild extends Gio.Cancellable {
+ _init(parent) {
+ if (parent && !(parent instanceof Gio.Cancellable))
+ throw TypeError('Not a valid cancellable');
+
+ super._init({parent});
+
+ if (parent) {
+ if (parent.is_cancelled()) {
+ this.cancel();
+ return;
+ }
+
+ this._connectToParent();
+ }
+ }
+
+ _connectToParent() {
+ this._connectId = this.parent.connect(() => {
+ this._realCancel();
+
+ if (this._disconnectIdle)
+ return;
+
+ this._disconnectIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ delete this._disconnectIdle;
+ this._disconnectFromParent();
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+ }
+
+ _disconnectFromParent() {
+ if (this._connectId && !this._disconnectIdle) {
+ this.parent.disconnect(this._connectId);
+ delete this._connectId;
+ }
+ }
+
+ _realCancel() {
+ Gio.Cancellable.prototype.cancel.call(this);
+ }
+
+ cancel() {
+ this._disconnectFromParent();
+ this._realCancel();
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/LICENSE b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js
new file mode 100644
index 0000000..8824777
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js
@@ -0,0 +1,265 @@
+import Shell from 'gi://Shell';
+import Clutter from 'gi://Clutter';
+import Cogl from 'gi://Cogl';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import { adjustAnimationTime } from 'resource:///org/gnome/shell/misc/animationUtils.js';
+
+import { PaintSignals } from '../conveniences/paint_signals.js';
+// TODO drop Tweener in favour of Clutter's `ease` (will need to extend the blur effect for it)
+const Tweener = imports.tweener.tweener;
+
+// TODO: Drop GNOME 46 backwards compatibility
+const transparent = Clutter.Color ?
+ Clutter.Color.from_pixel(0x00000000) :
+ new Cogl.Color({
+ red: 0,
+ green: 0,
+ blue: 0,
+ alpha: 0
+ });
+const FOLDER_DIALOG_ANIMATION_TIME = 200;
+
+const DIALOGS_STYLES = [
+ "appfolder-dialogs-transparent",
+ "appfolder-dialogs-light",
+ "appfolder-dialogs-dark"
+];
+
+let original_zoomAndFadeIn = null;
+let original_zoomAndFadeOut = null;
+let sigma;
+let brightness;
+
+let _zoomAndFadeIn = function () {
+ let [sourceX, sourceY] =
+ this._source.get_transformed_position();
+ let [dialogX, dialogY] =
+ this.child.get_transformed_position();
+
+ this.child.set({
+ translation_x: sourceX - dialogX,
+ translation_y: sourceY - dialogY,
+ scale_x: this._source.width / this.child.width,
+ scale_y: this._source.height / this.child.height,
+ opacity: 0,
+ });
+
+ this.set_background_color(transparent);
+
+ let blur_effect = this.get_effect("appfolder-blur");
+
+ blur_effect.radius = 0;
+ blur_effect.brightness = 1.0;
+ Tweener.addTween(blur_effect,
+ {
+ radius: sigma * 2,
+ brightness: brightness,
+ time: adjustAnimationTime(FOLDER_DIALOG_ANIMATION_TIME / 1000),
+ transition: 'easeOutQuad'
+ }
+ );
+
+ this.child.ease({
+ translation_x: 0,
+ translation_y: 0,
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+
+ this._needsZoomAndFade = false;
+
+ if (this._sourceMappedId === 0) {
+ this._sourceMappedId = this._source.connect(
+ 'notify::mapped', this._zoomAndFadeOut.bind(this));
+ }
+};
+
+let _zoomAndFadeOut = function () {
+ if (!this._isOpen)
+ return;
+
+ if (!this._source.mapped) {
+ this.hide();
+ return;
+ }
+
+ let [sourceX, sourceY] =
+ this._source.get_transformed_position();
+ let [dialogX, dialogY] =
+ this.child.get_transformed_position();
+
+ this.set_background_color(transparent);
+
+ let blur_effect = this.get_effect("appfolder-blur");
+ Tweener.addTween(blur_effect,
+ {
+ radius: 0,
+ brightness: 1.0,
+ time: adjustAnimationTime(FOLDER_DIALOG_ANIMATION_TIME / 1000),
+ transition: 'easeInQuad'
+ }
+ );
+
+ this.child.ease({
+ translation_x: sourceX - dialogX,
+ translation_y: sourceY - dialogY,
+ scale_x: this._source.width / this.child.width,
+ scale_y: this._source.height / this.child.height,
+ opacity: 0,
+ duration: FOLDER_DIALOG_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this.child.set({
+ translation_x: 0,
+ translation_y: 0,
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ });
+ this.hide();
+
+ this._popdownCallbacks.forEach(func => func());
+ this._popdownCallbacks = [];
+ },
+ });
+
+ this._needsZoomAndFade = false;
+};
+
+
+export const AppFoldersBlur = class AppFoldersBlur {
+ // we do not use the effects manager and dummy pipelines here because we
+ // really want to manage our sigma value ourself during the transition
+ constructor(connections, settings, _) {
+ this.connections = connections;
+ this.paint_signals = new PaintSignals(connections);
+ this.settings = settings;
+ }
+
+ enable() {
+ this._log("blurring appfolders");
+
+ brightness = this.settings.appfolder.BRIGHTNESS;
+ sigma = this.settings.appfolder.SIGMA;
+
+ let appDisplay = Main.overview._overview.controls._appDisplay;
+
+ if (appDisplay._folderIcons.length > 0) {
+ this.blur_appfolders();
+ }
+
+ this.connections.connect(
+ appDisplay, 'view-loaded', _ => this.blur_appfolders()
+ );
+ }
+
+ blur_appfolders() {
+ let appDisplay = Main.overview._overview.controls._appDisplay;
+
+ if (this.settings.HACKS_LEVEL === 1)
+ this._log("appfolders hack level 1");
+
+ appDisplay._folderIcons.forEach(icon => {
+ icon._ensureFolderDialog();
+
+ if (original_zoomAndFadeIn == null) {
+ original_zoomAndFadeIn = icon._dialog._zoomAndFadeIn;
+ }
+ if (original_zoomAndFadeOut == null) {
+ original_zoomAndFadeOut = icon._dialog._zoomAndFadeOut;
+ }
+
+ let blur_effect = new Shell.BlurEffect({
+ name: "appfolder-blur",
+ radius: sigma * 2,
+ brightness: brightness,
+ mode: Shell.BlurMode.BACKGROUND
+ });
+
+ icon._dialog.remove_effect_by_name("appfolder-blur");
+ icon._dialog.add_effect(blur_effect);
+
+ DIALOGS_STYLES.forEach(
+ style => icon._dialog._viewBox.remove_style_class_name(style)
+ );
+
+ if (this.settings.appfolder.STYLE_DIALOGS > 0)
+ icon._dialog._viewBox.add_style_class_name(
+ DIALOGS_STYLES[this.settings.appfolder.STYLE_DIALOGS - 1]
+ );
+
+ // finally override the builtin functions
+ icon._dialog._zoomAndFadeIn = _zoomAndFadeIn;
+ icon._dialog._zoomAndFadeOut = _zoomAndFadeOut;
+
+
+ // 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.paint_signals.disconnect_all_for_actor(icon._dialog);
+ this.paint_signals.connect(icon._dialog, blur_effect);
+ } else {
+ this.paint_signals.disconnect_all();
+ }
+ });
+ };
+
+ set_sigma(s) {
+ sigma = s;
+ if (this.settings.appfolder.BLUR)
+ this.blur_appfolders();
+ }
+
+ set_brightness(b) {
+ brightness = b;
+ if (this.settings.appfolder.BLUR)
+ this.blur_appfolders();
+ }
+
+ disable() {
+ this._log("removing blur from appfolders");
+
+ let appDisplay = Main.overview._overview.controls._appDisplay;
+
+ if (original_zoomAndFadeIn != null) {
+ appDisplay._folderIcons.forEach(icon => {
+ if (icon._dialog)
+ icon._dialog._zoomAndFadeIn = original_zoomAndFadeIn;
+ });
+ }
+
+ if (original_zoomAndFadeOut != null) {
+ appDisplay._folderIcons.forEach(icon => {
+ if (icon._dialog)
+ icon._dialog._zoomAndFadeOut = original_zoomAndFadeOut;
+ });
+ }
+
+ appDisplay._folderIcons.forEach(icon => {
+ if (icon._dialog) {
+ icon._dialog.remove_effect_by_name("appfolder-blur");
+ DIALOGS_STYLES.forEach(
+ s => icon._dialog._viewBox.remove_style_class_name(s)
+ );
+ }
+ });
+
+ this.connections.disconnect_all();
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > appfolders] ${str}`);
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
new file mode 100644
index 0000000..e783046
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
@@ -0,0 +1,614 @@
+import Meta from 'gi://Meta';
+import Gio from 'gi://Gio';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Config from 'resource:///org/gnome/shell/misc/config.js';
+
+import { ApplicationsService } from '../dbus/services.js';
+import { PaintSignals } from '../conveniences/paint_signals.js';
+import { DummyPipeline } from '../conveniences/dummy_pipeline.js';
+import { Pipeline } from '../conveniences/pipeline.js';
+
+
+/// Converts a wildcard pattern to a RegExp object.
+/// Supports * (matches any sequence) and ? (matches any single character).
+/// Matching is case-insensitive.
+///
+/// @param {string} pattern - The wildcard pattern (e.g., "Firefox*", "*Code*")
+/// @returns {RegExp} The compiled regex pattern
+function wildcardToRegex(pattern) {
+ // Escape special regex characters except * and ?
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
+ // Convert wildcards: * -> .*, ? -> .
+ const regex = '^' + escaped.replace(/\*/g, '.*').replace(/\?/g, '.') + '$';
+ return new RegExp(regex, 'i');
+}
+
+
+/// Compiles an array of wildcard patterns into RegExp objects.
+/// Caches the results to avoid recompilation on every check.
+///
+/// @param {string[]} patterns - Array of wildcard patterns
+/// @param {Map} cache - Cache map storing pattern -> regex mappings
+/// @returns {RegExp[]} Array of compiled regex patterns
+function compilePatterns(patterns, cache) {
+ return patterns.map(pattern => {
+ if (cache.has(pattern)) {
+ return cache.get(pattern);
+ }
+ const regex = wildcardToRegex(pattern);
+ cache.set(pattern, regex);
+ return regex;
+ });
+}
+
+
+/// Tests if a value matches any of the compiled patterns.
+///
+/// @param {string} value - The value to test (e.g., wm_class)
+/// @param {RegExp[]} patterns - Array of compiled regex patterns
+/// @returns {boolean} True if value matches any pattern
+function matchesAnyPattern(value, patterns) {
+ if (!value || patterns.length === 0) {
+ return false;
+ }
+ return patterns.some(pattern => pattern.test(value));
+}
+
+export const ApplicationsBlur = class ApplicationsBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.settings = settings;
+ this.effects_manager = effects_manager;
+ this.paint_signals = new PaintSignals(connections);
+
+ // stores every blurred meta window
+ this.meta_window_map = new Map();
+
+ // cache for compiled patterns to avoid recompilation
+ this._whitelist_pattern_cache = new Map();
+ this._blacklist_pattern_cache = new Map();
+ this._compiled_whitelist = [];
+ this._compiled_blacklist = [];
+
+ // compile initial patterns
+ this._update_patterns();
+ }
+
+ /// Updates the compiled whitelist and blacklist patterns from settings.
+ /// Called during initialization and when whitelist/blacklist settings change.
+ _update_patterns() {
+ const whitelist = this.settings.applications.WHITELIST || [];
+ const blacklist = this.settings.applications.BLACKLIST || [];
+
+ this._compiled_whitelist = compilePatterns(whitelist, this._whitelist_pattern_cache);
+ this._compiled_blacklist = compilePatterns(blacklist, this._blacklist_pattern_cache);
+
+ this._log(`Patterns updated - whitelist: ${whitelist.length}, blacklist: ${blacklist.length}`);
+ }
+
+ enable() {
+ this._log("blurring applications...");
+
+ // export dbus service for preferences
+ this.service = new ApplicationsService;
+ this.service.export();
+
+ this.mutter_gsettings = new Gio.Settings({ schema: 'org.gnome.mutter' });
+
+ // blur already existing windows
+ this.update_all_windows();
+
+ // blur every new window
+ this.connections.connect(
+ global.display,
+ 'window-created',
+ (_meta_display, meta_window) => {
+ this._log("window created");
+
+ if (meta_window)
+ this.track_new(meta_window);
+ }
+ );
+
+ // update window blur when focus is changed
+ this.focused_window_pid = null;
+ this.init_dynamic_opacity();
+ this.connections.connect(
+ global.display,
+ 'focus-window',
+ (_meta_display, meta_window, _p0) => {
+ if (meta_window && meta_window.bms_pid != this.focused_window_pid)
+ this.set_focus_for_window(meta_window);
+ else if (!meta_window)
+ this.set_focus_for_window(null);
+ }
+ );
+
+ this.connect_to_overview();
+ }
+
+ /// Initializes the dynamic opacity for windows, without touching to the connections.
+ /// This is used both when enabling the component, and when changing the dynamic-opacity pref.
+ init_dynamic_opacity() {
+ if (this.settings.applications.DYNAMIC_OPACITY) {
+ // make the currently focused window solid
+ if (global.display.focus_window)
+ this.set_focus_for_window(global.display.focus_window);
+ } else {
+ // remove old focused window if the pref was changed
+ if (this.focused_window_pid)
+ this.set_focus_for_window(null);
+ }
+ }
+
+ /// Connect to the overview being opened/closed to force the blur being
+ /// shown on every window of the workspaces viewer.
+ connect_to_overview() {
+ this.connections.disconnect_all_for(Main.overview);
+
+ if (this.settings.applications.BLUR_ON_OVERVIEW) {
+ // when the overview is opened, show every window actors (which
+ // allows the blur to be shown too)
+ this.connections.connect(
+ Main.overview, 'showing',
+ _ => this.meta_window_map.forEach((meta_window, _pid) => {
+ let window_actor = meta_window.get_compositor_private();
+ window_actor?.show();
+ })
+ );
+
+ // when the overview is closed, hide every actor that is not on the
+ // current workspace (to mimic the original behaviour)
+ this.connections.connect(
+ Main.overview, 'hidden',
+ _ => {
+ this.meta_window_map.forEach((meta_window, _pid) => {
+ let window_actor = meta_window.get_compositor_private();
+
+ if (
+ (!meta_window.get_workspace().active) || meta_window.minimized
+ )
+ window_actor.hide();
+ });
+ }
+ );
+ }
+ }
+
+ /// Iterate through all existing windows and add blur as needed.
+ update_all_windows() {
+ // Recompile patterns in case whitelist/blacklist changed
+ this._update_patterns();
+
+ // remove all previously blurred windows, in the case where the
+ // whitelist was changed
+ this.meta_window_map.forEach(((_meta_window, pid) => {
+ this.remove_blur(pid);
+ }));
+
+ for (
+ let i = 0;
+ i < global.workspace_manager.get_n_workspaces();
+ ++i
+ ) {
+ let workspace = global.workspace_manager.get_workspace_by_index(i);
+ let windows = workspace.list_windows();
+
+ windows.forEach(meta_window => this.track_new(meta_window));
+ }
+ }
+
+ /// Adds the needed signals to every new tracked window, and adds blur if
+ /// needed.
+ /// Accepts only untracked meta windows (i.e no `bms_pid` set)
+ track_new(meta_window) {
+ // create a pid that will follow the window during its whole life
+ const pid = ("" + Math.random()).slice(2, 16);
+ meta_window.bms_pid = pid;
+
+ this._log(`new window tracked, pid: ${pid}`);
+
+ // register the blurred window
+ this.meta_window_map.set(pid, meta_window);
+
+ // update the blur when wm-class is changed
+ this.connections.connect(
+ meta_window, 'notify::wm-class',
+ _ => this.check_blur(meta_window)
+ );
+
+ // update the clip, position, and/or size when the window changes
+ this.connections.connect(
+ meta_window, 'size-changed',
+ _ => this.update_size(pid)
+ );
+ if (this.settings.applications.STATIC_BLUR) {
+ this.connections.connect(
+ meta_window, 'position-changed',
+ _ => this.update_size(pid)
+ );
+ }
+
+ // remove the blur when the window is unmanaged
+ this.connections.connect(
+ meta_window, 'unmanaging',
+ _ => this.untrack_meta_window(pid)
+ );
+
+ this.check_blur(meta_window);
+
+ if (this.settings.applications.STATIC_BLUR && meta_window.get_client_type() === Meta.WindowClientType.X11) {
+ const window_actor = meta_window.get_compositor_private();
+ window_actor.connect('child-added', _ => {
+ if (!meta_window.blur_actor) {
+ this._warn("can't move blur actor to back, it doesn't exist");
+ return;
+ }
+
+ window_actor.set_child_below_sibling(meta_window.blur_actor, null);
+ });
+ }
+ }
+
+ /// Updates the size of the blur actor associated to a meta window from its pid.
+ /// Accepts only tracked meta window (i.e `bms_pid` set), be it blurred or not.
+ update_size(pid) {
+ if (this.meta_window_map.has(pid)) {
+ const meta_window = this.meta_window_map.get(pid);
+ const blur_actor = meta_window.blur_actor;
+ if (blur_actor) {
+ if (this.settings.applications.STATIC_BLUR) {
+ const bg_manager = meta_window.bg_manager;
+ const bg_actor_monitor_index = bg_manager.backgroundActor.monitor;
+ const window_monitor_index = meta_window.get_monitor();
+ const monitor = Main.layoutManager.monitors[window_monitor_index];
+
+ if (bg_actor_monitor_index !== window_monitor_index) {
+ this._log(`application (pid ${pid}) switching to monitor: ${window_monitor_index}`);
+
+ // Recreate the BackgroundActor on the right monitor. This is necessary to make sure differently
+ // sized monitors have the correct scaled image of the wallpaper.
+ bg_manager._monitorIndex = window_monitor_index;
+ bg_manager._updateBackgroundActor();
+
+ // Also to fix differently sized monitor issues.
+ blur_actor.width = monitor.width;
+ blur_actor.height = monitor.height;
+ }
+
+ const frame = meta_window.get_frame_rect();
+ const buffer = meta_window.get_buffer_rect();
+ blur_actor.x = monitor.x - buffer.x;
+ blur_actor.y = monitor.y - buffer.y;
+
+ // set_clip(x-offset, y-offset, width, height)
+ blur_actor.set_clip(frame.x - monitor.x, frame.y - monitor.y, frame.width, frame.height);
+ } else {
+ const allocation = this.compute_allocation(meta_window);
+ blur_actor.x = allocation.x;
+ blur_actor.y = allocation.y;
+ blur_actor.width = allocation.width;
+ blur_actor.height = allocation.height;
+ }
+ }
+ } else
+ // the pid was visibly not removed
+ this.untrack_meta_window(pid);
+ }
+
+ /// Checks if the given actor needs to be blurred.
+ /// Accepts only tracked meta window, be it blurred or not.
+ ///
+ /// In order to be blurred, a window either:
+ /// - is whitelisted in the user preferences if not enable-all
+ /// - is not blacklisted if enable-all
+ ///
+ /// Whitelist and blacklist support wildcard patterns:
+ /// - * matches any sequence of characters
+ /// - ? matches any single character
+ /// - Matching is case-insensitive
+ check_blur(meta_window) {
+ const window_wm_class = meta_window.get_wm_class();
+ const enable_all = this.settings.applications.ENABLE_ALL;
+ if (window_wm_class)
+ this._log(`pid ${meta_window.bms_pid} associated to wm class name ${window_wm_class}`);
+
+
+ // if we are in blacklist mode and the window is not blacklisted
+ // or if we are in whitelist mode and the window is whitelisted
+ if (
+ window_wm_class !== ""
+ && ((enable_all && !matchesAnyPattern(window_wm_class, this._compiled_blacklist))
+ || (!enable_all && matchesAnyPattern(window_wm_class, this._compiled_whitelist))
+ )
+ && [
+ Meta.FrameType.NORMAL,
+ Meta.FrameType.DIALOG,
+ Meta.FrameType.MODAL_DIALOG
+ ].includes(meta_window.get_frame_type())
+ ) {
+ // only blur the window if it is not already done
+ if (!meta_window.blur_actor)
+ this.create_blur_effect(meta_window);
+ }
+
+ // remove blur it is not explicitly whitelisted or un-blacklisted
+ else if (meta_window.blur_actor)
+ this.remove_blur(meta_window.bms_pid);
+ }
+
+ /// Add the blur effect to the window.
+ /// Accepts only tracked meta window that is NOT already blurred.
+ create_blur_effect(meta_window) {
+ const pid = meta_window.bms_pid;
+ const window_actor = meta_window.get_compositor_private();
+
+ let blur_actor;
+
+ if (this.settings.applications.STATIC_BLUR) {
+ const pipeline = new Pipeline(this.effects_manager, global.blur_my_shell._pipelines_manager, this.settings.applications.PIPELINE);
+ const bg_managers = [];
+ blur_actor = pipeline.create_background_with_effects(
+ meta_window.get_monitor(), bg_managers, window_actor,
+ 'bms-application-blurred-widget'
+ );
+
+ if (bg_managers.length > 0) meta_window.bg_manager = bg_managers[0];
+ else // I've never seen this happen, but just in case
+ this._warn(`no bg_manager on blur creation for pid ${pid}`);
+ } else {
+ const pipeline = new DummyPipeline(this.effects_manager, this.settings.applications);
+ [blur_actor, meta_window.bg_manager] = pipeline.create_background_with_effect(
+ window_actor, 'bms-application-blurred-widget'
+ );
+
+ // if hacks are selected, force to repaint the window
+ if (this.settings.HACKS_LEVEL === 1) {
+ this._log("hack level 1");
+
+ this.paint_signals.disconnect_all_for_actor(blur_actor);
+ this.paint_signals.connect(blur_actor, pipeline.effect);
+ } else {
+ this.paint_signals.disconnect_all_for_actor(blur_actor);
+ }
+ }
+
+ meta_window.blur_actor = blur_actor;
+
+ // make sure window is blurred in overview
+ if (this.settings.applications.BLUR_ON_OVERVIEW)
+ this.enforce_window_visibility_on_overview_for(window_actor);
+
+ // update the size
+ this.update_size(pid);
+
+ // set the window actor's opacity
+ this.set_window_opacity(window_actor, this.settings.applications.OPACITY);
+
+ // now set up the signals, for the window actor only: they are disconnected
+ // in `remove_blur`, whereas the signals for the meta window are disconnected
+ // only when the whole component is disabled
+
+ // update the window opacity when it changes, else we don't control it fully
+ this.connections.connect(
+ window_actor, 'notify::opacity',
+ _ => {
+ if (this.focused_window_pid != pid)
+ this.set_window_opacity(window_actor, this.settings.applications.OPACITY);
+ }
+ );
+
+ // hide the blur if window becomes invisible
+ if (!window_actor.visible)
+ blur_actor.hide();
+
+ this.connections.connect(
+ window_actor,
+ 'notify::visible',
+ window_actor => {
+ if (window_actor.visible)
+ meta_window.blur_actor.show();
+ else
+ meta_window.blur_actor.hide();
+ }
+ );
+ }
+
+ /// With `focus=true`, tells us we are focused on said window (which can be null if
+ /// we are not focused anymore). It automatically removes the ancient focus.
+ /// With `focus=false`, just remove the focus from said window (which can still be null).
+ set_focus_for_window(meta_window, focus = true) {
+ let blur_actor = null;
+ let window_actor = null;
+ let new_pid = null;
+ if (meta_window) {
+ blur_actor = meta_window.blur_actor;
+ window_actor = meta_window.get_compositor_private();
+ new_pid = meta_window.bms_pid;
+ }
+
+ if (focus) {
+ // remove old focused window if any
+ if (this.focused_window_pid) {
+ const old_focused_window = this.meta_window_map.get(this.focused_window_pid);
+ if (old_focused_window)
+ this.set_focus_for_window(old_focused_window, false);
+ }
+ // set new focused window pid
+ this.focused_window_pid = new_pid;
+ // if we have blur, hide it and make the window opaque
+ if (this.settings.applications.DYNAMIC_OPACITY && blur_actor) {
+ blur_actor.hide();
+ this.set_window_opacity(window_actor, 255);
+ }
+ }
+ // if we remove the focus and have blur, show it and make the window transparent
+ else if (blur_actor) {
+ blur_actor.show();
+ this.set_window_opacity(window_actor, this.settings.applications.OPACITY);
+ }
+ }
+
+ /// Makes sure that, when the overview is visible, the window actor will
+ /// stay visible no matter what.
+ /// We can instead hide the last child of the window actor, which will
+ /// improve performances without hiding the blur effect.
+ enforce_window_visibility_on_overview_for(window_actor) {
+ this.connections.connect(window_actor, 'notify::visible',
+ _ => {
+ if (this.settings.applications.BLUR_ON_OVERVIEW) {
+ if (
+ !window_actor.visible
+ && Main.overview.visible
+ ) {
+ window_actor.show();
+ window_actor.get_last_child().hide();
+ } else if (
+ window_actor.visible
+ )
+ window_actor.get_last_child().show();
+ }
+ }
+ );
+ }
+
+ /// Set the opacity of the window actor that sits on top of the blur effect.
+ set_window_opacity(window_actor, opacity) {
+ window_actor?.get_children().forEach(child => {
+ if (child.name !== "blur-actor" && child.opacity != opacity)
+ child.opacity = opacity;
+ });
+ }
+
+ /// Update the opacity of all window actors.
+ set_opacity() {
+ let opacity = this.settings.applications.OPACITY;
+
+ this.meta_window_map.forEach(((meta_window, pid) => {
+ if (pid != this.focused_window_pid && meta_window.blur_actor) {
+ let window_actor = meta_window.get_compositor_private();
+ this.set_window_opacity(window_actor, opacity);
+ }
+ }));
+ }
+
+ /// Find the system's window scaling.
+ /// 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_scale(meta_window) {
+ // TODO: Drop GNOME <50 compatibility
+ const gnome_shell_major_version = parseInt(Config.PACKAGE_VERSION.split('.')[0]);
+ const scale_monitor_framebuffer =
+ gnome_shell_major_version >= 50 ||
+ this.mutter_gsettings
+ .get_strv('experimental-features')
+ .includes('scale-monitor-framebuffer');
+ const is_wayland = gnome_shell_major_version >= 50 ||
+ (typeof Meta.is_wayland_compositor === 'function'
+ ? Meta.is_wayland_compositor()
+ : global.display?.is_wayland_compositor?.() ?? true);
+ const monitor_index = meta_window.get_monitor();
+ // check if the window is using wayland, or xwayland/xorg for rendering
+ return !scale_monitor_framebuffer && is_wayland && meta_window.get_client_type() == 0
+ ? Main.layoutManager.monitors[monitor_index].geometry_scale
+ : 1;
+ }
+
+ /// Compute the size and position for a blur actor.
+ /// Coordinates are relative to window buffer's corner.
+ compute_allocation(meta_window) {
+ const scale = this.compute_scale(meta_window);
+
+ let frame = meta_window.get_frame_rect();
+ let buffer = meta_window.get_buffer_rect();
+
+ return {
+ x: (frame.x - buffer.x) / scale,
+ y: (frame.y - buffer.y) / scale,
+ width: frame.width / scale,
+ height: frame.height / scale
+ };
+ }
+
+ change_blur_type() {
+ this._log("resetting...");
+
+ this.disable();
+ setTimeout(_ => this.enable(), 1);
+ }
+
+ change_pipeline() {
+ this.update_all_windows();
+ }
+
+ /// Removes the blur actor to make a blurred window become normal again.
+ /// It however does not untrack the meta window itself.
+ /// Accepts a pid corresponding (or not) to a blurred (or not) meta window.
+ remove_blur(pid) {
+ this._log(`removing blur for pid ${pid}`);
+
+ let meta_window = this.meta_window_map.get(pid);
+ if (meta_window) {
+ let window_actor = meta_window.get_compositor_private();
+ let blur_actor = meta_window.blur_actor;
+ let bg_manager = meta_window.bg_manager;
+
+ if (blur_actor && window_actor) {
+ // reset the opacity
+ this.set_window_opacity(window_actor, 255);
+
+ // remove the blurred actor
+ window_actor.remove_child(blur_actor);
+ bg_manager._bms_pipeline.destroy();
+ bg_manager.destroy();
+ blur_actor.destroy();
+
+ // kinda untrack the blurred actor, as its presence is how we know
+ // whether we are blurred or not
+ delete meta_window.blur_actor;
+ delete meta_window.bg_manager;
+
+ // disconnect the signals of the window actor
+ this.paint_signals.disconnect_all_for_actor(blur_actor);
+ this.connections.disconnect_all_for(window_actor);
+ }
+ }
+ }
+
+ /// Kinda the same as `remove_blur`, but better: it also untracks the window.
+ /// This needs to be called when the component is being disabled, else it
+ /// would cause havoc by having untracked windows during normal operations,
+ /// which is not the point at all!
+ /// Accepts a pid corresponding (or not) to a blurred (or not) meta window.
+ untrack_meta_window(pid) {
+ this.remove_blur(pid);
+ let meta_window = this.meta_window_map.get(pid);
+ if (meta_window) {
+ this.connections.disconnect_all_for(meta_window);
+ this.meta_window_map.delete(pid);
+ }
+ }
+
+ disable() {
+ this._log("removing blur from applications...");
+
+ this.service?.unexport();
+ delete this.mutter_gsettings;
+
+ this.meta_window_map.forEach((_meta_window, pid) => {
+ this.untrack_meta_window(pid);
+ });
+
+ this.connections.disconnect_all();
+ this.paint_signals.disconnect_all();
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > applications] ${str}`);
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > applications] ${str}`);
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/coverflow_alt_tab.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/coverflow_alt_tab.js
new file mode 100644
index 0000000..4e6062a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/coverflow_alt_tab.js
@@ -0,0 +1,91 @@
+import * as Main from "resource:///org/gnome/shell/ui/main.js";
+
+import { PaintSignals } from "../conveniences/paint_signals.js";
+import { Pipeline } from "../conveniences/pipeline.js";
+
+export const CoverflowAltTabBlur = class CoverflowAltTabBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.settings = settings;
+ this.paint_signals = new PaintSignals(connections);
+ this.effects_manager = effects_manager;
+ this.background_actors = [];
+ this.background_managers = [];
+ }
+
+ enable() {
+ this._log("blurring coverflow alt-tab");
+
+ this.update_backgrounds();
+
+ this.connections.connect(
+ Main.layoutManager.uiGroup,
+ "child-added",
+ (_, child) => this.try_blur(child)
+ );
+
+ this.connections.connect(Main.layoutManager, "monitors-changed", (_) => {
+ this.update_backgrounds();
+ });
+ }
+
+ update_backgrounds() {
+ this.remove_background_actors();
+
+ Main.layoutManager.uiGroup
+ .get_children()
+ .forEach((child) => this.try_blur(child));
+ }
+
+ try_blur(actor) {
+ if (
+ actor.constructor.name !== "Meta_BackgroundGroup" ||
+ actor.get_name() !== "coverflow-alt-tab-background-group"
+ ) {
+ return;
+ }
+
+ this._log("found coverflow alt-tab to blur");
+
+ for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
+ const pipeline = new Pipeline(
+ this.effects_manager,
+ global.blur_my_shell._pipelines_manager,
+ this.settings.coverflow_alt_tab.PIPELINE
+ );
+
+ const background_actor = pipeline.create_background_with_effects(
+ i,
+ this.background_managers,
+ actor,
+ "bms-coverflow-alt-tab-blurred-widget"
+ );
+
+ this.background_actors.push(background_actor);
+ }
+ }
+
+ remove_background_actors() {
+ this.background_actors.forEach((actor) => actor.destroy());
+ this.background_actors = [];
+
+ this.background_managers.forEach((background_manager) => {
+ background_manager._bms_pipeline.destroy();
+ background_manager.destroy();
+ });
+ this.background_managers = [];
+ }
+
+ disable() {
+ this._log("removing blur from coverflow alt-tab");
+
+ this.remove_background_actors();
+ this.connections.disconnect_all();
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG) {
+ console.log(`[Blur my Shell > coverflow alt-tab] ${str}`);
+ }
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js
new file mode 100644
index 0000000..4ff212c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js
@@ -0,0 +1,421 @@
+import Meta from 'gi://Meta';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Signals from 'resource:///org/gnome/shell/misc/signals.js';
+
+import { PaintSignals } from '../conveniences/paint_signals.js';
+
+import { Pipeline } from '../conveniences/pipeline.js';
+import { DummyPipeline } from '../conveniences/dummy_pipeline.js';
+
+const DASH_STYLES = [
+ "transparent-dash",
+ "light-dash",
+ "dark-dash"
+];
+
+
+/// 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, dash_container, dash_background,
+ background, background_group, bg_manager
+ ) {
+ // the parent DashBlur object, to communicate
+ this.dash_blur = dash_blur;
+ this.dash = dash;
+ this.dash_container = dash_container;
+ this.dash_background = dash_background;
+ this.background = background;
+ this.background_group = background_group;
+ this.bg_manager = bg_manager;
+ this.settings = dash_blur.settings;
+ this.old_style = this.dash._background.style;
+
+ this.dash_destroy_id = dash.connect('destroy', () => this.remove_dash_blur(false));
+ this.dash_blur_connections_ids = [];
+ this.dash_blur_connections_ids.push(
+ this.dash_blur.connect('remove-dashes', () => this.remove_dash_blur()),
+ this.dash_blur.connect('override-style', () => this.override_style()),
+ this.dash_blur.connect('remove-style', () => this.remove_style()),
+ this.dash_blur.connect('show', () => this.background_group.show()),
+ this.dash_blur.connect('hide', () => this.background_group.hide()),
+ this.dash_blur.connect('update-size', () => this.update_size()),
+ this.dash_blur.connect('change-blur-type', () => this.change_blur_type()),
+ this.dash_blur.connect('update-pipeline', () => this.update_pipeline())
+ );
+ }
+
+ // IMPORTANT: do never call this in a mutable `this.dash_blur.forEach`
+ remove_dash_blur(dash_not_already_destroyed = true) {
+ // remove the style and destroy the effects
+ this.remove_style();
+ this.destroy_dash(dash_not_already_destroyed);
+
+ // remove the dash infos from their list
+ const dash_infos_index = this.dash_blur.dashes.indexOf(this);
+ if (dash_infos_index >= 0)
+ this.dash_blur.dashes.splice(dash_infos_index, 1);
+
+ // disconnect everything
+ this.dash_blur_connections_ids.forEach(id => { if (id) this.dash_blur.disconnect(id); });
+ this.dash_blur_connections_ids = [];
+ if (this.dash_destroy_id)
+ this.dash.disconnect(this.dash_destroy_id);
+ this.dash_destroy_id = null;
+ }
+
+ override_style() {
+ this.remove_style();
+
+ this.dash.set_style_class_name(
+ DASH_STYLES[this.settings.dash_to_dock.STYLE_DASH_TO_DOCK]
+ );
+ }
+
+ remove_style() {
+ this.dash._background.style = this.old_style;
+
+ DASH_STYLES.forEach(
+ style => this.dash.remove_style_class_name(style)
+ );
+ }
+
+ destroy_dash(dash_not_already_destroyed = true) {
+ if (!dash_not_already_destroyed)
+ this.bg_manager.backgroundActor = null;
+
+ this.paint_signals?.disconnect_all();
+ this.dash.get_parent().remove_child(this.background_group);
+ this.bg_manager._bms_pipeline.destroy();
+ this.bg_manager.destroy();
+ this.background_group.destroy();
+ }
+
+ change_blur_type() {
+ this.destroy_dash();
+
+ let [
+ background, background_group, bg_manager, paint_signals
+ ] = this.dash_blur.add_blur(this.dash);
+
+ this.background = background;
+ this.background_group = background_group;
+ this.bg_manager = bg_manager;
+ this.paint_signals = paint_signals;
+
+ this.dash.get_parent().insert_child_at_index(this.background_group, 0);
+
+ this.update_size();
+ }
+
+ update_pipeline() {
+ this.bg_manager._bms_pipeline.change_pipeline_to(
+ this.settings.dash_to_dock.PIPELINE
+ );
+ }
+
+ 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;
+
+ if (this.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 (this.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 (this.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 (this.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;
+ }
+ }
+
+ get_dash_position(dash_container, dash_background) {
+ var x, y;
+
+ let monitor = Main.layoutManager.findMonitorForActor(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 extends Signals.EventEmitter {
+ constructor(connections, settings, _) {
+ super();
+ this.dashes = [];
+ this.connections = connections;
+ this.settings = settings;
+ this.paint_signals = new PaintSignals(connections);
+ this.is_static = this.settings.dash_to_dock.STATIC_BLUR;
+ this.enabled = false;
+ }
+
+ enable() {
+ this.connections.connect(Main.uiGroup, 'child-added', (_, actor) => {
+ if (
+ (actor.get_name() === "dashtodockContainer") &&
+ (actor.constructor.name === 'DashToDock')
+ )
+ this.try_blur(actor);
+ });
+
+ this.blur_existing_dashes();
+ this.connect_to_overview();
+
+ this.update_size();
+
+ this.enabled = true;
+ }
+
+ // Finds all existing dashes on every monitor, and call `try_blur` on them
+ // We cannot only blur `Main.overview.dash`, as there could be several
+ blur_existing_dashes() {
+ this._log("searching for dash");
+
+ // blur every dash found, filtered by name
+ Main.uiGroup.get_children().filter((child) => {
+ return (child.get_name() === "dashtodockContainer") &&
+ (child.constructor.name === 'DashToDock');
+ }).forEach(dash_container => this.try_blur(dash_container));
+ }
+
+ // Tries to blur the dash contained in the given actor
+ try_blur(dash_container) {
+ let dash_box = dash_container._slider.get_child();
+
+ // verify that we did not already blur that dash
+ if (!dash_box.get_children().some(child =>
+ child.get_name() === "bms-dash-backgroundgroup"
+ )) {
+ this._log("dash to dock found, blurring it");
+
+ // finally blur the dash
+ let dash = dash_box.get_children().find(child => {
+ return child.get_name() === 'dash';
+ });
+
+ this.dashes.push(this.blur_dash_from(dash, dash_container));
+ }
+ }
+
+ // Blurs the dash and returns a `DashInfos` containing its information
+ blur_dash_from(dash, dash_container) {
+ let [background, background_group, bg_manager, paint_signals] = this.add_blur(dash);
+
+ // insert the background group to the right element
+ dash.get_parent().insert_child_at_index(background_group, 0);
+
+ // updates size and position on change
+ this.connections.connect(
+ dash,
+ ['notify::width', 'notify::height'],
+ _ => this.update_size()
+ );
+ this.connections.connect(
+ dash_container,
+ ['notify::width', 'notify::height', 'notify::y', 'notify::x'],
+ _ => this.update_size()
+ );
+
+ const dash_background = dash.get_children().find(child => {
+ return child.get_style_class_name() === 'dash-background';
+ });
+
+ // create infos
+ let infos = new DashInfos(
+ this,
+ dash,
+ dash_container,
+ dash_background,
+ background,
+ background_group,
+ bg_manager,
+ paint_signals
+ );
+
+ this.update_size();
+ this.update_background();
+
+ // returns infos
+ return infos;
+ }
+
+ add_blur(dash) {
+ const monitor = Main.layoutManager.findMonitorForActor(dash);
+ if (!monitor)
+ return;
+
+ const background_group = new Meta.BackgroundGroup({
+ name: 'bms-dash-backgroundgroup', width: 0, height: 0
+ });
+
+ let background, bg_manager, paint_signals;
+ let static_blur = this.settings.dash_to_dock.STATIC_BLUR;
+ if (static_blur) {
+ let bg_manager_list = [];
+ const pipeline = new Pipeline(
+ global.blur_my_shell._effects_manager,
+ global.blur_my_shell._pipelines_manager,
+ this.settings.dash_to_dock.PIPELINE
+ );
+ background = pipeline.create_background_with_effects(
+ monitor.index, bg_manager_list,
+ background_group, 'bms-dash-blurred-widget'
+ );
+ bg_manager = bg_manager_list[0];
+ }
+ else {
+ const pipeline = new DummyPipeline(
+ global.blur_my_shell._effects_manager,
+ this.settings.dash_to_dock
+ );
+ [background, bg_manager] = pipeline.create_background_with_effect(
+ background_group, 'bms-dash-blurred-widget'
+ );
+
+ paint_signals = new PaintSignals(this.connections);
+
+ // 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 dash
+ // buttons to cause artifacts on the dash itself
+ //
+ // [1]: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2857
+
+ if (this.settings.HACKS_LEVEL === 1) {
+ this._log("hack level 1");
+
+ paint_signals.disconnect_all();
+ paint_signals.connect(background, pipeline.effect);
+ } else {
+ paint_signals.disconnect_all();
+ }
+ }
+
+ return [background, background_group, bg_manager, paint_signals];
+ }
+
+ change_blur_type() {
+ this.is_static = this.settings.dash_to_dock.STATIC_BLUR;
+ this.emit('change-blur-type');
+
+ this.update_background();
+ }
+
+ /// Connect when overview if opened/closed to hide/show the blur accordingly
+ connect_to_overview() {
+ this.connections.disconnect_all_for(Main.overview);
+
+ if (this.settings.dash_to_dock.UNBLUR_IN_OVERVIEW) {
+ this.connections.connect(
+ Main.overview, 'showing', _ => this.hide()
+ );
+ this.connections.connect(
+ Main.overview, 'hidden', _ => this.show()
+ );
+ }
+ };
+
+ /// Updates the background to either remove it or not, according to the
+ /// user preferences.
+ update_background() {
+ this._log("updating background");
+ if (this.settings.dash_to_dock.OVERRIDE_BACKGROUND)
+ this.emit('override-style');
+ else
+ this.emit('remove-style');
+ }
+
+ update_pipeline() {
+ this.emit('update-pipeline');
+ }
+
+ update_size() {
+ this.emit('update-size');
+ }
+
+ show() {
+ this.emit('show');
+ }
+ hide() {
+ this.emit('hide');
+ }
+
+ disable() {
+ this._log("removing blur from dashes");
+
+ this.emit('remove-dashes');
+
+ this.dashes = [];
+ this.connections.disconnect_all();
+
+ this.enabled = false;
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > dash manager] ${str}`);
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > dash manager] ${str}`);
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/lockscreen.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/lockscreen.js
new file mode 100644
index 0000000..7755ba9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/lockscreen.js
@@ -0,0 +1,89 @@
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import { UnlockDialog } from 'resource:///org/gnome/shell/ui/unlockDialog.js';
+
+import { Pipeline } from '../conveniences/pipeline.js';
+
+const original_createBackground =
+ UnlockDialog.prototype._createBackground;
+const original_updateBackgroundEffects =
+ UnlockDialog.prototype._updateBackgroundEffects;
+const original_updateBackgrounds =
+ UnlockDialog.prototype._updateBackgrounds;
+
+
+export const LockscreenBlur = class LockscreenBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.settings = settings;
+ this.effects_manager = effects_manager;
+ this.enabled = false;
+ }
+
+ enable() {
+ this._log("blurring lockscreen");
+
+ this.update_lockscreen();
+
+ this.enabled = true;
+ }
+
+ update_lockscreen() {
+ UnlockDialog.prototype._createBackground =
+ this._createBackground;
+ UnlockDialog.prototype._updateBackgroundEffects =
+ this._updateBackgroundEffects;
+ UnlockDialog.prototype._updateBackgrounds =
+ this._updateBackgrounds;
+ }
+
+ _createBackground(monitor_index) {
+ let pipeline = new Pipeline(
+ global.blur_my_shell._effects_manager, global.blur_my_shell._pipelines_manager,
+ global.blur_my_shell._settings.lockscreen.PIPELINE
+ );
+
+ pipeline.create_background_with_effects(
+ monitor_index,
+ this._bgManagers,
+ this._backgroundGroup,
+ "screen-shield-background"
+ );
+ }
+
+ _updateBackgroundEffects() {
+ this._updateBackgrounds();
+ }
+
+ _updateBackgrounds() {
+ for (let i = 0; i < this._bgManagers.length; i++) {
+ this._bgManagers[i]._bms_pipeline.destroy();
+ this._bgManagers[i].destroy();
+ }
+
+ this._bgManagers = [];
+ this._backgroundGroup.destroy_all_children();
+
+ for (let i = 0; i < Main.layoutManager.monitors.length; i++)
+ this._createBackground(i);
+ }
+
+ disable() {
+ this._log("removing blur from lockscreen");
+
+ UnlockDialog.prototype._createBackground =
+ original_createBackground;
+ UnlockDialog.prototype._updateBackgroundEffects =
+ original_updateBackgroundEffects;
+ UnlockDialog.prototype._updateBackgrounds =
+ original_updateBackgrounds;
+
+ this.connections.disconnect_all();
+
+ this.enabled = false;
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > lockscreen] ${str}`);
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js
new file mode 100644
index 0000000..50a625e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js
@@ -0,0 +1,226 @@
+import Meta from 'gi://Meta';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import { WorkspaceAnimationController } from 'resource:///org/gnome/shell/ui/workspaceAnimation.js';
+const wac_proto = WorkspaceAnimationController.prototype;
+
+import { Pipeline } from '../conveniences/pipeline.js';
+
+const OVERVIEW_COMPONENTS_STYLE = [
+ "overview-components-light",
+ "overview-components-dark",
+ "overview-components-transparent"
+];
+
+
+export const OverviewBlur = class OverviewBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.settings = settings;
+ this.effects_manager = effects_manager;
+ this.overview_background_managers = [];
+ this.overview_background_group = new Meta.BackgroundGroup(
+ { name: 'bms-overview-backgroundgroup' }
+ );
+ this.animation_background_managers = [];
+ this.animation_background_group = new Meta.BackgroundGroup(
+ { name: 'bms-animation-backgroundgroup' }
+ );
+ this.enabled = false;
+ this.proto_patched = false;
+ }
+
+ enable() {
+ this._log("blurring overview");
+
+ // add css class name for workspace-switch background
+ Main.uiGroup.add_style_class_name("blurred-overview");
+
+ // add css class name to make components semi-transparent if wanted
+ this.update_components_classname();
+
+ // update backgrounds when the component is enabled
+ this.update_backgrounds();
+
+ // connect to monitors change
+ this.connections.connect(Main.layoutManager, 'monitors-changed',
+ _ => this.update_backgrounds()
+ );
+
+ // part for the workspace animation switch
+
+ // make sure not to do this part if the functions were patched prior, as
+ // the functions would call themselves and cause infinite recursion
+ if (!this.proto_patched) {
+ // store original workspace switching methods for restoring them on
+ // disable()
+ this._original_PrepareSwitch = wac_proto._prepareWorkspaceSwitch;
+ this._original_FinishSwitch = wac_proto._finishWorkspaceSwitch;
+
+ const w_m = global.workspace_manager;
+ const outer_this = this;
+
+ // create a blurred background actor for each monitor during a
+ // workspace switch
+ wac_proto._prepareWorkspaceSwitch = function (...params) {
+ outer_this._log("prepare workspace switch");
+ outer_this._original_PrepareSwitch.apply(this, params);
+
+ // this permits to show the blur behind windows that are on
+ // workspaces on the left and right
+ if (
+ outer_this.settings.applications.BLUR
+ ) {
+ let ws_index = w_m.get_active_workspace_index();
+ [ws_index - 1, ws_index + 1].forEach(
+ i => w_m.get_workspace_by_index(i)?.list_windows().forEach(
+ window => window.get_compositor_private().show()
+ )
+ );
+ }
+
+ Main.uiGroup.insert_child_above(
+ outer_this.animation_background_group,
+ global.window_group
+ );
+
+ outer_this.animation_background_managers.forEach(bg_manager => {
+ if (bg_manager._bms_pipeline.actor)
+ if (
+ Meta.prefs_get_workspaces_only_on_primary() &&
+ bg_manager._monitorIndex !== Main.layoutManager.primaryMonitor.index
+ )
+ bg_manager._bms_pipeline.actor.visible = false;
+ else
+ bg_manager._bms_pipeline.actor.visible = true;
+ });
+ };
+
+ // remove the workspace-switch actors when the switch is done
+ wac_proto._finishWorkspaceSwitch = function (...params) {
+ outer_this._log("finish workspace switch");
+ outer_this._original_FinishSwitch.apply(this, params);
+
+ // this hides windows that are not on the current workspace
+ if (
+ outer_this.settings.applications.BLUR
+ )
+ for (let i = 0; i < w_m.get_n_workspaces(); i++) {
+ if (i != w_m.get_active_workspace_index())
+ w_m.get_workspace_by_index(i)?.list_windows().forEach(
+ window => window.get_compositor_private().hide()
+ );
+ }
+
+ Main.uiGroup.remove_child(outer_this.animation_background_group);
+ };
+
+ this.proto_patched = true;
+ }
+
+ this.enabled = true;
+ }
+
+ update_backgrounds() {
+ // remove every old background
+ this.remove_background_actors();
+ // create new backgrounds for the overview and the animation
+ for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
+ const pipeline_overview = new Pipeline(
+ this.effects_manager,
+ global.blur_my_shell._pipelines_manager,
+ this.settings.overview.PIPELINE
+ );
+ pipeline_overview.create_background_with_effects(
+ i, this.overview_background_managers,
+ this.overview_background_group, 'bms-overview-blurred-widget'
+ );
+
+ const pipeline_animation = new Pipeline(
+ this.effects_manager,
+ global.blur_my_shell._pipelines_manager,
+ this.settings.overview.PIPELINE
+ );
+ pipeline_animation.create_background_with_effects(
+ i, this.animation_background_managers,
+ this.animation_background_group, 'bms-animation-blurred-widget'
+ );
+ }
+ // add the container widget for the overview only to the overview group
+ Main.layoutManager.overviewGroup.insert_child_at_index(this.overview_background_group, 0);
+ // make sure it stays below
+ this.connections.connect(Main.layoutManager.overviewGroup, "child-added", (_, child) => {
+ if (child !== this.overview_background_group) {
+ if (this.overview_background_group.get_parent())
+ Main.layoutManager.overviewGroup.remove_child(this.overview_background_group);
+ Main.layoutManager.overviewGroup.insert_child_at_index(this.overview_background_group, 0);
+ }
+ });
+ }
+
+ /// Updates the classname to style overview components with semi-transparent
+ /// backgrounds.
+ update_components_classname() {
+ OVERVIEW_COMPONENTS_STYLE.forEach(
+ style => Main.uiGroup.remove_style_class_name(style)
+ );
+
+ if (this.settings.overview.STYLE_COMPONENTS > 0)
+ Main.uiGroup.add_style_class_name(
+ OVERVIEW_COMPONENTS_STYLE[this.settings.overview.STYLE_COMPONENTS - 1]
+ );
+ }
+
+ remove_background_actors() {
+ this.overview_background_group.remove_all_children();
+ this.animation_background_group.remove_all_children();
+
+ this.connections.disconnect_all_for(Main.layoutManager.overviewGroup);
+ if (this.overview_background_group.get_parent())
+ Main.layoutManager.overviewGroup.remove_child(this.overview_background_group);
+
+ this.overview_background_managers.forEach(background_manager => {
+ background_manager._bms_pipeline.destroy();
+ background_manager.destroy();
+ });
+ this.animation_background_managers.forEach(background_manager => {
+ background_manager._bms_pipeline.destroy();
+ background_manager.destroy();
+ });
+ this.overview_background_managers = [];
+ this.animation_background_managers = [];
+ }
+
+ disable() {
+ this._log("removing blur from overview");
+
+ this.remove_background_actors();
+ Main.uiGroup.remove_style_class_name("blurred-overview");
+ OVERVIEW_COMPONENTS_STYLE.forEach(
+ style => Main.uiGroup.remove_style_class_name(style)
+ );
+
+ this.connections.disconnect_all();
+ this.enabled = false;
+ }
+
+ restore_patched_proto() {
+ if (this.proto_patched) {
+ if (this._original_PrepareSwitch)
+ wac_proto._prepareWorkspaceSwitch = this._original_PrepareSwitch;
+ if (this._original_FinishSwitch)
+ wac_proto._finishWorkspaceSwitch = this._original_FinishSwitch;
+
+ this.proto_patched = false;
+ }
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > overview] ${str}`);
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > overview] ${str}`);
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js
new file mode 100644
index 0000000..6c084cf
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js
@@ -0,0 +1,577 @@
+import St from 'gi://St';
+import GLib from 'gi://GLib';
+import Meta from 'gi://Meta';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import { PaintSignals } from '../conveniences/paint_signals.js';
+
+import { Pipeline } from '../conveniences/pipeline.js';
+import { DummyPipeline } from '../conveniences/dummy_pipeline.js';
+
+const DASH_TO_PANEL_UUID = 'dash-to-panel@jderose9.github.com';
+const PANEL_STYLES = [
+ "transparent-panel",
+ "light-panel",
+ "dark-panel",
+ "contrasted-panel"
+];
+
+// global listener, so we don't miss the panel destruction event
+let isMainPanelAlive = true;
+Main.panel.connect('destroy', () => isMainPanelAlive = false);
+
+export const PanelBlur = class PanelBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.window_signal_ids = new Map();
+ this.settings = settings;
+ this.effects_manager = effects_manager;
+ this.actors_list = [];
+ this.enabled = false;
+ }
+
+ enable() {
+ if (this.enabled) {
+ this._log("blur already enabled");
+ return;
+ }
+
+ this._log("blurring top panel");
+
+ // check for panels when Dash to Panel is activated
+ this.connections.connect(
+ Main.extensionManager,
+ 'extension-state-changed',
+ (_, extension) => {
+ if (extension.uuid === DASH_TO_PANEL_UUID
+ && extension.state === 1
+ ) {
+ this.connections.connect(
+ global.dashToPanel,
+ 'panels-created',
+ _ => this.blur_dtp_panels()
+ );
+
+ this.blur_existing_panels();
+ }
+ }
+ );
+
+ this.blur_existing_panels();
+
+ // connect to overview being opened/closed, and dynamically show or not
+ // the blur when a window is near a panel
+ this.connect_to_windows_and_overview();
+
+ // connect to workareas change
+ this.connections.connect(global.display, 'workareas-changed',
+ _ => this.reset()
+ );
+
+ this.enabled = true;
+ }
+
+ reset() {
+ if (!this.enabled) {
+ this._log("reset called but blur is not enabled");
+ return;
+ }
+
+ this._log("resetting...");
+
+ this.disable();
+ setTimeout(_ => this.enable(), 1);
+ }
+
+ /// Check for already existing panels and blur them if they are not already
+ blur_existing_panels() {
+ // check if dash-to-panel is present
+ if (global.dashToPanel) {
+ // blur already existing ones
+ if (global.dashToPanel.panels)
+ this.blur_dtp_panels();
+
+ // blur main panel if requested in settings
+ if (this.settings.dash_to_panel.BLUR_ORIGINAL_PANEL && isMainPanelAlive)
+ this.maybe_blur_panel(Main.panel);
+ } else {
+ // if no dash-to-panel, blur the main and only panel
+ this.maybe_blur_panel(Main.panel);
+ }
+ }
+
+ blur_dtp_panels() {
+ // Defer the blurring to the next idle cycle.
+ // This is crucial to ensure the panel actors have been allocated their
+ // final size and position by the compositor, avoiding race conditions
+ // during extension startup.
+ GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
+ if (!global.dashToPanel?.panels) {
+ return GLib.SOURCE_REMOVE;
+ }
+
+ this._log("Blurring Dash to Panel panels after idle.");
+
+ // blur every panel, except Main.panel (this is handled in blur_existing_panels() method above)
+ global.dashToPanel.panels.forEach(p => {
+ if (p.panel != Main.panel)
+ this.maybe_blur_panel(p.panel);
+ });
+
+ return GLib.SOURCE_REMOVE;
+ });
+ };
+
+ /// Blur a panel only if it is not already blurred (contained in the list)
+ maybe_blur_panel(panel) {
+ // check if the panel is contained in the list
+ let actors = this.actors_list.find(
+ actors => actors.widgets.panel == panel
+ );
+
+ if (!actors)
+ // if the actors is not blurred, blur it
+ this.blur_panel(panel);
+ }
+
+ /// Blur a panel
+ blur_panel(panel) {
+ let panel_box = panel.get_parent();
+ let is_dtp_panel = false;
+ if (!panel_box.name) {
+ is_dtp_panel = true;
+ panel_box = panel_box.get_parent();
+ }
+
+ let monitor = Main.layoutManager.findMonitorForActor(panel);
+ if (!monitor)
+ return;
+
+ let background_group = new Meta.BackgroundGroup(
+ { name: 'bms-panel-backgroundgroup', width: 0, height: 0 }
+ );
+
+ let background, bg_manager;
+ let static_blur = this.settings.panel.STATIC_BLUR;
+ if (static_blur) {
+ let bg_manager_list = [];
+ const pipeline = new Pipeline(
+ this.effects_manager,
+ global.blur_my_shell._pipelines_manager,
+ this.settings.panel.PIPELINE
+ );
+ background = pipeline.create_background_with_effects(
+ monitor.index, bg_manager_list,
+ background_group, 'bms-panel-blurred-widget'
+ );
+ bg_manager = bg_manager_list[0];
+ }
+ else {
+ const pipeline = new DummyPipeline(this.effects_manager, this.settings.panel);
+ [background, bg_manager] = pipeline.create_background_with_effect(
+ background_group, 'bms-panel-blurred-widget'
+ );
+
+ let paint_signals = new PaintSignals(this.connections);
+
+ // 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("panel hack level 1");
+
+ paint_signals.disconnect_all();
+ paint_signals.connect(background, pipeline.effect);
+ } else {
+ paint_signals.disconnect_all();
+ }
+ }
+ }
+
+ // insert the background group to the panel box
+ panel_box.insert_child_at_index(background_group, 0);
+
+ // the object that is used to remembering each elements that is linked to the blur effect
+ let actors = {
+ widgets: { panel, panel_box, background, background_group },
+ static_blur,
+ monitor,
+ bg_manager,
+ is_dtp_panel
+ };
+ this.actors_list.push(actors);
+
+ // update the size of the actor
+ this.update_size(actors);
+
+ // connect to panel, panel_box and its parent position or size change
+ // this should fire update_size every time one of its params change
+ this.connections.connect(
+ panel,
+ 'notify::position',
+ _ => this.update_size(actors)
+ );
+ this.connections.connect(
+ panel_box,
+ ['notify::size', 'notify::position'],
+ _ => this.update_size(actors)
+ );
+ this.connections.connect(
+ panel_box.get_parent(),
+ 'notify::position',
+ _ => this.update_size(actors)
+ );
+
+ // connect to the panel getting destroyed
+ this.connections.connect(
+ panel,
+ 'destroy',
+ _ => this.destroy_blur(actors, true)
+ );
+ }
+
+ update_size(actors) {
+ let panel = actors.widgets.panel;
+ let panel_box = actors.widgets.panel_box;
+ let background = actors.widgets.background;
+ let [width, height] = panel_box.get_size();
+
+ // if static blur, need to clip the background
+ if (actors.static_blur) {
+ let monitor = Main.layoutManager.findMonitorForActor(panel);
+ if (!monitor)
+ return;
+
+ // an alternative to panel.get_transformed_position, because it
+ // sometimes yields NaN (probably when the actor is not fully
+ // positionned yet)
+ let [p_x, p_y] = panel_box.get_position();
+ let [p_p_x, p_p_y] = panel_box.get_parent().get_position();
+ let x = p_x + p_p_x - monitor.x + (width - panel.width) / 2;
+ let y = p_y + p_p_y - monitor.y + (height - panel.height) / 2;
+
+ background.set_clip(x, y, panel.width, panel.height);
+ background.x = (width - panel.width) / 2 - x;
+ background.y = .5 + (height - panel.height) / 2 - y;
+ } else {
+ background.x = panel.x;
+ background.y = panel.y;
+ background.width = panel.width;
+ background.height = panel.height;
+ }
+
+ // update the monitor panel is on
+ actors.monitor = Main.layoutManager.findMonitorForActor(panel);
+ }
+
+ /// Connect when overview if opened/closed to hide/show the blur accordingly
+ ///
+ /// If HIDETOPBAR is set, we need just to hide the blur when showing appgrid
+ /// (so no shadow is cropped)
+ connect_to_overview() {
+ // may be called when panel blur is disabled, if hidetopbar
+ // compatibility is toggled on/off
+ // if this is the case, do nothing as only the panel blur interfers with
+ // hidetopbar
+ if (
+ this.settings.panel.BLUR &&
+ this.settings.panel.UNBLUR_IN_OVERVIEW
+ ) {
+ if (!this.settings.hidetopbar.COMPATIBILITY) {
+ this.connections.connect(
+ Main.overview, 'showing', _ => this.hide()
+ );
+ this.connections.connect(
+ Main.overview, 'hidden', _ => this.show()
+ );
+ } else {
+ let appDisplay = Main.overview._overview._controls._appDisplay;
+
+ this.connections.connect(
+ appDisplay, 'show', _ => this.hide()
+ );
+ this.connections.connect(
+ appDisplay, 'hide', _ => this.show()
+ );
+ this.connections.connect(
+ Main.overview, 'hidden', _ => this.show()
+ );
+ }
+
+ }
+ }
+
+ /// Connect to windows disable transparency when a window is too close
+ connect_to_windows() {
+ if (
+ this.settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY
+ ) {
+ // connect to overview opening/closing
+ this.connections.connect(Main.overview, ['showing', 'hiding'],
+ _ => this.update_visibility()
+ );
+
+ // connect to session mode update
+ this.connections.connect(Main.sessionMode, 'updated',
+ _ => this.update_visibility()
+ );
+
+ // manage already-existing windows
+ for (const meta_window_actor of global.get_window_actors()) {
+ this.on_window_actor_added(
+ meta_window_actor.get_parent(), meta_window_actor
+ );
+ }
+
+ // manage windows at their creation/removal
+ this.connections.connect(global.window_group, 'child-added',
+ this.on_window_actor_added.bind(this)
+ );
+ this.connections.connect(global.window_group, 'child-removed',
+ this.on_window_actor_removed.bind(this)
+ );
+
+ // connect to a workspace change
+ this.connections.connect(global.window_manager, 'switch-workspace',
+ _ => this.update_visibility()
+ );
+
+ // perform early update
+ this.update_visibility();
+ } else {
+ // reset transparency for every panels
+ this.actors_list.forEach(
+ actors => this.set_should_override_panel(actors, true)
+ );
+ }
+ }
+
+ /// An helper to connect to both the windows and overview signals.
+ /// This is the only function that should be directly called, to prevent
+ /// inconsistencies with signals not being disconnected.
+ connect_to_windows_and_overview() {
+ this.disconnect_from_windows_and_overview();
+ this.connect_to_overview();
+ this.connect_to_windows();
+ }
+
+ /// Disconnect all the connections created by connect_to_windows
+ disconnect_from_windows_and_overview() {
+ // disconnect the connections to actors
+ for (const actor of [
+ Main.overview, Main.sessionMode,
+ global.window_group, global.window_manager,
+ Main.overview._overview._controls._appDisplay
+ ]) {
+ this.connections.disconnect_all_for(actor);
+ }
+
+ // disconnect the connections from windows
+ for (const [actor, ids] of this.window_signal_ids) {
+ for (const id of ids) {
+ actor.disconnect(id);
+ }
+ }
+ this.window_signal_ids = new Map();
+ }
+
+ /// Update the css classname of the panel for light theme
+ update_light_text_classname(disable = false) {
+ if (!isMainPanelAlive) {
+ this._log("cannot update light text classname, Main.panel is not alive");
+ return;
+ }
+
+ 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, [
+ meta_window_actor.connect('notify::allocation',
+ _ => this.update_visibility()
+ ),
+ meta_window_actor.connect('notify::visible',
+ _ => this.update_visibility()
+ )
+ ]);
+ this.update_visibility();
+ }
+
+ /// Callback when a window is removed
+ on_window_actor_removed(container, meta_window_actor) {
+ for (const signalId of this.window_signal_ids.get(meta_window_actor)) {
+ meta_window_actor.disconnect(signalId);
+ }
+ this.window_signal_ids.delete(meta_window_actor);
+ this.update_visibility();
+ }
+
+ /// Update the visibility of the blur effect
+ update_visibility() {
+ if (
+ isMainPanelAlive && Main.panel.has_style_pseudo_class('overview')
+ || !Main.sessionMode.hasWindows
+ ) {
+ this.actors_list.forEach(
+ actors => this.set_should_override_panel(actors, true)
+ );
+ return;
+ }
+
+ if (!Main.layoutManager.primaryMonitor)
+ return;
+
+ // get all the windows in the active workspace that are visible
+ const workspace = global.workspace_manager.get_active_workspace();
+ const windows = workspace.list_windows().filter(meta_window =>
+ meta_window.showing_on_its_workspace()
+ && !meta_window.is_hidden()
+ && 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
+ // accordingly
+ const scale = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ this.actors_list
+ // do not apply for dtp panels, as it would only cause bugs and it
+ // can be done from its preferences anyway
+ .filter(actors => !actors.is_dtp_panel)
+ .forEach(actors => {
+ let panel = actors.widgets.panel;
+ let panel_top = panel.get_transformed_position()[1];
+ let panel_bottom = panel_top + panel.get_height();
+
+ // check if at least a window is near enough the panel
+ let window_overlap_panel = false;
+ windows.forEach(meta_window => {
+ let window_monitor_i = meta_window.get_monitor();
+ let same_monitor = actors.monitor.index == window_monitor_i;
+
+ let window_vertical_pos = meta_window.get_frame_rect().y;
+ let window_vertical_bottom = window_vertical_pos + meta_window.get_frame_rect().height;
+
+ // if so, and if in the same monitor, then it overlaps
+ if (same_monitor
+ &&
+ // check if panel is on top
+ ((panel_top === 0 && window_vertical_pos < panel_bottom + 5 * scale) ||
+ // check if panel is at the bottom
+ (panel_top > 0 && window_vertical_bottom > panel_top - 5 * scale))
+ )
+ window_overlap_panel = true;
+ });
+
+ // if no window overlaps, then the panel is transparent
+ this.set_should_override_panel(
+ actors, !window_overlap_panel
+ );
+ });
+ }
+
+ /// Choose wether or not the panel background should be overriden, in
+ /// respect to its argument and the `override-background` setting.
+ set_should_override_panel(actors, should_override) {
+ let panel = actors.widgets.panel;
+
+ PANEL_STYLES.forEach(style => panel.remove_style_class_name(style));
+
+ if (
+ this.settings.panel.OVERRIDE_BACKGROUND
+ &&
+ should_override
+ ) {
+ panel.add_style_class_name(
+ PANEL_STYLES[this.settings.panel.STYLE_PANEL]
+ );
+ }
+
+ // update the classname if the panel to have or have not light text
+ this.update_light_text_classname(!should_override);
+ }
+
+ update_pipeline() {
+ this.actors_list.forEach(actors =>
+ actors.bg_manager._bms_pipeline.change_pipeline_to(
+ this.settings.panel.PIPELINE
+ )
+ );
+ }
+
+ show() {
+ this.actors_list.forEach(actors => {
+ actors.widgets.background.show();
+ });
+ }
+
+ hide() {
+ this.actors_list.forEach(actors => {
+ actors.widgets.background.hide();
+ });
+ }
+
+ // IMPORTANT: do never call this in a mutable `this.actors_list.forEach`
+ destroy_blur(actors, panel_already_destroyed) {
+ this.set_should_override_panel(actors, false);
+
+ actors.bg_manager._bms_pipeline.destroy();
+
+ if (panel_already_destroyed)
+ actors.bg_manager.backgroundActor = null;
+ actors.bg_manager.destroy();
+
+ if (!panel_already_destroyed) {
+ actors.widgets.panel_box.remove_child(actors.widgets.background_group);
+ actors.widgets.background_group.destroy_all_children();
+ actors.widgets.background_group.destroy();
+ }
+
+ let index = this.actors_list.indexOf(actors);
+ if (index >= 0)
+ this.actors_list.splice(index, 1);
+ }
+
+ disable() {
+ if (!this.enabled) {
+ this._log("blur already removed");
+ return;
+ }
+
+ this._log("removing blur from top panel");
+
+ this.disconnect_from_windows_and_overview();
+
+ this.update_light_text_classname(true);
+
+ const immutable_actors_list = [...this.actors_list];
+ immutable_actors_list.forEach(actors => this.destroy_blur(actors, false));
+ this.actors_list = [];
+
+ this.connections.disconnect_all();
+
+ this.enabled = false;
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > panel] ${str}`);
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > panel] ${str}`);
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/screenshot.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/screenshot.js
new file mode 100644
index 0000000..dcf05eb
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/screenshot.js
@@ -0,0 +1,110 @@
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import { Pipeline } from '../conveniences/pipeline.js';
+
+export const ScreenshotBlur = class ScreenshotBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.settings = settings;
+ this.screenshot_background_managers = [];
+ this.effects_manager = effects_manager;
+ }
+
+ enable() {
+ this._log("blurring screenshot's window selector");
+
+ // connect to monitors change
+ this.connections.connect(Main.layoutManager, 'monitors-changed',
+ _ => this.update_backgrounds()
+ );
+
+ // update backgrounds when the component is enabled
+ this.update_backgrounds();
+ }
+
+ update_backgrounds() {
+ // remove every old background
+ this.remove_background_actors();
+ // create new backgrounds for the screenshot window selector
+ for (let i = 0; i < Main.screenshotUI._windowSelectors.length; i++) {
+ const window_selector = Main.screenshotUI._windowSelectors[i];
+ const pipeline = new Pipeline(
+ this.effects_manager,
+ global.blur_my_shell._pipelines_manager,
+ this.settings.screenshot.PIPELINE
+ );
+ pipeline.create_background_with_effects(
+ window_selector._monitorIndex, this.screenshot_background_managers,
+ window_selector, 'bms-screenshot-blurred-widget', false
+ );
+
+ // prevent old `BackgroundActor` from being accessed, which creates a whole bug of logs
+ this.connections.connect(window_selector.get_parent(), 'destroy', _ => {
+ this.screenshot_background_managers.forEach(background_manager => {
+ if (background_manager.backgroundActor) {
+ let widget = background_manager.backgroundActor.get_parent();
+ let parent = widget?.get_parent();
+
+ if (parent == window_selector) {
+ background_manager._bms_pipeline.destroy();
+ parent.remove_child(widget);
+ }
+ }
+ background_manager.destroy();
+ });
+
+ window_selector.get_children().forEach(child => {
+ if (child.get_name() == 'bms-screenshot-blurred-widget')
+ window_selector.remove_child(child);
+ });
+
+ let index = this.screenshot_background_managers.indexOf(window_selector);
+ this.screenshot_background_managers.splice(index, 1);
+ });
+ }
+ }
+
+ update_pipeline() {
+ this.screenshot_background_managers.forEach(background_manager =>
+ background_manager._bms_pipeline.change_pipeline_to(
+ this.settings.screenshot.PIPELINE
+ )
+ );
+ }
+
+ remove_background_actors() {
+ this.screenshot_background_managers.forEach(background_manager => {
+ background_manager._bms_pipeline.destroy();
+ if (background_manager.backgroundActor) {
+ let widget = background_manager.backgroundActor.get_parent();
+ widget?.get_parent()?.remove_child(widget);
+ }
+ background_manager.destroy();
+ });
+
+ Main.screenshotUI._windowSelectors.forEach(window_selector =>
+ window_selector.get_children().forEach(child => {
+ if (child.get_name() == 'bms-screenshot-blurred-widget')
+ window_selector.remove_child(child);
+ })
+ );
+
+ this.screenshot_background_managers = [];
+ }
+
+ disable() {
+ this._log("removing blur from screenshot's window selector");
+
+ this.remove_background_actors();
+ this.connections.disconnect_all();
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > screenshot] ${str}`);
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > screenshot] ${str}`);
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js
new file mode 100644
index 0000000..6ab0644
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js
@@ -0,0 +1,148 @@
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import { PaintSignals } from '../conveniences/paint_signals.js';
+import { DummyPipeline } from '../conveniences/dummy_pipeline.js';
+
+
+export const WindowListBlur = class WindowListBlur {
+ constructor(connections, settings, effects_manager) {
+ this.connections = connections;
+ this.settings = settings;
+ this.paint_signals = new PaintSignals(connections);
+ this.effects_manager = effects_manager;
+ this.pipelines = [];
+ }
+
+ enable() {
+ this._log("blurring window list");
+
+ // blur if window-list is found
+ Main.layoutManager.uiGroup.get_children().forEach(
+ child => this.try_blur(child)
+ );
+
+ // listen to new actors in `Main.layoutManager.uiGroup` and blur it if
+ // if is window-list
+ this.connections.connect(
+ Main.layoutManager.uiGroup,
+ 'child-added',
+ (_, child) => this.try_blur(child)
+ );
+
+ // connect to overview
+ this.connections.connect(Main.overview, 'showing', _ => {
+ this.hide();
+ });
+ this.connections.connect(Main.overview, 'hidden', _ => {
+ this.show();
+ });
+ }
+
+ try_blur(actor) {
+ if (
+ actor.constructor.name === "WindowList" &&
+ actor.style !== "background:transparent;"
+ ) {
+ this._log("found window list to blur");
+
+ const pipeline = new DummyPipeline(
+ this.effects_manager, this.settings.window_list
+ );
+ pipeline.attach_effect_to_actor(actor);
+ this.pipelines.push(pipeline);
+
+ actor.set_style("background:transparent;");
+
+ actor._windowList.get_children().forEach(
+ window => this.style_window_button(window)
+ );
+
+ this.connections.connect(
+ actor._windowList,
+ 'child-added',
+ (_, window) => this.style_window_button(window)
+ );
+
+ this.connections.connect(
+ actor,
+ 'destroy',
+ _ => this.destroy_blur(pipeline, true)
+ );
+
+
+ // 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("window list hack level 1");
+
+ this.paint_signals.disconnect_all_for_actor(actor);
+ this.paint_signals.connect(actor, pipeline.effect);
+ } else {
+ this.paint_signals.disconnect_all_for_actor(actor);
+ }
+ }
+ }
+
+ style_window_button(window) {
+ window.get_child_at_index(0).set_style(
+ "box-shadow:none; background-color:rgba(0,0,0,0.2); border-radius:5px;"
+ );
+ }
+
+ // IMPORTANT: do never call this in a mutable `this.pipelines.forEach`
+ destroy_blur(pipeline, actor_destroyed = false) {
+ if (!actor_destroyed) {
+ this.remove_style(pipeline.actor);
+ this.paint_signals.disconnect_all_for_actor(pipeline.actor);
+ }
+
+ pipeline.destroy();
+
+ let index = this.pipelines.indexOf(pipeline);
+ if (index >= 0)
+ this.pipelines.splice(pipeline, 1);
+ }
+
+ remove_style(actor) {
+ if (
+ actor.constructor.name === "WindowList" &&
+ actor.style === "background:transparent;"
+ ) {
+ actor.style = null;
+ actor._windowList.get_children().forEach(
+ child => child.get_child_at_index(0).set_style(null)
+ );
+ }
+ }
+
+ hide() {
+ this.pipelines.forEach(pipeline => pipeline.effect?.set_enabled(false));
+ }
+
+ show() {
+ this.pipelines.forEach(pipeline => pipeline.effect?.set_enabled(true));
+ }
+
+ disable() {
+ this._log("removing blur from window list");
+
+ const immutable_pipelines_list = [...this.pipelines];
+ immutable_pipelines_list.forEach(pipeline => this.destroy_blur(pipeline));
+
+ this.pipelines = [];
+ this.connections.disconnect_all();
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > window list] ${str}`);
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js
new file mode 100644
index 0000000..0583db3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/connections.js
@@ -0,0 +1,106 @@
+import GObject from 'gi://GObject';
+
+/// An object to easily manage signals.
+export const Connections = class Connections {
+ constructor() {
+ this.buffer = [];
+ }
+
+ /// Adds a connection.
+ ///
+ /// Takes as arguments:
+ /// - an actor, which fires the signal
+ /// - signal(s) (string or array of strings), which are watched for
+ /// - a callback, which is called when the signal is fired
+ connect(actor, signals, handler) {
+ if (signals instanceof Array) {
+ signals.forEach(signal => {
+ let id = actor.connect(signal, handler);
+ this.process_connection(actor, id);
+ });
+ } else {
+ let id = actor.connect(signals, handler);
+ this.process_connection(actor, id);
+ }
+
+ }
+
+ /// Process the given actor and id.
+ ///
+ /// This makes sure that the signal is disconnected when the actor is
+ /// destroyed, and that the signal can be managed through other Connections
+ /// methods.
+ process_connection(actor, id) {
+ let infos = {
+ actor: actor,
+ id: id
+ };
+
+ // remove the signal when the actor is destroyed
+ if (
+ actor.connect &&
+ (
+ !(actor instanceof GObject.Object) ||
+ GObject.signal_lookup('destroy', actor)
+ )
+ ) {
+ let destroy_id = actor.connect('destroy', () => {
+ actor.disconnect(id);
+ actor.disconnect(destroy_id);
+
+ let index = this.buffer.indexOf(infos);
+ if (index >= 0) {
+ this.buffer.splice(index, 1);
+ }
+ });
+ infos.destroy_id = destroy_id;
+ }
+
+ this.buffer.push(infos);
+ }
+
+ /// Disconnects every connection found for an actor.
+ disconnect_all_for(actor) {
+ // get every connection stored for the actor
+ let actor_connections = this.buffer.filter(
+ infos => infos.actor === actor
+ );
+
+ // remove each of them
+ actor_connections.forEach(connection => {
+ // disconnect
+ try {
+ connection.actor.disconnect(connection.id);
+ if ('destroy_id' in connection)
+ connection.actor.disconnect(connection.destroy_id);
+ } catch (e) {
+ this._warn(`error removing connection: ${e}; continuing`);
+ }
+
+ // remove from buffer
+ let index = this.buffer.indexOf(connection);
+ this.buffer.splice(index, 1);
+ });
+ }
+
+ /// Disconnect every connection for each actor.
+ disconnect_all() {
+ this.buffer.forEach(connection => {
+ // disconnect
+ try {
+ connection.actor.disconnect(connection.id);
+ if ('destroy_id' in connection)
+ connection.actor.disconnect(connection.destroy_id);
+ } catch (e) {
+ this._warn(`error removing connection: ${e}; continuing`);
+ }
+ });
+
+ // reset buffer
+ this.buffer = [];
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > connections] ${str}`);
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js
new file mode 100644
index 0000000..544d68c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/dummy_pipeline.js
@@ -0,0 +1,112 @@
+import St from 'gi://St';
+import Clutter from 'gi://Clutter';
+
+/// A dummy `Pipeline`, for dynamic blur only.
+/// Instead of a pipeline id, we take the settings of the component we want to blur.
+export const DummyPipeline = class DummyPipeline {
+ constructor(effects_manager, settings, actor = null) {
+ this.effects_manager = effects_manager;
+ this.settings = settings;
+ this.effect = null;
+ this.attach_effect_to_actor(actor);
+ }
+
+ create_background_with_effect(
+ background_group,
+ widget_name
+ ) {
+ // create the new actor
+ this.actor = new St.Widget({ name: widget_name });
+
+ this.attach_effect_to_actor(this.actor);
+
+ // a dummy `BackgroundManager`, just to access the pipeline easily
+ let bg_manager = new Clutter.Actor;
+ bg_manager.backgroundActor = this.actor;
+ bg_manager._bms_pipeline = this;
+
+ background_group.insert_child_at_index(this.actor, 0);
+
+ return [this.actor, bg_manager];
+ };
+
+ attach_effect_to_actor(actor) {
+ // set the actor
+ if (actor)
+ this.actor = actor;
+ else {
+ this.remove_pipeline_from_actor();
+ return;
+ }
+
+ // build the new effect to be added
+ this.build_effect({
+ unscaled_radius: 2 * this.settings.SIGMA,
+ brightness: this.settings.BRIGHTNESS,
+ });
+
+ this.actor_destroy_id = this.actor.connect(
+ "destroy", () => this.remove_pipeline_from_actor()
+ );
+
+ // add the effect to the actor
+ if (this.actor)
+ this.actor.add_effect(this.effect);
+ else
+ this._warn(`could not add effect to actor, actor does not exist anymore`);
+ }
+
+ remove_pipeline_from_actor() {
+ this.remove_effect();
+ if (this.actor && this.actor_destroy_id)
+ this.actor.disconnect(this.actor_destroy_id);
+ this.actor_destroy_id = null;
+ this.actor = null;
+ }
+
+ build_effect(params) {
+ // create the effect
+ this.effect = this.effects_manager.new_native_dynamic_gaussian_blur_effect(params);
+
+ // connect to settings changes, using the true gsettings object
+ this._sigma_changed_id = this.settings.settings.connect(
+ 'changed::sigma', () => this.effect.unscaled_radius = 2 * this.settings.SIGMA
+ );
+ this._brightness_changed_id = this.settings.settings.connect(
+ 'changed::brightness', () => this.effect.brightness = this.settings.BRIGHTNESS
+ );
+ }
+
+ repaint_effect() {
+ this.effect?.queue_repaint();
+ }
+
+ /// Remove every effect from the actor it is attached to. Please note that they are not
+ /// destroyed, but rather stored (thanks to the `EffectManager` class) to be reused later.
+ remove_effect() {
+ if (this.effect)
+ this.effects_manager.remove(this.effect);
+ this.effect = null;
+
+ if (this._sigma_changed_id)
+ this.settings.settings.disconnect(this._sigma_changed_id);
+ if (this._brightness_changed_id)
+ this.settings.settings.disconnect(this._brightness_changed_id);
+ delete this._sigma_changed_id;
+ delete this._brightness_changed_id;
+ }
+
+ /// Do nothing for this dummy pipeline.
+ /// Note: exposed to public API.
+ change_pipeline_to() { return; }
+
+ /// Note: exposed to public API.
+ destroy() {
+ this.remove_effect();
+ this.remove_pipeline_from_actor();
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > dummy pip] ${str}`);
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js
new file mode 100644
index 0000000..947d535
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/effects_manager.js
@@ -0,0 +1,90 @@
+import { get_supported_effects } from '../effects/effects.js';
+
+/// An object to manage effects (by not destroying them all the time)
+export const EffectsManager = class EffectsManager {
+ constructor(connections) {
+ this.connections = connections;
+ this.used = [];
+ this.SUPPORTED_EFFECTS = get_supported_effects();
+
+ Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
+ // init the arrays containing each unused effect
+ this[effect_name + '_effects'] = [];
+
+ // init the functions for each effect
+ this['new_' + effect_name + '_effect'] = function (params) {
+ let effect;
+ if (this[effect_name + '_effects'].length > 0) {
+ effect = this[effect_name + '_effects'].splice(0, 1)[0];
+ effect.set({
+ ...this.SUPPORTED_EFFECTS[effect_name].class.default_params, ...params
+ });
+ } else
+ effect = new this.SUPPORTED_EFFECTS[effect_name].class({
+ ...this.SUPPORTED_EFFECTS[effect_name].class.default_params, ...params
+ });
+
+ this.used.push(effect);
+ this.connect_to_destroy(effect);
+ return effect;
+ };
+ });
+ }
+
+ connect_to_destroy(effect) {
+ effect.old_actor = effect.get_actor();
+ if (effect.old_actor)
+ effect.old_actor_id = effect.old_actor.connect('destroy', _ => {
+ this.remove(effect, true);
+ });
+
+ this.connections.connect(effect, 'notify::actor', _ => {
+ let actor = effect.get_actor();
+
+ if (effect.old_actor && actor != effect.old_actor)
+ effect.old_actor.disconnect(effect.old_actor_id);
+
+ if (actor && actor != effect.old_actor) {
+ effect.old_actor_id = actor.connect('destroy', _ => {
+ this.remove(effect, true);
+ });
+ }
+ });
+ }
+
+ // IMPORTANT: do never call this in a mutable `this.used.forEach`
+ remove(effect, actor_already_destroyed = false) {
+ if (!actor_already_destroyed)
+ try {
+ effect.get_actor()?.remove_effect(effect);
+ } catch (e) {
+ this._warn(`could not remove the effect, continuing: ${e}`);
+ }
+ if (effect.old_actor)
+ effect.old_actor.disconnect(effect.old_actor_id);
+ delete effect.old_actor;
+ delete effect.old_actor_id;
+
+ let index = this.used.indexOf(effect);
+ if (index >= 0) {
+ this.used.splice(index, 1);
+
+ Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
+ if (effect instanceof this.SUPPORTED_EFFECTS[effect_name].class)
+ this[effect_name + '_effects'].push(effect);
+ });
+ }
+ }
+
+ destroy_all() {
+ const immutable_used_list = [...this.used];
+ immutable_used_list.forEach(effect => this.remove(effect));
+ Object.keys(this.SUPPORTED_EFFECTS).forEach(effect_name => {
+ this[effect_name + '_effects'].splice(0, this[effect_name + '_effects'].length);
+ });
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > effects mng] ${str}`);
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js
new file mode 100644
index 0000000..fcde72b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/keys.js
@@ -0,0 +1,190 @@
+import { Type } from './settings.js';
+
+// This lists the preferences keys
+export const KEYS = [
+ {
+ component: "general", schemas: [
+ { type: Type.PIPELINES, name: "pipelines" },
+ { type: Type.I, name: "hacks-level" },
+ { type: Type.B, name: "debug" },
+ ]
+ },
+ {
+ component: "overview", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "style-components" },
+ ]
+ },
+ {
+ component: "appfolder", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.I, name: "style-dialogs" },
+ ]
+ },
+ {
+ component: "panel", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.B, name: "static-blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { 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" },
+ ]
+ },
+ {
+ component: "dash-to-dock", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.B, name: "static-blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.B, name: "unblur-in-overview" },
+ { type: Type.B, name: "override-background" },
+ { type: Type.I, name: "style-dash-to-dock" },
+ ]
+ },
+ {
+ component: "applications", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.B, name: "static-blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.I, name: "opacity" },
+ { type: Type.B, name: "dynamic-opacity" },
+ { type: Type.B, name: "blur-on-overview" },
+ { type: Type.B, name: "enable-all" },
+ { type: Type.AS, name: "whitelist" },
+ { type: Type.AS, name: "blacklist" },
+ ]
+ },
+ {
+ component: "lockscreen", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ ]
+ },
+ {
+ component: "window-list", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ ]
+ },
+ {
+ component: "coverflow-alt-tab", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ ]
+ },
+ {
+ component: "screenshot", schemas: [
+ { type: Type.B, name: "blur" },
+ { type: Type.S, name: "pipeline" },
+ ]
+ },
+ {
+ component: "hidetopbar", schemas: [
+ { type: Type.B, name: "compatibility" },
+ ]
+ },
+ {
+ component: "dash-to-panel", schemas: [
+ { type: Type.B, name: "blur-original-panel" },
+ ]
+ },
+];
+
+
+// This lists the deprecated preferences keys
+export const DEPRECATED_KEYS = [
+ {
+ component: "general", schemas: [
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ { type: Type.B, name: "color-and-noise" },
+ ]
+ },
+ {
+ component: "overview", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "appfolder", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "panel", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "dash-to-dock", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ { type: Type.I, name: "corner-radius" },
+ ]
+ },
+ {
+ component: "applications", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "lockscreen", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "window-list", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+ {
+ component: "screenshot", schemas: [
+ { type: Type.B, name: "customize" },
+ { type: Type.I, name: "sigma" },
+ { type: Type.D, name: "brightness" },
+ { type: Type.C, name: "color" },
+ { type: Type.D, name: "noise-amount" },
+ { type: Type.D, name: "noise-lightness" },
+ ]
+ },
+]; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js
new file mode 100644
index 0000000..5fbfee4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/paint_signals.js
@@ -0,0 +1,91 @@
+import GObject from 'gi://GObject';
+import Clutter from 'gi://Clutter';
+
+
+export const PaintSignals = class PaintSignals {
+ constructor(connections) {
+ this.buffer = [];
+ this.connections = connections;
+ }
+
+ connect(actor, blur_effect) {
+ let paint_effect = new EmitPaintSignal();
+ let infos = {
+ actor: actor,
+ paint_effect: paint_effect
+ };
+ let counter = 0;
+
+ actor.add_effect(paint_effect);
+ this.connections.connect(paint_effect, 'update-blur', () => {
+ try {
+ // checking if blur_effect.queue_repaint() has been recently called
+ if (counter === 0) {
+ counter = 2;
+ blur_effect.queue_repaint();
+ }
+ else counter--;
+ } catch (e) { }
+ });
+
+ // remove the actor from buffer when it is destroyed
+ if (
+ actor.connect &&
+ (
+ !(actor instanceof GObject.Object) ||
+ GObject.signal_lookup('destroy', actor)
+ )
+ )
+ this.connections.connect(actor, 'destroy', () => {
+ const immutable_buffer = [...this.buffer];
+ immutable_buffer.forEach(infos => {
+ if (infos.actor === actor) {
+ // remove from buffer
+ let index = this.buffer.indexOf(infos);
+ this.buffer.splice(index, 1);
+ }
+ });
+ });
+
+ this.buffer.push(infos);
+ }
+
+ disconnect_all_for_actor(actor) {
+ const immutable_buffer = [...this.buffer];
+ immutable_buffer.forEach(infos => {
+ if (infos.actor === actor) {
+ this.connections.disconnect_all_for(infos.paint_effect);
+ infos.actor.remove_effect(infos.paint_effect);
+
+ // remove from buffer
+ let index = this.buffer.indexOf(infos);
+ this.buffer.splice(index, 1);
+ }
+ });
+ }
+
+ disconnect_all() {
+ this.buffer.forEach(infos => {
+ this.connections.disconnect_all_for(infos.paint_effect);
+ infos.actor.remove_effect(infos.paint_effect);
+ });
+
+ this.buffer = [];
+ }
+};
+
+export const EmitPaintSignal = GObject.registerClass({
+ GTypeName: 'EmitPaintSignal',
+ Signals: {
+ 'update-blur': {
+ param_types: []
+ },
+ }
+},
+ class EmitPaintSignal extends Clutter.Effect {
+ vfunc_paint(node, paint_context, paint_flags) {
+ this.emit("update-blur");
+ super.vfunc_paint(node, paint_context, paint_flags);
+ }
+ }
+); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js
new file mode 100644
index 0000000..84b1d7a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipeline.js
@@ -0,0 +1,223 @@
+import St from 'gi://St';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Background from 'resource:///org/gnome/shell/ui/background.js';
+
+/// A `Pipeline` object is a handy way to manage the effects attached to an actor. It only manages
+/// one actor at a time (so blurring multiple widgets will need multiple `Pipeline`), and is
+/// linked to a `pipeline_id` that has been (hopefully) defined in the settings.
+///
+/// It communicates with the settings through the `PipelinesManager` object, and receives different
+/// signals (with `pipeline_id` being an unique string):
+/// - `'pipeline_id'::pipeline-updated`, handing a new pipeline descriptor object, when the pipeline
+/// has been changed enough that it needs to rebuild the effects configuration
+/// - `'pipeline_id'::pipeline-destroyed`, when the pipeline has been destroyed; thus making the
+/// `Pipeline` change its id to `pipeline_default`
+///
+/// And each effect, with an unique `id`, is connected to the `PipelinesManager` for the signals:
+/// - `'pipeline_id'::effect-'id'-key-removed`, handing the key that was removed
+/// - `'pipeline_id'::effect-'id'-key-updated`, handing the key that was changed and its new value
+/// - `'pipeline_id'::effect-'id'-key-added`, handing the key that was added and its value
+export const Pipeline = class Pipeline {
+ constructor(effects_manager, pipelines_manager, pipeline_id, actor = null) {
+ this.effects_manager = effects_manager;
+ this.pipelines_manager = pipelines_manager;
+ this.effects = [];
+ this.set_pipeline_id(pipeline_id);
+ this.attach_pipeline_to_actor(actor);
+ }
+
+ /// Create a background linked to the monitor with index `monitor_index`, with a
+ /// `BackgroundManager` that is appended to the list `background_managers`. The background actor
+ /// will be given the name `widget_name` and inserted into the given `background_group`.
+ /// If `use_absolute_position` is false, then the position used is at (0,0); useful when the
+ /// positioning is relative.
+ /// Note: exposed to public API.
+ create_background_with_effects(
+ monitor_index,
+ background_managers,
+ background_group,
+ widget_name,
+ use_absolute_position = true
+ ) {
+ let monitor = Main.layoutManager.monitors[monitor_index];
+
+ // create the new actor
+ this.actor = new St.Widget({
+ name: widget_name,
+ x: use_absolute_position ? monitor.x : 0,
+ y: .5 + (use_absolute_position ? monitor.y : 0), // add 1 to correct z-position
+ z_position: 1, // seems to fix the multi-monitor glitch
+ width: monitor.width,
+ height: monitor.height
+ });
+
+ // remove the effects, wether or not we attach the pipeline to the actor: if they are fired
+ // while the actor has changed, this could go bad
+ this.remove_all_effects();
+ if (this.pipeline_id)
+ this.attach_pipeline_to_actor(this.actor);
+
+ let bg_manager = new Background.BackgroundManager({
+ container: this.actor,
+ monitorIndex: monitor_index,
+ controlPosition: false,
+ });
+ bg_manager._bms_pipeline = this;
+
+ background_managers.push(bg_manager);
+ background_group.insert_child_at_index(this.actor, 0);
+
+ return this.actor;
+ };
+
+ /// Set the pipeline id, correctly connecting the `Pipeline` object to listen the pipelines
+ /// manager for pipeline-wide changes. This does not update the effects in consequence, call
+ /// `change_pipeline_to` instead if you want to reconstruct the effects too.
+ set_pipeline_id(pipeline_id) {
+ // disconnect ancient signals
+ this.remove_connections();
+
+ // change the id
+ this.pipeline_id = pipeline_id;
+
+ // connect to settings changes
+ this._pipeline_changed_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::pipeline-updated',
+ (_, new_pipeline) => this.update_effects_from_pipeline(new_pipeline)
+ );
+ this._pipeline_destroyed_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::pipeline-destroyed',
+ _ => this.change_pipeline_to("pipeline_default")
+ );
+ }
+
+ /// Disconnect the signals for the pipeline changes. Please note that the signals related to the
+ /// effects are stored with them and removed with `remove_all_effects`.
+ remove_connections() {
+ if (this._pipeline_changed_id)
+ this.pipelines_manager.disconnect(this._pipeline_changed_id);
+ if (this._pipeline_destroyed_id)
+ this.pipelines_manager.disconnect(this._pipeline_destroyed_id);
+ this._pipeline_changed_id = null;
+ this._pipeline_destroyed_id = null;
+ }
+
+ /// Attach a Pipeline object with `pipeline_id` already set to an actor.
+ attach_pipeline_to_actor(actor) {
+ // set the actor
+ if (actor)
+ this.actor = actor;
+ else {
+ this.remove_pipeline_from_actor();
+ return;
+ }
+
+ // attach the pipeline
+ let pipeline = this.pipelines_manager.pipelines[this.pipeline_id];
+ if (!pipeline) {
+ this._warn(`could not attach pipeline to actor, pipeline "${this.pipeline_id}" not found`);
+ // do not recurse...
+ if ("pipeline_default" in this.pipelines_manager.pipelines) {
+ this.set_pipeline_id("pipeline_default");
+ pipeline = this.pipelines_manager.pipelines["pipeline_default"];
+ } else
+ return;
+ }
+
+ this.actor_destroy_id = this.actor.connect(
+ "destroy", () => this.remove_pipeline_from_actor()
+ );
+
+ // update the effects
+ this.update_effects_from_pipeline(pipeline);
+ }
+
+ remove_pipeline_from_actor() {
+ this.remove_all_effects();
+ if (this.actor && this.actor_destroy_id)
+ this.actor.disconnect(this.actor_destroy_id);
+ this.actor_destroy_id = null;
+ this.actor = null;
+ }
+
+ /// Update the effects from the given pipeline object, the hard way.
+ update_effects_from_pipeline(pipeline) {
+ // remove all effects
+ this.remove_all_effects();
+
+ // build the new effects to be added
+ pipeline.effects.forEach(effect => {
+ if ('new_' + effect.type + '_effect' in this.effects_manager)
+ this.build_effect(effect);
+ else
+ this._warn(`could not add effect to actor, effect "${effect.type}" not found`);
+ });
+ this.effects.reverse();
+
+ // add the effects to the actor
+ if (this.actor)
+ this.effects.forEach(effect => this.actor.add_effect(effect));
+ else
+ this._warn(`could not add effect to actor, actor does not exist anymore`);
+ }
+
+ /// Given an `effect_infos` object containing the effect type, id and params, build an effect
+ /// and append it to the effects list
+ build_effect(effect_infos) {
+ let effect = this.effects_manager['new_' + effect_infos.type + '_effect'](effect_infos.params);
+ this.effects.push(effect);
+
+ // connect to settings changes
+ effect._effect_key_removed_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::effect-' + effect_infos.id + '-key-removed',
+ (_, key) => effect[key] = effect.constructor.default_params[key]
+ );
+ effect._effect_key_updated_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::effect-' + effect_infos.id + '-key-updated',
+ (_, key, value) => effect[key] = value
+ );
+ effect._effect_key_added_id = this.pipelines_manager.connect(
+ this.pipeline_id + '::effect-' + effect_infos.id + '-key-added',
+ (_, key, value) => effect[key] = value
+ );
+ }
+
+ /// Remove every effect from the actor it is attached to. Please note that they are not
+ /// destroyed, but rather stored (thanks to the `EffectManager` class) to be reused later.
+ remove_all_effects() {
+ this.effects.forEach(effect => {
+ this.effects_manager.remove(effect);
+ [
+ effect._effect_key_removed_id,
+ effect._effect_key_updated_id,
+ effect._effect_key_added_id
+ ].forEach(
+ id => { if (id) this.pipelines_manager.disconnect(id); }
+ );
+ delete effect._effect_key_removed_id;
+ delete effect._effect_key_updated_id;
+ delete effect._effect_key_added_id;
+ });
+ this.effects = [];
+ }
+
+ /// Change the pipeline id, and update the effects according to this change.
+ /// Note: exposed to public API.
+ change_pipeline_to(pipeline_id) {
+ this.set_pipeline_id(pipeline_id);
+ this.attach_pipeline_to_actor(this.actor);
+ }
+
+ /// Resets the `Pipeline` object to a sane state, removing every effect and signal.
+ /// Note: exposed to public API.
+ destroy() {
+ this.remove_all_effects();
+ this.remove_connections();
+ this.remove_pipeline_from_actor();
+ this.pipeline_id = null;
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > pipeline] ${str}`);
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
new file mode 100644
index 0000000..0fdfe9e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/pipelines_manager.js
@@ -0,0 +1,168 @@
+const Signals = imports.signals;
+
+/// The `PipelinesManager` object permits to store the list of pipelines and their effects in
+/// memory. It is meant to *always* be in sync with the `org.gnome.shell.extensions.blur-my-shell`'s
+/// `pipelines` gschema. However, we do not want to re-create every effect each time this schema is
+/// changed, so the pipelines manager handles it, and dispatches the updates with targeted signals.
+///
+/// It is only connected to ONE signal (the pipelines schema being changed), and emits numerous
+/// which are connected to by both the different `Pipeline` objects in the extension, and by the
+/// different pages of the extension preferences.
+/// It emits three different types of signals:
+///
+/// - general changes to the pipelines list, connected to by the extension preferences:
+/// - `pipeline-list-changed`, when the list of pipelines has changed (by creation or deletion)
+/// - `pipeline-names-changed`, when the name of a pipeline is changed
+///
+/// - signals that are targeted towards a given pipeline, with `pipeline_id` being its unique id:
+/// - `'pipeline_id'::pipeline-updated`, handing a new pipeline descriptor object, when the
+/// pipeline has been changed quite a bit (added/destroyed/reordered the effects)
+/// - `'pipeline_id'::pipeline-destroyed`, when the pipeline has been destroyed
+/// - `'pipeline_id'::pipeline-renamed`, handing the new name, when the pipeline has been
+/// renamed, which is only important for the preferences
+///
+/// - signals that are targeted towards a given effect, with `effect_id` being its unique id, and
+/// `pipeline_id` the unique id of the pipeline it is attached to:
+/// - `'pipeline_id'::effect-'effect_id'-key-removed`, handing the key that was removed
+/// - `'pipeline_id'::effect-'effect_id'-key-updated`, handing the key that was changed and its
+/// new value
+/// - `'pipeline_id'::effect-'effect_id'-key-added`, handing the key that was added and its
+/// value
+export class PipelinesManager {
+ constructor(settings) {
+ this.settings = settings;
+ this.pipelines = this.settings.PIPELINES;
+ this.settings.PIPELINES_changed(_ => this.on_pipeline_update());
+ }
+
+ create_pipeline(name, effects = []) {
+ // select a random id for the pipeline
+ let id = "pipeline_" + ("" + Math.random()).slice(2, 16);
+ // add a random ID for each effect, to help tracking them
+ effects.forEach(effect => effect.id = "effect_" + ("" + Math.random()).slice(2, 16));
+
+ this.pipelines[id] = { name, effects };
+ this.settings.PIPELINES = this.pipelines;
+ this._emit('pipeline-created', id, this.pipelines[id]);
+ this._emit('pipeline-list-changed');
+ return id;
+ }
+
+ duplicate_pipeline(id) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not duplicate pipeline, id ${id} does not exist`);
+ return;
+ }
+ const pipeline = this.pipelines[id];
+ this.create_pipeline(pipeline.name + " - duplicate", [...pipeline.effects]);
+ this.settings.PIPELINES = this.pipelines;
+ }
+
+ delete_pipeline(id) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not delete pipeline, id ${id} does not exist`);
+ return;
+ }
+ if (id == "pipeline_default") {
+ this._warn(`could not delete pipeline "pipeline_default" as it is immutable`);
+ return;
+ }
+ delete this.pipelines[id];
+ this.settings.PIPELINES = this.pipelines;
+ this._emit(id + '::pipeline-destroyed');
+ this._emit('pipeline-list-changed');
+ }
+
+ update_pipeline_effects(id, effects, emit_update_signal = true) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not update pipeline effects, id ${id} does not exist`);
+ return;
+ }
+ this.pipelines[id].effects = [...effects];
+ this.settings.PIPELINES = this.pipelines;
+ if (emit_update_signal)
+ this._emit(id + '::pipeline-updated');
+ }
+
+ rename_pipeline(id, name) {
+ if (!(id in this.pipelines)) {
+ this._warn(`could not rename pipeline, id ${id} does not exist`);
+ return;
+ }
+ this.pipelines[id].name = name.slice();
+ this.settings.PIPELINES = this.pipelines;
+ this._emit(id + '::pipeline-renamed', name);
+ this._emit('pipeline-names-changed');
+ }
+
+ on_pipeline_update() {
+ const old_pipelines = this.pipelines;
+ this.pipelines = this.settings.PIPELINES;
+
+ for (var pipeline_id in old_pipelines) {
+ // if we find a pipeline that does not exist anymore, signal it
+ if (!(pipeline_id in this.pipelines)) {
+ this._emit(pipeline_id + '::pipeline-destroyed');
+ continue;
+ }
+
+ const old_pipeline = old_pipelines[pipeline_id];
+ const new_pipeline = this.pipelines[pipeline_id];
+
+ // verify if both pipelines have effects in the same order
+ // if they have, then check for their parameters
+ if (
+ old_pipeline.effects.length == new_pipeline.effects.length &&
+ old_pipeline.effects.every((effect, i) => effect.id === new_pipeline.effects[i].id)
+ ) {
+ for (let i = 0; i < old_pipeline.effects.length; i++) {
+ const old_effect = old_pipeline.effects[i];
+ const new_effect = new_pipeline.effects[i];
+ const id = old_effect.id;
+ for (let key in old_effect.params) {
+ // if a key was removed, we emit to tell the effect to use the default value
+ if (!(key in new_effect.params))
+ this._emit(
+ pipeline_id + '::effect-' + id + '-key-removed', key
+ );
+ // if a key was updated, we emit to tell the effect to change its value
+ else if (old_effect.params[key] != new_effect.params[key])
+ this._emit(
+ pipeline_id + '::effect-' + id + '-key-updated', key, new_effect.params[key]
+ );
+ }
+ for (let key in new_effect.params) {
+ // if a key was added, we emit to tell the effect the key and its value
+ if (!(key in old_effect.params))
+ this._emit(
+ pipeline_id + '::effect-' + id + '-key-added', key, new_effect.params[key]
+ );
+ }
+ }
+ }
+ // if either the order has changed, or there are new effects, then rebuild it
+ else
+ this._emit(pipeline_id + '::pipeline-updated', new_pipeline);
+ }
+ }
+
+ destroy() {
+ this.settings.PIPELINES_disconnect();
+ }
+
+ _emit(signal, ...args) {
+ this.emit(signal, ...args);
+ this._log(`signal: '${signal}', arguments: ${args}`);
+ }
+
+ _log(str) {
+ if (this.settings.DEBUG)
+ console.log(`[Blur my Shell > pipelines] ${str}`);
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > pipelines] ${str}`);
+ }
+}
+
+Signals.addSignalMethods(PipelinesManager.prototype); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js
new file mode 100644
index 0000000..53f8497
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings.js
@@ -0,0 +1,363 @@
+import GLib from 'gi://GLib';
+const Signals = imports.signals;
+
+/// An enum non-extensively describing the type of gsettings key.
+export const Type = {
+ B: 'Boolean',
+ I: 'Integer',
+ D: 'Double',
+ S: 'String',
+ C: 'Color',
+ AS: 'StringArray',
+ PIPELINES: 'Pipelines'
+};
+
+/// An object to get and manage the gsettings preferences.
+///
+/// Should be initialized with an array of keys, for example:
+///
+/// let settings = new Settings([
+/// { type: Type.I, name: "panel-corner-radius" },
+/// { type: Type.B, name: "debug" }
+/// ]);
+///
+/// Each {type, name} object represents a gsettings key, which must be created
+/// in the gschemas.xml file of the extension.
+export const Settings = class Settings {
+ constructor(keys, settings) {
+ this.settings = settings;
+ this.keys = keys;
+
+ this.keys.forEach(bundle => {
+ let component = this;
+ let component_settings = settings;
+ if (bundle.component !== "general") {
+ let bundle_component = bundle.component.replaceAll('-', '_');
+ this[bundle_component] = {
+ settings: this.settings.get_child(bundle.component)
+ };
+ component = this[bundle_component];
+ component_settings = settings.get_child(bundle.component);
+ }
+
+
+ bundle.schemas.forEach(key => {
+ let property_name = this.get_property_name(key.name);
+
+ switch (key.type) {
+ case Type.B:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_boolean(key.name);
+ },
+ set(v) {
+ component_settings.set_boolean(key.name, v);
+ }
+ });
+ break;
+
+ case Type.I:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_int(key.name);
+ },
+ set(v) {
+ component_settings.set_int(key.name, v);
+ }
+ });
+ break;
+
+ case Type.D:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_double(key.name);
+ },
+ set(v) {
+ component_settings.set_double(key.name, v);
+ }
+ });
+ break;
+
+ case Type.S:
+ Object.defineProperty(component, property_name, {
+ get() {
+ return component_settings.get_string(key.name);
+ },
+ set(v) {
+ component_settings.set_string(key.name, v);
+ }
+ });
+ break;
+
+ case Type.C:
+ Object.defineProperty(component, property_name, {
+ // returns the array [red, blue, green, alpha] with
+ // values between 0 and 1
+ get() {
+ let val = component_settings.get_value(key.name);
+ return val.deep_unpack();
+ },
+ // takes an array [red, blue, green, alpha] with
+ // values between 0 and 1
+ set(v) {
+ let val = new GLib.Variant("(dddd)", v);
+ component_settings.set_value(key.name, val);
+ }
+ });
+ break;
+
+ case Type.AS:
+ Object.defineProperty(component, property_name, {
+ get() {
+ let val = component_settings.get_value(key.name);
+ return val.deep_unpack();
+ },
+ set(v) {
+ let val = new GLib.Variant("as", v);
+ component_settings.set_value(key.name, val);
+ }
+ });
+ break;
+
+ case Type.PIPELINES:
+ Object.defineProperty(component, property_name, {
+ get() {
+ let pips = component_settings.get_value(key.name).deep_unpack();
+ Object.keys(pips).forEach(pipeline_id => {
+ let pipeline = pips[pipeline_id];
+
+ if (!('name' in pipeline)) {
+ this._warn('impossible to get pipelines, pipeline has not name, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+ let name = pipeline.name.deep_unpack();
+ if (typeof name !== 'string') {
+ this._warn('impossible to get pipelines, pipeline name is not a string, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+
+ if (!('effects' in pipeline)) {
+ this._warn('impossible to get pipelines, pipeline has not effects, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+ let effects = pipeline.effects.deep_unpack();
+ if (!Array.isArray(effects)) {
+ this._warn('impossible to get pipelines, pipeline effects is not an array, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+
+ effects = effects.map(effect => effect.deep_unpack());
+ effects.forEach(effect => {
+ if (!('type' in effect)) {
+ this._warn('impossible to get pipelines, effect has not type, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+ let type = effect.type.deep_unpack();
+ if (typeof type !== 'string') {
+ this._warn('impossible to get pipelines, effect type is not a string, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+
+ if (!('id' in effect)) {
+ this._warn('impossible to get pipelines, effect has not id, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+ let id = effect.id.deep_unpack();
+ if (typeof id !== 'string') {
+ this._warn('impossible to get pipelines, effect id is not a string, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+
+ let params = {};
+ if ('params' in effect)
+ params = effect.params.deep_unpack();
+ if (!(params && typeof params === 'object' && params.constructor === Object)) {
+ this._warn('impossible to get pipelines, effect params is not an object, resetting');
+ component[property_name + '_reset']();
+ return component[property_name];
+ }
+ Object.keys(params).forEach(param_key => {
+ params[param_key] = params[param_key].deep_unpack();
+ });
+
+ effect.type = type;
+ effect.id = id;
+ effect.params = params;
+ });
+ pipeline.name = name;
+ pipeline.effects = effects;
+ });
+ return pips;
+ },
+ set(pips) {
+ let pipelines = {};
+ Object.keys(pips).forEach(pipeline_id => {
+ let pipeline = pips[pipeline_id];
+ if (!(pipeline && typeof pipeline === 'object' && pipeline.constructor === Object)) {
+ this._warn('impossible to set pipelines, pipeline is not an object');
+ return;
+ }
+
+ if (!('name' in pipeline)) {
+ this._warn('impossible to set pipelines, pipeline has no name');
+ return;
+ }
+ if (typeof pipeline.name !== 'string') {
+ this._warn('impossible to set pipelines, pipeline name is not a string');
+ return;
+ }
+
+ if (!('effects' in pipeline)) {
+ this._warn('impossible to set pipelines, pipeline has no effect');
+ return;
+ }
+ if (!Array.isArray(pipeline.effects)) {
+ this._warn('impossible to set pipelines, effects is not an array');
+ return;
+ }
+
+ let gvariant_effects = [];
+ pipeline.effects.forEach(effect => {
+ if (!(effect instanceof Object)) {
+ this._warn('impossible to set pipelines, effect is not an object');
+ return;
+ }
+
+ if (!('type' in effect)) {
+ this._warn('impossible to set pipelines, effect has not type');
+ return;
+ }
+ if (typeof effect.type !== 'string') {
+ this._warn('impossible to set pipelines, effect type is not a string');
+ return;
+ }
+
+ if (!('id' in effect)) {
+ this._warn('impossible to set pipelines, effect has not id');
+ return;
+ }
+ if (typeof effect.id !== 'string') {
+ this._warn('impossible to set pipelines, effect id is not a string');
+ return;
+ }
+
+ let params = {};
+ if ('params' in effect) {
+ params = effect.params;
+ }
+ let gvariant_params = {};
+ Object.keys(params).forEach(param_key => {
+ let param = params[param_key];
+ if (typeof param === 'boolean')
+ gvariant_params[param_key] = GLib.Variant.new_boolean(param);
+ else if (typeof param === 'number') {
+ if (Number.isInteger(param))
+ gvariant_params[param_key] = GLib.Variant.new_int32(param);
+ else
+ gvariant_params[param_key] = GLib.Variant.new_double(param);
+ } else if (typeof param === 'string')
+ gvariant_params[param_key] = GLib.Variant.new_string(param);
+ else if (Array.isArray(param) && param.length == 4)
+ gvariant_params[param_key] = new GLib.Variant("(dddd)", param);
+ else
+ this._warn('impossible to set pipeline, effect parameter type is unknown');
+ });
+
+ gvariant_effects.push(
+ new GLib.Variant("a{sv}", {
+ type: GLib.Variant.new_string(effect.type),
+ id: GLib.Variant.new_string(effect.id),
+ params: new GLib.Variant("a{sv}", gvariant_params)
+ })
+ );
+ });
+
+ pipelines[pipeline_id] = {
+ name: GLib.Variant.new_string(pipeline.name),
+ effects: new GLib.Variant("av", gvariant_effects)
+ };
+ });
+ let val = new GLib.Variant("a{sa{sv}}", pipelines);
+ component_settings.set_value(key.name, val);
+ }
+ });
+ break;
+ }
+
+
+ component[property_name + '_reset'] = function () {
+ return component_settings.reset(key.name);
+ };
+
+ component[property_name + '_signal_ids'] = [];
+ component[property_name + '_changed'] = function (cb) {
+ component[property_name + '_signal_ids'].push(
+ component_settings.connect('changed::' + key.name, cb)
+ );
+ };
+
+ component[property_name + '_disconnect'] = function () {
+ component[property_name + '_signal_ids'].forEach(
+ id => component_settings.disconnect(id)
+ );
+ component[property_name + '_signal_ids'] = [];
+ };
+ });
+ });
+ };
+
+ /// Reset the preferences.
+ reset() {
+ this.keys.forEach(bundle => {
+ let component = this;
+ if (bundle.component !== "general") {
+ let bundle_component = bundle.component.replaceAll('-', '_');
+ component = this[bundle_component];
+ }
+
+ bundle.schemas.forEach(key => {
+ let property_name = this.get_property_name(key.name);
+ component[property_name + '_reset']();
+ });
+ });
+
+ this.emit('reset', true);
+ }
+
+ /// From the gschema name, returns the name of the associated property on
+ /// the Settings object.
+ get_property_name(name) {
+ return name.replaceAll('-', '_').toUpperCase();
+ }
+
+ /// Remove all connections managed by the Settings object, i.e. created with
+ /// `settings.PROPERTY_changed(callback)`.
+ disconnect_all_settings() {
+ this.keys.forEach(bundle => {
+ let component = this;
+ if (bundle.component !== "general") {
+ let bundle_component = bundle.component.replaceAll('-', '_');
+ component = this[bundle_component];
+ }
+
+ bundle.schemas.forEach(key => {
+ let property_name = this.get_property_name(key.name);
+ component[property_name + '_disconnect']();
+ });
+ });
+ }
+
+ _warn(str) {
+ console.warn(`[Blur my Shell > settings] ${str}`);
+ }
+};
+
+Signals.addSignalMethods(Settings.prototype); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js
new file mode 100644
index 0000000..2affbc1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/settings_updater.js
@@ -0,0 +1,40 @@
+import { Settings } from './settings.js';
+import { KEYS, DEPRECATED_KEYS } from './keys.js';
+
+const CURRENT_SETTINGS_VERSION = 2;
+
+export function update_from_old_settings(gsettings) {
+ const preferences = new Settings(KEYS, gsettings);
+ const deprecated_preferences = new Settings(DEPRECATED_KEYS, gsettings);
+ const old_version = preferences.settings.get_int('settings-version');
+
+ if (old_version < CURRENT_SETTINGS_VERSION) {
+ // set artifacts hacks to be 1 at most, as it should be suitable now that most big bugs have
+ // been resolved (and especially because hack levels to 2 now means disabling clipped
+ // redraws entirely, which is very much not what we want for users that update)
+ if (preferences.HACKS_LEVEL > 1)
+ preferences.HACKS_LEVEL = 1;
+
+ // enable dash-to-dock blurring, as most disabled it due to the lack of rounded corners; set
+ // it to static blur by default too and with transparent background
+ preferences.dash_to_dock.BLUR = true;
+ preferences.dash_to_dock.STATIC_BLUR = true;
+ preferences.dash_to_dock.STYLE_DASH_TO_DOCK = 0;
+
+ // 'customize' has been removed: we merge the current used settings
+ ['appfolder', 'panel', 'dash_to_dock', 'applications', 'window_list'].forEach(
+ component_name => {
+ const deprecated_component = deprecated_preferences[component_name];
+ const new_component = preferences[component_name];
+ if (!deprecated_component.CUSTOMIZE) {
+ new_component.SIGMA = deprecated_preferences.SIGMA;
+ new_component.BRIGHTNESS = deprecated_preferences.BRIGHTNESS;
+ }
+ });
+
+ // remove old preferences in order not to clutter the gsettings
+ deprecated_preferences.reset();
+ }
+
+ preferences.settings.set_int('settings-version', CURRENT_SETTINGS_VERSION);
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js
new file mode 100644
index 0000000..0277e0e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/conveniences/utils.js
@@ -0,0 +1,38 @@
+import GLib from 'gi://GLib';
+
+export const IS_IN_PREFERENCES = typeof global === 'undefined';
+
+// Taken from https://github.com/Schneegans/Burn-My-Windows/blob/main/src/utils.js
+// This method can be used to import a module in the GNOME Shell process only. This
+// is useful if you want to use a module in extension.js, but not in the preferences
+// process. This method returns null if it is called in the preferences process.
+export async function import_in_shell_only(module) {
+ if (IS_IN_PREFERENCES)
+ return null;
+ return (await import(module)).default;
+}
+
+// In use for the effects, to prevent boilerplate code
+export function setup_params(outer_this, params) {
+ // setup each parameter, either with the given or the default value
+ for (const params_name in outer_this.constructor.default_params) {
+ outer_this["_" + params_name] = null;
+ outer_this[params_name] = params_name in params ?
+ params[params_name] :
+ outer_this.constructor.default_params[params_name];
+ }
+};
+
+export const get_shader_source = (Shell, shader_filename, self_uri) => {
+ if (!Shell)
+ return;
+ const shader_path = GLib.filename_from_uri(
+ GLib.uri_resolve_relative(self_uri, shader_filename, GLib.UriFlags.NONE)
+ )[0];
+ try {
+ return Shell.get_file_contents_utf8_sync(shader_path);
+ } catch (e) {
+ console.warn(`[Blur my Shell > effect] error loading shader from ${shader_path}: ${e}`);
+ return null;
+ }
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/client.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/client.js
new file mode 100644
index 0000000..3867b3a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/client.js
@@ -0,0 +1,58 @@
+import Gio from 'gi://Gio';
+
+const bus_name = 'org.gnome.Shell';
+const iface_name = 'dev.aunetx.BlurMyShell';
+const obj_path = '/dev/aunetx/BlurMyShell';
+
+
+/// Call pick() from the DBus service, it will open the Inspector from
+/// gnome-shell to pick an actor on stage.
+export function pick() {
+ Gio.DBus.session.call(
+ bus_name,
+ obj_path,
+ iface_name,
+ 'pick',
+ null,
+ null,
+ Gio.DBusCallFlags.NO_AUTO_START,
+ -1,
+ null,
+ null
+ );
+}
+
+/// Connect to DBus 'picking' signal, which will be emitted when the inspector
+/// is picking a window.
+export function on_picking(cb) {
+ const id = Gio.DBus.session.signal_subscribe(
+ bus_name,
+ iface_name,
+ 'picking',
+ obj_path,
+ null,
+ Gio.DBusSignalFlags.NONE,
+ _ => {
+ cb();
+ Gio.DBus.session.signal_unsubscribe(id);
+ }
+ );
+}
+
+/// Connect to DBus 'picked' signal, which will be emitted when a window is
+/// picked.
+export function on_picked(cb) {
+ const id = Gio.DBus.session.signal_subscribe(
+ bus_name,
+ iface_name,
+ 'picked',
+ obj_path,
+ null,
+ Gio.DBusSignalFlags.NONE,
+ (conn, sender, obj_path, iface, signal, params) => {
+ const val = params.get_child_value(0);
+ cb(val.get_string()[0]);
+ Gio.DBus.session.signal_unsubscribe(id);
+ }
+ );
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/iface.xml b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/iface.xml
new file mode 100644
index 0000000..4f298ad
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/iface.xml
@@ -0,0 +1,12 @@
+<node>
+ <interface name="dev.aunetx.BlurMyShell">
+ <!-- This method is called in preferences to pick a window -->
+ <method name="pick" />
+ <!-- When window is picking, send a signal to preferences -->
+ <signal name="picking"></signal>
+ <!-- If window is picked, send a signal to preferences -->
+ <signal name="picked">
+ <arg name="window" type="s" />
+ </signal>
+ </interface>
+</node> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/services.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/services.js
new file mode 100644
index 0000000..4b1f4f0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/dbus/services.js
@@ -0,0 +1,93 @@
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as LookingGlass from 'resource:///org/gnome/shell/ui/lookingGlass.js';
+
+
+export const ApplicationsService = class ApplicationsService {
+ constructor() {
+ let decoder = new TextDecoder();
+ let path = GLib.filename_from_uri(GLib.uri_resolve_relative(
+ import.meta.url, 'iface.xml', GLib.UriFlags.NONE)
+ )[0];
+ let [, buffer] = GLib.file_get_contents(path);
+ let iface = decoder.decode(buffer);
+ GLib.free(buffer);
+
+ this.DBusImpl = Gio.DBusExportedObject.wrapJSObject(iface, this);
+ }
+
+ /// Pick Window for Preferences Page, exported to DBus client.
+ pick() {
+ // emit `picking` signal to know we are listening
+ const send_picking_signal = _ =>
+ this.DBusImpl.emit_signal(
+ 'picking',
+ null
+ );
+
+ // emit `picked` signal to send wm_class
+ const send_picked_signal = wm_class =>
+ this.DBusImpl.emit_signal(
+ 'picked',
+ new GLib.Variant('(s)', [wm_class])
+ );
+
+ // notify the preferences that we are listening
+ send_picking_signal();
+
+ // A very interesting way to pick a window:
+ // 1. Open LookingGlass to mask all event handles of window
+ // 2. Use inspector to pick window, thats is also lookingGlass do
+ // 3. Close LookingGlass when done
+ // It will restore event handles of window
+
+ // open then hide LookingGlass
+ const looking_class = Main.createLookingGlass();
+ looking_class.open();
+ looking_class.hide();
+
+ // inspect window now
+ const inspector = new LookingGlass.Inspector(Main.createLookingGlass());
+ inspector.connect('target', (me, target, x, y) => {
+ // remove border effect when window is picked.
+ const effect_name = 'lookingGlass_RedBorderEffect';
+ target
+ .get_effects()
+ .filter(e => e.toString().includes(effect_name))
+ .forEach(e => target.remove_effect(e));
+
+ // get wm_class_instance property of window, then pass it to DBus
+ // client
+ const type_str = target.toString();
+
+ let actor = target;
+ if (type_str.includes('MetaSurfaceActor'))
+ actor = target.get_parent();
+
+ if (!actor.toString().includes('WindowActor'))
+ actor = actor.get_parent();
+
+ if (!actor.toString().includes('WindowActor'))
+ return send_picked_signal('window-not-found');
+
+ send_picked_signal(
+ actor.meta_window.get_wm_class() ?? 'window-not-found'
+ );
+ });
+
+ // close LookingGlass when we're done
+ inspector.connect('closed', _ => looking_class.close());
+ }
+
+ export() {
+ this.DBusImpl.export(
+ Gio.DBus.session,
+ '/dev/aunetx/BlurMyShell'
+ );
+ };
+
+ unexport() {
+ this.DBusImpl.unexport();
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.glsl
new file mode 100644
index 0000000..5df6512
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.glsl
@@ -0,0 +1,111 @@
+uniform sampler2D tex;
+uniform float red;
+uniform float green;
+uniform float blue;
+uniform float blend;
+uniform int mode;
+
+const int NORMAL = 0;
+const int MULTIPLY = 1;
+const int SCREEN = 2;
+const int OVERLAY = 3;
+const int DARKEN = 4;
+const int LIGHTEN = 5;
+const int PLUS_DARKER = 6;
+const int PLUS_LIGHTER = 7;
+const int COLOR_DODGE = 8;
+const int COLOR_BURN = 9;
+const int HARD_LIGHT = 10;
+const int SOFT_LIGHT = 11;
+const int DIFFERENCE = 12;
+const int EXCLUSION = 13;
+const int HUE = 14;
+const int SATURATION = 15;
+const int COLOR = 16;
+const int LUMINOSITY = 17;
+
+vec3 rgb_to_hsl(vec3 c) {
+ vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
+ vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
+ vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
+
+ float d = q.x - min(q.w, q.y);
+ float e = 1.0e-10;
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+}
+
+vec3 hsl_to_rgb(vec3 c) {
+ vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+}
+
+float soft_light_channel(float base, float _blend) {
+ if (_blend < 0.5) {
+ return base - (1.0 - 2.0 * _blend) * base * (1.0 - base);
+ } else {
+ float d = (base < 0.25)
+ ? ((16.0 * base - 12.0) * base + 4.0) * base
+ : sqrt(base);
+ return base + (2.0 * _blend - 1.0) * (d - base);
+ }
+}
+
+vec3 get_blend(vec3 base, vec3 _blend) {
+ if (mode == MULTIPLY) return base * _blend;
+ if (mode == SCREEN) return 1 - (1 - base) * (1 - _blend);
+ if (mode == OVERLAY) {
+ vec3 result;
+ result.r = base.r < 0.5 ? (2.0 * base.r * _blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - _blend.r));
+ result.g = base.g < 0.5 ? (2.0 * base.g * _blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - _blend.g));
+ result.b = base.b < 0.5 ? (2.0 * base.b * _blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - _blend.b));
+ return result;
+ }
+ if (mode == DARKEN) return min(base, _blend);
+ if (mode == LIGHTEN) return max(base, _blend);
+ if (mode == PLUS_DARKER) return base + _blend - 1;
+ if (mode == PLUS_LIGHTER) return base + _blend;
+ if (mode == COLOR_DODGE) return base / (1 - _blend);
+ if (mode == COLOR_BURN) return 1 - (1 - base) / _blend;
+ if (mode == HARD_LIGHT) {
+ vec3 result;
+ result.r = _blend.r < 0.5 ? (2.0 * base.r * _blend.r) : (1.0 - 2.0 * (1.0 - base.r) * (1.0 - _blend.r));
+ result.g = _blend.g < 0.5 ? (2.0 * base.g * _blend.g) : (1.0 - 2.0 * (1.0 - base.g) * (1.0 - _blend.g));
+ result.b = _blend.b < 0.5 ? (2.0 * base.b * _blend.b) : (1.0 - 2.0 * (1.0 - base.b) * (1.0 - _blend.b));
+ return result;
+ }
+ if (mode == SOFT_LIGHT) {
+ return vec3(soft_light_channel(base.r, _blend.r), soft_light_channel(base.g, _blend.g), soft_light_channel(base.b, _blend.b));
+ }
+ if (mode == DIFFERENCE) return abs(base - _blend);
+ if (mode == EXCLUSION) return 0.5 - 2 * (base - 0.5) * (_blend - 0.5);
+ if (mode == HUE) {
+ vec3 base_hsl = rgb_to_hsl(base);
+ vec3 blend_hsl = rgb_to_hsl(_blend);
+ return hsl_to_rgb(vec3(blend_hsl.x, base_hsl.y, base_hsl.z));
+ }
+ if (mode == SATURATION) {
+ vec3 base_hsl = rgb_to_hsl(base);
+ vec3 blend_hsl = rgb_to_hsl(_blend);
+ return hsl_to_rgb(vec3(base_hsl.x, blend_hsl.y, base_hsl.z));
+ }
+ if (mode == COLOR) {
+ vec3 base_hsl = rgb_to_hsl(base);
+ vec3 blend_hsl = rgb_to_hsl(_blend);
+ return hsl_to_rgb(vec3(blend_hsl.x, blend_hsl.y, base_hsl.z));
+ }
+ if (mode == LUMINOSITY) {
+ vec3 base_hsl = rgb_to_hsl(base);
+ vec3 blend_hsl = rgb_to_hsl(_blend);
+ return hsl_to_rgb(vec3(base_hsl.x, base_hsl.y, blend_hsl.z));
+ }
+ return _blend; // For NORMAL
+}
+
+void main() {
+ vec4 c = texture2D(tex, cogl_tex_coord_in[0].st);
+ vec3 pix_color = c.xyz;
+ vec3 color = get_blend(pix_color, vec3(red, green, blue));
+
+ cogl_color_out = vec4(mix(pix_color, color, blend), 1.);
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.js
new file mode 100644
index 0000000..1f4bdf4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/color.js
@@ -0,0 +1,168 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'color.glsl';
+const DEFAULT_PARAMS = {
+ color: [0.0, 0.0, 0.0, 0.0],
+ blend_mode: 0
+};
+
+
+export const ColorEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "ColorEffect",
+ Properties: {
+ 'red': GObject.ParamSpec.double(
+ `red`,
+ `Red`,
+ `Red value in shader`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 1.0,
+ 0.0,
+ ),
+ 'green': GObject.ParamSpec.double(
+ `green`,
+ `Green`,
+ `Green value in shader`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 1.0,
+ 0.0,
+ ),
+ 'blue': GObject.ParamSpec.double(
+ `blue`,
+ `Blue`,
+ `Blue value in shader`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 1.0,
+ 0.0,
+ ),
+ 'blend': GObject.ParamSpec.double(
+ `blend`,
+ `Blend`,
+ `Amount of blending between the colors`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 1.0,
+ 0.0,
+ ),
+ 'blend_mode': GObject.ParamSpec.int(
+ `blend_mode`,
+ `Blend mode`,
+ `Blend mode`,
+ GObject.ParamFlags.READWRITE,
+ 0, 17,
+ 0,
+ )
+ }
+ // Normal (0), Multiply (1), Screen (2), Overlay (3), Darken (4), Lighten (5), Plus darker (6), Plus lighter (7), Color dodge (8),
+ // Color burn (9), Hard light (10), Soft light (11), Difference (12), Exclusion (13), Hue (14), Saturation (15), Color (16),
+ // Luminosity (17)
+ }, class ColorEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ // initialize without color as a parameter
+ const { color, ...parent_params } = params;
+ super(parent_params);
+
+ this._red = null;
+ this._green = null;
+ this._blue = null;
+ this._blend = null;
+ this._blend_mode = null;
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+
+ // set params; utils.setup_params doesn't work here with color
+ this.color = 'color' in params ? color : this.constructor.default_params.color;
+ this.blend_mode = 'blend_mode' in params ? params.blend_mode : this.constructor.default_params.blend_mode;
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get red() {
+ return this._red;
+ }
+
+ set red(value) {
+ if (this._red !== value) {
+ this._red = value;
+
+ this.set_uniform_value('red', parseFloat(this._red - 1e-6));
+ }
+ }
+
+ get green() {
+ return this._green;
+ }
+
+ set green(value) {
+ if (this._green !== value) {
+ this._green = value;
+
+ this.set_uniform_value('green', parseFloat(this._green - 1e-6));
+ }
+ }
+
+ get blue() {
+ return this._blue;
+ }
+
+ set blue(value) {
+ if (this._blue !== value) {
+ this._blue = value;
+
+ this.set_uniform_value('blue', parseFloat(this._blue - 1e-6));
+ }
+ }
+
+ get blend() {
+ return this._blend;
+ }
+
+ set blend(value) {
+ if (this._blend !== value) {
+ this._blend = value;
+
+ this.set_uniform_value('blend', parseFloat(this._blend - 1e-6));
+ this.set_enabled(this.blend > 0);
+ }
+ }
+
+ get blend_mode() {
+ return this._blend_mode;
+ }
+
+ set blend_mode(value) {
+ if (this._blend_mode !== value) {
+ this._blend_mode = value;
+
+ this.set_uniform_value('mode', this._blend_mode);
+ }
+ }
+
+ set color(rgba) {
+ let [r, g, b, a] = rgba;
+ this.red = r;
+ this.green = g;
+ this.blue = b;
+ this.blend = a;
+ }
+
+ get color() {
+ return [this.red, this.green, this.blue, this.blend];
+ }
+
+ /// False set function, only cares about the color. Too hard to change.
+ set(params) {
+ this.color = params.color;
+ this.blend_mode = params.blend_mode;
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.glsl
new file mode 100644
index 0000000..760798a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.glsl
@@ -0,0 +1,93 @@
+// Heavily based on https://github.com/yilozt/rounded-window-corners
+// which is itself based on upstream Mutter code
+
+uniform sampler2D tex;
+uniform float radius;
+uniform float width;
+uniform float height;
+uniform bool corners_top;
+uniform bool corners_bottom;
+
+uniform float clip_x0;
+uniform float clip_y0;
+uniform float clip_width;
+uniform float clip_height;
+
+float circle_bounds(vec2 p, vec2 center, float clip_radius) {
+ vec2 delta = p - center;
+ float dist_squared = dot(delta, delta);
+
+ float outer_radius = clip_radius + 0.5;
+ if (dist_squared >= (outer_radius * outer_radius))
+ return 0.0;
+
+ float inner_radius = clip_radius - 0.5;
+ if (dist_squared <= (inner_radius * inner_radius))
+ return 1.0;
+
+ return outer_radius - sqrt(dist_squared);
+}
+
+vec4 getTexture(vec2 uv) {
+ if (uv.x < 2. / width)
+ uv.x = 2. / width;
+
+ if (uv.y < 2. / height)
+ uv.y = 2. / height;
+
+ if (uv.x > 1. - 3. / width)
+ uv.x = 1. - 3. / width;
+
+ if (uv.y > 1. - 3. / height)
+ uv.y = 1. - 3. / height;
+
+ return texture2D(tex, uv);
+}
+
+float rounded_rect_coverage(vec2 p, vec4 bounds, float clip_radius) {
+ // Outside the bounds
+ if (p.x < bounds.x || p.x > bounds.z || p.y < bounds.y || p.y > bounds.w) {
+ return 0.;
+ }
+
+ vec2 center;
+
+ float center_left = bounds.x + clip_radius;
+ float center_right = bounds.z - clip_radius;
+
+ if (p.x < center_left)
+ center.x = center_left + 2.;
+ else if (p.x > center_right)
+ center.x = center_right - 1.;
+ else
+ return 1.0;
+
+ float center_top = bounds.y + clip_radius;
+ float center_bottom = bounds.w - clip_radius;
+
+ if (corners_top && p.y < center_top)
+ center.y = center_top + 2.;
+ else if (corners_bottom && p.y > center_bottom)
+ center.y = center_bottom - 1.;
+ else
+ return 1.0;
+
+ return circle_bounds(p, center, clip_radius);
+}
+
+void main(void) {
+ vec2 uv = cogl_tex_coord_in[0].xy;
+ vec2 pos = uv * vec2(width, height);
+ vec4 c = getTexture(uv);
+
+ vec4 bounds;
+ if (clip_width < 0. || clip_height < 0.) {
+ bounds = vec4(clip_x0, clip_y0, clip_x0 + width, clip_y0 + height);
+ } else {
+ bounds = vec4(clip_x0, clip_y0, clip_x0 + clip_width, clip_y0 + clip_height);
+ }
+
+ float alpha = rounded_rect_coverage(pos, bounds, radius);
+
+ cogl_color_out = vec4(c.rgb * alpha, min(alpha, c.a));
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.js
new file mode 100644
index 0000000..109be12
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/corner.js
@@ -0,0 +1,211 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const St = await utils.import_in_shell_only('gi://St');
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'corner.glsl';
+const DEFAULT_PARAMS = {
+ radius: 12, width: 0, height: 0,
+ corners_top: true, corners_bottom: true,
+ clip: [0, 0, -1, -1]
+};
+
+
+export const CornerEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "CornerEffect",
+ Properties: {
+ 'radius': GObject.ParamSpec.double(
+ `radius`,
+ `Corner Radius`,
+ `Corner Radius`,
+ GObject.ParamFlags.READWRITE,
+ 0, Number.MAX_SAFE_INTEGER,
+ 12,
+ ),
+ 'width': GObject.ParamSpec.double(
+ `width`,
+ `Width`,
+ `Width`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'height': GObject.ParamSpec.double(
+ `height`,
+ `Height`,
+ `Height`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'corners_top': GObject.ParamSpec.boolean(
+ `corners_top`,
+ `Round top corners`,
+ `Round top corners`,
+ GObject.ParamFlags.READWRITE,
+ true,
+ ),
+ 'corners_bottom': GObject.ParamSpec.boolean(
+ `corners_bottom`,
+ `Round bottom corners`,
+ `Round bottom corners`,
+ GObject.ParamFlags.READWRITE,
+ true,
+ ),
+ // FIXME this works but it logs an error, because I'm not a double...
+ // I don't want to fiddle with GVariants again
+ 'clip': GObject.ParamSpec.double(
+ `clip`,
+ `Clip`,
+ `Clip`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ }
+ }, class CornerEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ this._clip_x0 = null;
+ this._clip_y0 = null;
+ this._clip_width = null;
+ this._clip_height = null;
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+
+ const theme_context = St.ThemeContext.get_for_stage(global.stage);
+ theme_context.connectObject('notify::scale-factor', _ => this.update_radius(), this);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get radius() {
+ return this._radius;
+ }
+
+ set radius(value) {
+ if (this._radius !== value) {
+ this._radius = value;
+
+ this.update_radius();
+ }
+ }
+
+ update_radius() {
+ const theme_context = St.ThemeContext.get_for_stage(global.stage);
+ let radius = Math.min(
+ this.radius * theme_context.scale_factor,
+ this.width / 2, this.height / 2
+ );
+ if (this._clip_width >= 0 || this._clip_height >= 0)
+ radius = Math.min(radius, this._clip_width / 2, this._clip_height / 2);
+
+ this.set_uniform_value('radius', parseFloat(radius - 1e-6));
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ if (this._width !== value) {
+ this._width = value;
+
+ this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
+ this.update_radius();
+ }
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ if (this._height !== value) {
+ this._height = value;
+
+ this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
+ this.update_radius();
+ }
+ }
+
+ get corners_top() {
+ return this._corners_top;
+ }
+
+ set corners_top(value) {
+ if (this._corners_top !== value) {
+ this._corners_top = value;
+
+ this.set_uniform_value('corners_top', this._corners_top ? 1 : 0);
+ }
+ }
+
+ get corners_bottom() {
+ return this._corners_bottom;
+ }
+
+ set corners_bottom(value) {
+ if (this._corners_bottom !== value) {
+ this._corners_bottom = value;
+
+ this.set_uniform_value('corners_bottom', this._corners_bottom ? 1 : 0);
+ }
+ }
+
+ get clip() {
+ return [this._clip_x0, this._clip_y0, this._clip_width, this._clip_height];
+ }
+
+ set clip(value) {
+ [this._clip_x0, this._clip_y0, this._clip_width, this._clip_height] = value;
+ this.set_uniform_value('clip_x0', parseFloat(this._clip_x0 - 1e-6));
+ this.set_uniform_value('clip_y0', parseFloat(this._clip_y0 - 1e-6));
+ this.set_uniform_value('clip_width', parseFloat(this._clip_width + 3 - 1e-6));
+ this.set_uniform_value('clip_height', parseFloat(this._clip_height + 3 - 1e-6));
+ this.update_radius();
+ }
+
+ vfunc_set_actor(actor) {
+ if (this._actor_connection_size_id) {
+ let old_actor = this.get_actor();
+ old_actor?.disconnect(this._actor_connection_size_id);
+ }
+ if (this._actor_connection_clip_rect_id) {
+ let old_actor = this.get_actor();
+ old_actor?.disconnect(this._actor_connection_clip_rect_id);
+ }
+
+ if (actor) {
+ this.width = actor.width;
+ this.height = actor.height;
+ this._actor_connection_size_id = actor.connect('notify::size', _ => {
+ this.width = actor.width;
+ this.height = actor.height;
+ });
+
+ this.clip = actor.has_clip ? actor.get_clip() : [0, 0, -10, -10];
+ this._actor_connection_clip_rect_id = actor.connect('notify::clip-rect', _ => {
+ this.clip = actor.has_clip ? actor.get_clip() : [0, 0, -10, -10];
+ });
+ }
+ else {
+ this._actor_connection_size_id = null;
+ this._actor_connection_clip_rect_id = null;
+ }
+
+ super.vfunc_set_actor(actor);
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.glsl
new file mode 100644
index 0000000..64c1682
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.glsl
@@ -0,0 +1,71 @@
+uniform sampler2D tex;
+uniform int operation;
+uniform float width;
+uniform float height;
+
+#define CORRECTION 2.25
+#define SIZE_ADDITION 3
+
+vec4 get_texture_at_position(vec2 position) {
+ vec2 raw_position = position + vec2(CORRECTION, CORRECTION);
+ vec2 raw_uv = raw_position / vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
+
+ return texture2D(tex, raw_uv);
+}
+
+vec4 try_get_texture_at_position(vec2 position, inout int count) {
+ if (any(greaterThanEqual(position, vec2(width, height))) ||
+ any(lessThan(position, vec2(0, 0)))) {
+ return vec4(0);
+ } else {
+ count++;
+ return get_texture_at_position(position);
+ }
+}
+
+ivec2 get_corrected_position() {
+ vec2 raw_uv = cogl_tex_coord0_in.st;
+ vec2 raw_position = raw_uv * vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
+ return ivec2(raw_position - vec2(CORRECTION, CORRECTION));
+}
+
+void main() {
+ ivec2 corrected_position = get_corrected_position();
+
+ // 1-step derivative
+ if (operation == 0) {
+ vec4 color = vec4(0);
+ int c = 0;
+ color += try_get_texture_at_position(corrected_position + vec2(0, 1), c);
+ color -= try_get_texture_at_position(corrected_position + vec2(0, 0), c);
+ color += try_get_texture_at_position(corrected_position + vec2(1, 0), c);
+ color -= try_get_texture_at_position(corrected_position + vec2(0, 0), c);
+ if (c < 4) {
+ color = vec4(0);
+ }
+ cogl_color_out = vec4(color.xyz, 1);
+ } else
+ // 2-step derivative
+ if (operation == 1) {
+ vec4 color = vec4(0);
+ int c = 0;
+ color += try_get_texture_at_position(corrected_position + vec2(0, 1), c);
+ color -= try_get_texture_at_position(corrected_position + vec2(0, -1), c);
+ color += try_get_texture_at_position(corrected_position + vec2(1, 0), c);
+ color -= try_get_texture_at_position(corrected_position + vec2(-1, 0), c);
+ if (c < 4) {
+ color = vec4(0);
+ }
+ cogl_color_out = vec4(color.xyz / 2, 1);
+ } else
+ // laplacian
+ if (operation == 2) {
+ vec4 color = vec4(0);
+ color = -4 * get_texture_at_position(corrected_position);
+ color += get_texture_at_position(corrected_position + vec2(0, 1));
+ color += get_texture_at_position(corrected_position + vec2(0, -1));
+ color += get_texture_at_position(corrected_position + vec2(1, 0));
+ color += get_texture_at_position(corrected_position + vec2(-1, 0));
+ cogl_color_out = vec4(color.xyz, 1);
+ }
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.js
new file mode 100644
index 0000000..04d6100
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/derivative.js
@@ -0,0 +1,120 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'derivative.glsl';
+const DEFAULT_PARAMS = {
+ operation: 0, width: 0, height: 0
+};
+
+
+export const DerivativeEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "DerivativeEffect",
+ Properties: {
+ 'operation': GObject.ParamSpec.int(
+ `operation`,
+ `Operation`,
+ `Operation`,
+ GObject.ParamFlags.READWRITE,
+ 0, 2,
+ 0,
+ ),
+ 'width': GObject.ParamSpec.double(
+ `width`,
+ `Width`,
+ `Width`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'height': GObject.ParamSpec.double(
+ `height`,
+ `Height`,
+ `Height`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ )
+ }
+ }, class DerivativeEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get operation() {
+ return this._operation;
+ }
+
+ set operation(value) {
+ if (this._operation !== value) {
+ this._operation = value;
+
+ this.set_uniform_value('operation', this._operation);
+ }
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ if (this._width !== value) {
+ this._width = value;
+
+ this.set_uniform_value('width', parseFloat(this._width - 1e-6));
+ }
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ if (this._height !== value) {
+ this._height = value;
+
+ this.set_uniform_value('height', parseFloat(this._height - 1e-6));
+ }
+ }
+
+ vfunc_set_actor(actor) {
+ if (this._actor_connection_size_id) {
+ let old_actor = this.get_actor();
+ old_actor?.disconnect(this._actor_connection_size_id);
+ }
+ if (actor) {
+ this.width = actor.width;
+ this.height = actor.height;
+ this._actor_connection_size_id = actor.connect('notify::size', _ => {
+ this.width = actor.width;
+ this.height = actor.height;
+ });
+ }
+ else
+ this._actor_connection_size_id = null;
+
+ super.vfunc_set_actor(actor);
+ }
+
+ vfunc_paint_target(paint_node, paint_context) {
+ // force setting nearest-neighbour texture filtering
+ this.get_pipeline().set_layer_filters(0, 9728, 9728);
+
+ super.vfunc_paint_target(paint_node, paint_context);
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.glsl
new file mode 100644
index 0000000..9871268
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.glsl
@@ -0,0 +1,76 @@
+uniform sampler2D tex;
+uniform int divider;
+uniform float width;
+uniform float height;
+uniform int downsampling_mode;
+
+#define CORRECTION 2.25
+#define SIZE_ADDITION 3
+
+vec4 get_texture_at_position(vec2 position) {
+ vec2 raw_position = position + vec2(CORRECTION, CORRECTION);
+ vec2 raw_uv = raw_position / vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
+
+ return texture2D(tex, raw_uv);
+}
+
+vec2 get_corrected_position() {
+ vec2 raw_uv = cogl_tex_coord0_in.st;
+ vec2 raw_position = raw_uv * vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
+ return raw_position - vec2(CORRECTION, CORRECTION);
+}
+
+void main() {
+ ivec2 corrected_position = ivec2(get_corrected_position());
+ vec2 multiplied_position = corrected_position * divider;
+
+ if (any(greaterThan(multiplied_position, vec2(width, height)))) {
+ discard;
+ }
+
+ // mode 0: boxcar downsampling
+ if (downsampling_mode == 0) {
+
+ vec4 color = vec4(0.);
+ int count = 0;
+ for (int i = 0; i < divider; i++) {
+ for (int j = 0; j < divider; j++) {
+ vec2 lookup_position = multiplied_position + vec2(i, j);
+ if (all(greaterThanEqual(lookup_position, vec2(0, 0))) &&
+ all(lessThan(lookup_position, vec2(width, height)))) {
+ color += get_texture_at_position(lookup_position);
+ count += 1;
+ }
+ }
+ }
+ cogl_color_out = color / count;
+
+ } else
+ // mode 1: triangular downsampling
+ if (downsampling_mode == 1) {
+
+ vec4 color = vec4(0.);
+ int count = 0;
+ int force = 1;
+ for (int i = 0; i < divider; i++) {
+ for (int j = 0; j < divider; j++) {
+ vec2 lookup_position = multiplied_position + vec2(i, j);
+ if (all(greaterThanEqual(lookup_position, vec2(0, 0))) &&
+ all(lessThan(lookup_position, vec2(width, height)))) {
+ force = 1 + divider - int(abs(divider - i - j));
+ color += get_texture_at_position(lookup_position) * force;
+ count += force;
+ }
+ }
+ }
+ cogl_color_out = color / count;
+
+ } else
+ // mode 2: Dirac downsampling
+ if (downsampling_mode == 2) {
+
+ vec2 lookup_position = min(multiplied_position + vec2(divider, divider) / 2, vec2(width - 1, height - 1));
+ cogl_color_out = get_texture_at_position(lookup_position);
+
+ }
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.js
new file mode 100644
index 0000000..8103486
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/downscale.js
@@ -0,0 +1,140 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'downscale.glsl';
+const DEFAULT_PARAMS = {
+ divider: 8, downsampling_mode: 0, width: 0, height: 0
+};
+
+
+export const DownscaleEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "DownscaleEffect",
+ Properties: {
+ 'divider': GObject.ParamSpec.int(
+ `divider`,
+ `Divider`,
+ `Divider`,
+ GObject.ParamFlags.READWRITE,
+ 0, 64,
+ 8,
+ ),
+ 'downsampling_mode': GObject.ParamSpec.int(
+ `downsampling_mode`,
+ `Downsampling mode`,
+ `Downsampling mode`,
+ GObject.ParamFlags.READWRITE,
+ 0, 2,
+ 0,
+ ),
+ 'width': GObject.ParamSpec.double(
+ `width`,
+ `Width`,
+ `Width`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'height': GObject.ParamSpec.double(
+ `height`,
+ `Height`,
+ `Height`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ )
+ }
+ }, class DownscaleEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get divider() {
+ return this._divider;
+ }
+
+ set divider(value) {
+ if (this._divider !== value) {
+ this._divider = value;
+
+ this.set_uniform_value('divider', this._divider);
+ }
+ }
+
+ get downsampling_mode() {
+ return this._downsampling_mode;
+ }
+
+ set downsampling_mode(value) {
+ if (this._downsampling_mode !== value) {
+ this._downsampling_mode = value;
+
+ this.set_uniform_value('downsampling_mode', this._downsampling_mode);
+ }
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ if (this._width !== value) {
+ this._width = value;
+
+ this.set_uniform_value('width', parseFloat(this._width - 1e-6));
+ }
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ if (this._height !== value) {
+ this._height = value;
+
+ this.set_uniform_value('height', parseFloat(this._height - 1e-6));
+ }
+ }
+
+ vfunc_set_actor(actor) {
+ if (this._actor_connection_size_id) {
+ let old_actor = this.get_actor();
+ old_actor?.disconnect(this._actor_connection_size_id);
+ }
+ if (actor) {
+ this.width = actor.width;
+ this.height = actor.height;
+ this._actor_connection_size_id = actor.connect('notify::size', _ => {
+ this.width = actor.width;
+ this.height = actor.height;
+ });
+ }
+ else
+ this._actor_connection_size_id = null;
+
+ super.vfunc_set_actor(actor);
+ }
+
+ vfunc_paint_target(paint_node, paint_context) {
+ // force setting nearest-neighbour texture filtering
+ this.get_pipeline().set_layer_filters(0, 9728, 9728);
+
+ super.vfunc_paint_target(paint_node, paint_context);
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/effects.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/effects.js
new file mode 100644
index 0000000..e35588d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/effects.js
@@ -0,0 +1,362 @@
+import { NativeDynamicBlurEffect } from '../effects/native_dynamic_gaussian_blur.js';
+import { NativeStaticBlurEffect } from '../effects/native_static_gaussian_blur.js';
+import { GaussianBlurEffect } from '../effects/gaussian_blur.js';
+import { MonteCarloBlurEffect } from '../effects/monte_carlo_blur.js';
+import { ColorEffect } from '../effects/color.js';
+import { NoiseEffect } from '../effects/noise.js';
+import { CornerEffect } from '../effects/corner.js';
+import { DownscaleEffect } from './downscale.js';
+import { UpscaleEffect } from './upscale.js';
+import { PixelizeEffect } from './pixelize.js';
+import { DerivativeEffect } from './derivative.js';
+import { RgbToHslEffect } from './rgb_to_hsl.js';
+import { HslToRgbEffect } from './hsl_to_rgb.js';
+
+// We do in this way because I've not found another way to store our preferences in a dictionnary
+// while calling `gettext` on it while in preferences. Not so pretty, but works.
+export function get_effects_groups(_ = _ => "") {
+ return {
+ blur_effects: {
+ name: _("Blur effects"),
+ contains: [
+ "native_static_gaussian_blur",
+ "gaussian_blur",
+ "monte_carlo_blur"
+ ]
+ },
+ texture_effects: {
+ name: _("Texture effects"),
+ contains: [
+ "downscale",
+ "upscale",
+ "pixelize",
+ "derivative",
+ "noise",
+ "color",
+ "rgb_to_hsl",
+ "hsl_to_rgb"
+ ]
+ },
+ shape_effects: {
+ name: _("Shape effects"),
+ contains: [
+ "corner"
+ ]
+ }
+ };
+};
+
+export function get_supported_effects(_ = () => "") {
+ return {
+ native_dynamic_gaussian_blur: {
+ class: NativeDynamicBlurEffect
+ },
+
+ native_static_gaussian_blur: {
+ class: NativeStaticBlurEffect,
+ name: _("Native gaussian blur"),
+ description: _("An optimized blur effect that smoothly blends pixels within a given radius."),
+ is_advanced: false,
+ editable_params: {
+ unscaled_radius: {
+ name: _("Radius"),
+ description: _("The intensity of the blur effect."),
+ type: "float",
+ min: 0.,
+ max: 100.,
+ increment: 1.0,
+ big_increment: 10.,
+ digits: 0
+ },
+ brightness: {
+ name: _("Brightness"),
+ description: _("The brightness of the blur effect, a high value might make the text harder to read."),
+ type: "float",
+ min: 0.,
+ max: 1.,
+ increment: 0.01,
+ big_increment: 0.1,
+ digits: 2
+ },
+ }
+ },
+
+ gaussian_blur: {
+ class: GaussianBlurEffect,
+ name: _("Gaussian blur (advanced effect)"),
+ description: _("A blur effect that smoothly blends pixels within a given radius. This effect is more precise, but way less optimized."),
+ is_advanced: true,
+ editable_params: {
+ radius: {
+ name: _("Radius"),
+ description: _("The intensity of the blur effect. The bigger it is, the slower it will be."),
+ type: "float",
+ min: 0.,
+ max: 100.,
+ increment: .1,
+ big_increment: 10.,
+ digits: 1
+ },
+ brightness: {
+ name: _("Brightness"),
+ description: _("The brightness of the blur effect, a high value might make the text harder to read."),
+ type: "float",
+ min: 0.,
+ max: 1.,
+ increment: 0.01,
+ big_increment: 0.1,
+ digits: 2
+ },
+ }
+ },
+
+ monte_carlo_blur: {
+ class: MonteCarloBlurEffect,
+ name: _("Monte Carlo blur"),
+ description: _("A blur effect that mimics a random walk, by picking pixels further and further away from its origin and mixing them all together."),
+ is_advanced: false,
+ editable_params: {
+ radius: {
+ name: _("Radius"),
+ description: _("The maximum travel distance for each step in the random walk. A higher value will make the blur more randomized."),
+ type: "float",
+ min: 0.,
+ max: 10.,
+ increment: 0.01,
+ big_increment: 0.1,
+ digits: 2
+ },
+ iterations: {
+ name: _("Iterations"),
+ description: _("The number of iterations. The more there are, the smoother the blur is."),
+ type: "integer",
+ min: 0,
+ max: 50,
+ increment: 1
+ },
+ brightness: {
+ name: _("Brightness"),
+ description: _("The brightness of the blur effect, a high value might make the text harder to read."),
+ type: "float",
+ min: 0.,
+ max: 1.,
+ increment: 0.01,
+ big_increment: 0.1,
+ digits: 2
+ },
+ use_base_pixel: {
+ name: _("Use base pixel"),
+ description: _("Whether or not the original pixel is counted for the blur. If it is, the image will be more legible."),
+ type: "boolean"
+ },
+ prefer_closer_pixels: {
+ name: _("Prefer closer pixels"),
+ description: _("Whether or not the pixels that are closer to the original pixel will have more weight."),
+ type: "boolean"
+ }
+ }
+ },
+
+ color: {
+ class: ColorEffect,
+ name: _("Color"),
+ description: _("An effect that blends a color into the pipeline."),
+ is_advanced: false,
+ // TODO make this RGB + blend
+ editable_params: {
+ color: {
+ name: _("Color"),
+ description: _("The color to blend in. The blending amount is controled by the opacity of the color."),
+ type: "rgba"
+ },
+ blend_mode: {
+ name: _("Blend mode"),
+ description: _("How the color is blended in."),
+ type: "dropdown",
+ options: [
+ _("Normal"),
+ _("Multiply"),
+ _("Screen"),
+ _("Overlay"),
+ _("Darken"),
+ _("Lighten"),
+ _("Plus darker"),
+ _("Plus lighter"),
+ _("Color dodge"),
+ _("Color burn"),
+ _("Hard light"),
+ _("Soft light"),
+ _("Difference"),
+ _("Exclusion"),
+ _("Hue"),
+ _("Saturation"),
+ _("Color"),
+ _("Luminosity")
+ ]
+ }
+ }
+ },
+
+ pixelize: {
+ class: PixelizeEffect,
+ name: _("Pixelize"),
+ description: _("An effect that pixelizes the image."),
+ is_advanced: false,
+ editable_params: {
+ factor: {
+ name: _("Factor"),
+ description: _("How much to scale down the image."),
+ type: "integer",
+ min: 1,
+ max: 50,
+ increment: 1
+ },
+ downsampling_mode: {
+ name: _("Downsampling mode"),
+ description: _("The downsampling method that is used."),
+ type: "dropdown",
+ options: [
+ _("Boxcar"),
+ _("Triangular"),
+ _("Dirac")
+ ]
+ }
+ }
+ },
+
+ downscale: {
+ class: DownscaleEffect,
+ name: _("Downscale (advanced effect)"),
+ description: _("An effect that downscales the image and put it on the top-left corner."),
+ is_advanced: true,
+ editable_params: {
+ divider: {
+ name: _("Factor"),
+ description: _("How much to scale down the image."),
+ type: "integer",
+ min: 1,
+ max: 50,
+ increment: 1
+ },
+ downsampling_mode: {
+ name: _("Downsampling mode"),
+ description: _("The downsampling method that is used."),
+ type: "dropdown",
+ options: [
+ _("Boxcar"),
+ _("Triangular"),
+ _("Dirac")
+ ]
+ }
+ }
+ },
+
+ upscale: {
+ class: UpscaleEffect,
+ name: _("Upscale (advanced effect)"),
+ description: _("An effect that upscales the image from the top-left corner."),
+ is_advanced: true,
+ editable_params: {
+ factor: {
+ name: _("Factor"),
+ description: _("How much to scale up the image."),
+ type: "integer",
+ min: 1,
+ max: 50,
+ increment: 1
+ }
+ }
+ },
+
+ derivative: {
+ class: DerivativeEffect,
+ name: _("Derivative"),
+ description: _("Apply a spatial derivative, or a laplacian."),
+ is_advanced: false,
+ editable_params: {
+ operation: {
+ name: _("Operation"),
+ description: _("The mathematical operation to apply."),
+ type: "dropdown",
+ options: [
+ _("1-step derivative"),
+ _("2-step derivative"),
+ _("Laplacian")
+ ]
+ }
+ }
+ },
+
+ noise: {
+ class: NoiseEffect,
+ name: _("Noise"),
+ description: _("An effect that adds a random noise. Prefer the Monte Carlo blur for a more organic effect if needed."),
+ is_advanced: false,
+ editable_params: {
+ noise: {
+ name: _("Noise"),
+ description: _("The amount of noise to add."),
+ type: "float",
+ min: 0.,
+ max: 1.,
+ increment: 0.01,
+ big_increment: 0.1,
+ digits: 2
+ },
+ lightness: {
+ name: _("Lightness"),
+ description: _("The luminosity of the noise. A setting of '1.0' will make the effect transparent."),
+ type: "float",
+ min: 0.,
+ max: 2.,
+ increment: 0.01,
+ big_increment: 0.1,
+ digits: 2
+ }
+ }
+ },
+
+ rgb_to_hsl: {
+ class: RgbToHslEffect,
+ name: _("RGB to HSL (advanced effect)"),
+ description: _("Converts the image from RGBA colorspace to HSLA."),
+ is_advanced: true,
+ editable_params: {}
+ },
+
+ hsl_to_rgb: {
+ class: HslToRgbEffect,
+ name: _("HSL to RGB (advanced effect)"),
+ description: _("Converts the image from HSLA colorspace to RGBA."),
+ is_advanced: true,
+ editable_params: {}
+ },
+
+ corner: {
+ class: CornerEffect,
+ name: _("Corner"),
+ description: _("An effect that draws corners. Add it last not to have the other effects perturb the corners."),
+ is_advanced: false,
+ editable_params: {
+ radius: {
+ name: _("Radius"),
+ description: _("The radius of the corner. GNOME apps use a radius of 12 px by default."),
+ type: "integer",
+ min: 0,
+ max: 50,
+ increment: 1,
+ },
+ corners_top: {
+ name: _("Top corners"),
+ description: _("Whether or not to round the top corners."),
+ type: "boolean"
+ },
+ corners_bottom: {
+ name: _("Bottom corners"),
+ description: _("Whether or not to round the bottom corners."),
+ type: "boolean"
+ }
+ }
+ }
+ };
+}; \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.glsl
new file mode 100644
index 0000000..809cc93
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.glsl
@@ -0,0 +1,70 @@
+uniform sampler2D tex;
+uniform float sigma;
+uniform int dir;
+uniform float brightness;
+uniform float width;
+uniform float height;
+
+vec4 getTexture(vec2 uv) {
+ if (uv.x < 3. / width)
+ uv.x = 3. / width;
+
+ if (uv.y < 3. / height)
+ uv.y = 3. / height;
+
+ if (uv.x > 1. - 3. / width)
+ uv.x = 1. - 3. / width;
+
+ if (uv.y > 1. - 3. / height)
+ uv.y = 1. - 3. / height;
+
+ return texture2D(tex, uv);
+}
+
+void main(void) {
+ vec2 uv = cogl_tex_coord_in[0].xy;
+ vec2 direction = vec2(dir, (1.0 - dir));
+
+ float pixel_step;
+ if (dir == 0)
+ pixel_step = 1.0 / height;
+ else
+ pixel_step = 1.0 / width;
+
+ vec3 gauss_coefficient;
+ gauss_coefficient.x = 1.0 / (sqrt(2.0 * 3.14159265) * sigma);
+ gauss_coefficient.y = exp(-0.5 / (sigma * sigma));
+ gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y;
+
+ float gauss_coefficient_total = gauss_coefficient.x;
+
+ vec4 ret = getTexture(uv) * gauss_coefficient.x;
+ gauss_coefficient.xy *= gauss_coefficient.yz;
+
+ int n_steps = int(ceil(1.5 * sigma)) * 2;
+
+ for (int i = 1; i <= n_steps; i += 2) {
+ float coefficient_subtotal = gauss_coefficient.x;
+ gauss_coefficient.xy *= gauss_coefficient.yz;
+ coefficient_subtotal += gauss_coefficient.x;
+
+ float gauss_ratio = gauss_coefficient.x / coefficient_subtotal;
+
+ float foffset = float(i) + gauss_ratio;
+ vec2 offset = direction * foffset * pixel_step;
+
+ ret += getTexture(uv + offset) * coefficient_subtotal;
+ ret += getTexture(uv - offset) * coefficient_subtotal;
+
+ gauss_coefficient_total += 2.0 * coefficient_subtotal;
+ gauss_coefficient.xy *= gauss_coefficient.yz;
+ }
+ vec4 outColor = ret / gauss_coefficient_total;
+
+ // apply brightness on the second pass (dir==0 comes last)
+ if (dir == 0) {
+ outColor.rgb *= brightness;
+ }
+
+ cogl_color_out = outColor;
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.js
new file mode 100644
index 0000000..113a6fd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/gaussian_blur.js
@@ -0,0 +1,213 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const St = await utils.import_in_shell_only('gi://St');
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'gaussian_blur.glsl';
+const DEFAULT_PARAMS = {
+ radius: 30, brightness: .6,
+ width: 0, height: 0, direction: 0, chained_effect: null
+};
+
+
+export const GaussianBlurEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "GaussianBlurEffect",
+ Properties: {
+ 'radius': GObject.ParamSpec.double(
+ `radius`,
+ `Radius`,
+ `Blur radius`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 2000.0,
+ 30.0,
+ ),
+ 'brightness': GObject.ParamSpec.double(
+ `brightness`,
+ `Brightness`,
+ `Blur brightness`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 1.0,
+ 0.6,
+ ),
+ 'width': GObject.ParamSpec.double(
+ `width`,
+ `Width`,
+ `Width`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'height': GObject.ParamSpec.double(
+ `height`,
+ `Height`,
+ `Height`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'direction': GObject.ParamSpec.int(
+ `direction`,
+ `Direction`,
+ `Direction`,
+ GObject.ParamFlags.READWRITE,
+ 0, 1,
+ 0,
+ ),
+ 'chained_effect': GObject.ParamSpec.object(
+ `chained_effect`,
+ `Chained Effect`,
+ `Chained Effect`,
+ GObject.ParamFlags.READWRITE,
+ GObject.Object,
+ ),
+ }
+ }, class GaussianBlurEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+
+ const theme_context = St.ThemeContext.get_for_stage(global.stage);
+ theme_context.connectObject(
+ 'notify::scale-factor', _ =>
+ this.set_uniform_value('sigma',
+ parseFloat(this.radius * theme_context.scale_factor / 2 - 1e-6)
+ ),
+ this
+ );
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get radius() {
+ return this._radius;
+ }
+
+ set radius(value) {
+ if (this._radius !== value) {
+ this._radius = value;
+
+ const scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+
+ // like Clutter, we use the assumption radius = 2*sigma
+ this.set_uniform_value('sigma', parseFloat(this._radius * scale_factor / 2 - 1e-6));
+ this.set_enabled(this.radius > 0.);
+
+ if (this.chained_effect)
+ this.chained_effect.radius = value;
+ }
+ }
+
+ get brightness() {
+ return this._brightness;
+ }
+
+ set brightness(value) {
+ if (this._brightness !== value) {
+ this._brightness = value;
+
+ this.set_uniform_value('brightness', parseFloat(this._brightness - 1e-6));
+
+ if (this.chained_effect)
+ this.chained_effect.brightness = value;
+ }
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ if (this._width !== value) {
+ this._width = value;
+
+ this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
+
+ if (this.chained_effect)
+ this.chained_effect.width = value;
+ }
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ if (this._height !== value) {
+ this._height = value;
+
+ this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
+
+ if (this.chained_effect)
+ this.chained_effect.height = value;
+ }
+ }
+
+ get direction() {
+ return this._direction;
+ }
+
+ set direction(value) {
+ if (this._direction !== value)
+ this._direction = value;
+ }
+
+ get chained_effect() {
+ return this._chained_effect;
+ }
+
+ set chained_effect(value) {
+ this._chained_effect = value;
+ }
+
+ vfunc_set_actor(actor) {
+ if (this._actor_connection_size_id) {
+ let old_actor = this.get_actor();
+ old_actor?.disconnect(this._actor_connection_size_id);
+ }
+ if (actor) {
+ this.width = actor.width;
+ this.height = actor.height;
+ this._actor_connection_size_id = actor.connect('notify::size', _ => {
+ this.width = actor.width;
+ this.height = actor.height;
+ });
+ }
+ else
+ this._actor_connection_size_id = null;
+
+ super.vfunc_set_actor(actor);
+
+ if (this.direction == 0) {
+ if (this.chained_effect)
+ this.chained_effect.get_actor()?.remove_effect(this.chained_effect);
+ else
+ this.chained_effect = new GaussianBlurEffect({
+ radius: this.radius,
+ brightness: this.brightness,
+ width: this.width,
+ height: this.height,
+ direction: 1
+ });
+ if (actor !== null)
+ actor.add_effect(this.chained_effect);
+ }
+ }
+
+ vfunc_paint_target(paint_node, paint_context) {
+ this.set_uniform_value("dir", this.direction);
+
+ super.vfunc_paint_target(paint_node, paint_context);
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.glsl
new file mode 100644
index 0000000..58d2694
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.glsl
@@ -0,0 +1,14 @@
+uniform sampler2D tex;
+
+vec3 hsl_to_rgb(vec3 c) {
+ vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+}
+
+void main(void) {
+ vec2 uv = cogl_tex_coord_in[0].xy;
+ vec4 hsla = texture2D(tex, uv);
+ vec4 rgba = vec4(hsl_to_rgb(hsla.xyz), hsla.w);
+ cogl_color_out = rgba;
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.js
new file mode 100644
index 0000000..fe53114
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/hsl_to_rgb.js
@@ -0,0 +1,31 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'hsl_to_rgb.glsl';
+const DEFAULT_PARAMS = {};
+
+
+export const HslToRgbEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "HslToRgbEffect",
+ Properties: {}
+ }, class HslToRgbEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.glsl
new file mode 100644
index 0000000..27b3806
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.glsl
@@ -0,0 +1,52 @@
+uniform sampler2D tex;
+uniform float radius;
+uniform int iterations;
+uniform float brightness;
+uniform float width;
+uniform float height;
+uniform bool use_base_pixel;
+uniform bool prefer_closer_pixels;
+
+float srand(vec2 a) {
+ return sin(dot(a, vec2(1233.224, 1743.335)));
+}
+
+float rand(inout float r) {
+ r = fract(3712.65 * r + 0.61432);
+ return (r - 0.5) * 2.0;
+}
+
+void main() {
+ vec2 uv = cogl_tex_coord0_in.st;
+ vec2 p = 16 * radius / vec2(width, height);
+ float r = srand(uv);
+
+ vec2 rv;
+ vec2 dir;
+ vec2 new_uv;
+ int strength;
+
+ int count = 0;
+ vec4 c = vec4(0.);
+
+ for (int i = 0; i < iterations; i++) {
+ rv.x = rand(r);
+ rv.y = rand(r) * 3.141592;
+ dir = vec2(cos(rv.y), sin(rv.y));
+ new_uv = uv + rv.x * dir * p;
+ if (new_uv.x > 2. / width && new_uv.y > 2. / height && new_uv.x < 1. - 3. / width && new_uv.y < 1. - 3. / height) {
+ strength = prefer_closer_pixels ? (iterations - i)^2 : 1;
+ c += strength * texture2D(tex, new_uv);
+ count += strength;
+ }
+ }
+
+ if (count == 0 || use_base_pixel) {
+ strength = prefer_closer_pixels ? (iterations + 1)^2 : 1;
+ c += strength * texture2D(tex, uv);
+ count += strength;
+ }
+
+ c.xyz *= brightness;
+ cogl_color_out = c / count;
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.js
new file mode 100644
index 0000000..0da56ed
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/monte_carlo_blur.js
@@ -0,0 +1,207 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const St = await utils.import_in_shell_only('gi://St');
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'monte_carlo_blur.glsl';
+const DEFAULT_PARAMS = {
+ radius: 2., iterations: 5, brightness: .6,
+ width: 0, height: 0, use_base_pixel: true,
+ prefer_closer_pixels: true,
+};
+
+
+export const MonteCarloBlurEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "MonteCarloBlurEffect",
+ Properties: {
+ 'radius': GObject.ParamSpec.double(
+ `radius`,
+ `Radius`,
+ `Blur radius`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 2000.0,
+ 2.0,
+ ),
+ 'iterations': GObject.ParamSpec.int(
+ `iterations`,
+ `Iterations`,
+ `Blur iterations`,
+ GObject.ParamFlags.READWRITE,
+ 0, 64,
+ 5,
+ ),
+ 'brightness': GObject.ParamSpec.double(
+ `brightness`,
+ `Brightness`,
+ `Blur brightness`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 1.0,
+ 0.6,
+ ),
+ 'width': GObject.ParamSpec.double(
+ `width`,
+ `Width`,
+ `Width`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'height': GObject.ParamSpec.double(
+ `height`,
+ `Height`,
+ `Height`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'use_base_pixel': GObject.ParamSpec.boolean(
+ `use_base_pixel`,
+ `Use base pixel`,
+ `Use base pixel`,
+ GObject.ParamFlags.READWRITE,
+ true,
+ ),
+ 'prefer_closer_pixels': GObject.ParamSpec.boolean(
+ `prefer_closer_pixels`,
+ `Prefer closer pixels`,
+ `Prefer closer pixels`,
+ GObject.ParamFlags.READWRITE,
+ true,
+ ),
+ }
+ }, class MonteCarloBlurEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+
+ const theme_context = St.ThemeContext.get_for_stage(global.stage);
+ theme_context.connectObject(
+ 'notify::scale-factor',
+ _ => this.set_uniform_value('radius',
+ parseFloat(this._radius * theme_context.scale_factor - 1e-6)
+ ),
+ this
+ );
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get radius() {
+ return this._radius;
+ }
+
+ set radius(value) {
+ if (this._radius !== value) {
+ this._radius = value;
+
+ const scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+
+ this.set_uniform_value('radius', parseFloat(this._radius * scale_factor - 1e-6));
+ this.set_enabled(this.radius > 0. && this.iterations > 0);
+ }
+ }
+
+ get iterations() {
+ return this._iterations;
+ }
+
+ set iterations(value) {
+ if (this._iterations !== value) {
+ this._iterations = value;
+
+ this.set_uniform_value('iterations', this._iterations);
+ this.set_enabled(this.radius > 0. && this.iterations > 0);
+ }
+ }
+
+ get brightness() {
+ return this._brightness;
+ }
+
+ set brightness(value) {
+ if (this._brightness !== value) {
+ this._brightness = value;
+
+ this.set_uniform_value('brightness', parseFloat(this._brightness - 1e-6));
+ }
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ if (this._width !== value) {
+ this._width = value;
+
+ this.set_uniform_value('width', parseFloat(this._width + 3.0 - 1e-6));
+ }
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ if (this._height !== value) {
+ this._height = value;
+
+ this.set_uniform_value('height', parseFloat(this._height + 3.0 - 1e-6));
+ }
+ }
+
+ get use_base_pixel() {
+ return this._use_base_pixel;
+ }
+
+ set use_base_pixel(value) {
+ if (this._use_base_pixel !== value) {
+ this._use_base_pixel = value;
+
+ this.set_uniform_value('use_base_pixel', this._use_base_pixel ? 1 : 0);
+ }
+ }
+
+ get prefer_closer_pixels() {
+ return this._prefer_closer_pixels;
+ }
+
+ set prefer_closer_pixels(value) {
+ if (this._prefer_closer_pixels !== value) {
+ this._prefer_closer_pixels = value;
+
+ this.set_uniform_value('prefer_closer_pixels', this._prefer_closer_pixels ? 1 : 0);
+ }
+ }
+
+ vfunc_set_actor(actor) {
+ if (this._actor_connection_size_id) {
+ let old_actor = this.get_actor();
+ old_actor?.disconnect(this._actor_connection_size_id);
+ }
+ if (actor) {
+ this.width = actor.width;
+ this.height = actor.height;
+ this._actor_connection_size_id = actor.connect('notify::size', _ => {
+ this.width = actor.width;
+ this.height = actor.height;
+ });
+ }
+ else
+ this._actor_connection_size_id = null;
+
+ super.vfunc_set_actor(actor);
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_dynamic_gaussian_blur.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_dynamic_gaussian_blur.js
new file mode 100644
index 0000000..556ffa4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_dynamic_gaussian_blur.js
@@ -0,0 +1,43 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const St = await utils.import_in_shell_only('gi://St');
+const Shell = await utils.import_in_shell_only('gi://Shell');
+
+const DEFAULT_PARAMS = {
+ unscaled_radius: 30, brightness: 0.6
+};
+
+
+export const NativeDynamicBlurEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "NativeDynamicBlurEffect"
+ }, class NativeDynamicBlurEffect extends Shell.BlurEffect {
+ constructor(params) {
+ const { unscaled_radius, brightness, ...parent_params } = params;
+ super({ ...parent_params, mode: Shell.BlurMode.BACKGROUND });
+
+ this._theme_context = St.ThemeContext.get_for_stage(global.stage);
+ this._theme_context.connectObject(
+ 'notify::scale-factor',
+ _ => this.radius = this.unscaled_radius * this._theme_context.scale_factor,
+ this
+ );
+
+ utils.setup_params(this, params);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get unscaled_radius() {
+ return this._unscaled_radius;
+ }
+
+ set unscaled_radius(value) {
+ this._unscaled_radius = value;
+ this.radius = value * this._theme_context.scale_factor;
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_static_gaussian_blur.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_static_gaussian_blur.js
new file mode 100644
index 0000000..fe003f5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/native_static_gaussian_blur.js
@@ -0,0 +1,43 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const St = await utils.import_in_shell_only('gi://St');
+const Shell = await utils.import_in_shell_only('gi://Shell');
+
+const DEFAULT_PARAMS = {
+ unscaled_radius: 30, brightness: 0.6
+};
+
+
+export const NativeStaticBlurEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "NativeStaticBlurEffect"
+ }, class NativeStaticBlurEffect extends Shell.BlurEffect {
+ constructor(params) {
+ const { unscaled_radius, brightness, ...parent_params } = params;
+ super({ ...parent_params, mode: Shell.BlurMode.ACTOR });
+
+ this._theme_context = St.ThemeContext.get_for_stage(global.stage);
+ this._theme_context.connectObject(
+ 'notify::scale-factor',
+ _ => this.radius = this.unscaled_radius * this._theme_context.scale_factor,
+ this
+ );
+
+ utils.setup_params(this, params);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get unscaled_radius() {
+ return this._unscaled_radius;
+ }
+
+ set unscaled_radius(value) {
+ this._unscaled_radius = value;
+ this.radius = value * this._theme_context.scale_factor;
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.glsl
new file mode 100644
index 0000000..6d701b6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.glsl
@@ -0,0 +1,20 @@
+uniform sampler2D tex;
+uniform float noise;
+uniform float lightness;
+
+float PHI = 1.61803398874989484820459;
+float SEED = 24;
+
+float noise_gen(in vec2 xy) {
+ float r = fract(tan(distance(xy * PHI, xy) * SEED) * xy.x);
+ r = r != r ? 0.0 : r;
+ return r;
+}
+
+void main() {
+ vec4 c = texture2D(tex, cogl_tex_coord_in[0].st);
+ vec3 pix_color = c.xyz;
+ float blend = noise * (1. - noise_gen(gl_FragCoord.xy));
+
+ cogl_color_out = vec4(mix(pix_color, lightness * pix_color, blend), 1.);
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.js
new file mode 100644
index 0000000..3049fa1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/noise.js
@@ -0,0 +1,76 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'noise.glsl';
+const DEFAULT_PARAMS = {
+ noise: 0.4, lightness: 0.4
+};
+
+
+export const NoiseEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "NoiseEffect",
+ Properties: {
+ 'noise': GObject.ParamSpec.double(
+ `noise`,
+ `Noise`,
+ `Amount of noise integrated with the image`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 1.0,
+ 0.4,
+ ),
+ 'lightness': GObject.ParamSpec.double(
+ `lightness`,
+ `Lightness`,
+ `Lightness of the grey used for the noise`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, 2.0,
+ 0.4,
+ ),
+ }
+ }, class NoiseEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get noise() {
+ return this._noise;
+ }
+
+ set noise(value) {
+ if (this._noise !== value) {
+ this._noise = value;
+
+ this.set_uniform_value('noise', parseFloat(this._noise - 1e-6));
+ this.set_enabled(this.noise > 0. && this.lightness != 1);
+ }
+ }
+
+ get lightness() {
+ return this._lightness;
+ }
+
+ set lightness(value) {
+ if (this._lightness !== value) {
+ this._lightness = value;
+
+ this.set_uniform_value('lightness', parseFloat(this._lightness - 1e-6));
+ this.set_enabled(this.noise > 0. && this.lightness != 1);
+ }
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/pixelize.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/pixelize.js
new file mode 100644
index 0000000..e9cad9b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/pixelize.js
@@ -0,0 +1,88 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+import { UpscaleEffect } from './upscale.js';
+import { DownscaleEffect } from './downscale.js';
+
+const DEFAULT_PARAMS = {
+ factor: 8, downsampling_mode: 0
+};
+
+
+export const PixelizeEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "PixelizeEffect",
+ Properties: {
+ 'factor': GObject.ParamSpec.int(
+ `factor`,
+ `Factor`,
+ `Factor`,
+ GObject.ParamFlags.READWRITE,
+ 0, 64,
+ 8,
+ ),
+ 'downsampling_mode': GObject.ParamSpec.int(
+ `downsampling_mode`,
+ `Downsampling mode`,
+ `Downsampling mode`,
+ GObject.ParamFlags.READWRITE,
+ 0, 2,
+ 0,
+ )
+ }
+ }, class PixelizeEffect extends Clutter.Effect {
+ constructor(params) {
+ super();
+
+ this.upscale_effect = new UpscaleEffect({});
+ this.downscale_effect = new DownscaleEffect({});
+
+ utils.setup_params(this, params);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get factor() {
+ // should be the same as `this.downscale_effect.divider`
+ return this.upscale_effect.factor;
+ }
+
+ set factor(value) {
+ this.upscale_effect.factor = value;
+ this.downscale_effect.divider = value;
+ }
+
+ get downsampling_mode() {
+ return this.downscale_effect.downsampling_mode;
+ }
+
+ set downsampling_mode(value) {
+ this.downscale_effect.downsampling_mode = value;
+ }
+
+ vfunc_set_actor(actor) {
+ // deattach effects from old actor
+ this.upscale_effect?.actor?.remove_effect(this.upscale_effect);
+ this.downscale_effect?.actor?.remove_effect(this.downscale_effect);
+ // attach effects to new actor
+ if (actor) {
+ if (this.upscale_effect)
+ actor.add_effect(this.upscale_effect);
+ if (this.downscale_effect)
+ actor.add_effect(this.downscale_effect);
+ }
+
+ super.vfunc_set_actor(actor);
+ }
+
+ vfunc_set_enabled(is_enabled) {
+ this.upscale_effect?.set_enabled(is_enabled);
+ this.downscale_effect?.set_enabled(is_enabled);
+ super.vfunc_set_enabled(is_enabled);
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.glsl
new file mode 100644
index 0000000..b7a35ac
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.glsl
@@ -0,0 +1,18 @@
+uniform sampler2D tex;
+
+vec3 rgb_to_hsl(vec3 c) {
+ vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
+ vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
+ vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
+
+ float d = q.x - min(q.w, q.y);
+ float e = 1.0e-10;
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+}
+
+void main(void) {
+ vec2 uv = cogl_tex_coord_in[0].xy;
+ vec4 rgba = texture2D(tex, uv);
+ vec4 hsla = vec4(rgb_to_hsl(rgba.xyz), rgba.w);
+ cogl_color_out = hsla;
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.js
new file mode 100644
index 0000000..5c4eb05
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/rgb_to_hsl.js
@@ -0,0 +1,31 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'rgb_to_hsl.glsl';
+const DEFAULT_PARAMS = {};
+
+
+export const RgbToHslEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "RgbToHslEffect",
+ Properties: {}
+ }, class RgbToHslEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.glsl b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.glsl
new file mode 100644
index 0000000..5cc06ec
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.glsl
@@ -0,0 +1,57 @@
+uniform sampler2D tex;
+uniform int factor;
+uniform float width;
+uniform float height;
+
+#define CORRECTION 2.25
+#define SIZE_ADDITION 3
+
+vec4 get_texture_at_position(vec2 position) {
+ vec2 raw_position = position + vec2(CORRECTION, CORRECTION);
+ vec2 raw_uv = raw_position / vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
+
+ return texture2D(tex, raw_uv);
+}
+
+ivec2 get_corrected_position() {
+ vec2 raw_uv = cogl_tex_coord0_in.st;
+ vec2 raw_position = raw_uv * vec2(width + SIZE_ADDITION, height + SIZE_ADDITION);
+ return ivec2(raw_position - vec2(CORRECTION, CORRECTION));
+}
+
+void main() {
+ ivec2 corrected_position = get_corrected_position();
+
+ vec2 adjusted_position = corrected_position / factor;
+
+ cogl_color_out = get_texture_at_position(adjusted_position);
+
+ // round
+ if (distance(corrected_position, (floor(adjusted_position) + 0.5) * factor) < factor / 2.5) {
+ //cogl_color_out = get_texture_at_position(adjusted_position);
+ } else {
+ //cogl_color_out = vec4(0, 0, 0, 1);
+ }
+
+ // square
+ if (mod(corrected_position.x, factor) >= 2 && mod(corrected_position.y, factor) >= 2) {
+ //cogl_color_out = get_texture_at_position(adjusted_position);
+ } else {
+ //cogl_color_out = vec4(0, 0, 0, 1);
+ }
+
+ // local mix
+ vec4 color = vec4(0);
+ int count = 0;
+ for (int i = -1; i <= 1; i++) {
+ for (int j = -1; j <= 1; j++) {
+ vec2 lookup_position = adjusted_position + vec2(i, j);
+ if (all(greaterThanEqual(lookup_position, vec2(0, 0))) &&
+ all(lessThan(lookup_position, vec2(width, height) / factor))) {
+ color += get_texture_at_position(lookup_position);
+ count += 1;
+ }
+ }
+ }
+ //cogl_color_out = color / count;
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.js
new file mode 100644
index 0000000..fb719c3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/effects/upscale.js
@@ -0,0 +1,120 @@
+import GObject from 'gi://GObject';
+
+import * as utils from '../conveniences/utils.js';
+const Shell = await utils.import_in_shell_only('gi://Shell');
+const Clutter = await utils.import_in_shell_only('gi://Clutter');
+
+const SHADER_FILENAME = 'upscale.glsl';
+const DEFAULT_PARAMS = {
+ factor: 8, width: 0, height: 0
+};
+
+
+export const UpscaleEffect = utils.IS_IN_PREFERENCES ?
+ { default_params: DEFAULT_PARAMS } :
+ new GObject.registerClass({
+ GTypeName: "UpscaleEffect",
+ Properties: {
+ 'factor': GObject.ParamSpec.int(
+ `factor`,
+ `Factor`,
+ `Factor`,
+ GObject.ParamFlags.READWRITE,
+ 0, 64,
+ 8,
+ ),
+ 'width': GObject.ParamSpec.double(
+ `width`,
+ `Width`,
+ `Width`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ ),
+ 'height': GObject.ParamSpec.double(
+ `height`,
+ `Height`,
+ `Height`,
+ GObject.ParamFlags.READWRITE,
+ 0.0, Number.MAX_SAFE_INTEGER,
+ 0.0,
+ )
+ }
+ }, class UpscaleEffect extends Clutter.ShaderEffect {
+ constructor(params) {
+ super(params);
+
+ utils.setup_params(this, params);
+
+ // set shader source
+ this._source = utils.get_shader_source(Shell, SHADER_FILENAME, import.meta.url);
+ if (this._source)
+ this.set_shader_source(this._source);
+ }
+
+ static get default_params() {
+ return DEFAULT_PARAMS;
+ }
+
+ get factor() {
+ return this._factor;
+ }
+
+ set factor(value) {
+ if (this._factor !== value) {
+ this._factor = value;
+
+ this.set_uniform_value('factor', this._factor);
+ }
+ }
+
+ get width() {
+ return this._width;
+ }
+
+ set width(value) {
+ if (this._width !== value) {
+ this._width = value;
+
+ this.set_uniform_value('width', parseFloat(this._width - 1e-6));
+ }
+ }
+
+ get height() {
+ return this._height;
+ }
+
+ set height(value) {
+ if (this._height !== value) {
+ this._height = value;
+
+ this.set_uniform_value('height', parseFloat(this._height - 1e-6));
+ }
+ }
+
+ vfunc_set_actor(actor) {
+ if (this._actor_connection_size_id) {
+ let old_actor = this.get_actor();
+ old_actor?.disconnect(this._actor_connection_size_id);
+ }
+ if (actor) {
+ this.width = actor.width;
+ this.height = actor.height;
+ this._actor_connection_size_id = actor.connect('notify::size', _ => {
+ this.width = actor.width;
+ this.height = actor.height;
+ });
+ }
+ else
+ this._actor_connection_size_id = null;
+
+ super.vfunc_set_actor(actor);
+ }
+
+ vfunc_paint_target(paint_node, paint_context) {
+ // force setting nearest-neighbour texture filtering
+ this.get_pipeline().set_layer_filters(0, 9728, 9728);
+
+ super.vfunc_paint_target(paint_node, paint_context);
+ }
+ }); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/extension.js
new file mode 100644
index 0000000..fbfb54c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/extension.js
@@ -0,0 +1,614 @@
+import Meta from 'gi://Meta';
+import Clutter from 'gi://Clutter';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as Config from 'resource:///org/gnome/shell/misc/config.js';
+
+import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import { update_from_old_settings } from './conveniences/settings_updater.js';
+import { PipelinesManager } from './conveniences/pipelines_manager.js';
+import { EffectsManager } from './conveniences/effects_manager.js';
+import { Connections } from './conveniences/connections.js';
+import { Settings } from './conveniences/settings.js';
+import { KEYS } from './conveniences/keys.js';
+
+import { PanelBlur } from './components/panel.js';
+import { OverviewBlur } from './components/overview.js';
+import { DashBlur } from './components/dash_to_dock.js';
+import { LockscreenBlur } from './components/lockscreen.js';
+import { AppFoldersBlur } from './components/appfolders.js';
+import { WindowListBlur } from './components/window_list.js';
+import { CoverflowAltTabBlur } from './components/coverflow_alt_tab.js';
+import { ApplicationsBlur } from './components/applications.js';
+import { ScreenshotBlur } from './components/screenshot.js';
+
+
+/// The main extension class, created when the GNOME Shell is loaded.
+export default class BlurMyShell extends Extension {
+ /// Enables the extension.
+ enable() {
+ // add the extension to global to make it accessible to other extensions
+ // create it first as it is very useful when debugging crashes
+ global.blur_my_shell = this;
+
+ // update from old settings, very important for hacks level specifically
+ update_from_old_settings(this.getSettings());
+
+ // create a Settings instance, to manage extension's preferences
+ // it needs to be loaded before logging, as it checks for DEBUG
+ this._settings = new Settings(KEYS, this.getSettings());
+
+ this._log("enabling extension...");
+
+ // create main extension Connections instance
+ this._connection = new Connections;
+
+ // store it in a global array
+ this._connections = [this._connection];
+
+ // create a global effects manager (to prevent RAM bleeding)
+ this._effects_manager = new EffectsManager(this._connection);
+
+ // create a global pipelines manager, that helps talking with preferences
+ this._pipelines_manager = new PipelinesManager(this._settings);
+
+ // create an instance of each component, with its associated Connections
+ let init = () => {
+ // create a Connections instance, to manage signals
+ let connection = new Connections;
+
+ // store it to keeps track of them globally
+ this._connections.push(connection);
+
+ return [connection, this._settings, this._effects_manager];
+ };
+
+ this._panel_blur = new PanelBlur(...init());
+ this._dash_to_dock_blur = new DashBlur(...init());
+ this._overview_blur = new OverviewBlur(...init());
+ this._lockscreen_blur = new LockscreenBlur(...init());
+ this._appfolder_blur = new AppFoldersBlur(...init());
+ this._window_list_blur = new WindowListBlur(...init());
+ this._coverflow_alt_tab_blur = new CoverflowAltTabBlur(...init());
+ this._applications_blur = new ApplicationsBlur(...init());
+ this._screenshot_blur = new ScreenshotBlur(...init());
+
+ // connect each component to preferences change
+ this._connect_to_settings();
+
+ // enable the lockscreen blur, only one important in both `user` session and `unlock-dialog`
+ if (this._settings.lockscreen.BLUR && !this._lockscreen_blur.enabled)
+ this._lockscreen_blur.enable();
+
+ // ensure we take the correct action for the current session mode
+ this._user_session_mode_enabled = false;
+ this._on_session_mode_changed(Main.sessionMode);
+
+ // watch for changes to the session mode
+ this._connection.connect(Main.sessionMode, 'updated',
+ () => this._on_session_mode_changed(Main.sessionMode)
+ );
+ }
+
+ /// Enables the components related to the user session (everything except lockscreen blur).
+ _enable_user_session() {
+ this._log("changing mode to user session...");
+
+ // maybe disable clipped redraw
+ this._update_clipped_redraws();
+
+ // enable every component
+ // if the shell is still starting up, wait for it to be entirely loaded;
+ // this should prevent bugs like #136 and #137
+ if (Main.layoutManager._startingUp) {
+ this._connection.connect(
+ Main.layoutManager,
+ 'startup-complete',
+ () => this._enable_components()
+ );
+ } else
+ this._enable_components();
+
+ // try to enable the components as soon as possible anyway, this way the
+ // overview may load before the user sees it
+ try {
+ if (this._settings.overview.BLUR && !this._overview_blur.enabled)
+ this._overview_blur.enable();
+ } catch (e) {
+ this._log("Could not enable overview blur directly");
+ this._log(e);
+ }
+ try {
+ if (this._settings.dash_to_dock.BLUR
+ && !this._dash_to_dock_blur.enabled)
+ this._dash_to_dock_blur.enable();
+ } catch (e) {
+ this._log("Could not enable dash-to-dock blur directly");
+ this._log(e);
+ }
+ try {
+ if (this._settings.panel.BLUR && !this._panel_blur.enabled)
+ this._panel_blur.enable();
+ } catch (e) {
+ this._log("Could not enable panel blur directly");
+ this._log(e);
+ }
+
+ // tells the extension we have enabled the user session components, so that we do not
+ // disable them later if they were not even enabled to begin with
+ this._user_session_mode_enabled = true;
+ }
+
+ /// Disables the extension.
+ ///
+ /// This extension needs to use the 'unlock-dialog' session mode in order to change the blur on
+ /// the lockscreen. We have kind of two states of enablement for this extension:
+ /// - the 'enabled' state, which means that we have created the necessary components (which only
+ /// are js objects) and enabled the lockscreen blur (which means swapping two functions from
+ /// the `UnlockDialog` constructor with our ones;
+ /// - the 'user session enabled` mode, which means that we are in the 'enabled' mode AND we are
+ /// in the user mode, and so we enable all the other components that we created before.
+ /// We switch from one state to the other thanks to `this._on_session_mode_changed`, and we
+ /// track wether or not we are in the user mode with `this._user_session_mode_enabled` (because
+ /// `this._on_session_mode_changed` might be called multiple times while in the user session
+ /// mode, typically when going back from simple lockscreen and not sleep mode).
+ disable() {
+ this._log("disabling extension...");
+
+ // disable every component from user session mode
+ if (this._user_session_mode_enabled)
+ this._disable_user_session();
+ this._overview_blur.restore_patched_proto();
+
+ // disable lockscreen blur too
+ this._lockscreen_blur.disable();
+
+ // untrack them
+ this._panel_blur = null;
+ this._dash_to_dock_blur = null;
+ this._overview_blur = null;
+ this._appfolder_blur = null;
+ this._lockscreen_blur = null;
+ this._window_list_blur = null;
+ this._coverflow_alt_tab_blur = null;
+ this._applications_blur = null;
+ this._screenshot_blur = null;
+
+ // make sure no settings change can re-enable them
+ this._settings.disconnect_all_settings();
+
+ // force disconnecting every signal, even if component crashed
+ this._connections.forEach((connections) => {
+ connections.disconnect_all();
+ });
+ this._connections = [];
+
+ // remove the clipped redraws flag
+ this._reenable_clipped_redraws();
+
+ // remove the extension from GJS's global
+ delete global.blur_my_shell;
+
+ this._log("extension disabled.");
+
+ this._settings = null;
+ }
+
+ /// Disables the components related to the user session (everything except lockscreen blur).
+ _disable_user_session() {
+ this._log("disabling user session mode...");
+
+ // disable every component except lockscreen blur
+ this._panel_blur.disable();
+ this._dash_to_dock_blur.disable();
+ this._overview_blur.disable();
+ this._appfolder_blur.disable();
+ this._window_list_blur.disable();
+ this._coverflow_alt_tab_blur.disable();
+ this._applications_blur.disable();
+ this._screenshot_blur.disable();
+
+ // remove the clipped redraws flag
+ this._reenable_clipped_redraws();
+
+ // tells the extension we have disabled the user session components, so that we do not
+ // disable them later again if they were already disabled
+ this._user_session_mode_enabled = false;
+ }
+
+ /// Restarts the components related to the user session.
+ _restart() {
+ this._log("restarting...");
+
+ this._disable_user_session();
+ this._enable_user_session();
+
+ this._log("restarted.");
+ }
+
+ /// Changes the extension to operate either on 'user' mode or 'unlock-dialog' mode, switching
+ /// from one to the other means enabling/disabling every component except lockscreen blur.
+ _on_session_mode_changed(session) {
+ if (session.currentMode === 'user' || session.parentMode === 'user') {
+ if (!this._user_session_mode_enabled)
+ // we need to activate everything
+ this._enable_user_session();
+ }
+ else if (session.currentMode === 'unlock-dialog') {
+ if (this._user_session_mode_enabled)
+ // we need to disable the components related to the user session mode
+ this._disable_user_session();
+ }
+ }
+
+ /// Add or remove the clutter debug flag to disable clipped redraws.
+ /// This will entirely fix the blur effect, but should not be used except if
+ /// the user really needs it, as clipped redraws are a huge performance
+ /// boost for the compositor.
+ _update_clipped_redraws() {
+ if (this._settings.HACKS_LEVEL === 2)
+ this._disable_clipped_redraws();
+ else
+ this._reenable_clipped_redraws();
+ }
+
+ /// Add the Clutter debug flag.
+ _disable_clipped_redraws() {
+ let gnome_shell_major_version = parseInt(Config.PACKAGE_VERSION.split('.')[0]);
+ if (gnome_shell_major_version >= 48)
+ Clutter.add_debug_flags(
+ null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
+ );
+ else
+ Meta.add_clutter_debug_flags(
+ null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
+ );
+ }
+
+ /// Remove the Clutter debug flag.
+ _reenable_clipped_redraws() {
+ let gnome_shell_major_version = parseInt(Config.PACKAGE_VERSION.split('.')[0]);
+ if (gnome_shell_major_version >= 48)
+ Clutter.remove_debug_flags(
+ null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
+ );
+ else
+ Meta.remove_clutter_debug_flags(
+ null, Clutter.DrawDebugFlag.DISABLE_CLIPPED_REDRAWS, null
+ );
+ }
+
+ /// Enables every component from the user session needed, should be called when the shell is
+ /// entirely loaded as the `enable` methods interact with it.
+ _enable_components() {
+ // enable each component if needed, and if it is not already enabled
+
+ if (this._settings.panel.BLUR && !this._panel_blur.enabled)
+ this._panel_blur.enable();
+
+ if (this._settings.dash_to_dock.BLUR && !this._dash_to_dock_blur.enabled)
+ this._dash_to_dock_blur.enable();
+
+ if (this._settings.overview.BLUR && !this._overview_blur.enabled)
+ this._overview_blur.enable();
+
+ if (this._settings.appfolder.BLUR)
+ this._appfolder_blur.enable();
+
+ if (this._settings.applications.BLUR)
+ this._applications_blur.enable();
+
+ if (this._settings.window_list.BLUR)
+ this._window_list_blur.enable();
+
+ if (this._settings.coverflow_alt_tab.BLUR)
+ this._coverflow_alt_tab_blur.enable();
+
+ if (this._settings.screenshot.BLUR)
+ this._screenshot_blur.enable();
+
+ this._log("all components enabled.");
+ }
+
+ /// Updates needed things in each component when a preference changed
+ _connect_to_settings() {
+ // restart the extension when hacks level is changed, easier than
+ // restarting individual components and should not happen often either
+ this._settings.HACKS_LEVEL_changed(() => this._restart());
+
+
+ // ---------- OVERVIEW ----------
+
+ // toggled on/off
+ this._settings.overview.BLUR_changed(() => {
+ if (this._settings.overview.BLUR)
+ this._overview_blur.enable();
+ else
+ this._overview_blur.disable();
+ });
+
+ // overview pipeline changed
+ this._settings.overview.PIPELINE_changed(() => {
+ if (this._settings.overview.BLUR)
+ this._overview_blur.update_backgrounds();
+ });
+
+ // overview components style changed
+ this._settings.overview.STYLE_COMPONENTS_changed(() => {
+ if (this._settings.overview.BLUR)
+ this._overview_blur.update_components_classname();
+ });
+
+
+ // ---------- APPFOLDER ----------
+
+ // toggled on/off
+ this._settings.appfolder.BLUR_changed(() => {
+ if (this._settings.appfolder.BLUR)
+ this._appfolder_blur.enable();
+ else
+ this._appfolder_blur.disable();
+ });
+
+ // appfolder sigma changed
+ this._settings.appfolder.SIGMA_changed(() => {
+ if (this._settings.appfolder.BLUR)
+ this._appfolder_blur.set_sigma(
+ this._settings.appfolder.SIGMA
+ );
+ });
+
+ // appfolder brightness changed
+ this._settings.appfolder.BRIGHTNESS_changed(() => {
+ if (this._settings.appfolder.BLUR)
+ this._appfolder_blur.set_brightness(
+ this._settings.appfolder.BRIGHTNESS
+ );
+ });
+
+ // appfolder dialogs style changed
+ this._settings.appfolder.STYLE_DIALOGS_changed(() => {
+ if (this._settings.appfolder.BLUR)
+ this._appfolder_blur.blur_appfolders();
+ });
+
+
+ // ---------- PANEL ----------
+
+ // toggled on/off
+ this._settings.panel.BLUR_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.enable();
+ else
+ this._panel_blur.disable();
+ });
+
+ // static blur toggled on/off, really we can just reload the blur at this point
+ this._settings.panel.STATIC_BLUR_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.reset();
+ });
+
+ // panel pipeline changed
+ this._settings.panel.PIPELINE_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.update_pipeline();
+ });
+
+ // panel blur's overview connection toggled on/off
+ this._settings.panel.UNBLUR_IN_OVERVIEW_changed(() => {
+ this._panel_blur.connect_to_windows_and_overview();
+ });
+
+ // force light text toggled on/off
+ this._settings.panel.FORCE_LIGHT_TEXT_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.update_light_text_classname();
+ });
+
+ // panel override background toggled on/off
+ this._settings.panel.OVERRIDE_BACKGROUND_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.connect_to_windows_and_overview();
+ });
+
+ // panel style changed
+ this._settings.panel.STYLE_PANEL_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.connect_to_windows_and_overview();
+ });
+
+ // panel background's dynamic overriding toggled on/off
+ this._settings.panel.OVERRIDE_BACKGROUND_DYNAMICALLY_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.connect_to_windows_and_overview();
+ });
+
+
+ // ---------- DASH TO DOCK ----------
+
+ // toggled on/off
+ this._settings.dash_to_dock.BLUR_changed(() => {
+ if (this._settings.dash_to_dock.BLUR)
+ this._dash_to_dock_blur.enable();
+ else
+ this._dash_to_dock_blur.disable();
+ });
+
+ // static blur toggled on/off
+ this._settings.dash_to_dock.STATIC_BLUR_changed(() => {
+ if (this._settings.dash_to_dock.BLUR)
+ this._dash_to_dock_blur.change_blur_type();
+ });
+
+ // overview pipeline changed
+ this._settings.dash_to_dock.PIPELINE_changed(() => {
+ if (this._settings.dash_to_dock.BLUR)
+ this._dash_to_dock_blur.update_pipeline();
+ });
+
+ // dash-to-dock override background toggled on/off
+ this._settings.dash_to_dock.OVERRIDE_BACKGROUND_changed(() => {
+ if (this._settings.dash_to_dock.BLUR)
+ this._dash_to_dock_blur.update_background();
+ });
+
+ // dash-to-dock style changed
+ this._settings.dash_to_dock.STYLE_DASH_TO_DOCK_changed(() => {
+ if (this._settings.dash_to_dock.BLUR)
+ this._dash_to_dock_blur.update_background();
+ });
+
+ // dash-to-dock blur's overview connection toggled on/off
+ this._settings.dash_to_dock.UNBLUR_IN_OVERVIEW_changed(() => {
+ if (this._settings.dash_to_dock.BLUR)
+ this._dash_to_dock_blur.connect_to_overview();
+ });
+
+
+ // ---------- APPLICATIONS ----------
+
+ // toggled on/off
+ this._settings.applications.BLUR_changed(() => {
+ if (this._settings.applications.BLUR)
+ this._applications_blur.enable();
+ else
+ this._applications_blur.disable();
+ });
+
+ // static blur toggled on/off
+ this._settings.applications.STATIC_BLUR_changed(() => {
+ if (this._settings.applications.BLUR)
+ this._applications_blur.change_blur_type();
+ });
+
+ // pipeline changed
+ this._settings.applications.PIPELINE_changed(() => {
+ if (this._settings.applications.BLUR)
+ this._applications_blur.change_pipeline();
+ });
+
+ // application opacity changed
+ this._settings.applications.OPACITY_changed(() => {
+ if (this._settings.applications.BLUR)
+ this._applications_blur.set_opacity(
+ this._settings.applications.OPACITY
+ );
+ });
+
+ // application dynamic-opacity changed
+ this._settings.applications.DYNAMIC_OPACITY_changed(() => {
+ if (this._settings.applications.BLUR)
+ this._applications_blur.init_dynamic_opacity();
+ });
+
+ // application blur-on-overview changed
+ this._settings.applications.BLUR_ON_OVERVIEW_changed(() => {
+ if (this._settings.applications.BLUR)
+ this._applications_blur.connect_to_overview();
+ });
+
+ // application enable-all changed
+ this._settings.applications.ENABLE_ALL_changed(() => {
+ if (this._settings.applications.BLUR)
+ this._applications_blur.update_all_windows();
+ });
+
+ // application whitelist changed
+ this._settings.applications.WHITELIST_changed(() => {
+ if (
+ this._settings.applications.BLUR
+ && !this._settings.applications.ENABLE_ALL
+ )
+ this._applications_blur.update_all_windows();
+ });
+
+ // application blacklist changed
+ this._settings.applications.BLACKLIST_changed(() => {
+ if (
+ this._settings.applications.BLUR
+ && this._settings.applications.ENABLE_ALL
+ )
+ this._applications_blur.update_all_windows();
+ });
+
+
+ // ---------- LOCKSCREEN ----------
+
+ // toggled on/off
+ this._settings.lockscreen.BLUR_changed(() => {
+ if (this._settings.lockscreen.BLUR)
+ this._lockscreen_blur.enable();
+ else
+ this._lockscreen_blur.disable();
+ });
+
+ // lockscreen pipeline changed
+ this._settings.lockscreen.PIPELINE_changed(() => {
+ if (this._settings.lockscreen.BLUR)
+ this._lockscreen_blur.update_lockscreen();
+ });
+
+
+ // ---------- WINDOW LIST ----------
+
+ // toggled on/off
+ this._settings.window_list.BLUR_changed(() => {
+ if (this._settings.window_list.BLUR)
+ this._window_list_blur.enable();
+ else
+ this._window_list_blur.disable();
+ });
+
+
+ // ---------- COVERFLOW ALT-TAB ----------
+
+ // toggled on/off
+ this._settings.coverflow_alt_tab.BLUR_changed(() => {
+ if (this._settings.coverflow_alt_tab.BLUR)
+ this._coverflow_alt_tab_blur.enable();
+ else
+ this._coverflow_alt_tab_blur.disable();
+ });
+
+
+ // ---------- HIDETOPBAR ----------
+
+ // toggled on/off
+ this._settings.hidetopbar.COMPATIBILITY_changed(() => {
+ // no need to verify if it is enabled or not, it is done anyway
+ this._panel_blur.connect_to_windows_and_overview();
+ });
+
+
+ // ---------- DASH TO PANEL ----------
+
+ // toggled on/off
+ this._settings.dash_to_panel.BLUR_ORIGINAL_PANEL_changed(() => {
+ if (this._settings.panel.BLUR)
+ this._panel_blur.reset();
+ });
+
+
+ // ---------- SCREENSHOT ----------
+
+ // toggled on/off
+ this._settings.screenshot.BLUR_changed(() => {
+ if (this._settings.screenshot.BLUR)
+ this._screenshot_blur.enable();
+ else
+ this._screenshot_blur.disable();
+ });
+
+ // screenshot pipeline changed
+ this._settings.screenshot.PIPELINE_changed(() => {
+ if (this._settings.screenshot.BLUR)
+ this._screenshot_blur.update_pipeline();
+ });
+ }
+
+ _log(str) {
+ if (this._settings.DEBUG)
+ console.log(`[Blur my Shell > extension] ${str}`);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/add-row-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/add-row-symbolic.svg
new file mode 100644
index 0000000..bdab309
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/add-row-symbolic.svg
@@ -0,0 +1,4 @@
+<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <rect x="2" y="7" width="11" height="1" fill="#363636"/>
+ <rect transform="rotate(90)" x="2" y="-8" width="11" height="1" fill="#363636"/>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/applications-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/applications-symbolic.svg
new file mode 100644
index 0000000..87f307f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/applications-symbolic.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg7"
+ sodipodi:docname="applications.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview9"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="44.6875"
+ inkscape:cx="5.5944056"
+ inkscape:cy="8"
+ inkscape:window-width="1500"
+ inkscape:window-height="963"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg7" />
+ <defs
+ id="defs3">
+ <style
+ id="current-color-scheme"
+ type="text/css">.ColorScheme-Text { color:#363636; }</style>
+ </defs>
+ <path
+ d="M 2,1 C 0.892,1 0,1.892 0,3 v 9 c 0,1.108 0.892,2 2,2 h 12 c 1.108,0 2,-0.892 2,-2 V 3 C 16,1.892 15.108,1 14,1 Z m 0,1 h 12 c 0.554,0 1,0.446 1,1 H 1 C 1,2.446 1.446,2 2,2 Z M 1,3 h 14 v 9 c 0,0.554 -0.446,1 -1,1 H 2 C 1.446,13 1,12.554 1,12 Z"
+ style="fill:currentColor"
+ class="ColorScheme-Text"
+ id="path5"
+ sodipodi:nodetypes="sssssssssssccsccssssc" />
+ <g
+ id="g68"
+ transform="translate(0.3993007,0.15314685)">
+ <path
+ d="M 9.5,4 C 9.223,4 9,4.223 9,4.5 v 2 A 0.499,0.499 0 0 0 9.5,7 h 2 C 11.777,7 12,6.777 12,6.5 v -2 C 12,4.223 11.777,4 11.5,4 Z M 10,5 h 1 v 1 h -1 z"
+ fill="#363636"
+ id="path2" />
+ <path
+ d="M 4,6 C 3.446,6 3,6.446 3,7 v 3 c 0,0.554 0.446,1 1,1 h 3 c 0.554,0 1,-0.446 1,-1 V 7 C 8,6.446 7.554,6 7,6 Z m 0,1 h 3 v 3 H 4 Z"
+ fill="#363636"
+ id="path8" />
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/bottom-panel-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/bottom-panel-symbolic.svg
new file mode 100644
index 0000000..2eda98b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/bottom-panel-symbolic.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg7"
+ sodipodi:docname="panel.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview9"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="44.6875"
+ inkscape:cx="5.5944056"
+ inkscape:cy="8"
+ inkscape:window-width="1500"
+ inkscape:window-height="963"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg7" />
+ <defs
+ id="defs3">
+ <style
+ id="current-color-scheme"
+ type="text/css">.ColorScheme-Text { color:#363636; }</style>
+ </defs>
+ <path
+ d="M 2,1 C 0.892,1 0,1.892 0,3 v 9 c 0,1.108 0.892,2 2,2 h 12 c 1.108,0 2,-0.892 2,-2 V 3 C 16,1.892 15.108,1 14,1 Z m 0,1 h 12 c 0.554,0 1,0.446 1,1 H 1 C 1,2.446 1.446,2 2,2 Z M 1,4 h 14 v 8 c 0,0.554 -0.446,1 -1,1 H 2 C 1.446,13 1,12.554 1,12 Z"
+ style="fill:currentColor"
+ class="ColorScheme-Text"
+ id="path5"
+ sodipodi:nodetypes="sssssssssssccsccssssc" />
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dash-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dash-symbolic.svg
new file mode 100644
index 0000000..5a78b46
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dash-symbolic.svg
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg7"
+ sodipodi:docname="top-panel.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview9"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="44.6875"
+ inkscape:cx="5.5944056"
+ inkscape:cy="8"
+ inkscape:window-width="1500"
+ inkscape:window-height="963"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg7" />
+ <defs
+ id="defs3">
+ <style
+ id="current-color-scheme"
+ type="text/css">.ColorScheme-Text { color:#363636; }</style>
+ </defs>
+ <path
+ d="M 2,1 C 0.892,1 0,1.892 0,3 v 9 c 0,1.108 0.892,2 2,2 h 12 c 1.108,0 2,-0.892 2,-2 V 3 C 16,1.892 15.108,1 14,1 Z m 0,1 h 12 c 0.554,0 1,0.446 1,1 H 1 C 1,2.446 1.446,2 2,2 Z M 1,3 h 14 v 9 c 0,0.554 -0.446,1 -1,1 H 2 C 1.446,13 1,12.554 1,12 Z m 3,7 c -0.554,0 -1,0.446 -1,1 0,0.554 0.446,1 1,1 h 8 c 0.554,0 1,-0.446 1,-1 0,-0.554 -0.446,-1 -1,-1 z"
+ style="fill:currentColor"
+ class="ColorScheme-Text"
+ id="path5"
+ sodipodi:nodetypes="sssssssssssccsccsssscsssssss" />
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/down-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/down-symbolic.svg
new file mode 100644
index 0000000..652f8c0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/down-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 2.292969 6.707031 l 5 5 c 0.390625 0.390625 1.023437 0.390625 1.414062 0 l 5 -5 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 s -1.023437 -0.390625 -1.414062 0 l -4.292969 4.292969 l -4.292969 -4.292969 c -0.390625 -0.390625 -1.023437 -0.390625 -1.414062 0 s -0.390625 1.023437 0 1.414062 z m 0 0" fill="#222222" fill-rule="evenodd"/></svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/duplicate-row-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/duplicate-row-symbolic.svg
new file mode 100644
index 0000000..925e900
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/duplicate-row-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M7 5c-1.108 0-2 .892-2 2v7c0 1.108.892 2 2 2h7c1.108 0 2-.892 2-2V7c0-1.108-.892-2-2-2Zm0 1h7c.554 0 1 .446 1 1v7c0 .554-.446 1-1 1H7c-.554 0-1-.446-1-1V7c0-.554.446-1 1-1Z" style="fill:#dedede;stroke:none;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;stop-color:#000"/><path d="M2 0C.892 0 0 .892 0 2v7c0 1.108.892 2 2 2h2v-1H2c-.554 0-1-.446-1-1V2c0-.554.446-1 1-1h7c.554 0 1 .446 1 1v2h1V2c0-1.108-.892-2-2-2Z" style="fill:#dedede;stroke:none;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke fill markers;stop-color:#000"/></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dynamic-mode-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dynamic-mode-symbolic.svg
new file mode 100644
index 0000000..c37b9aa
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/dynamic-mode-symbolic.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16px"
+ height="16px"
+ viewBox="0 0 16 16"
+ version="1.1"
+ id="svg5"
+ sodipodi:docname="dynamic-mode-symbolic.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs5" />
+ <sodipodi:namedview
+ id="namedview5"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="44.75"
+ inkscape:cx="7.9888268"
+ inkscape:cy="8"
+ inkscape:window-width="1500"
+ inkscape:window-height="963"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg5"
+ showguides="true" />
+ <path
+ d="m 11.5,11.34375 a 5.75,5.75 0 0 1 -1,0.134766 v 2.234375 C 10.5,14.425813 9.9258135,15 9.2128906,15 H 2.2871094 C 1.5741865,15 1,14.425813 1,13.712891 V 6.7871094 C 1,6.0741865 1.5741865,5.5 2.2871094,5.5 h 2.234375 A 5.75,5.75 0 0 1 4.65625,4.5 H 1.5585937 C 0.69558189,4.5 0,5.1955819 0,6.0585937 V 14.441406 C 0,15.304418 0.69558189,16 1.5585937,16 H 9.9414063 C 10.804418,16 11.5,15.304418 11.5,14.441406 Z"
+ style="opacity:1;fill:#dedede;stroke-linejoin:round"
+ id="path14" />
+ <path
+ d="M 10.5,11.478516 A 5.75,5.75 0 0 1 10.25,11.5 5.75,5.75 0 0 1 4.5,5.75 5.75,5.75 0 0 1 4.5214844,5.5 H 2.2871094 C 1.5741865,5.5 1,6.0741865 1,6.7871094 V 13.712891 C 1,14.425813 1.5741865,15 2.2871094,15 H 9.2128906 C 9.9258135,15 10.5,14.425813 10.5,13.712891 Z"
+ style="opacity:0.25;fill:#dedede;stroke-width:0.826087;stroke-linejoin:round"
+ id="path15" />
+ <path
+ d="m 11.5,11.34375 v -1.029297 a 4.75,4.75 0 0 1 -1,0.164063 v 1 a 5.75,5.75 0 0 0 1,-0.134766 z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path22" />
+ <path
+ d="M 10.25,0 A 5.75,5.75 0 0 0 4.65625,4.5 H 5.6855469 A 4.75,4.75 0 0 1 10.25,1 4.75,4.75 0 0 1 15,5.75 4.75,4.75 0 0 1 11.5,10.314453 V 11.34375 A 5.75,5.75 0 0 0 16,5.75 5.75,5.75 0 0 0 10.25,0 Z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path18" />
+ <path
+ d="m 4.5214844,5.5 h 1 a 4.75,4.75 0 0 1 0.1640625,-1 H 4.65625 a 5.75,5.75 0 0 0 -0.1347656,1 z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path17" />
+ <path
+ d="m 10.5,11.478516 v -1 A 4.75,4.75 0 0 1 10.25,10.5 4.75,4.75 0 0 1 5.5,5.75 4.75,4.75 0 0 1 5.5214844,5.5 h -1 A 5.75,5.75 0 0 0 4.5,5.75 a 5.75,5.75 0 0 0 5.75,5.75 5.75,5.75 0 0 0 0.25,-0.02148 z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path16" />
+ <path
+ d="M 10.5,10.478516 V 6.7871094 C 10.5,6.0741865 9.9258135,5.5 9.2128906,5.5 H 5.5214844 A 4.75,4.75 0 0 0 5.5,5.75 a 4.75,4.75 0 0 0 4.75,4.75 4.75,4.75 0 0 0 0.25,-0.02148 z"
+ style="opacity:0.125;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.03551;stroke-linejoin:round;-inkscape-stroke:hairline;fill-opacity:1"
+ id="path21" />
+ <path
+ d="m 5.6855469,4.5 a 4.75,4.75 0 0 0 -0.1640625,1 H 9.2128906 C 9.9258135,5.5 10.5,6.0741865 10.5,6.7871094 v 3.6914066 a 4.75,4.75 0 0 0 1,-0.164063 V 6.0585937 C 11.5,5.1955819 10.804418,4.5 9.9414063,4.5 Z"
+ style="opacity:0.5;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.03551;stroke-linejoin:round;-inkscape-stroke:hairline;fill-opacity:1"
+ id="path20" />
+ <path
+ d="M 5.6855469,4.5 H 9.9414063 C 10.804418,4.5 11.5,5.1955819 11.5,6.0585937 V 10.314453 A 4.75,4.75 0 0 0 15,5.75 4.75,4.75 0 0 0 10.25,1 4.75,4.75 0 0 0 5.6855469,4.5 Z"
+ style="opacity:0;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.03551;stroke-linejoin:round;-inkscape-stroke:hairline;fill-opacity:1"
+ id="path19" />
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/edit-row-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/edit-row-symbolic.svg
new file mode 100644
index 0000000..f4115ad
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/edit-row-symbolic.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata>
+ <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>
+ <path d="m4 1c-1.108 0-2 0.892-2 2v10c0 1.108 0.892 2 2 2h8c1.108 0 2-0.892 2-2v-5.0488l-1 1v4.0488c0 0.554-0.446 1-1 1h-8c-0.554 0-1-0.446-1-1v-10c0-0.554 0.446-1 1-1h6.0488l1-1z" fill="#dedede"/>
+ <path class="ColorScheme-Text" d="m14.607 1.6863c-0.78347-0.78347-2.045-0.78347-2.8284 0l3.5355 3.5355c0.78347-0.78347 0.78348-2.045 0-2.8284zm-3.5355 0.70711-7.0711 7.0711-6e-7 3.5355h3.5355l7.0711-7.0711zm0 1.4142 2.1213 2.1213-5.6569 5.6569-2.1213-2.1213z" fill="#dedede"/>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/heart-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/heart-symbolic.svg
new file mode 100644
index 0000000..fa045a8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/heart-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 5.074219 1.9375 c -3.136719 -0.3125 -5.875 3.914062 -2.816407 6.613281 l 5.742188 5.117188 l 5.746094 -5.117188 c 2.585937 -2.238281 1.070312 -6.492187 -2.351563 -6.585937 c -0.972656 -0.027344 -1.921875 0.324218 -2.636719 0.984375 l -0.757812 0.675781 l -0.753906 -0.675781 c -0.703125 -0.628907 -1.449219 -0.941407 -2.171875 -1.011719 z m -0.097657 0.988281 c 0.507813 0.046875 1.039063 0.265625 1.597657 0.769531 h 0.003906 l 1.421875 1.269532 l 1.425781 -1.273438 l 0.007813 -0.007812 c 0.523437 -0.480469 1.21875 -0.738282 1.933594 -0.71875 c 1.300781 0.035156 2.15625 0.816406 2.515624 1.824218 c 0.355469 1.007813 0.191407 2.15625 -0.792968 3.003907 l -0.003906 0.007812 l -5.085938 4.527344 l -5.078125 -4.527344 h -0.003906 c -0.753907 -0.664062 -0.992188 -1.332031 -0.996094 -2 c 0 -0.667969 0.285156 -1.351562 0.761719 -1.890625 c 0.480468 -0.535156 1.128906 -0.898437 1.789062 -0.976562 c 0.167969 -0.015625 0.335938 -0.019532 0.503906 -0.007813 z m 0 0" fill="#222222"/></svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/other-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/other-symbolic.svg
new file mode 100644
index 0000000..45e703e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/other-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" enable-background="new"><defs><filter id="a" color-interpolation-filters="sRGB"><feBlend mode="darken" in2="BackgroundImage"/></filter></defs><g transform="matrix(0 -1 -1 0 -413 -83.997)" color="#000" fill="#dedede"><rect y="-418" x="-92.997" width="2" style="marker:none" ry="1" rx="1" height="2" overflow="visible" enable-background="new"/><rect y="-422" x="-92.997" width="2" style="marker:none" ry="1" rx="1" height="2" overflow="visible" enable-background="new"/><rect y="-426" x="-92.997" width="2" style="marker:none" ry="1" rx="1" height="2" overflow="visible" enable-background="new"/></g></svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/overview-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/overview-symbolic.svg
new file mode 100644
index 0000000..445f662
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/overview-symbolic.svg
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg7"
+ sodipodi:docname="overview.svg"
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview9"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="44.6875"
+ inkscape:cx="5.5944056"
+ inkscape:cy="8"
+ inkscape:window-width="1500"
+ inkscape:window-height="963"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g68"
+ inkscape:snap-bbox="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-bbox-midpoints="true" />
+ <defs
+ id="defs3">
+ <style
+ id="current-color-scheme"
+ type="text/css">.ColorScheme-Text { color:#363636; }</style>
+ </defs>
+ <path
+ d="M 2,1 C 0.892,1 0,1.892 0,3 v 9 c 0,1.108 0.892,2 2,2 h 12 c 1.108,0 2,-0.892 2,-2 V 3 C 16,1.892 15.108,1 14,1 Z m 0,1 h 12 c 0.554,0 1,0.446 1,1 H 1 C 1,2.446 1.446,2 2,2 Z M 1,3 h 14 v 9 c 0,0.554 -0.446,1 -1,1 H 2 C 1.446,13 1,12.554 1,12 Z"
+ style="fill:currentColor"
+ class="ColorScheme-Text"
+ id="path5"
+ sodipodi:nodetypes="sssssssssssccsccssssc" />
+ <g
+ id="g68"
+ transform="translate(0.3993007,0.15314685)">
+ <path
+ d="m 3.1756095,3.5602457 c -0.8440582,0 -1,0.446 -1,1 v 5.5732153 c 0,0.677136 0.446,1 1,1 H 12.02579 c 0.554,0 0.999999,-0.446 0.999999,-1 V 4.5602457 c 0,-0.554 -0.445999,-1 -0.999999,-1 z m 0,1 H 12.02579 V 10.133461 H 3.1756095 Z"
+ fill="#363636"
+ id="path8"
+ sodipodi:nodetypes="sssssssssccccc" />
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/pipelines-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/pipelines-symbolic.svg
new file mode 100644
index 0000000..d451b23
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/pipelines-symbolic.svg
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16"
+ height="16"
+ enable-background="new"
+ version="1.1"
+ id="svg16"
+ sodipodi:docname="pipelines-symbolic.svg"
+ inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs20" />
+ <sodipodi:namedview
+ id="namedview18"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ showgrid="false"
+ inkscape:zoom="24.174578"
+ inkscape:cx="13.98163"
+ inkscape:cy="8.3765683"
+ inkscape:window-width="1500"
+ inkscape:window-height="963"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g14" />
+ <g
+ fill="#363636"
+ id="g14"
+ transform="rotate(90,7.75,8.25)">
+ <path
+ d="m 2.9500144,1 c -0.277,0 -0.5,0.223 -0.5,0.5 v 6.5547 a 2.5,2.5 0 0 1 0.5,-0.054688 2.5,2.5 0 0 1 0.5,0.050781 v -6.5508 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 0.5,11.945 a 2.5,2.5 0 0 1 -0.5,0.05469 2.5,2.5 0 0 1 -0.5,-0.05078 v 1.5508 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 v -1.5547 z"
+ id="path2" />
+ <path
+ d="M 7.5,1 C 7.223,1 7,1.223 7,1.5 V 3.0547 A 2.5,2.5 0 0 1 7.5,3.000012 2.5,2.5 0 0 1 8,3.050793 v -1.5508 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z M 8,7.9453 A 2.5,2.5 0 0 1 7.5,7.999988 2.5,2.5 0 0 1 7,7.949207 v 6.5508 c 0,0.277 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223 0.5,-0.5 v -6.5547 z"
+ id="path4" />
+ <path
+ d="m 12.049986,1.0001482 c -0.277,0 -0.5,0.223 -0.5,0.5 v 6.5547 a 2.5,2.5 0 0 1 0.5,-0.054688 2.5,2.5 0 0 1 0.5,0.050781 v -6.5508 c 0,-0.277 -0.223,-0.5 -0.5,-0.5 z m 0.5,11.9450008 a 2.5,2.5 0 0 1 -0.5,0.05469 2.5,2.5 0 0 1 -0.5,-0.05078 v 1.5508 c 0,0.276999 0.223,0.5 0.5,0.5 0.277,0 0.5,-0.223001 0.5,-0.5 v -1.5547 z"
+ id="path6" />
+ <circle
+ cx="2.9500144"
+ cy="10.5"
+ id="circle8"
+ r="1.5" />
+ <circle
+ cx="7.5"
+ cy="5.5"
+ r="1.5"
+ id="circle10" />
+ <circle
+ cx="12.049986"
+ cy="10.500149"
+ id="circle12"
+ r="1.5" />
+ </g>
+</svg> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/remove-row-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/remove-row-symbolic.svg
new file mode 100644
index 0000000..a8da14f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/remove-row-symbolic.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <path d="m4.4647 3.9648c-0.12775 0-0.2555 0.048567-0.35339 0.14649-0.19578 0.19586-0.19578 0.51116 0 0.70703l3.1816 3.1816-3.1816 3.1816c-0.19578 0.19586-0.19578 0.51116 0 0.70703 0.19578 0.19586 0.51118 0.19586 0.70704 0l3.1816-3.1816 3.1816 3.1816c0.19578 0.19586 0.51114 0.19586 0.70704 0 0.19578-0.19586 0.19578-0.51116 0-0.70703l-3.1816-3.1816 3.1816-3.1816c0.19578-0.19586 0.19578-0.51116 0-0.70703-0.19578-0.19586-0.51118-0.19586-0.70704 0l-3.1816 3.1816-3.1816-3.1816c-0.09789-0.097928-0.22564-0.14649-0.35339-0.14649z" fill="#363636" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.9149" style="paint-order:stroke fill markers"/>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/reset-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/reset-symbolic.svg
new file mode 100644
index 0000000..e443de4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/reset-symbolic.svg
@@ -0,0 +1,7 @@
+<svg width="16" height="16" enable-background="new" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#363636">
+ <path d="m8 3a6 6 0 0 0-6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6h-1a5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5z"/>
+ <path d="m8.1719 0.67188-0.70703 0.70703 2.1211 2.1211-2.1211 2.1211 0.70703 0.70703 2.8281-2.8281-0.70703-0.70703z"/>
+ <rect x="8" y="3" width="2" height="1"/>
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-row-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-row-symbolic.svg
new file mode 100644
index 0000000..e6d2d9e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-row-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16px" viewBox="0 0 16 16" width="16px"><filter id="a" height="100%" width="100%" x="0%" y="0%"><feColorMatrix color-interpolation-filters="sRGB" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/></filter><mask id="b"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.5"/></g></mask><clipPath id="c"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="d"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.7"/></g></mask><clipPath id="e"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><mask id="f"><g filter="url(#a)"><path d="m -1.6 -1.6 h 19.2 v 19.2 h -19.2 z" fill-opacity="0.35"/></g></mask><clipPath id="g"><path d="m 0 0 h 1600 v 1200 h -1600 z"/></clipPath><g mask="url(#b)"><g clip-path="url(#c)" transform="matrix(1 0 0 1 -860 -60)"><path d="m 550 182 c -0.351562 0.003906 -0.695312 0.101562 -1 0.28125 v 3.4375 c 0.304688 0.179688 0.648438 0.277344 1 0.28125 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 c -0.339844 0 -0.679688 0.058594 -1 0.175781 v 6.824219 h 4 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#d)"><g clip-path="url(#e)" transform="matrix(1 0 0 1 -860 -60)"><path d="m 569 182 v 4 c 1.105469 0 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 5 v 7 h 3 v -4 c 0 -1.65625 -1.34375 -3 -3 -3 z m 0 0"/></g></g><g mask="url(#f)"><g clip-path="url(#g)" transform="matrix(1 0 0 1 -860 -60)"><path d="m 573 182.269531 v 3.449219 c 0.613281 -0.355469 0.996094 -1.007812 1 -1.71875 c 0 -0.714844 -0.382812 -1.375 -1 -1.730469 z m 0 4.90625 v 6.824219 h 2 v -4 c 0 -1.269531 -0.800781 -2.402344 -2 -2.824219 z m 0 0"/></g></g><path d="m 7.292969 1.707031 l 7 7 v -1.414062 l -7 7 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 s 1.023437 0.390625 1.414062 0 l 7 -7 c 0.390625 -0.390625 0.390625 -1.023437 0 -1.414062 l -7 -7 c -0.390625 -0.3906252 -1.023437 -0.3906252 -1.414062 0 c -0.390625 0.390625 -0.390625 1.023437 0 1.414062 z m 0 0"/><path d="m 15 7 h -14 c -0.550781 0 -1 0.449219 -1 1 s 0.449219 1 1 1 h 14 c 0.550781 0 1 -0.449219 1 -1 s -0.449219 -1 -1 -1 z m 0 0"/></svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-window-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-window-symbolic.svg
new file mode 100644
index 0000000..cad2da8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/select-window-symbolic.svg
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="16px" viewBox="0 0 16 16" width="16px"><path d="m 2.953125 1.074219 l 2.417969 13.210937 l 3.238281 -2.398437 l 2.054687 2.648437 c 1.03125 1.433594 3.148438 -0.210937 2.011719 -1.5625 l -2.015625 -2.59375 l 2.984375 -2.175781 z m 0 0" fill="#222222"/></svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/static-mode-symbolic.svg b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/static-mode-symbolic.svg
new file mode 100644
index 0000000..5b0eb20
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/icons/hicolor/scalable/actions/static-mode-symbolic.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="16px"
+ height="16px"
+ viewBox="0 0 16 16"
+ version="1.1"
+ id="svg5"
+ sodipodi:docname="static-mode-symbolic.svg"
+ inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs5" />
+ <sodipodi:namedview
+ id="namedview5"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:zoom="44.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-width="1500"
+ inkscape:window-height="963"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg5"
+ showguides="true" />
+ <path
+ d="m 11.5,11.34375 a 5.75,5.75 0 0 1 -1,0.134766 v 2.234375 C 10.5,14.425813 9.9258135,15 9.2128906,15 H 2.2871094 C 1.5741865,15 1,14.425813 1,13.712891 V 6.7871094 C 1,6.0741865 1.5741865,5.5 2.2871094,5.5 h 2.234375 A 5.75,5.75 0 0 1 4.65625,4.5 H 1.5585937 C 0.69558189,4.5 0,5.1955819 0,6.0585937 V 14.441406 C 0,15.304418 0.69558189,16 1.5585937,16 H 9.9414063 C 10.804418,16 11.5,15.304418 11.5,14.441406 Z"
+ style="opacity:1;fill:#dedede;stroke-linejoin:round"
+ id="path14" />
+ <path
+ d="M 10.5,11.478516 A 5.75,5.75 0 0 1 10.25,11.5 5.75,5.75 0 0 1 4.5,5.75 5.75,5.75 0 0 1 4.5214844,5.5 H 2.2871094 C 1.5741865,5.5 1,6.0741865 1,6.7871094 V 13.712891 C 1,14.425813 1.5741865,15 2.2871094,15 H 9.2128906 C 9.9258135,15 10.5,14.425813 10.5,13.712891 Z"
+ style="opacity:0.25;fill:#dedede;stroke-width:0.826087;stroke-linejoin:round"
+ id="path15" />
+ <path
+ d="m 11.5,11.34375 v -1.029297 a 4.75,4.75 0 0 1 -1,0.164063 v 1 a 5.75,5.75 0 0 0 1,-0.134766 z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path22" />
+ <path
+ d="M 10.25,0 A 5.75,5.75 0 0 0 4.65625,4.5 H 5.6855469 A 4.75,4.75 0 0 1 10.25,1 4.75,4.75 0 0 1 15,5.75 4.75,4.75 0 0 1 11.5,10.314453 V 11.34375 A 5.75,5.75 0 0 0 16,5.75 5.75,5.75 0 0 0 10.25,0 Z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path18" />
+ <path
+ d="m 4.5214844,5.5 h 1 a 4.75,4.75 0 0 1 0.1640625,-1 H 4.65625 a 5.75,5.75 0 0 0 -0.1347656,1 z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path17" />
+ <path
+ d="m 10.5,11.478516 v -1 A 4.75,4.75 0 0 1 10.25,10.5 4.75,4.75 0 0 1 5.5,5.75 4.75,4.75 0 0 1 5.5214844,5.5 h -1 A 5.75,5.75 0 0 0 4.5,5.75 a 5.75,5.75 0 0 0 5.75,5.75 5.75,5.75 0 0 0 0.25,-0.02148 z"
+ style="opacity:1;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.25351;stroke-linejoin:round;-inkscape-stroke:hairline"
+ id="path16" />
+ <path
+ d="M 10.5,10.478516 V 6.7871094 C 10.5,6.0741865 9.9258135,5.5 9.2128906,5.5 H 5.5214844 A 4.75,4.75 0 0 0 5.5,5.75 a 4.75,4.75 0 0 0 4.75,4.75 4.75,4.75 0 0 0 0.25,-0.02148 z"
+ style="opacity:0;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.03551;stroke-linejoin:round;-inkscape-stroke:hairline;fill-opacity:1"
+ id="path21" />
+ <path
+ d="m 5.6855469,4.5 a 4.75,4.75 0 0 0 -0.1640625,1 H 9.2128906 C 9.9258135,5.5 10.5,6.0741865 10.5,6.7871094 v 3.6914066 a 4.75,4.75 0 0 0 1,-0.164063 V 6.0585937 C 11.5,5.1955819 10.804418,4.5 9.9414063,4.5 Z"
+ style="opacity:0;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.03551;stroke-linejoin:round;-inkscape-stroke:hairline;fill-opacity:1"
+ id="path20" />
+ <path
+ d="M 5.6855469,4.5 H 9.9414063 C 10.804418,4.5 11.5,5.1955819 11.5,6.0585937 V 10.314453 A 4.75,4.75 0 0 0 15,5.75 4.75,4.75 0 0 0 10.25,1 4.75,4.75 0 0 0 5.6855469,4.5 Z"
+ style="opacity:0;vector-effect:non-scaling-stroke;fill:#dedede;stroke-width:1.03551;stroke-linejoin:round;-inkscape-stroke:hairline;fill-opacity:1"
+ id="path19" />
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/af/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/af/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..7fd4c31
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/af/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ar/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ar/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..aac168e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ar/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/az/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/az/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..2d37384
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/az/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/be/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/be/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..6229b38
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/be/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/bg/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/bg/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..9d8da30
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/bg/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ca/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ca/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..f804035
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ca/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/cs/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/cs/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..8f46756
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/cs/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/da/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/da/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..b44f3c8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/da/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/de/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/de/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..923e3e6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/de/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/el/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/el/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..c671ffa
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/el/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/es/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/es/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..352d3bc
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/es/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/et/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/et/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..a92f357
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/et/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fi/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fi/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..4e50603
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fi/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fr/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fr/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..a3a72a3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/fr/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/he/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/he/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..c00a0b0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/he/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hi/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hi/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..d3801bb
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hi/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hr/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hr/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..45ed92c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hr/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hu/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hu/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..40e34c4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/hu/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/id/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/id/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..034f55a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/id/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/it/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/it/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..a6fc86a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/it/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ja/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ja/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..12d1d52
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ja/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ka/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ka/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..f38aa09
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ka/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ko/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ko/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..9088bf3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ko/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ml/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ml/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..a03d776
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ml/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/mrh/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/mrh/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..52a61c4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/mrh/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/my/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/my/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..0d81fb0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/my/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nb_NO/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nb_NO/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..50cdf68
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nb_NO/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nl/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nl/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..a1a3517
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nl/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nn/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nn/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..28f7c42
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/nn/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pl/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pl/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..47fc350
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pl/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..10d3aa1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt_BR/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt_BR/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..3dcad8d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/pt_BR/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ro/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ro/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..dd7822e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ro/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ru/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ru/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..f7ba1de
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ru/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sk/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sk/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..be8e9a4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sk/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sl/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sl/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..34b6505
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sl/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sv/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sv/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..c7bc46f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/sv/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ta/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ta/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..8fc6e3d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/ta/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/th/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/th/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..e04e4a5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/th/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/tr/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/tr/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..04fce82
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/tr/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/uk/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/uk/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..9957d99
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/uk/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/vi/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/vi/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..385f8fa
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/vi/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_CN/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_CN/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..7b84952
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_CN/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_TW/LC_MESSAGES/blur-my-shell@aunetx.mo b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_TW/LC_MESSAGES/blur-my-shell@aunetx.mo
new file mode 100644
index 0000000..20e60f5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/locale/zh_TW/LC_MESSAGES/blur-my-shell@aunetx.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/metadata.json
new file mode 100644
index 0000000..cce8059
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/metadata.json
@@ -0,0 +1,28 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Adds a blur look to different parts of the GNOME Shell, including the top panel, dash and overview.\n\nYou can support my work by sponsoring me on:\n- github: https://github.com/sponsors/aunetx\n- ko-fi: https://ko-fi.com/aunetx\n\nIf you use Ubuntu, you may want to change the radius of the corner effect for Dash-to-Dock in preferences, else you may see unpleasantly mixed radii in your dock.\n\nNote: if the extension shows an error after updating, please make sure to restart your session to see if it persists. This is due to a bug in gnome shell, which I can't fix by myself.",
+ "donations": {
+ "github": "aunetx",
+ "kofi": "aunetx"
+ },
+ "gettext-domain": "blur-my-shell@aunetx",
+ "name": "Blur my Shell",
+ "original-authors": [
+ "me@aunetx.dev"
+ ],
+ "session-modes": [
+ "unlock-dialog",
+ "user"
+ ],
+ "settings-schema": "org.gnome.shell.extensions.blur-my-shell",
+ "shell-version": [
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/aunetx/blur-my-shell",
+ "uuid": "blur-my-shell@aunetx",
+ "version": 71
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications.js
new file mode 100644
index 0000000..a4dd5be
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications.js
@@ -0,0 +1,217 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+
+import { ApplicationRow } from './applications_management/application_row.js';
+
+
+const make_array = prefs_group => {
+ let list_box = prefs_group
+ .get_first_child()
+ .get_last_child()
+ .get_first_child();
+
+ let elements = [];
+ let i = 0;
+ let element = list_box.get_row_at_index(i);
+ while (element) {
+ elements.push(element);
+ i++;
+ element = list_box.get_row_at_index(i);
+ }
+
+ return elements;
+};
+
+
+export const Applications = GObject.registerClass({
+ GTypeName: 'Applications',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../ui/applications.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'blur',
+ 'pipeline_choose_row',
+ 'mode_static',
+ 'mode_dynamic',
+ 'sigma_row',
+ 'sigma',
+ 'brightness_row',
+ 'brightness',
+ 'opacity',
+ 'dynamic_opacity',
+ 'blur_on_overview',
+ 'enable_all',
+ 'whitelist',
+ 'add_window_whitelist',
+ 'blacklist',
+ 'add_window_blacklist'
+ ],
+}, class Applications extends Adw.PreferencesPage {
+ constructor(preferences, preferences_window, pipelines_manager, pipelines_page) {
+ super({});
+ this._preferences_window = preferences_window;
+
+ this.preferences = preferences;
+ this.pipelines_manager = pipelines_manager;
+ this.pipelines_page = pipelines_page;
+
+ this.preferences.applications.settings.bind(
+ 'blur', this._blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._pipeline_choose_row.initialize(
+ this.preferences.applications, this.pipelines_manager, this.pipelines_page
+ );
+
+ this.change_blur_mode(this.preferences.applications.STATIC_BLUR, true);
+
+ this._mode_static.connect('toggled',
+ () => this.preferences.applications.STATIC_BLUR = this._mode_static.active
+ );
+ this.preferences.applications.STATIC_BLUR_changed(
+ () => this.change_blur_mode(this.preferences.applications.STATIC_BLUR, false)
+ );
+
+ this.preferences.applications.settings.bind(
+ 'opacity', this._opacity, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.applications.settings.bind(
+ 'dynamic-opacity', this._dynamic_opacity, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.applications.settings.bind(
+ 'blur-on-overview', this._blur_on_overview, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.applications.settings.bind(
+ 'enable-all', this._enable_all, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.applications.settings.bind(
+ 'sigma', this._sigma, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.applications.settings.bind(
+ 'brightness', this._brightness, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ // connect 'enable all' button to whitelist/blacklist visibility
+ this._enable_all.bind_property(
+ 'active', this._whitelist, 'visible',
+ GObject.BindingFlags.INVERT_BOOLEAN
+ );
+ this._enable_all.bind_property(
+ 'active', this._blacklist, 'visible',
+ GObject.BindingFlags.DEFAULT
+ );
+
+ // make sure that blacklist / whitelist is correctly hidden
+ if (this._enable_all.active)
+ this._whitelist.visible = false;
+ this._blacklist.visible = !this._whitelist.visible;
+
+ // listen to app row addition
+ this._add_window_whitelist.connect('clicked',
+ () => this.add_to_whitelist()
+ );
+ this._add_window_blacklist.connect('clicked',
+ () => this.add_to_blacklist()
+ );
+
+ // add initial applications
+ this.add_widgets_from_lists();
+
+ this.preferences.connect('reset', () => {
+ this.remove_all_widgets();
+ this.add_widgets_from_lists();
+ });
+ }
+
+ // A way to retriew the whitelist widgets.
+ get _whitelist_elements() {
+ return make_array(this._whitelist);
+ }
+
+ // A way to retriew the blacklist widgets.
+ get _blacklist_elements() {
+ return make_array(this._blacklist);
+ }
+
+ add_widgets_from_lists() {
+ this.preferences.applications.WHITELIST.forEach(
+ app_name => this.add_to_whitelist(app_name)
+ );
+
+ this.preferences.applications.BLACKLIST.forEach(
+ app_name => this.add_to_blacklist(app_name)
+ );
+
+ }
+
+ close_all_expanded_rows() {
+ this._whitelist_elements.forEach(
+ element => element.set_expanded(false)
+ );
+ this._blacklist_elements.forEach(
+ element => element.set_expanded(false)
+ );
+ }
+
+ remove_all_widgets() {
+ this._whitelist_elements.forEach(
+ element => this._whitelist.remove(element)
+ );
+ this._blacklist_elements.forEach(
+ element => this._blacklist.remove(element)
+ );
+ }
+
+ add_to_whitelist(app_name = null) {
+ let window_row = new ApplicationRow('whitelist', this, app_name);
+ this._whitelist.add(window_row);
+ }
+
+ add_to_blacklist(app_name = null) {
+ let window_row = new ApplicationRow('blacklist', this, app_name);
+ this._blacklist.add(window_row);
+ }
+
+ update_whitelist_titles() {
+ let titles = this._whitelist_elements
+ .map(element => element._window_class.buffer.text)
+ .filter(title => title != "");
+
+ this.preferences.applications.WHITELIST = titles;
+ }
+
+ update_blacklist_titles() {
+ let titles = this._blacklist_elements
+ .map(element => element._window_class.buffer.text)
+ .filter(title => title != "");
+
+ this.preferences.applications.BLACKLIST = titles;
+ }
+
+ remove_from_whitelist(widget) {
+ this._whitelist.remove(widget);
+ this.update_whitelist_titles();
+ }
+
+ remove_from_blacklist(widget) {
+ this._blacklist.remove(widget);
+ this.update_blacklist_titles();
+ }
+
+ change_blur_mode(is_static_blur, first_run) {
+ this._mode_static.set_active(is_static_blur);
+ if (first_run)
+ this._mode_dynamic.set_active(!is_static_blur);
+
+ this._pipeline_choose_row.set_visible(is_static_blur);
+ this._sigma_row.set_visible(!is_static_blur);
+ this._brightness_row.set_visible(!is_static_blur);
+ }
+}); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications_management/application_row.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications_management/application_row.js
new file mode 100644
index 0000000..4af3bf8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/applications_management/application_row.js
@@ -0,0 +1,112 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+import Gtk from 'gi://Gtk';
+
+import { pick, on_picking, on_picked } from '../../dbus/client.js';
+
+
+export const ApplicationRow = GObject.registerClass({
+ GTypeName: 'ApplicationRow',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../../ui/application-row.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'window_picker',
+ 'window_class',
+ 'picking_failure_toast',
+ 'window_not_found_toast'
+ ],
+}, class ApplicationRow extends Adw.ExpanderRow {
+ constructor(list, app_page, app_name) {
+ super({});
+ this._list = list;
+ this._app_page = app_page;
+
+ // add a 'remove' button before the text
+ let action_row = this.child.get_first_child().get_first_child();
+ let remove_button = new Gtk.Button({
+ 'icon-name': 'remove-row-symbolic',
+ 'width-request': 38,
+ 'height-request': 38,
+ 'margin-top': 6,
+ 'margin-bottom': 6,
+ });
+ remove_button.add_css_class('circular');
+ remove_button.add_css_class('flat');
+ action_row.add_prefix(remove_button);
+
+ // connect the button to the whitelist / blacklist removal
+ remove_button.connect('clicked', () => this._remove_row());
+
+ // bind row title to text buffer
+ this._window_class.buffer.bind_property(
+ 'text', this, 'title',
+ Gio.SettingsBindFlags.BIDIRECTIONNAL
+ );
+
+ // set application name if it exists, or open the revealer and pick one
+ if (app_name)
+ this._window_class.buffer.text = app_name;
+ else {
+ app_page.close_all_expanded_rows();
+ this.set_expanded(true);
+ this._do_pick_window(true);
+ }
+
+ // pick a window when the picker button is clicked
+ this._window_picker.connect('clicked', () => this._do_pick_window());
+
+ // update list on text buffer change
+ this._window_class.connect('changed',
+ () => this._update_rows_titles()
+ );
+ }
+
+ _remove_row() {
+ this._app_page["remove_from_" + this._list](this);
+ }
+
+ _update_rows_titles() {
+ this._app_page["update_" + this._list + "_titles"](this);
+ }
+
+ _do_pick_window(remove_if_failed = false) {
+ // a mechanism to know if the extension is listening correcly
+ let has_responded = false;
+ let should_take_answer = true;
+ setTimeout(() => {
+ if (!has_responded) {
+ // show toast about failure
+ this._app_page._preferences_window.add_toast(
+ this._picking_failure_toast
+ );
+
+ // prevent title from changing with later picks
+ should_take_answer = false;
+
+ // remove row if asked
+ if (remove_if_failed)
+ this._remove_row();
+ }
+ }, 250);
+
+ on_picking(() =>
+ has_responded = true
+ );
+
+ on_picked(wm_class => {
+ if (should_take_answer) {
+ if (wm_class == 'window-not-found') {
+ console.warn("Can't pick window from here");
+ this._app_page._preferences_window.add_toast(
+ this._window_not_found_toast
+ );
+ return;
+ }
+ this._window_class.buffer.text = wm_class;
+ }
+ });
+
+ pick();
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/dash.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/dash.js
new file mode 100644
index 0000000..aae3768
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/dash.js
@@ -0,0 +1,81 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+
+
+export const Dash = GObject.registerClass({
+ GTypeName: 'Dash',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../ui/dash.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'blur',
+ 'pipeline_choose_row',
+ 'mode_static',
+ 'mode_dynamic',
+ 'sigma_row',
+ 'sigma',
+ 'brightness_row',
+ 'brightness',
+ 'override_background',
+ 'style_dash_to_dock',
+ 'unblur_in_overview'
+ ],
+}, class Dash extends Adw.PreferencesPage {
+ constructor(preferences, pipelines_manager, pipelines_page) {
+ super({});
+
+ this.preferences = preferences;
+ this.pipelines_manager = pipelines_manager;
+ this.pipelines_page = pipelines_page;
+
+ this.preferences.dash_to_dock.settings.bind(
+ 'blur', this._blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._pipeline_choose_row.initialize(
+ this.preferences.dash_to_dock, this.pipelines_manager, this.pipelines_page
+ );
+
+ this.change_blur_mode(this.preferences.dash_to_dock.STATIC_BLUR, true);
+
+ this._mode_static.connect('toggled',
+ () => this.preferences.dash_to_dock.STATIC_BLUR = this._mode_static.active
+ );
+ this.preferences.dash_to_dock.STATIC_BLUR_changed(
+ () => this.change_blur_mode(this.preferences.dash_to_dock.STATIC_BLUR, false)
+ );
+
+ this.preferences.dash_to_dock.settings.bind(
+ 'sigma', this._sigma, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.dash_to_dock.settings.bind(
+ 'brightness', this._brightness, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.dash_to_dock.settings.bind(
+ 'override-background',
+ this._override_background, 'enable-expansion',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.dash_to_dock.settings.bind(
+ 'style-dash-to-dock', this._style_dash_to_dock, 'selected',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.dash_to_dock.settings.bind(
+ 'unblur-in-overview', this._unblur_in_overview, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ }
+
+ change_blur_mode(is_static_blur, first_run) {
+ this._mode_static.set_active(is_static_blur);
+ if (first_run)
+ this._mode_dynamic.set_active(!is_static_blur);
+
+ this._pipeline_choose_row.set_visible(is_static_blur);
+ this._sigma_row.set_visible(!is_static_blur);
+ this._brightness_row.set_visible(!is_static_blur);
+ }
+}); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/menu.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/menu.js
new file mode 100644
index 0000000..899fdcf
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/menu.js
@@ -0,0 +1,68 @@
+import Gdk from 'gi://Gdk';
+import Gtk from 'gi://Gtk';
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+
+
+export function addMenu(window) {
+ const builder = new Gtk.Builder();
+
+ // add a dummy page and remove it immediately, to access headerbar
+ builder.add_from_file(GLib.filename_from_uri(GLib.uri_resolve_relative(import.meta.url, '../ui/menu.ui', GLib.UriFlags.NONE))[0]);
+ let menu_util = builder.get_object('menu_util');
+ window.add(menu_util);
+ try {
+ addMenuToHeader(window, builder);
+ } catch (error) {
+ // could not add menu... not so bad
+ }
+ window.remove(menu_util);
+}
+
+function addMenuToHeader(window, builder) {
+ // a little hack to get to the headerbar
+ const page = builder.get_object('menu_util');
+ const pages_stack = page.get_parent(); // AdwViewStack
+ const content_stack = pages_stack.get_parent().get_parent(); // GtkStack
+ const preferences = content_stack.get_parent(); // GtkBox
+ const headerbar = preferences.get_first_child().get_next_sibling()
+ .get_first_child().get_first_child().get_first_child(); // AdwHeaderBar
+ headerbar.pack_start(builder.get_object('info_menu'));
+
+ // setup menu actions
+ const actionGroup = new Gio.SimpleActionGroup();
+ window.insert_action_group('prefs', actionGroup);
+
+ // a list of actions with their associated link
+ const actions = [
+ {
+ name: 'open-bug-report',
+ link: 'https://github.com/aunetx/blur-my-shell/issues'
+ },
+ {
+ name: 'open-readme',
+ link: 'https://github.com/aunetx/blur-my-shell'
+ },
+ {
+ name: 'open-license',
+ link: 'https://github.com/aunetx/blur-my-shell/blob/master/LICENSE'
+ },
+ {
+ name: 'donate-github',
+ link: 'https://github.com/sponsors/aunetx'
+ },
+ {
+ name: 'donate-kofi',
+ link: 'https://ko-fi.com/aunetx'
+ },
+ ];
+
+ actions.forEach(action => {
+ let act = new Gio.SimpleAction({ name: action.name });
+ act.connect(
+ 'activate',
+ () => Gtk.show_uri(window, action.link, Gdk.CURRENT_TIME)
+ );
+ actionGroup.add_action(act);
+ });
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/other.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/other.js
new file mode 100644
index 0000000..0fac364
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/other.js
@@ -0,0 +1,86 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+
+
+export const Other = GObject.registerClass({
+ GTypeName: 'Other',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../ui/other.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'lockscreen_blur',
+ 'lockscreen_pipeline_choose_row',
+
+ 'screenshot_blur',
+ 'screenshot_pipeline_choose_row',
+
+ 'window_list_blur',
+ 'window_list_sigma',
+ 'window_list_brightness',
+
+ 'coverflow_alt_tab_blur',
+ 'coverflow_alt_tab_pipeline_choose_row',
+
+ 'hack_level',
+ 'debug',
+ 'reset'
+ ],
+}, class Overview extends Adw.PreferencesPage {
+ constructor(preferences, pipelines_manager, pipelines_page) {
+ super({});
+
+ this.preferences = preferences;
+ this.pipelines_manager = pipelines_manager;
+ this.pipelines_page = pipelines_page;
+
+ this.preferences.lockscreen.settings.bind(
+ 'blur', this._lockscreen_blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._lockscreen_pipeline_choose_row.initialize(
+ this.preferences.lockscreen, this.pipelines_manager, this.pipelines_page
+ );
+
+ this.preferences.screenshot.settings.bind(
+ 'blur', this._screenshot_blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._screenshot_pipeline_choose_row.initialize(
+ this.preferences.screenshot, this.pipelines_manager, this.pipelines_page
+ );
+
+ this.preferences.window_list.settings.bind(
+ 'blur', this._window_list_blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.window_list.settings.bind(
+ 'sigma', this._window_list_sigma, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.window_list.settings.bind(
+ 'brightness', this._window_list_brightness, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this.preferences.coverflow_alt_tab.settings.bind(
+ 'blur', this._coverflow_alt_tab_blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this._coverflow_alt_tab_pipeline_choose_row.initialize(
+ this.preferences.coverflow_alt_tab, this.pipelines_manager, this.pipelines_page
+ );
+
+ this.preferences.settings.bind(
+ 'hacks-level', this._hack_level, 'selected',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.settings.bind(
+ 'debug', this._debug, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._reset.connect('clicked', () => this.preferences.reset());
+ }
+}); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/overview.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/overview.js
new file mode 100644
index 0000000..f6fe592
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/overview.js
@@ -0,0 +1,59 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+
+
+export const Overview = GObject.registerClass({
+ GTypeName: 'Overview',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../ui/overview.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'overview_blur',
+ 'pipeline_choose_row',
+ 'overview_style_components',
+
+ 'appfolder_blur',
+ 'appfolder_sigma',
+ 'appfolder_brightness',
+ 'appfolder_style_dialogs'
+ ],
+}, class Overview extends Adw.PreferencesPage {
+ constructor(preferences, pipelines_manager, pipelines_page) {
+ super({});
+
+ this.preferences = preferences;
+ this.pipelines_manager = pipelines_manager;
+ this.pipelines_page = pipelines_page;
+
+ this.preferences.overview.settings.bind(
+ 'blur', this._overview_blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._pipeline_choose_row.initialize(
+ this.preferences.overview, this.pipelines_manager, this.pipelines_page
+ );
+
+ this.preferences.overview.settings.bind(
+ 'style-components', this._overview_style_components, 'selected',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this.preferences.appfolder.settings.bind(
+ 'blur', this._appfolder_blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.appfolder.settings.bind(
+ 'sigma', this._appfolder_sigma, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.appfolder.settings.bind(
+ 'brightness', this._appfolder_brightness, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.appfolder.settings.bind(
+ 'style-dialogs', this._appfolder_style_dialogs, 'selected',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ }
+}); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/panel.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/panel.js
new file mode 100644
index 0000000..c49f323
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/panel.js
@@ -0,0 +1,102 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gio from 'gi://Gio';
+
+
+export const Panel = GObject.registerClass({
+ GTypeName: 'Panel',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../ui/panel.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'blur',
+ 'pipeline_choose_row',
+ 'mode_static',
+ 'mode_dynamic',
+ 'sigma_row',
+ 'sigma',
+ 'brightness_row',
+ 'brightness',
+ 'unblur_in_overview',
+ 'force_light_text',
+ 'override_background',
+ 'style_panel',
+ 'override_background_dynamically',
+ 'hidetopbar_compatibility',
+ 'dtp_blur_original_panel'
+ ],
+}, class Panel extends Adw.PreferencesPage {
+ constructor(preferences, pipelines_manager, pipelines_page) {
+ super({});
+
+ this.preferences = preferences;
+ this.pipelines_manager = pipelines_manager;
+ this.pipelines_page = pipelines_page;
+
+ this.preferences.panel.settings.bind(
+ 'blur', this._blur, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._pipeline_choose_row.initialize(
+ this.preferences.panel, this.pipelines_manager, this.pipelines_page
+ );
+
+ this.change_blur_mode(this.preferences.panel.STATIC_BLUR, true);
+
+ this._mode_static.connect('toggled',
+ () => this.preferences.panel.STATIC_BLUR = this._mode_static.active
+ );
+ this.preferences.panel.STATIC_BLUR_changed(
+ () => this.change_blur_mode(this.preferences.panel.STATIC_BLUR, false)
+ );
+
+ this.preferences.panel.settings.bind(
+ 'sigma', this._sigma, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.panel.settings.bind(
+ 'brightness', this._brightness, 'value',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.panel.settings.bind(
+ 'unblur-in-overview', this._unblur_in_overview, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.panel.settings.bind(
+ 'force-light-text', this._force_light_text, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.panel.settings.bind(
+ 'override-background',
+ this._override_background, 'enable-expansion',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.panel.settings.bind(
+ 'style-panel', this._style_panel, 'selected',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.panel.settings.bind(
+ 'override-background-dynamically',
+ this._override_background_dynamically, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.hidetopbar.settings.bind(
+ 'compatibility', this._hidetopbar_compatibility, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this.preferences.dash_to_panel.settings.bind(
+ 'blur-original-panel', this._dtp_blur_original_panel, 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ }
+
+ change_blur_mode(is_static_blur, first_run) {
+ this._mode_static.set_active(is_static_blur);
+ if (first_run)
+ this._mode_dynamic.set_active(!is_static_blur);
+
+ this._pipeline_choose_row.set_visible(is_static_blur);
+ this._sigma_row.set_visible(!is_static_blur);
+ this._brightness_row.set_visible(!is_static_blur);
+ }
+}); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines.js
new file mode 100644
index 0000000..158ea9a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines.js
@@ -0,0 +1,98 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import { PipelineGroup } from './pipelines_management/pipeline_group.js';
+import { EffectsDialog } from './pipelines_management/effects_dialog.js';
+
+
+export const Pipelines = GObject.registerClass({
+ GTypeName: 'Pipelines',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../ui/pipelines.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'add_pipeline'
+ ],
+}, class Pipelines extends Adw.PreferencesPage {
+ constructor(preferences, pipelines_manager, window) {
+ super({});
+
+ this.preferences = preferences;
+ this.pipelines_manager = pipelines_manager;
+ this.window = window;
+
+ this.pipelines_map = new Map;
+
+ for (let pipeline_id in this.pipelines_manager.pipelines)
+ this.add_pipeline(pipeline_id, false);
+
+ this.preferences.connect('reset', _ => {
+ this.pipelines_map.forEach((_infos, pid) => this.remove_pipeline(pid));
+ for (let pipeline_id in this.pipelines_manager.pipelines)
+ this.add_pipeline(pipeline_id, false);
+ });
+
+ this._add_pipeline.connect(
+ "clicked",
+ () => this.pipelines_manager.create_pipeline(_("New pipeline"))
+ );
+
+ this.pipelines_manager.connect(
+ "pipeline-created",
+ (_obj, id, _pipeline) => this.add_pipeline(id, true)
+ );
+ }
+
+ add_pipeline(pipeline_id, scroll_to_bottom) {
+ let pipeline = this.pipelines_manager.pipelines[pipeline_id];
+ let pipeline_group = new PipelineGroup(
+ this.pipelines_manager, pipeline_id, pipeline, this
+ );
+
+ let pipeline_destroyed_id = this.pipelines_manager.connect(
+ pipeline_id + "::pipeline-destroyed",
+ () => this.remove_pipeline(pipeline_id)
+ );
+
+ let pipeline_renamed_id = this.pipelines_manager.connect(
+ pipeline_id + "::pipeline-renamed",
+ (_obj, name) => this.rename_pipeline(pipeline_id, name)
+ );
+
+ this.pipelines_map.set(pipeline_id, {
+ pipeline_group, pipeline_destroyed_id, pipeline_renamed_id
+ });
+
+ this.add(pipeline_group);
+
+ // scroll to the bottom of the page
+ if (scroll_to_bottom) {
+ this.window.set_visible_page(this);
+ setTimeout(() => {
+ const scroll_adjustment = this.get_first_child().get_vadjustment();
+ scroll_adjustment.value = scroll_adjustment.get_upper();
+ }, 10);
+ pipeline_group._title.grab_focus();
+ }
+ }
+
+ remove_pipeline(pipeline_id) {
+ let pipeline_infos = this.pipelines_map.get(pipeline_id);
+ if (pipeline_infos) {
+ this.pipelines_manager.disconnect(pipeline_infos.pipeline_destroyed_id);
+ this.remove(pipeline_infos.pipeline_group);
+ this.pipelines_map.delete(pipeline_id);
+ }
+ }
+
+ rename_pipeline(pipeline_id, name) {
+ let pipeline_infos = this.pipelines_map.get(pipeline_id);
+ if (pipeline_infos)
+ pipeline_infos.pipeline_group.set_title(name.length > 0 ? name : " ");
+ }
+
+ open_effects_dialog(pipeline_id) {
+ let dialog = new EffectsDialog(this.pipelines_manager, pipeline_id);
+ dialog.present(this.window);
+ }
+}); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effect_row.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effect_row.js
new file mode 100644
index 0000000..130edfd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effect_row.js
@@ -0,0 +1,222 @@
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk';
+import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import { get_supported_effects } from '../../effects/effects.js';
+
+
+export const EffectRow = GObject.registerClass({
+ GTypeName: 'EffectRow',
+ InternalChildren: [],
+}, class EffectRow extends Adw.ExpanderRow {
+ constructor(effect, effects_dialog) {
+ super({});
+
+ this.SUPPORTED_EFFECTS = get_supported_effects(_);
+
+ this.effect = effect;
+ this.effects_dialog = effects_dialog;
+ this.pipeline_id = effects_dialog.pipeline_id;
+ this.pipelines_manager = effects_dialog.pipelines_manager;
+
+ if (effect.type in this.SUPPORTED_EFFECTS) {
+ this.set_title(this.SUPPORTED_EFFECTS[effect.type].name);
+ this.set_subtitle(this.SUPPORTED_EFFECTS[effect.type].description);
+ this.populate_options();
+ }
+ else {
+ this._warn(`could not assign effect ${effect.type} to its correct name`);
+ this.set_title(effect.type);
+ }
+
+ let prefix_bin = new Gtk.Box({
+ spacing: 6
+ });
+ this.add_prefix(prefix_bin);
+
+ let move_bin = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ 'width-request': 38,
+ 'height-request': 38,
+ 'margin-top': 6,
+ 'margin-bottom': 6
+ });
+ prefix_bin.append(move_bin);
+ move_bin.add_css_class('linked');
+
+ this._move_up_button = new Gtk.Button({
+ 'icon-name': 'go-up-symbolic',
+ 'width-request': 38,
+ 'height-request': 19
+ });
+ this._move_down_button = new Gtk.Button({
+ 'icon-name': 'go-down-symbolic',
+ 'width-request': 38,
+ 'height-request': 19
+ });
+ this._move_up_button.add_css_class('flat');
+ this._move_down_button.add_css_class('flat');
+ move_bin.append(this._move_up_button);
+ move_bin.append(this._move_down_button);
+
+ this._move_up_button.connect('clicked', () => effects_dialog.move_row_by(this, -1));
+ this._move_down_button.connect('clicked', () => effects_dialog.move_row_by(this, +1));
+
+ let remove_button = new Gtk.Button({
+ 'icon-name': 'remove-row-symbolic',
+ 'width-request': 38,
+ 'height-request': 38,
+ 'margin-top': 6,
+ 'margin-bottom': 6,
+ 'valign': Gtk.Align.CENTER
+ });
+ prefix_bin.append(remove_button);
+ remove_button.add_css_class('destructive-action');
+
+ remove_button.connect('clicked', () => effects_dialog.remove_row(this));
+ }
+
+ populate_options() {
+ const editable_params = this.SUPPORTED_EFFECTS[this.effect.type].editable_params;
+
+ if (Object.keys(editable_params).length == 0)
+ this.enable_expansion = false;
+
+ for (const param_key in editable_params) {
+ let param = editable_params[param_key];
+ let row;
+ switch (param.type) {
+ case "integer":
+ row = new Adw.SpinRow({
+ adjustment: new Gtk.Adjustment({
+ lower: param.min,
+ upper: param.max,
+ step_increment: param.increment
+ })
+ });
+ row.adjustment.set_value(this.get_effect_param(param_key));
+ row.adjustment.connect(
+ 'value-changed', () => this.set_effect_param(param_key, row.adjustment.value)
+ );
+ break;
+
+ case "float":
+ row = new Adw.ActionRow;
+ let scale = new Gtk.Scale({
+ valign: Gtk.Align.CENTER,
+ hexpand: true,
+ width_request: 200,
+ draw_value: true,
+ value_pos: Gtk.PositionType.RIGHT,
+ digits: param.digits,
+ adjustment: new Gtk.Adjustment({
+ lower: param.min,
+ upper: param.max,
+ step_increment: param.increment,
+ page_increment: param.big_increment
+ })
+ });
+ // TODO check if it's a good idea to set the default parameter, as the "good"
+ // value really change depending on the user wallpaper... if so, do for dynamic
+ // blur too
+ scale.add_mark(
+ this.get_default_effect_param(param_key), Gtk.PositionType.BOTTOM, null
+ );
+ row.add_suffix(scale);
+ scale.adjustment.set_value(this.get_effect_param(param_key));
+ scale.adjustment.connect(
+ 'value-changed', () => this.set_effect_param(param_key, scale.adjustment.value)
+ );
+ break;
+
+ case "boolean":
+ row = new Adw.SwitchRow;
+ row.set_active(this.get_effect_param(param_key));
+ row.connect(
+ 'notify::active', () => this.set_effect_param(param_key, row.active)
+ );
+ break;
+
+ case "dropdown":
+ row = new Adw.ComboRow({ model: new Gtk.StringList });
+ param.options.forEach(option => row.model.append(option));
+ row.selected = this.get_effect_param(param_key);
+ row.connect(
+ 'notify::selected', () => this.set_effect_param(param_key, row.selected)
+ );
+ break;
+
+ case "rgba":
+ row = new Adw.ActionRow;
+ let color_button = new Gtk.ColorButton({
+ valign: Gtk.Align.CENTER,
+ width_request: 75,
+ height_request: 45,
+ show_editor: true,
+ use_alpha: true
+ });
+ row.add_suffix(color_button);
+ // set original color
+ let c = color_button.get_rgba().copy();
+ [c.red, c.green, c.blue, c.alpha] = this.get_effect_param(param_key);
+ color_button.set_rgba(c);
+ // update on on 'color-set'
+ color_button.connect(
+ 'color-set', () => {
+ let c = color_button.get_rgba();
+ this.set_effect_param(param_key, [c.red, c.green, c.blue, c.alpha]);
+ }
+ );
+ break;
+
+ default:
+ row = new Adw.ActionRow;
+ break;
+ }
+ row.set_title(param.name);
+ row.set_subtitle(param.description);
+ this.add_row(row);
+ }
+ }
+
+ get_effect_param(key) {
+ let effects = this.pipelines_manager.pipelines[this.pipeline_id].effects;
+ const gsettings_effect = effects.find(e => e.id == this.effect.id);
+
+ if ('params' in gsettings_effect && key in gsettings_effect.params)
+ return gsettings_effect.params[key];
+ else
+ return this.get_default_effect_param(key);
+ }
+
+ get_default_effect_param(key) {
+ return this.SUPPORTED_EFFECTS[this.effect.type].class.default_params[key];
+ }
+
+ set_effect_param(key, value) {
+ // we must pay attention not to change the effects in the pipelines manager before updating
+ // it in gsettings, else it won't be updated (or every effect will be)
+ let effects = this.pipelines_manager.pipelines[this.pipeline_id].effects;
+ const effect_index = effects.findIndex(e => e.id == this.effect.id);
+
+ if (effect_index >= 0) {
+ effects[effect_index] = {
+ ...this.effect, params: { ...this.effect.params }
+ };
+ effects[effect_index].params[key] = value;
+ this.effect = effects[effect_index];
+ }
+ else
+ this._warn(`effect not found when setting key ${key}`);
+
+ this.pipelines_manager.update_pipeline_effects(this.pipeline_id, effects, false);
+ }
+
+ _warn(str) {
+ console.warn(
+ `[Blur my Shell > effect row] pipeline '${this.pipeline_id}',`
+ + ` effect '${this.effect.id}': ${str}`
+ );
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effects_dialog.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effects_dialog.js
new file mode 100644
index 0000000..222a92e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/effects_dialog.js
@@ -0,0 +1,174 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk';
+import Gio from 'gi://Gio';
+import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import { EffectRow } from './effect_row.js';
+import { get_effects_groups, get_supported_effects } from '../../effects/effects.js';
+
+export const EffectsDialog = GObject.registerClass({
+ GTypeName: 'EffectsDialog',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../../ui/effects-dialog.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ "add_effect",
+ "add_effect_alt_menu",
+ "effects_list"
+ ],
+}, class EffectsDialog extends Adw.PreferencesDialog {
+ constructor(pipelines_manager, pipeline_id) {
+ super({});
+
+ this.EFFECTS_GROUPS = get_effects_groups(_);
+ this.SUPPORTED_EFFECTS = get_supported_effects(_);
+
+ this.pipelines_manager = pipelines_manager;
+ this.pipeline_id = pipeline_id;
+
+ let pipeline = pipelines_manager.pipelines[pipeline_id];
+
+ this.set_title(pipeline.name.length > 0 ? _(`Effects for "${pipeline.name}"`) : _("Effects"));
+
+ pipeline.effects.forEach(effect => {
+ const effect_row = new EffectRow(effect, this);
+ this._effects_list.add(effect_row);
+ this.update_rows_insensitive_mover(effect_row);
+ });
+
+ // setup advanced effects chooser action
+ this.show_advanced_effects = false;
+ let action_group = new Gio.SimpleActionGroup();
+ this.insert_action_group('effects-dialog', action_group);
+ let advanced_effects_action = Gio.SimpleAction.new_stateful(
+ 'advanced-effects-bool',
+ null,
+ GLib.Variant.new_boolean(this.show_advanced_effects)
+ );
+ advanced_effects_action.connect(
+ 'change-state',
+ (_, state) => {
+ this.show_advanced_effects = state.get_boolean();
+ this.build_effects_chooser();
+ advanced_effects_action.set_state(state);
+ }
+ );
+ action_group.add_action(advanced_effects_action);
+
+ this.build_effects_chooser();
+ this._add_effect.connect('clicked', () => this.effects_chooser_dialog.present(this));
+ }
+
+ build_effects_chooser() {
+ this.effects_chooser_dialog = new Adw.Dialog({
+ presentation_mode: Adw.DialogPresentationMode.BOTTOM_SHEET,
+ content_width: 450
+ });
+
+ let page = new Adw.PreferencesPage;
+ this.effects_chooser_dialog.set_child(page);
+
+ for (const effects_group in this.EFFECTS_GROUPS) {
+ const group_infos = this.EFFECTS_GROUPS[effects_group];
+
+ let group = new Adw.PreferencesGroup({
+ title: group_infos.name
+ });
+ page.add(group);
+
+ for (const effect_type of group_infos.contains) {
+ if (!(effect_type in this.SUPPORTED_EFFECTS))
+ continue;
+
+ if (!this.show_advanced_effects && this.SUPPORTED_EFFECTS[effect_type].is_advanced)
+ continue;
+
+ let action_row = new Adw.ActionRow({
+ title: this.SUPPORTED_EFFECTS[effect_type].name,
+ subtitle: this.SUPPORTED_EFFECTS[effect_type].description
+ });
+ let select_button = new Gtk.Button({
+ 'icon-name': 'select-row-symbolic',
+ 'width-request': 38,
+ 'height-request': 38,
+ 'margin-top': 6,
+ 'margin-bottom': 6
+ });
+ group.add(action_row);
+
+ select_button.add_css_class('flat');
+ action_row.add_suffix(select_button);
+ action_row.set_activatable_widget(select_button);
+ select_button.connect('clicked', () => {
+ this.append_effect(effect_type);
+ this.effects_chooser_dialog.close();
+ });
+ }
+ }
+ }
+
+ append_effect(effect_type) {
+ const effect = {
+ type: effect_type, id: "effect_" + ("" + Math.random()).slice(2, 16)
+ };
+ this.pipelines_manager.update_pipeline_effects(
+ this.pipeline_id,
+ [...this.pipelines_manager.pipelines[this.pipeline_id].effects, effect]
+ );
+
+ const effect_row = new EffectRow(effect, this);
+ this._effects_list.add(effect_row);
+ this.move_row_by(effect_row, 0);
+ this.update_rows_insensitive_mover(effect_row);
+ }
+
+ move_row_by(row, number) {
+ const effects = this.pipelines_manager.pipelines[this.pipeline_id].effects;
+ const effect_index = effects.findIndex(e => e.id == row.effect.id);
+
+ if (effect_index >= 0) {
+ effects.splice(effect_index, 1);
+ effects.splice(effect_index + number, 0, row.effect);
+
+ const listbox = row.get_parent();
+ listbox.set_sort_func((row_a, row_b) => {
+ const id_a = effects.findIndex(e => e.id == row_a.effect.id);
+ const id_b = effects.findIndex(e => e.id == row_b.effect.id);
+ return id_a > id_b;
+ });
+
+ this.update_rows_insensitive_mover(row);
+
+ this.pipelines_manager.update_pipeline_effects(
+ this.pipeline_id, effects
+ );
+ }
+ }
+
+ update_rows_insensitive_mover(any_row) {
+ if (this._insensitive_top)
+ this._insensitive_top.set_sensitive(true);
+ if (this._insensitive_bottom)
+ this._insensitive_bottom.set_sensitive(true);
+
+ const listbox = any_row.get_parent();
+ this._insensitive_top = listbox.get_first_child()._move_up_button;
+ this._insensitive_top?.set_sensitive(false);
+ this._insensitive_bottom = listbox.get_last_child()._move_down_button;
+ this._insensitive_bottom?.set_sensitive(false);
+ }
+
+ remove_row(row) {
+ const effects = this.pipelines_manager.pipelines[this.pipeline_id].effects;
+ const effect_index = effects.findIndex(e => e.id == row.effect.id);
+
+ if (effect_index >= 0) {
+ effects.splice(effect_index, 1);
+ this.pipelines_manager.update_pipeline_effects(
+ this.pipeline_id, effects
+ );
+ }
+
+ this._effects_list.remove(row);
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_choose_row.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_choose_row.js
new file mode 100644
index 0000000..e0c5165
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_choose_row.js
@@ -0,0 +1,106 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk';
+import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+
+export const PipelineChooseRow = GObject.registerClass({
+ GTypeName: 'PipelineChooseRow',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../../ui/pipeline-choose-row.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ 'pipeline_choose',
+ 'pipeline_model',
+ 'pipeline_edit'
+ ],
+}, class PipelineChooseRow extends Adw.ActionRow {
+ initialize(preferences, pipelines_manager, pipelines_page) {
+ this.preferences = preferences;
+ this.pipelines_manager = pipelines_manager;
+ this.pipelines_page = pipelines_page;
+
+ this.create_pipelines_list();
+
+ // display the correct pipeline name in the drop-down instead of their ids
+ const closure_func = string_object => {
+ const pipeline_id = string_object.get_string();
+ if (pipeline_id == 'create_new')
+ return _("Create new pipeline");
+ if (pipeline_id in this.pipelines_manager.pipelines)
+ return this.pipelines_manager.pipelines[pipeline_id].name;
+ else
+ return "";
+ };
+
+ const expression = new Gtk.ClosureExpression(GObject.TYPE_STRING, closure_func, []);
+ this._pipeline_choose.expression = expression;
+
+ // TODO fix the expression not being re-evaluated other than by setting it again
+ this.pipelines_manager.connect(
+ 'pipeline-names-changed',
+ () => this._pipeline_choose.expression = new Gtk.ClosureExpression(
+ GObject.TYPE_STRING, closure_func, []
+ )
+ );
+
+ this.preferences.PIPELINE_changed(() => this.on_settings_pipeline_changed());
+
+ this.pipelines_manager.connect('pipeline-list-changed', () => this.create_pipelines_list());
+
+ this._pipeline_choose.connect('notify::selected', () => this.on_selected_pipeline_changed());
+
+ this._pipeline_edit.connect(
+ 'clicked',
+ () => this.pipelines_page.open_effects_dialog(this.preferences.PIPELINE)
+ );
+ }
+
+ on_selected_pipeline_changed() {
+ if (!this._pipeline_choose.selected_item || this._is_creating_pipelines_list)
+ return;
+
+ const pipeline_id = this._pipeline_choose.selected_item.get_string();
+ if (pipeline_id == 'create_new') {
+ const id = this.pipelines_manager.create_pipeline(_("New pipeline"));
+ this.preferences.PIPELINE = id;
+ }
+ else
+ this.preferences.PIPELINE = pipeline_id;
+ }
+
+ on_settings_pipeline_changed() {
+ for (let i = 0; i < this._pipeline_model.n_items; i++) {
+ const pipeline_id = this._pipeline_model.get_string(i);
+ // if we have more pipelines than we should have: rebuild...
+ // that is the case when resetting the preferences for example
+ if (!(pipeline_id in this.pipelines_manager)) {
+ this.create_pipelines_list();
+ return;
+ }
+ if (pipeline_id == this.preferences.PIPELINE)
+ this._pipeline_choose.set_selected(i);
+ }
+ }
+
+ create_pipelines_list() {
+ // prevent the pipeline selector from being updated while re-creating the list
+ this._is_creating_pipelines_list = true;
+
+ // remove ancient items
+ this._pipeline_model.splice(0, this._pipeline_model.n_items, null);
+
+ // add new ones
+ let i = 0;
+ for (let pipeline_id in this.pipelines_manager.pipelines) {
+ this._pipeline_model.append(pipeline_id);
+ if (pipeline_id == this.preferences.PIPELINE)
+ this._pipeline_choose.set_selected(i);
+ i++;
+ }
+ this._pipeline_model.append('create_new');
+
+ // now update the drop-down selector
+ this._is_creating_pipelines_list = false;
+ this.on_selected_pipeline_changed();
+ }
+}); \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_group.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_group.js
new file mode 100644
index 0000000..627b576
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/preferences/pipelines_management/pipeline_group.js
@@ -0,0 +1,99 @@
+import Adw from 'gi://Adw';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk';
+import { gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import { get_supported_effects } from '../../effects/effects.js';
+
+
+export const PipelineGroup = GObject.registerClass({
+ GTypeName: 'PipelineGroup',
+ Template: GLib.uri_resolve_relative(import.meta.url, '../../ui/pipeline-group.ui', GLib.UriFlags.NONE),
+ InternalChildren: [
+ "title",
+ "effects_description_row",
+ "manage_effects"
+ ],
+}, class PipelineGroup extends Adw.PreferencesGroup {
+ constructor(pipelines_manager, pipeline_id, pipeline, pipelines_page) {
+ super({});
+
+ this.SUPPORTED_EFFECTS = get_supported_effects(_);
+
+ this._pipelines_manager = pipelines_manager;
+ this._pipelines_page = pipelines_page;
+ this._pipeline_id = pipeline_id;
+
+ // set the description
+ this.set_description(_(`Pipeline id: "${pipeline_id}"`));
+
+ // set the title and connect it to the text entry
+ this.set_title(pipeline.name.length > 0 ? pipeline.name : " ");
+ this._title.set_text(pipeline.name);
+ this._title.connect(
+ 'changed',
+ () => pipelines_manager.rename_pipeline(pipeline_id, this._title.get_text())
+ );
+
+ // the bin containing the actions
+ let prefix_bin = new Gtk.Box;
+ prefix_bin.add_css_class('linked');
+ this._title.add_prefix(prefix_bin);
+
+ // add a 'remove' button if we are not the default pipeline
+ if (pipeline_id != "pipeline_default") {
+ let remove_button = new Gtk.Button({
+ 'icon-name': 'remove-row-symbolic',
+ 'width-request': 38,
+ 'height-request': 38,
+ 'margin-top': 6,
+ 'margin-bottom': 6
+ });
+ remove_button.add_css_class('destructive-action');
+ prefix_bin.append(remove_button);
+ remove_button.connect('clicked', () => pipelines_manager.delete_pipeline(pipeline_id));
+ }
+ // add a 'duplicate' button
+ let duplicate_button = new Gtk.Button({
+ 'icon-name': 'duplicate-row-symbolic',
+ 'width-request': 38,
+ 'height-request': 38,
+ 'margin-top': 6,
+ 'margin-bottom': 6
+ });
+ prefix_bin.append(duplicate_button);
+ duplicate_button.connect('clicked', () => pipelines_manager.duplicate_pipeline(pipeline_id));
+
+ this.update_effects_description_row();
+ this._pipelines_manager.connect(
+ pipeline_id + '::pipeline-updated',
+ () => this.update_effects_description_row()
+ );
+
+ this._manage_effects.connect(
+ 'clicked',
+ () => pipelines_page.open_effects_dialog(pipeline_id)
+ );
+ }
+
+ update_effects_description_row() {
+ const effects = this._pipelines_manager.pipelines[this._pipeline_id].effects;
+
+ if (effects.length == 0)
+ this._effects_description_row.set_title(_("No effect"));
+ else if (effects.length == 1)
+ this._effects_description_row.set_title(_("1 effect"));
+ else
+ this._effects_description_row.set_title(_(`${effects.length} effects`));
+
+ let subtitle = "";
+ effects.forEach(effect => {
+ if (effect.type in this.SUPPORTED_EFFECTS)
+ subtitle += _(`${this.SUPPORTED_EFFECTS[effect.type].name}, `);
+ else
+ subtitle += _("Unknown effect, ");
+ });
+ this._effects_description_row.set_subtitle(subtitle.slice(0, -2));
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/prefs.js
new file mode 100644
index 0000000..43c4889
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/prefs.js
@@ -0,0 +1,51 @@
+import Gdk from 'gi://Gdk';
+import Gtk from 'gi://Gtk';
+import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import { update_from_old_settings } from './conveniences/settings_updater.js';
+import { PipelinesManager } from './conveniences/pipelines_manager.js';
+import { Settings } from './conveniences/settings.js';
+import { KEYS } from './conveniences/keys.js';
+
+import { addMenu } from './preferences/menu.js';
+import { Pipelines } from './preferences/pipelines.js';
+import { Panel } from './preferences/panel.js';
+import { Overview } from './preferences/overview.js';
+import { Dash } from './preferences/dash.js';
+import { Applications } from './preferences/applications.js';
+import { Other } from './preferences/other.js';
+
+import './preferences/pipelines_management/pipeline_choose_row.js';
+
+
+export default class BlurMyShellPreferences extends ExtensionPreferences {
+ constructor(metadata) {
+ super(metadata);
+
+ // load the icon theme
+ let iconPath = this.dir.get_child("icons").get_path();
+ let iconTheme = Gtk.IconTheme.get_for_display(Gdk.Display.get_default());
+ iconTheme.add_search_path(iconPath);
+ }
+
+ fillPreferencesWindow(window) {
+ addMenu(window);
+
+ // update from old settings, very important for hacks level specifically
+ update_from_old_settings(this.getSettings());
+
+ const preferences = new Settings(KEYS, this.getSettings());
+ const pipelines_manager = new PipelinesManager(preferences);
+
+ const pipelines_page = new Pipelines(preferences, pipelines_manager, window);
+
+ window.add(pipelines_page);
+ window.add(new Panel(preferences, pipelines_manager, pipelines_page));
+ window.add(new Overview(preferences, pipelines_manager, pipelines_page));
+ window.add(new Dash(preferences, pipelines_manager, pipelines_page));
+ window.add(new Applications(preferences, window, pipelines_manager, pipelines_page));
+ window.add(new Other(preferences, pipelines_manager, pipelines_page));
+
+ window.search_enabled = true;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/gschemas.compiled
new file mode 100644
index 0000000..c2b6708
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/org.gnome.shell.extensions.blur-my-shell.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/org.gnome.shell.extensions.blur-my-shell.gschema.xml
new file mode 100644
index 0000000..7526e30
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/schemas/org.gnome.shell.extensions.blur-my-shell.gschema.xml
@@ -0,0 +1,595 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+ <!-- GENERAL -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell"
+ path="/org/gnome/shell/extensions/blur-my-shell/">
+ <!-- SETTINGS VERSION -->
+ <key type="i" name="settings-version">
+ <default>1</default>
+ <summary>The version of the settings to update from.</summary>
+ </key>
+ <!-- PIPELINES -->
+ <key type="a{sa{sv}}" name="pipelines">
+ <default> <![CDATA[
+ {
+ 'pipeline_default': {
+ 'name': <'Default'>,
+ 'effects': <[
+ <{
+ 'type': <'native_static_gaussian_blur'>,
+ 'id': <'effect_000000000000'>,
+ 'params': <{
+ 'radius': <30>,
+ 'brightness': <0.6>
+ }>
+ }>
+ ]>
+ },
+ 'pipeline_default_rounded': {
+ 'name': <'Default rounded'>,
+ 'effects': <[
+ <{
+ 'type': <'native_static_gaussian_blur'>,
+ 'id': <'effect_000000000001'>,
+ 'params': <{
+ 'radius': <30>,
+ 'brightness': <0.6>
+ }>
+ }>,
+ <{
+ 'type': <'corner'>,
+ 'id': <'effect_000000000002'>,
+ 'params': <{
+ 'radius': <24>
+ }>
+ }>
+ ]>
+ }
+ }
+ ]]></default>
+ <summary>A dictionnary describing the different pipelines and their effects.</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Global gaussian sigma to use</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Global brightness to use</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ <!-- COLOR AND NOISE -->
+ <key type="b" name="color-and-noise">
+ <default>true</default>
+ <summary>Boolean, controls wether or not the color and noise effects are in use globally</summary>
+ </key>
+ <!-- HACKS LEVEL -->
+ <key type="i" name="hacks-level">
+ <default>1</default>
+ <summary>Level of hacks to use (from 0 to 2, 2 disabling clipped redraws entirely)</summary>
+ </key>
+ <!-- DEBUG -->
+ <key type="b" name="debug">
+ <default>false</default>
+ <summary>Boolean, set to true to activate debug mode (more verbose journalctl logs)</summary>
+ </key>
+
+ <child name='overview' schema='org.gnome.shell.extensions.blur-my-shell.overview'></child>
+ <child name='appfolder' schema='org.gnome.shell.extensions.blur-my-shell.appfolder'></child>
+ <child name='panel' schema='org.gnome.shell.extensions.blur-my-shell.panel'></child>
+ <child name='dash-to-dock' schema='org.gnome.shell.extensions.blur-my-shell.dash-to-dock'></child>
+ <child name='applications' schema='org.gnome.shell.extensions.blur-my-shell.applications'></child>
+ <child name='screenshot' schema='org.gnome.shell.extensions.blur-my-shell.screenshot'></child>
+ <child name='lockscreen' schema='org.gnome.shell.extensions.blur-my-shell.lockscreen'></child>
+ <child name='window-list' schema='org.gnome.shell.extensions.blur-my-shell.window-list'></child>
+ <child name='coverflow-alt-tab' schema='org.gnome.shell.extensions.blur-my-shell.coverflow-alt-tab'></child>
+ <child name='hidetopbar' schema='org.gnome.shell.extensions.blur-my-shell.hidetopbar'></child>
+ <child name='dash-to-panel' schema='org.gnome.shell.extensions.blur-my-shell.dash-to-panel'></child>
+ </schema>
+
+ <!-- OVERVIEW -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.overview"
+ path="/org/gnome/shell/extensions/blur-my-shell/overview/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>false</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ <!-- STYLE COMPONENTS -->
+ <key type="i" name="style-components">
+ <default>1</default>
+ <summary>Enum to select the style of the components in overview (0 not styled, 1 light, 2 dark, 3 transparent)</summary>
+ </key>
+ </schema>
+
+ <!-- APPFOLDER -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.appfolder"
+ path="/org/gnome/shell/extensions/blur-my-shell/appfolder/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>false</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ <!-- STYLE DIALOGS -->
+ <key type="i" name="style-dialogs">
+ <default>1</default>
+ <summary>Enum to select the style of the appfolder dialogs (0 not styled, 1 transparent, 2 light, 3 dark)</summary>
+ </key>
+ </schema>
+
+ <!-- PANEL -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.panel"
+ path="/org/gnome/shell/extensions/blur-my-shell/panel/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>false</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ <!-- STATIC BLUR -->
+ <key type="b" name="static-blur">
+ <default>true</default>
+ <summary>Boolean, whether to use a static or dynamic blur for this component</summary>
+ </key>
+ <!-- UNBLUR IN OVERVIEW -->
+ <key type="b" name="unblur-in-overview">
+ <default>true</default>
+ <summary>Boolean, whether to disable blur from this component when opening the overview or not</summary>
+ </key>
+ <!-- FORCE LIGHT TEXT -->
+ <key type="b" name="force-light-text">
+ <default>false</default>
+ <summary>Boolean, whether or not to force the panel to have light text, useful when using light theme</summary>
+ </key>
+ <!-- OVERRIDE BACKGROUND -->
+ <key type="b" name="override-background">
+ <default>true</default>
+ <summary>Boolean, whether to override the background or not</summary>
+ </key>
+ <!-- STYLE PANEL -->
+ <key type="i" name="style-panel">
+ <default>0</default>
+ <summary>Enum to select the style of the panel (0 transparent, 1 light, 2 dark, 3 contrasted)</summary>
+ </key>
+ <!-- UNBLUR DYNAMICALLY -->
+ <key type="b" name="override-background-dynamically">
+ <default>false</default>
+ <summary>Boolean, whether to disable blur from this component when a window is close to the panel</summary>
+ </key>
+ </schema>
+
+ <!-- DASH TO DOCK -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.dash-to-dock"
+ path="/org/gnome/shell/extensions/blur-my-shell/dash-to-dock/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default_rounded"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>false</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ <!-- STATIC BLUR -->
+ <key type="b" name="static-blur">
+ <default>true</default>
+ <summary>Boolean, whether to use static or dynamic blur for this component</summary>
+ </key>
+ <!-- OVERRIDE BACKGROUND -->
+ <key type="b" name="override-background">
+ <default>true</default>
+ <summary>Boolean, whether to override the background or not</summary>
+ </key>
+ <!-- STYLE DASH TO DOCK -->
+ <key type="i" name="style-dash-to-dock">
+ <default>0</default>
+ <summary>Enum to select the style of dash to dock (0 transparent, 1 light, 2 dark)</summary>
+ </key>
+ <!-- UNBLUR IN OVERVIEW -->
+ <key type="b" name="unblur-in-overview">
+ <default>false</default>
+ <summary>Boolean, whether to disable blur from this component when opening the overview or not</summary>
+ </key>
+ <!-- CORNER RADIUS -->
+ <key type="i" name="corner-radius">
+ <default>12</default>
+ <summary>Radius for the corner rounding effect</summary>
+ </key>
+ </schema>
+
+ <!-- APPLICATIONS -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.applications"
+ path="/org/gnome/shell/extensions/blur-my-shell/applications/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>false</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ <!-- STATIC BLUR -->
+ <key type="b" name="static-blur">
+ <default>false</default>
+ <summary>Boolean, whether to use static or dynamic blur for this component</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>true</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>1.</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ <!-- OPACITY -->
+ <key type="i" name="opacity">
+ <default>215</default>
+ <summary>Opacity of the window actor on top of the blur effect</summary>
+ </key>
+ <!-- DYNAMIC OPACITY -->
+ <key type="b" name="dynamic-opacity">
+ <default>true</default>
+ <summary>Wether or not to make the focused window opaque</summary>
+ </key>
+ <!-- BLUR ON OVERVIEW -->
+ <key type="b" name="blur-on-overview">
+ <default>false</default>
+ <summary>Wether or not to blur applications on the overview</summary>
+ </key>
+ <!-- ENABLE ALL -->
+ <key type="b" name="enable-all">
+ <default>false</default>
+ <summary>Wether or not to blur all applications by default</summary>
+ </key>
+ <!-- WHITELIST -->
+ <key type="as" name="whitelist">
+ <default>[]</default>
+ <summary>List of applications to blur</summary>
+ </key>
+ <!-- BLACKLIST -->
+ <key type="as" name="blacklist">
+ <default>["Plank","com.desktop.ding", "Conky"]</default>
+ <summary>List of applications not to blur</summary>
+ </key>
+ </schema>
+
+ <!-- SCREENSHOT -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.screenshot"
+ path="/org/gnome/shell/extensions/blur-my-shell/screenshot/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>false</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ </schema>
+
+ <!-- LOCKSCREEN -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.lockscreen"
+ path="/org/gnome/shell/extensions/blur-my-shell/lockscreen/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>false</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ </schema>
+
+ <!-- WINDOW LIST -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.window-list"
+ path="/org/gnome/shell/extensions/blur-my-shell/window-list/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ <!-- CUSTOMIZE -->
+ <key type="b" name="customize">
+ <default>false</default>
+ <summary>Boolean, whether to customize the blur effect sigma/brightness or use general values</summary>
+ </key>
+ <!-- SIGMA -->
+ <key type="i" name="sigma">
+ <default>30</default>
+ <summary>Gaussian sigma to use for the blur effect</summary>
+ </key>
+ <!-- BRIGHTNESS -->
+ <key type="d" name="brightness">
+ <default>0.6</default>
+ <summary>Brightness to use for the blur effect</summary>
+ </key>
+ <!-- COLOR -->
+ <key type="(dddd)" name="color">
+ <default>(0.,0.,0.,0.)</default>
+ <summary>Color to mix with the blur effect</summary>
+ </key>
+ <!-- NOISE AMOUNT -->
+ <key type="d" name="noise-amount">
+ <default>0.</default>
+ <summary>Amount of noise to add to the blur effect</summary>
+ </key>
+ <!-- NOISE LIGHTNESS -->
+ <key type="d" name="noise-lightness">
+ <default>0.</default>
+ <summary>Lightness of the noise added to the blur effect</summary>
+ </key>
+ </schema>
+
+ <!-- COVERFLOW ALT-TAB -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.coverflow-alt-tab"
+ path="/org/gnome/shell/extensions/blur-my-shell/coverflow-alt-tab/">
+ <!-- BLUR -->
+ <key type="b" name="blur">
+ <default>true</default>
+ <summary>Boolean, whether to blur activate the blur for this component or not</summary>
+ </key>
+ <!-- PIPELINE -->
+ <key type="s" name="pipeline">
+ <default>"pipeline_default"</default>
+ <summary>String, the name of the pipeline to use. It must exist, else "pipeline_default" will be used</summary>
+ </key>
+ </schema>
+
+ <!-- HIDETOPBAR -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.hidetopbar"
+ path="/org/gnome/shell/extensions/blur-my-shell/hidetopbar/">
+ <!-- COMPATIBILITY -->
+ <key type="b" name="compatibility">
+ <default>false</default>
+ <summary>Boolean, whether to try compatibility with hidetopbar@mathieu.bidon.ca or not</summary>
+ </key>
+ </schema>
+
+ <!-- DASH TO PANEL -->
+ <schema id="org.gnome.shell.extensions.blur-my-shell.dash-to-panel"
+ path="/org/gnome/shell/extensions/blur-my-shell/dash-to-panel/">
+ <!-- COMPATIBILITY -->
+ <key type="b" name="blur-original-panel">
+ <default>true</default>
+ <summary>Boolean, whether to blur the original panel (if option selected) with Dash to Panel</summary>
+ </key>
+ </schema>
+</schemalist> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/stylesheet.css b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/stylesheet.css
new file mode 100644
index 0000000..b61e530
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/stylesheet.css
@@ -0,0 +1,573 @@
+/*** PANEL ***/
+
+/*
+* `.transparent-panel`
+*/
+#panel.transparent-panel {
+ background: transparent;
+ transition-duration: 500ms;
+}
+
+/*
+* `.dark-panel`
+*/
+#panel.dark-panel {
+ background-color: rgba(100, 100, 100, 0.35);
+ box-shadow: none;
+ transition-duration: 500ms;
+}
+
+/*
+* `.light-panel`
+*/
+#panel.light-panel {
+ background-color: rgba(200, 200, 200, 0.2);
+ box-shadow: none;
+ transition-duration: 500ms;
+}
+
+/*
+* `.contrasted-panel`
+*/
+#panel.contrasted-panel {
+ background-color: transparent;
+ box-shadow: none;
+ transition-duration: 500ms;
+}
+
+.contrasted-panel .panel-button,
+.contrasted-panel .clock,
+.contrasted-panel .clock-display StIcon {
+ color: #ffffff;
+ border-radius: 14px;
+ border: 3px solid transparent;
+ background-color: rgba(0, 0, 0, 0.8);
+ box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.03);
+}
+
+
+.contrasted-panel .clock-display StIcon {
+ padding: 8px;
+ margin: 0;
+}
+
+.contrasted-panel .panel-button:hover,
+.contrasted-panel .panel-button:hover .clock {
+ color: #ffffff;
+ background-color: rgba(0, 0, 0, 0.8);
+}
+
+.contrasted-panel .clock-display {
+ background-color: transparent !important;
+ box-shadow: none !important;
+ border: none !important;
+}
+
+.contrasted-panel .clock {
+ margin: 0 !important;
+}
+
+
+/*** DASH-TO-DOCK ***/
+
+/*
+* `.transparent-dash`
+*/
+.transparent-dash .dash-background {
+ background-color: transparent !important;
+}
+
+/*
+* `.light-dash`
+*/
+.light-dash .dash-background {
+ background-color: rgba(200, 200, 200, 0.2) !important;
+}
+
+/*
+* `.dark-dash`
+*/
+.dark-dash .dash-background {
+ background-color: rgba(100, 100, 100, 0.35) !important;
+}
+
+
+/*** OVERVIEW ***/
+
+/*
+* Add transparency to the workspace animation (between workspaces)
+*/
+.blurred-overview .workspace-animation {
+ background-color: transparent !important;
+}
+
+
+/*
+* `.overview-components-transparent`
+*/
+
+.overview-components-transparent .workspace-thumbnail {
+ border: 1px solid rgba(100, 100, 100, 0.35);
+}
+
+.overview-components-transparent .search-entry {
+ color: white;
+ background-color: rgba(0, 0, 0, 0);
+ border-color: transparent;
+ box-shadow: none;
+}
+
+.overview-components-transparent .search-entry .search-entry-icon {
+ color: rgba(255, 255, 255, 0.65);
+}
+
+.overview-components-transparent .overview-tile,
+.overview-components-transparent .overview-icon,
+.overview-components-transparent .grid-search-result,
+.overview-components-transparent .list-search-result,
+.overview-components-transparent .search-provider-icon,
+/* prevents the extension from interfering with Just Perfection */
+.overview-components-transparent.just-perfection .search-section-content,
+/* remove the rectangular background from the dash */
+.overview-components-transparent #dash .overview-tile,
+.overview-components-transparent #dashtopanelScrollview .overview-tile {
+ background-color: transparent;
+}
+
+.overview-components-transparent .workspace-thumbnail,
+.overview-components-transparent .search-section-content,
+.overview-components-transparent .overview-tile.app-folder,
+.overview-components-transparent .page-navigation-arrow,
+.overview-components-transparent .app-folder-dialog .icon-button,
+/* this shouldn't apply to Dash to Dock */
+.overview-components-transparent StBoxLayout>StWidget>#dash>.dash-background {
+ background-color: rgba(0, 0, 0, 0);
+}
+
+.overview-components-transparent .overview-tile:hover,
+.overview-components-transparent .overview-tile:focus,
+.overview-components-transparent .overview-tile:selected,
+.overview-components-transparent .overview-tile:highlighted,
+.overview-components-transparent .grid-search-result:hover,
+.overview-components-transparent .grid-search-result:focus,
+.overview-components-transparent .grid-search-result:selected,
+.overview-components-transparent .grid-search-result:highlighted,
+.overview-components-transparent .list-search-result:hover,
+.overview-components-transparent .list-search-result:focus,
+.overview-components-transparent .list-search-result:selected,
+.overview-components-transparent .list-search-result:highlighted,
+.overview-components-transparent .search-provider-icon:hover,
+.overview-components-transparent .search-provider-icon:focus,
+.overview-components-transparent .page-navigation-arrow:hover,
+.overview-components-transparent .page-navigation-arrow:focus,
+.overview-components-transparent .app-folder-dialog .icon-button:hover,
+.overview-components-transparent .app-folder-dialog .icon-button:focus,
+.overview-components-transparent #dash .overview-tile:hover .overview-icon,
+.overview-components-transparent #dash .overview-tile:focus .overview-icon,
+.overview-components-transparent #dash .show-apps:hover .overview-icon,
+.overview-components-transparent #dash .show-apps:focus .overview-icon {
+ background-color: rgba(230, 230, 230, 0.08);
+}
+
+.overview-components-transparent .overview-tile:active,
+.overview-components-transparent .overview-tile:focus:hover,
+.overview-components-transparent .overview-tile:drop,
+.overview-components-transparent .overview-tile:selected:hover,
+.overview-components-transparent .overview-tile:highlighted:hover,
+.overview-components-transparent .grid-search-result:active,
+.overview-components-transparent .grid-search-result:focus:hover,
+.overview-components-transparent .grid-search-result:selected:hover,
+.overview-components-transparent .grid-search-result:highlighted:hover,
+.overview-components-transparent .list-search-result:active,
+.overview-components-transparent .list-search-result:focus:hover,
+.overview-components-transparent .list-search-result:selected:hover,
+.overview-components-transparent .list-search-result:highlighted:hover,
+.overview-components-transparent .search-provider-icon:active,
+.overview-components-transparent .search-provider-icon:focus:hover,
+.overview-components-transparent .page-navigation-arrow:active,
+.overview-components-transparent .page-navigation-arrow:focus:hover,
+.overview-components-transparent .app-folder-dialog .icon-button:active,
+.overview-components-transparent .app-folder-dialog .icon-button:focus:hover,
+.overview-components-transparent #dash .overview-tile:active .overview-icon,
+.overview-components-transparent #dash .overview-tile:focus:hover .overview-icon,
+.overview-components-transparent #dash .overview-tile:drop .overview-icon,
+.overview-components-transparent #dash .show-apps:checked .overview-icon {
+ background-color: rgba(230, 230, 230, 0.12);
+}
+
+.overview-components-transparent .overview-tile:focus:active,
+.overview-components-transparent .overview-tile:selected:active,
+.overview-components-transparent .overview-tile:highlighted:active,
+.overview-components-transparent .grid-search-result:focus:active,
+.overview-components-transparent .grid-search-result:selected:active,
+.overview-components-transparent .grid-search-result:highlighted:active,
+.overview-components-transparent .list-search-result:focus:active,
+.overview-components-transparent .list-search-result:selected:active,
+.overview-components-transparent .list-search-result:highlighted:active,
+.overview-components-transparent .search-provider-icon:focus:active,
+.overview-components-transparent .page-navigation-arrow:focus:active,
+.overview-components-transparent .app-folder-dialog .icon-button:focus:active,
+.overview-components-transparent #dash .show-apps:checked .overview-icon:hover {
+ background-color: rgba(230, 230, 230, 0.15);
+}
+
+
+/*
+* `.overview-components-light`
+*/
+
+.overview-components-light .search-entry {
+ color: white;
+ background-color: rgba(200, 200, 200, 0.2);
+ border-color: transparent;
+ box-shadow: none;
+}
+
+.overview-components-light .search-entry .search-entry-icon {
+ color: rgba(255, 255, 255, 0.65);
+}
+
+.overview-components-light .overview-tile,
+.overview-components-light .overview-icon,
+.overview-components-light .grid-search-result,
+.overview-components-light .list-search-result,
+.overview-components-light .search-provider-icon,
+/* prevents the extension from interfering with Just Perfection */
+.overview-components-light.just-perfection .search-section-content,
+/* remove the rectangular background from the dash */
+.overview-components-light #dash .overview-tile,
+.overview-components-light #dashtopanelScrollview .overview-tile {
+ background-color: transparent;
+}
+
+.overview-components-light .workspace-thumbnail,
+.overview-components-light .search-section-content,
+.overview-components-light .overview-tile.app-folder,
+.overview-components-light .page-navigation-arrow,
+.overview-components-light .app-folder-dialog .icon-button,
+/* this shouldn't apply to Dash to Dock */
+.overview-components-light StBoxLayout>StWidget>#dash>.dash-background {
+ background-color: rgba(200, 200, 200, 0.2);
+}
+
+.overview-components-light .overview-tile:hover,
+.overview-components-light .overview-tile:focus,
+.overview-components-light .overview-tile:selected,
+.overview-components-light .overview-tile:highlighted,
+.overview-components-light .grid-search-result:hover,
+.overview-components-light .grid-search-result:focus,
+.overview-components-light .grid-search-result:selected,
+.overview-components-light .grid-search-result:highlighted,
+.overview-components-light .list-search-result:hover,
+.overview-components-light .list-search-result:focus,
+.overview-components-light .list-search-result:selected,
+.overview-components-light .list-search-result:highlighted,
+.overview-components-light .search-provider-icon:hover,
+.overview-components-light .search-provider-icon:focus,
+.overview-components-light .page-navigation-arrow:hover,
+.overview-components-light .page-navigation-arrow:focus,
+.overview-components-light .app-folder-dialog .icon-button:hover,
+.overview-components-light .app-folder-dialog .icon-button:focus,
+.overview-components-light #dash .overview-tile:hover .overview-icon,
+.overview-components-light #dash .overview-tile:focus .overview-icon,
+.overview-components-light #dash .show-apps:hover .overview-icon,
+.overview-components-light #dash .show-apps:focus .overview-icon {
+ background-color: rgba(230, 230, 230, 0.2);
+}
+
+.overview-components-light .overview-tile:active,
+.overview-components-light .overview-tile:focus:hover,
+.overview-components-light .overview-tile:drop,
+.overview-components-light .overview-tile:selected:hover,
+.overview-components-light .overview-tile:highlighted:hover,
+.overview-components-light .grid-search-result:active,
+.overview-components-light .grid-search-result:focus:hover,
+.overview-components-light .grid-search-result:selected:hover,
+.overview-components-light .grid-search-result:highlighted:hover,
+.overview-components-light .list-search-result:active,
+.overview-components-light .list-search-result:focus:hover,
+.overview-components-light .list-search-result:selected:hover,
+.overview-components-light .list-search-result:highlighted:hover,
+.overview-components-light .search-provider-icon:active,
+.overview-components-light .search-provider-icon:focus:hover,
+.overview-components-light .page-navigation-arrow:active,
+.overview-components-light .page-navigation-arrow:focus:hover,
+.overview-components-light .app-folder-dialog .icon-button:active,
+.overview-components-light .app-folder-dialog .icon-button:focus:hover,
+.overview-components-light #dash .overview-tile:active .overview-icon,
+.overview-components-light #dash .overview-tile:focus:hover .overview-icon,
+.overview-components-light #dash .overview-tile:drop .overview-icon,
+.overview-components-light #dash .show-apps:checked .overview-icon {
+ background-color: rgba(230, 230, 230, 0.25);
+}
+
+.overview-components-light .overview-tile:focus:active,
+.overview-components-light .overview-tile:selected:active,
+.overview-components-light .overview-tile:highlighted:active,
+.overview-components-light .grid-search-result:focus:active,
+.overview-components-light .grid-search-result:selected:active,
+.overview-components-light .grid-search-result:highlighted:active,
+.overview-components-light .list-search-result:focus:active,
+.overview-components-light .list-search-result:selected:active,
+.overview-components-light .list-search-result:highlighted:active,
+.overview-components-light .search-provider-icon:focus:active,
+.overview-components-light .page-navigation-arrow:focus:active,
+.overview-components-light .app-folder-dialog .icon-button:focus:active,
+.overview-components-light #dash .show-apps:checked .overview-icon:hover {
+ background-color: rgba(230, 230, 230, 0.3);
+}
+
+
+/*
+* `.overview-components-dark`
+*/
+
+.overview-components-dark .search-entry {
+ color: white;
+ background-color: rgba(100, 100, 100, 0.35);
+ border-color: transparent;
+ box-shadow: none;
+}
+
+.overview-components-dark .search-entry .search-entry-icon {
+ color: rgba(255, 255, 255, 0.65);
+}
+
+.overview-components-dark .overview-tile,
+.overview-components-dark .overview-icon,
+.overview-components-dark .grid-search-result,
+.overview-components-dark .list-search-result,
+.overview-components-dark .search-provider-icon,
+/* prevents the extension from interfering with Just Perfection */
+.overview-components-dark.just-perfection .search-section-content,
+/* remove the rectangular background from the dash */
+.overview-components-dark #dash .overview-tile,
+.overview-components-dark #dashtopanelScrollview .overview-tile {
+ background-color: transparent;
+}
+
+.overview-components-dark .workspace-thumbnail,
+.overview-components-dark .search-section-content,
+.overview-components-dark .overview-tile.app-folder,
+.overview-components-dark .page-navigation-arrow,
+.overview-components-dark .app-folder-dialog .icon-button,
+/* this shouldn't apply to Dash to Dock */
+.overview-components-dark StBoxLayout>StWidget>#dash>.dash-background {
+ background-color: rgba(100, 100, 100, 0.35);
+}
+
+.overview-components-dark .overview-tile:hover,
+.overview-components-dark .overview-tile:focus,
+.overview-components-dark .overview-tile:selected,
+.overview-components-dark .overview-tile:highlighted,
+.overview-components-dark .grid-search-result:hover,
+.overview-components-dark .grid-search-result:focus,
+.overview-components-dark .grid-search-result:selected,
+.overview-components-dark .grid-search-result:highlighted,
+.overview-components-dark .list-search-result:hover,
+.overview-components-dark .list-search-result:focus,
+.overview-components-dark .list-search-result:selected,
+.overview-components-dark .list-search-result:highlighted,
+.overview-components-dark .search-provider-icon:hover,
+.overview-components-dark .search-provider-icon:focus,
+.overview-components-dark .page-navigation-arrow:hover,
+.overview-components-dark .page-navigation-arrow:focus,
+.overview-components-dark .app-folder-dialog .icon-button:hover,
+.overview-components-dark .app-folder-dialog .icon-button:focus,
+.overview-components-dark #dash .overview-tile:hover .overview-icon,
+.overview-components-dark #dash .overview-tile:focus .overview-icon,
+.overview-components-dark #dash .show-apps:hover .overview-icon,
+.overview-components-dark #dash .show-apps:focus .overview-icon {
+ background-color: rgba(120, 120, 120, 0.35);
+}
+
+.overview-components-dark .overview-tile:active,
+.overview-components-dark .overview-tile:focus:hover,
+.overview-components-dark .overview-tile:drop,
+.overview-components-dark .overview-tile:selected:hover,
+.overview-components-dark .overview-tile:highlighted:hover,
+.overview-components-dark .grid-search-result:active,
+.overview-components-dark .grid-search-result:focus:hover,
+.overview-components-dark .grid-search-result:selected:hover,
+.overview-components-dark .grid-search-result:highlighted:hover,
+.overview-components-dark .list-search-result:active,
+.overview-components-dark .list-search-result:focus:hover,
+.overview-components-dark .list-search-result:selected:hover,
+.overview-components-dark .list-search-result:highlighted:hover,
+.overview-components-dark .search-provider-icon:active,
+.overview-components-dark .search-provider-icon:focus:hover,
+.overview-components-dark .page-navigation-arrow:active,
+.overview-components-dark .page-navigation-arrow:focus:hover,
+.overview-components-dark .app-folder-dialog .icon-button:active,
+.overview-components-dark .app-folder-dialog .icon-button:focus:hover,
+.overview-components-dark #dash .overview-tile:active .overview-icon,
+.overview-components-dark #dash .overview-tile:focus:hover .overview-icon,
+.overview-components-dark #dash .overview-tile:drop .overview-icon,
+.overview-components-dark #dash .show-apps:checked .overview-icon {
+ background-color: rgba(120, 120, 120, 0.4);
+}
+
+.overview-components-dark .overview-tile:focus:active,
+.overview-components-dark .overview-tile:selected:active,
+.overview-components-dark .overview-tile:highlighted:active,
+.overview-components-dark .grid-search-result:focus:active,
+.overview-components-dark .grid-search-result:selected:active,
+.overview-components-dark .grid-search-result:highlighted:active,
+.overview-components-dark .list-search-result:focus:active,
+.overview-components-dark .list-search-result:selected:active,
+.overview-components-dark .list-search-result:highlighted:active,
+.overview-components-dark .search-provider-icon:focus:active,
+.overview-components-dark .page-navigation-arrow:focus:active,
+.overview-components-dark .app-folder-dialog .icon-button:focus:active,
+.overview-components-dark #dash .show-apps:checked .overview-icon:hover {
+ background-color: rgba(120, 120, 120, 0.45);
+}
+
+
+/*** APPFOLDER DIALOG ***/
+
+/*
+* `.appfolder-dialogs-transparent`
+*/
+
+.appfolder-dialogs-transparent {
+ background-color: rgba(0, 0, 0, 0);
+ border: none;
+ box-shadow: none;
+}
+
+.appfolder-dialogs-transparent .folder-name-entry {
+ color: white;
+ background-color: rgba(0, 0, 0, 0);
+ border: 0;
+ box-shadow: none;
+}
+
+/*
+* `.appfolder-dialogs-light`
+*/
+
+.appfolder-dialogs-light {
+ background-color: rgba(200, 200, 200, 0.2);
+ border: none;
+ box-shadow: none;
+}
+
+.appfolder-dialogs-light .folder-name-entry {
+ color: white;
+ background-color: rgba(200, 200, 200, 0.2);
+ border: 0;
+ box-shadow: none;
+}
+
+
+/*
+* `.appfolder-dialogs-dark`
+*/
+
+.appfolder-dialogs-dark {
+ background-color: rgba(100, 100, 100, 0.35);
+ border: none;
+ box-shadow: none;
+}
+
+.appfolder-dialogs-dark .folder-name-entry {
+ color: white;
+ background-color: rgba(100, 100, 100, 0.35);
+ border: 0;
+ box-shadow: none;
+}
+
+
+/*** PANEL LIGHT TEXT ***/
+
+/*
+* `.panel-light-text`, adapted from gnome-shell-light.css
+*/
+
+#panel.panel-light-text .panel-button,
+#panel.panel-light-text .panel-button.clock-display,
+#panel.panel-light-text .netSpeedLabel {
+ color: #f6f5f4;
+ box-shadow: none;
+}
+
+#panel.panel-light-text .panel-button:active,
+#panel.panel-light-text .panel-button:focus,
+#panel.panel-light-text .panel-button:checked,
+#panel.panel-light-text .panel-button.clock-display:active .clock,
+#panel.panel-light-text .panel-button.clock-display:focus .clock,
+#panel.panel-light-text .panel-button.clock-display:checked .clock {
+ box-shadow: inset 0 0 0 100px rgba(246, 245, 244, 0.25);
+}
+
+#panel.panel-light-text .panel-button:active:hover,
+#panel.panel-light-text .panel-button:focus:hover,
+#panel.panel-light-text .panel-button:checked:hover,
+#panel.panel-light-text .panel-button.clock-display:active:hover,
+#panel.panel-light-text .panel-button.clock-display:focus:hover,
+#panel.panel-light-text .panel-button.clock-display:checked:hover,
+#panel.panel-light-text .panel-button.clock-display:active .clock:hover,
+#panel.panel-light-text .panel-button.clock-display:focus .clock:hover,
+#panel.panel-light-text .panel-button.clock-display:checked .clock:hover {
+ box-shadow: inset 0 0 0 100px rgba(246, 245, 244, 0.35);
+}
+
+#panel.panel-light-text .panel-button:hover,
+#panel.panel-light-text .panel-button.clock-display:hover,
+#panel.panel-light-text .panel-button.clock-display:hover .clock {
+ box-shadow: inset 0 0 0 100px rgba(246, 245, 244, 0.2);
+}
+
+#panel.panel-light-text .panel-button#panelActivities .workspace-dot {
+ background-color: #f6f5f4;
+}
+
+#panel.panel-light-text .panel-button.clock-display:active,
+#panel.panel-light-text .panel-button.clock-display:focus,
+#panel.panel-light-text .panel-button.clock-display:checked,
+#panel.panel-light-text .panel-button.clock-display:hover {
+ box-shadow: none !important;
+}
+
+#panel.panel-light-text .panel-button.screen-recording-indicator {
+ color: #f6f5f4;
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.8);
+}
+
+#panel.panel-light-text .panel-button.screen-recording-indicator:active,
+#panel.panel-light-text .panel-button.screen-recording-indicator:focus,
+#panel.panel-light-text .panel-button.screen-recording-indicator:checked {
+ box-shadow: inset 0 0 0 100px #c01c28;
+}
+
+#panel.panel-light-text .panel-button.screen-recording-indicator:active:hover,
+#panel.panel-light-text .panel-button.screen-recording-indicator:focus:hover,
+#panel.panel-light-text .panel-button.screen-recording-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.95);
+}
+
+#panel.panel-light-text .panel-button.screen-recording-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(192, 28, 40, 0.9);
+}
+
+#panel.panel-light-text .panel-button.screen-sharing-indicator {
+ color: #282828;
+ box-shadow: inset 0 0 0 100px rgba(255, 120, 0, 0.8);
+}
+
+#panel.panel-light-text .panel-button.screen-sharing-indicator:active,
+#panel.panel-light-text .panel-button.screen-sharing-indicator:focus,
+#panel.panel-light-text .panel-button.screen-sharing-indicator:checked {
+ box-shadow: inset 0 0 0 100px #ff7800;
+}
+
+#panel.panel-light-text .panel-button.screen-sharing-indicator:active:hover,
+#panel.panel-light-text .panel-button.screen-sharing-indicator:focus:hover,
+#panel.panel-light-text .panel-button.screen-sharing-indicator:checked:hover {
+ box-shadow: inset 0 0 0 100px rgba(255, 120, 0, 0.95);
+}
+
+#panel.panel-light-text .panel-button.screen-sharing-indicator:hover {
+ box-shadow: inset 0 0 0 100px rgba(255, 120, 0, 0.9);
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/application-row.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/application-row.ui
new file mode 100644
index 0000000..2a26d14
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/application-row.ui
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="ApplicationRow" parent="AdwExpanderRow">
+ <property name="title" translatable="yes">Window Name</property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Select window</property>
+ <property name="subtitle" translatable="yes">Pick a window or select it by its classname.</property>
+
+ <child>
+ <object class="GtkBox" id="window_selector">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">175px</property>
+ <style>
+ <class name="linked" />
+ </style>
+
+ <child>
+ <object class="GtkButton" id="window_picker">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="icon-name">select-window-symbolic</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkEntry" id="window_class">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+
+ <object class="AdwToast" id="picking_failure_toast">
+ <property name="title">Could not pick window, make sure that the extension is enabled.</property>
+ </object>
+
+ <object class="AdwToast" id="window_not_found_toast">
+ <property name="title">Could not pick window from here, does not seem like a valid window.</property>
+ </object>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/applications.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/applications.ui
new file mode 100644
index 0000000..14c9364
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/applications.ui
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="Applications" parent="AdwPreferencesPage">
+ <property name="name">applications</property>
+ <property name="title" translatable="yes">Applications</property>
+ <property name="icon-name">applications-symbolic</property>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Applications blur</property>
+ <property name="description" translatable="yes">Adds blur to the applications.
+To get the best results possible, although with reduced performances, you can choose the option “No artifact” in the “Other → Hack level” tab.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Blur type</property>
+ <property name="subtitle" translatable="yes">The dynamic blur is slower and only compatible with a gaussian blur effect, but shows content behind windows.</property>
+ <property name="activatable-widget">blur_mode_choose</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkBox" id="blur_mode_choose">
+ <property name="valign">center</property>
+ <property name="hexpand">false</property>
+ <style>
+ <class name="linked" />
+ </style>
+
+ <child>
+ <object class="GtkToggleButton" id="mode_static">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="group">mode_dynamic</property>
+ <property name="child">
+ <object class="AdwButtonContent">
+ <property name="icon-name">static-mode-symbolic</property>
+ <property name="label" translatable="yes">Static</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="mode_dynamic">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="child">
+ <object class="AdwButtonContent">
+ <property name="icon-name">dynamic-mode-symbolic</property>
+ <property name="label" translatable="yes">Dynamic</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="PipelineChooseRow" id="pipeline_choose_row">
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow" id="sigma_row">
+ <property name="title" translatable="yes">Sigma</property>
+ <property name="subtitle" translatable="yes">The intensity of the blur.</property>
+ <property name="activatable-widget">sigma_scale</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="sigma_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">0</property>
+ <property name="adjustment">sigma</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow" id="brightness_row">
+ <property name="title" translatable="yes">Brightness</property>
+ <property name="subtitle" translatable="yes">The brightness of the blur effect, a high value might make the text harder to read.</property>
+ <property name="activatable-widget">brightness_scale</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="brightness_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">2</property>
+ <property name="adjustment">brightness</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Opacity</property>
+ <property name="subtitle" translatable="yes">The opacity of the window on top of the blur effect, a higher value will be more legible.</property>
+ <property name="activatable-widget">opacity_scale</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="opacity_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="orientation">horizontal</property>
+ <property name="digits">0</property>
+ <property name="adjustment">opacity</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Opaque focused window</property>
+ <property name="subtitle" translatable="yes">Makes the focused window opaque and the other ones blurred, helping with legibility.</property>
+ <property name="activatable-widget">dynamic_opacity</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkSwitch" id="dynamic_opacity">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Blur on overview</property>
+ <property name="subtitle" translatable="yes">Forces the blur to be properly shown on all workspaces on overview.
+This may cause some latency or performance issues.</property>
+ <property name="activatable-widget">blur_on_overview</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkSwitch" id="blur_on_overview">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Enable all by default</property>
+ <property name="subtitle" translatable="yes">Adds blur behind all windows by default.</property>
+ <property name="activatable-widget">enable_all</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkSwitch" id="enable_all">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup" id="whitelist">
+ <property name="title" translatable="yes">Whitelist</property>
+ <property name="description" translatable="yes">A list of windows to blur.
+Use * to match any sequence of characters (e.g., Firefox* or *Code*), and ? for a single one.</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+ <property name="header-suffix">
+ <object class="GtkButton" id="add_window_whitelist">
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <child>
+ <object class="GtkImage">
+ <property name="margin-end">6</property>
+ <property name="icon-name">add-row-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Add Window</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="circular" />
+ <class name="suggested-action" />
+ </style>
+ </object>
+ </property>
+
+ <!-- WINDOW ROW WIDGETS GO HERE -->
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup" id="blacklist">
+ <property name="title" translatable="yes">Blacklist</property>
+ <property name="description" translatable="yes">A list of windows not to blur.
+Use * to match any sequence of characters (e.g., Firefox* or *Code*), and ? for a single one.</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+ <property name="header-suffix">
+ <object class="GtkButton" id="add_window_blacklist">
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <child>
+ <object class="GtkImage">
+ <property name="margin-end">6</property>
+ <property name="icon-name">add-row-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Add Window</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="circular" />
+ <class name="suggested-action" />
+ </style>
+ </object>
+ </property>
+
+ <!-- WINDOW ROW WIDGETS GO HERE -->
+ </object>
+ </child>
+ </template>
+
+ <object class="GtkAdjustment" id="sigma">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step-increment">1</property>
+ </object>
+
+ <object class="GtkAdjustment" id="brightness">
+ <property name="lower">0.0</property>
+ <property name="upper">1.0</property>
+ <property name="step-increment">0.01</property>
+ </object>
+
+ <object class="GtkAdjustment" id="opacity">
+ <property name="lower">25</property>
+ <property name="upper">255</property>
+ <property name="step-increment">1</property>
+ </object>
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/dash.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/dash.ui
new file mode 100644
index 0000000..b08a4d1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/dash.ui
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="Dash" parent="AdwPreferencesPage">
+ <property name="name">dash</property>
+ <property name="title" translatable="yes">Dash</property>
+ <property name="icon-name">dash-symbolic</property>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Dash to Dock blur</property>
+ <property name="description" translatable="yes">Blur the background of the Dash to Dock extension, if it is used.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Blur type</property>
+ <property name="subtitle" translatable="yes">The dynamic blur is slower and only compatible with a gaussian blur effect.</property>
+ <property name="activatable-widget">blur_mode_choose</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkBox" id="blur_mode_choose">
+ <property name="valign">center</property>
+ <property name="hexpand">false</property>
+ <style>
+ <class name="linked" />
+ </style>
+
+ <child>
+ <object class="GtkToggleButton" id="mode_static">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="group">mode_dynamic</property>
+ <property name="child">
+ <object class="AdwButtonContent">
+ <property name="icon-name">static-mode-symbolic</property>
+ <property name="label" translatable="yes">Static</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="mode_dynamic">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="child">
+ <object class="AdwButtonContent">
+ <property name="icon-name">dynamic-mode-symbolic</property>
+ <property name="label" translatable="yes">Dynamic</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="PipelineChooseRow" id="pipeline_choose_row">
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow" id="sigma_row">
+ <property name="title" translatable="yes">Sigma</property>
+ <property name="subtitle" translatable="yes">The intensity of the blur.</property>
+ <property name="activatable-widget">sigma_scale</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="sigma_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">0</property>
+ <property name="adjustment">sigma</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow" id="brightness_row">
+ <property name="title" translatable="yes">Brightness</property>
+ <property name="subtitle" translatable="yes">The brightness of the blur effect, a high value might make the text harder to read.</property>
+ <property name="activatable-widget">brightness_scale</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="brightness_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">2</property>
+ <property name="adjustment">brightness</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwExpanderRow" id="override_background">
+ <property name="title" translatable="yes">Override background</property>
+ <property name="subtitle" translatable="yes">Makes the background either transparent or semi-transparent, disable this to use Dash to Dock preferences instead.</property>
+ <property name="show-enable-switch">true</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Background style</property>
+ <property name="subtitle" translatable="yes">The transparent/semi-transparent style for the dock background.</property>
+ <property name="activatable-widget">style_dash_to_dock</property>
+
+ <child>
+ <object class="GtkDropDown" id="style_dash_to_dock">
+ <property name="valign">center</property>
+ <property name="model">style_dash_to_dock_model</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Disable in overview</property>
+ <property name="subtitle" translatable="yes">Disables the blur from Dash to Dock when entering the overview.</property>
+ <property name="activatable-widget">unblur_in_overview</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkSwitch" id="unblur_in_overview">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+
+ <object class="GtkAdjustment" id="sigma">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step-increment">1</property>
+ </object>
+
+ <object class="GtkAdjustment" id="brightness">
+ <property name="lower">0.0</property>
+ <property name="upper">1.0</property>
+ <property name="step-increment">0.01</property>
+ </object>
+
+ <object class="GtkStringList" id="style_dash_to_dock_model">
+ <items>
+ <item translatable="yes">Transparent</item>
+ <item translatable="yes">Light</item>
+ <item translatable="yes">Dark</item>
+ </items>
+ </object>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/effects-dialog.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/effects-dialog.ui
new file mode 100644
index 0000000..c6baf62
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/effects-dialog.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="EffectsDialog" parent="AdwPreferencesDialog">
+ <property name="title" translatable="yes">Effects</property>
+ <property name="presentation-mode">ADW_DIALOG_BOTTOM_SHEET</property>
+ <property name="content-width">600</property>
+ <property name="content-height">480</property>
+
+ <child>
+ <object class="AdwPreferencesPage">
+ <child>
+ <object class="AdwPreferencesGroup" id="effects_list">
+ <property name="title">Effects list</property>
+ <property name="description">Effect on the top will be applied first.</property>
+
+ <property name="header-suffix">
+ <object class="GtkBox">
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <style>
+ <class name="linked" />
+ </style>
+ <child>
+ <object class="GtkButton" id="add_effect">
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <child>
+ <object class="GtkImage">
+ <property name="margin-end">6</property>
+ <property name="icon-name">list-add-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Add Effect</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="circular" />
+ <class name="suggested-action" />
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkMenuButton" id="add_effect_alt_menu">
+ <property name="valign">center</property>
+ <property name="icon-name">down-symbolic</property>
+ <property name="menu-model">add_effect_alt_menu_model</property>
+ <style>
+ <class name="circular" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+
+ <menu id="add_effect_alt_menu_model">
+ <section>
+ <item>
+ <attribute name="label" translatable="yes">Include advanced effects</attribute>
+ <attribute name="action">effects-dialog.advanced-effects-bool</attribute>
+ </item>
+ </section>
+ </menu>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/menu.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/menu.ui
new file mode 100644
index 0000000..6d2a474
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/menu.ui
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <menu id="info_menu_model">
+ <section>
+ <item>
+ <attribute name="label" translatable="yes">Project page</attribute>
+ <attribute name="action">prefs.open-readme</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">Report a Bug</attribute>
+ <attribute name="action">prefs.open-bug-report</attribute>
+ </item>
+ <item>
+ <attribute name="label" translatable="yes">License</attribute>
+ <attribute name="action">prefs.open-license</attribute>
+ </item>
+ <submenu>
+ <attribute name="label" translatable="yes">Donate</attribute>
+ <item>
+ <attribute name="label">GitHub</attribute>
+ <attribute name="action">prefs.donate-github</attribute>
+ </item>
+ <item>
+ <attribute name="label">Ko-fi</attribute>
+ <attribute name="action">prefs.donate-kofi</attribute>
+ </item>
+ </submenu>
+ </section>
+ </menu>
+
+ <object class="GtkMenuButton" id="info_menu">
+ <property name="menu-model">info_menu_model</property>
+ <property name="icon-name">heart-symbolic</property>
+ </object>
+
+ <object class="AdwPreferencesPage" id="menu_util"></object>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/other.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/other.ui
new file mode 100644
index 0000000..e136e38
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/other.ui
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="Other" parent="AdwPreferencesPage">
+ <property name="name">other</property>
+ <property name="title" translatable="yes">Other</property>
+ <property name="icon-name">other-symbolic</property>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Lockscreen blur</property>
+ <property name="description" translatable="yes">Change the blur of the lockscreen to use this extension's preferences.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="lockscreen_blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="PipelineChooseRow" id="lockscreen_pipeline_choose_row">
+ <property name="sensitive" bind-source="lockscreen_blur" bind-property="state" bind-flags="sync-create" />
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Screenshot blur</property>
+ <property name="description" translatable="yes">Add blur to the background of the window selector in the screenshot UI.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="screenshot_blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="PipelineChooseRow" id="screenshot_pipeline_choose_row">
+ <property name="sensitive" bind-source="screenshot_blur" bind-property="state" bind-flags="sync-create" />
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Window list extension blur</property>
+ <property name="description" translatable="yes">Make the window-list extension blurred, if it is used.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="window_list_blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Sigma</property>
+ <property name="subtitle" translatable="yes">The intensity of the blur.</property>
+ <property name="activatable-widget">window_list_sigma_scale</property>
+ <property name="sensitive" bind-source="window_list_blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="window_list_sigma_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">0</property>
+ <property name="adjustment">window_list_sigma</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Brightness</property>
+ <property name="subtitle" translatable="yes">The brightness of the blur effect, a high value might make the text harder to read.</property>
+ <property name="activatable-widget">window_list_brightness_scale</property>
+ <property name="sensitive" bind-source="window_list_blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="window_list_brightness_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">2</property>
+ <property name="adjustment">window_list_brightness</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Coverflow Alt-Tab extension blur</property>
+ <property name="description" translatable="yes">Make the coverflow alt-tab extension blurred, if it is used.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="coverflow_alt_tab_blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="PipelineChooseRow" id="coverflow_alt_tab_pipeline_choose_row">
+ <property name="sensitive" bind-source="coverflow_alt_tab_blur" bind-property="state" bind-flags="sync-create" />
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Performance</property>
+ <property name="description" translatable="yes">Various options to tweak the performance.</property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Hack level</property>
+ <property name="subtitle" translatable="yes">Changes the behaviour of the dynamic blur effect. The default value is highly recommended.
+The “No artifact” option will entirely disable clipped redraws in GNOME shell, and will impact your computer performances significantly.</property>
+ <property name="activatable-widget">hack_level</property>
+ <child>
+ <object class="GtkDropDown" id="hack_level">
+ <property name="valign">center</property>
+ <property name="model">hack_level_model</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Debug</property>
+ <property name="subtitle" translatable="yes">Makes the extension verbose in logs, activate when you need to report an issue.</property>
+ <property name="activatable-widget">debug</property>
+ <child>
+ <object class="GtkSwitch" id="debug">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Reset preferences</property>
+ <property name="description" translatable="yes">Resets preferences of Blur my Shell irreversibly.</property>
+ <property name="header-suffix">
+ <object class="GtkButton" id="reset">
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-start">20</property>
+ <property name="margin-end">20</property>
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">6</property>
+ <child>
+ <object class="GtkImage">
+ <property name="margin-end">6</property>
+ <property name="icon-name">reset-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Reset</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="circular" />
+ <class name="destructive-action" />
+ </style>
+ </object>
+ </property>
+ </object>
+ </child>
+ </template>
+
+ <object class="GtkAdjustment" id="window_list_sigma">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step-increment">1</property>
+ </object>
+
+ <object class="GtkAdjustment" id="window_list_brightness">
+ <property name="lower">0.0</property>
+ <property name="upper">1.0</property>
+ <property name="step-increment">0.01</property>
+ </object>
+
+ <object class="GtkStringList" id="hack_level_model">
+ <items>
+ <item translatable="yes">High performances</item>
+ <item translatable="yes">Default</item>
+ <item translatable="yes">No artifact</item>
+ </items>
+ </object>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/overview.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/overview.ui
new file mode 100644
index 0000000..36b91fc
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/overview.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="Overview" parent="AdwPreferencesPage">
+ <property name="name">overview</property>
+ <property name="title" translatable="yes">Overview</property>
+ <property name="icon-name">overview-symbolic</property>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Background blur</property>
+ <property name="description" translatable="yes">Add blur to the overview background, using the wallpaper picture.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="overview_blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="PipelineChooseRow" id="pipeline_choose_row">
+ <property name="sensitive" bind-source="overview_blur" bind-property="state" bind-flags="sync-create" />
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Overview components style</property>
+ <property name="subtitle" translatable="yes">The semi-transparent style for the dash, search entry/results, and application folders.</property>
+ <property name="sensitive" bind-source="overview_blur" bind-property="state" bind-flags="sync-create" />
+ <property name="activatable-widget">overview_style_components</property>
+
+ <child>
+ <object class="GtkDropDown" id="overview_style_components">
+ <property name="valign">center</property>
+ <property name="model">overview_style_components_model</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Application folder blur</property>
+ <property name="description" translatable="yes">Makes the background of application folder dialogs blurred.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="appfolder_blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Sigma</property>
+ <property name="subtitle" translatable="yes">The intensity of the blur.</property>
+ <property name="activatable-widget">appfolder_sigma_scale</property>
+ <property name="sensitive" bind-source="appfolder_blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="appfolder_sigma_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">0</property>
+ <property name="adjustment">appfolder_sigma</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Brightness</property>
+ <property name="subtitle" translatable="yes">The brightness of the blur effect, a high value might make the text harder to read.</property>
+ <property name="activatable-widget">appfolder_brightness_scale</property>
+ <property name="sensitive" bind-source="appfolder_blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="appfolder_brightness_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">2</property>
+ <property name="adjustment">appfolder_brightness</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Application folder dialogs style</property>
+ <property name="subtitle" translatable="yes">The semi-transparent style for the application folder dialogs.</property>
+ <property name="sensitive" bind-source="appfolder_blur" bind-property="state" bind-flags="sync-create" />
+ <property name="activatable-widget">appfolder_style_dialogs</property>
+
+ <child>
+ <object class="GtkDropDown" id="appfolder_style_dialogs">
+ <property name="valign">center</property>
+ <property name="model">appfolder_style_dialogs_model</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+
+ <object class="GtkStringList" id="overview_style_components_model">
+ <items>
+ <item translatable="yes">Do not style</item>
+ <item translatable="yes">Light</item>
+ <item translatable="yes">Dark</item>
+ <item translatable="yes">Transparent</item>
+ </items>
+ </object>
+
+ <object class="GtkAdjustment" id="appfolder_sigma">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step-increment">1</property>
+ </object>
+
+ <object class="GtkAdjustment" id="appfolder_brightness">
+ <property name="lower">0.0</property>
+ <property name="upper">1.0</property>
+ <property name="step-increment">0.01</property>
+ </object>
+
+
+ <object class="GtkStringList" id="appfolder_style_dialogs_model">
+ <items>
+ <item translatable="yes">Do not style</item>
+ <item translatable="yes">Transparent</item>
+ <item translatable="yes">Light</item>
+ <item translatable="yes">Dark</item>
+ </items>
+ </object>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/panel.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/panel.ui
new file mode 100644
index 0000000..306f4ef
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/panel.ui
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="Panel" parent="AdwPreferencesPage">
+ <property name="name">panel</property>
+ <property name="title" translatable="yes">Panel</property>
+ <property name="icon-name">bottom-panel-symbolic</property>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Panel blur</property>
+ <property name="description" translatable="yes">Blur the top panel using the background image.</property>
+ <property name="header-suffix">
+ <object class="GtkSwitch" id="blur">
+ <property name="valign">center</property>
+ </object>
+ </property>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Blur type</property>
+ <property name="subtitle" translatable="yes">The dynamic blur is slower and only compatible with a gaussian blur effect.</property>
+ <property name="activatable-widget">blur_mode_choose</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkBox" id="blur_mode_choose">
+ <property name="valign">center</property>
+ <property name="hexpand">false</property>
+ <style>
+ <class name="linked" />
+ </style>
+
+ <child>
+ <object class="GtkToggleButton" id="mode_static">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="group">mode_dynamic</property>
+ <property name="child">
+ <object class="AdwButtonContent">
+ <property name="icon-name">static-mode-symbolic</property>
+ <property name="label" translatable="yes">Static</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="mode_dynamic">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="child">
+ <object class="AdwButtonContent">
+ <property name="icon-name">dynamic-mode-symbolic</property>
+ <property name="label" translatable="yes">Dynamic</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="PipelineChooseRow" id="pipeline_choose_row">
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow" id="sigma_row">
+ <property name="title" translatable="yes">Sigma</property>
+ <property name="subtitle" translatable="yes">The intensity of the blur.</property>
+ <property name="activatable-widget">sigma_scale</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="sigma_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">0</property>
+ <property name="adjustment">sigma</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow" id="brightness_row">
+ <property name="title" translatable="yes">Brightness</property>
+ <property name="subtitle" translatable="yes">The brightness of the blur effect, a high value might make the text harder to read.</property>
+ <property name="activatable-widget">brightness_scale</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkScale" id="brightness_scale">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="width-request">200px</property>
+ <property name="draw-value">true</property>
+ <property name="value-pos">right</property>
+ <property name="digits">2</property>
+ <property name="adjustment">brightness</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Force light text</property>
+ <property name="subtitle" translatable="yes">Use a light text for the panel, useful when using gnome-shell's light theme.</property>
+ <property name="activatable-widget">force_light_text</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkSwitch" id="force_light_text">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwExpanderRow" id="override_background">
+ <property name="title" translatable="yes">Override background</property>
+ <property name="subtitle" translatable="yes">Override the background of the panel to use a transparent or semi-transparent one.
+Recommended unless you want to customize your GNOME theme.</property>
+ <property name="show-enable-switch">true</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Background style</property>
+ <property name="subtitle" translatable="yes">The transparent/semi-transparent style for the panel background.</property>
+ <property name="activatable-widget">style_panel</property>
+
+ <child>
+ <object class="GtkDropDown" id="style_panel">
+ <property name="valign">center</property>
+ <property name="model">style_panel_model</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Disable when a window is near</property>
+ <property name="subtitle" translatable="yes">Disables the transparency of the panel when a window is near it.</property>
+ <property name="activatable-widget">override_background_dynamically</property>
+
+ <child>
+ <object class="GtkSwitch" id="override_background_dynamically">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Disable in overview</property>
+ <property name="subtitle" translatable="yes">Disables the blur from the panel when entering the overview.</property>
+ <property name="activatable-widget">unblur_in_overview</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkSwitch" id="unblur_in_overview">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Compatibility</property>
+ <property name="description" translatable="yes">Various options to provide compatibility with other extensions.</property>
+ <property name="sensitive" bind-source="blur" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Hidetopbar extension</property>
+ <property name="subtitle" translatable="yes">Does not disable the blur in overview, best used with static blur.</property>
+ <property name="activatable-widget">hidetopbar_compatibility</property>
+ <property name="sensitive" bind-source="unblur_in_overview" bind-property="state" bind-flags="sync-create" />
+
+ <child>
+ <object class="GtkSwitch" id="hidetopbar_compatibility">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Blur original panel with Dash to Panel</property>
+ <property name="subtitle" translatable="yes">Enables the blurring of the original panel with Dash to Panel, if selected in the extension's options.</property>
+ <property name="activatable-widget">dtp_blur_original_panel</property>
+
+ <child>
+ <object class="GtkSwitch" id="dtp_blur_original_panel">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+
+ <object class="GtkStringList" id="style_panel_model">
+ <items>
+ <item translatable="yes">Transparent</item>
+ <item translatable="yes">Light</item>
+ <item translatable="yes">Dark</item>
+ <item translatable="yes">Contrasted</item>
+ </items>
+ </object>
+
+ <object class="GtkAdjustment" id="sigma">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step-increment">1</property>
+ </object>
+
+ <object class="GtkAdjustment" id="brightness">
+ <property name="lower">0.0</property>
+ <property name="upper">1.0</property>
+ <property name="step-increment">0.01</property>
+ </object>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-choose-row.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-choose-row.ui
new file mode 100644
index 0000000..72b854b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-choose-row.ui
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="PipelineChooseRow" parent="AdwActionRow">
+ <property name="title" translatable="yes">Pipeline</property>
+ <property name="subtitle" translatable="yes">The pipeline to be used with this component.</property>
+ <property name="activatable-widget">pipeline_choose</property>
+
+ <child>
+ <object class="GtkBox">
+ <property name="valign">center</property>
+ <property name="hexpand">false</property>
+ <style>
+ <class name="linked" />
+ </style>
+
+ <child>
+ <object class="GtkDropDown" id="pipeline_choose">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="model">
+ <object class="GtkStringList" id="pipeline_model">
+ <items>
+ </items>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkButton" id="pipeline_edit">
+ <property name="valign">center</property>
+ <property name="hexpand">true</property>
+ <property name="icon-name">edit-row-symbolic</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-group.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-group.ui
new file mode 100644
index 0000000..19a12bf
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipeline-group.ui
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="PipelineGroup" parent="AdwPreferencesGroup">
+ <child>
+ <object class="AdwEntryRow" id="title">
+ <property name="title" translatable="yes">Pipeline name</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow" id="effects_description_row">
+ <property name="activatable-widget">manage_effects</property>
+
+ <child>
+ <object class="GtkButton" id="manage_effects">
+ <property name="valign">center</property>
+ <property name="child">
+ <object class="AdwButtonContent">
+ <property name="icon-name">edit-row-symbolic</property>
+ <property name="label" translatable="yes">Manage effects</property>
+ <property name="use-underline">True</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipelines.ui b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipelines.ui
new file mode 100644
index 0000000..555ec0a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/ui/pipelines.ui
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="blur-my-shell@aunetx">
+ <template class="Pipelines" parent="AdwPreferencesPage">
+ <property name="name">pipelines</property>
+ <property name="title" translatable="yes">Pipelines</property>
+ <property name="icon-name">pipelines-symbolic</property>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="description" translatable="yes">Each component uses a pipeline, which can be chosen in the corresponding tab. Here you can create, modify and delete the pipelines and their effects.</property>
+
+ <property name="header-suffix">
+ <object class="GtkButton" id="add_pipeline">
+ <property name="halign">start</property>
+ <property name="valign">center</property>
+ <property name="margin-start">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <child>
+ <object class="GtkImage">
+ <property name="margin-end">6</property>
+ <property name="icon-name">list-add-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="label" translatable="yes">Add Pipeline</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="circular" />
+ <class name="suggested-action" />
+ </style>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ </template>
+</interface> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/LICENSE b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/extension.js
new file mode 100644
index 0000000..cff39d8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/extension.js
@@ -0,0 +1,631 @@
+//////////////////////////////////////////////////////////////////////////////////////////
+// ) ( //
+// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
+// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
+// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
+// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
+// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
+// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
+// |__/ //
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// SPDX-FileCopyrightText: Simon Schneegans <code@simonschneegans.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+'use strict';
+
+import Gio from 'gi://Gio';
+import Meta from 'gi://Meta';
+
+import {fromVersion26} from './src/migrate.js';
+import {ProfileManager} from './src/ProfileManager.js';
+import {WindowPicker} from './src/WindowPicker.js';
+import * as utils from './src/utils.js';
+
+import Apparition from './src/effects/Apparition.js';
+import AuraGlow from './src/effects/AuraGlow.js';
+import BrokenGlass from './src/effects/BrokenGlass.js';
+import Doom from './src/effects/Doom.js';
+import EnergizeA from './src/effects/EnergizeA.js';
+import EnergizeB from './src/effects/EnergizeB.js';
+import Fire from './src/effects/Fire.js';
+import Focus from './src/effects/Focus.js';
+import Glide from './src/effects/Glide.js';
+import Glitch from './src/effects/Glitch.js';
+import Hexagon from './src/effects/Hexagon.js';
+import Incinerate from './src/effects/Incinerate.js';
+import Matrix from './src/effects/Matrix.js';
+import Mushroom from './src/effects/Mushroom.js';
+import PaintBrush from './src/effects/PaintBrush.js';
+import Pixelate from './src/effects/Pixelate.js';
+import PixelWheel from './src/effects/PixelWheel.js';
+import PixelWipe from './src/effects/PixelWipe.js';
+import Portal from './src/effects/Portal.js';
+import RGBWarp from './src/effects/RGBWarp.js';
+import SnapOfDisintegration from './src/effects/SnapOfDisintegration.js';
+import TeamRocket from './src/effects/TeamRocket.js';
+import TRexAttack from './src/effects/TRexAttack.js';
+import TVEffect from './src/effects/TVEffect.js';
+import TVGlitch from './src/effects/TVGlitch.js';
+import Wisps from './src/effects/Wisps.js';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import {Workspace} from 'resource:///org/gnome/shell/ui/workspace.js';
+import {WindowPreview} from 'resource:///org/gnome/shell/ui/windowPreview.js';
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// This extensions modifies the window-close and window-open animations with all kinds //
+// of effects. The effects are implemented using GLSL shaders which are applied to the //
+// window's Clutter.Actor. The extension is actually very simple, much of the //
+// complexity comes from the fact that GNOME Shell usually does not show an animation //
+// when a window is closed in the overview. Several methods need to be monkey-patched //
+// to get this working. For more details, read the other comments in this file... //
+//////////////////////////////////////////////////////////////////////////////////////////
+
+export default class BurnMyWindows extends Extension {
+
+ // ------------------------------------------------------------------------ public stuff
+
+ // This function could be called after the extension is enabled, which could be done
+ // from GNOME Tweaks, when you log in or when the screen is unlocked.
+ enable() {
+
+ // New effects must be registered here and in prefs.js.
+ this._ALL_EFFECTS = [
+ new Apparition(), new AuraGlow(), new BrokenGlass(), new Doom(),
+ new EnergizeA(), new EnergizeB(), new Fire(), new Focus(),
+ new Glide(), new Glitch(), new Hexagon(), new Incinerate(),
+ new Matrix(), new PaintBrush(), new Pixelate(), new PixelWheel(),
+ new PixelWipe(), new Portal(), new RGBWarp(), new SnapOfDisintegration(),
+ new TeamRocket(), new TRexAttack(), new TVEffect(), new TVGlitch(),
+ new Wisps(), new Mushroom()
+ ];
+
+ // Load all of our resources.
+ this._resources =
+ Gio.Resource.load(this.path + '/resources/burn-my-windows.gresource');
+ Gio.resources_register(this._resources);
+
+ // Store a reference to the settings object.
+ this._settings = this.getSettings();
+
+ // Now we check whether the extension settings need to be migrated from a previous
+ // version. If this is the case, we defer the profile loading until this is finished.
+ const lastVersion = this._settings.get_int('last-extension-version');
+ if (lastVersion < this.metadata.version) {
+ if (lastVersion <= 26) {
+ // If the profile migration fails for some reason, the callback will create a
+ // default profile instead.
+ fromVersion26().finally(() => {
+ this._loadProfiles();
+ this._settings.set_int('last-extension-version', this.metadata.version);
+ });
+ } else {
+ this._loadProfiles();
+ }
+
+ } else {
+ this._loadProfiles();
+ }
+
+ // We reload all effect profiles whenever the currently edited profile in the
+ // preferences dialog changes. This is most likely a bit too often, but it will also
+ // happen whenever a new profile is created and whenever an old profile is deleted.
+ this._settings.connect('changed::active-profile', () => {
+ this._loadProfiles();
+ });
+
+ // This is used to get the desktop's color scheme.
+ this._shellSettings = new Gio.Settings({schema: 'org.gnome.desktop.interface'});
+
+ // Enable the window-picking D-Bus API for the preferences dialog.
+ this._windowPicker = new WindowPicker();
+ this._windowPicker.export();
+
+ // We will use extensionThis to refer to the extension inside the patched methods.
+ const extensionThis = this;
+
+ // This is used to get the battery state.
+ const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(
+ utils.getStringResource('/interfaces/org.freedesktop.UPower.xml'));
+ this._upowerProxy = new UPowerProxy(Gio.DBus.system, 'org.freedesktop.UPower',
+ '/org/freedesktop/UPower');
+
+ // This is used to get the current power profile.
+ try {
+ const PowerProfilesProxy = Gio.DBusProxy.makeProxyWrapper(
+ utils.getStringResource('/interfaces/net.hadess.PowerProfiles.xml'));
+ this._powerProfilesProxy = new PowerProfilesProxy(
+ Gio.DBus.system, 'net.hadess.PowerProfiles', '/net/hadess/PowerProfiles');
+ } catch (e) {
+ // Maybe the service is masked...
+ }
+
+ // We will monkey-patch these methods. Let's store the original ones.
+ this._origShouldAnimateActor = Main.wm._shouldAnimateActor;
+ this._origWaitForOverviewToHide = Main.wm._waitForOverviewToHide;
+ this._origAddWindowClone = Workspace.prototype._addWindowClone;
+ this._origWindowRemoved = Workspace.prototype._windowRemoved;
+ this._origDoRemoveWindow = Workspace.prototype._doRemoveWindow;
+
+ // ------------------------------- patching the window animations outside the overview
+
+ // If a window is created, the transitions are set up in the async _mapWindow() of the
+ // WindowManager:
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1436
+ // AFAIK, overriding this method is not possible as it's called by a signal to which
+ // it is bound via the bind() method. To tweak the async transition anyways, we
+ // override the actors ease() method once. We do this in _shouldAnimateActor() which
+ // is called right before the ease() in _mapWindow:
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1465
+
+ // The same trick is done for the window-close animation. This is set up in a similar
+ // fashion in the WindowManager's _destroyWindow():
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1525
+ // Here is _shouldAnimateActor() also called right before. So we use it again to
+ // monkey-patch the window actor's ease() once.
+
+ // We override WindowManager._shouldAnimateActor() also for another purpose: Usually,
+ // it returns false when we are in the overview. This prevents the window animations
+ // there. To enable animations in the overview, we check inside the method whether it
+ // was called by either _mapWindow or _destroyWindow. If so, we return true. Let's see
+ // if this breaks stuff left and right...
+ Main.wm._shouldAnimateActor = function(actor, types) {
+ const stack = (new Error()).stack;
+ const forClosing = stack.includes('_destroyWindow@');
+ const forOpening = stack.includes('_mapWindow@');
+
+ // This is also called in other cases, for instance when minimizing windows. We are
+ // only interested in window opening and window closing for now.
+ if (forClosing || forOpening) {
+
+ // If there is an applicable effect profile, we intercept the ease() method to
+ // setup our own effect.
+ const chosenEffect = extensionThis._chooseEffect(actor, forOpening);
+
+ if (chosenEffect) {
+ // Store the original ease() method of the actor.
+ const orig = actor.ease;
+
+ // Now intercept the next call to actor.ease().
+ actor.ease = function(...params) {
+ // There is a really weird issue in GNOME Shell 44: A few non-GTK windows are
+ // resized directly after they are mapped on X11. This happens for instance
+ // for keepassxc after it was closed in the maximized state. As the
+ // _mapWindow() method is called asynchronously, the window is not yet visible
+ // when the resize happens. Hence, our ease-override is called for the resize
+ // animation instead of the window-open or window-close animation. This is not
+ // what we want. So we check again whether the ease() call is for the
+ // window-open or window-close animation. If not, we just call the original
+ // ease() method. See also:
+ // https://github.com/Schneegans/Burn-My-Windows/issues/335
+ const stack = (new Error()).stack;
+ const forClosing = stack.includes('_destroyWindow@');
+ const forOpening = stack.includes('_mapWindow@');
+
+ if (forClosing || forOpening) {
+ // Quickly restore the original behavior. Nobody noticed, I guess :D
+ actor.ease = orig;
+
+ // And then create the effect!
+ extensionThis._setupEffect(actor, forOpening, chosenEffect.effect,
+ chosenEffect.profile);
+ } else {
+ orig.apply(this, params);
+ }
+ };
+
+ return true;
+ }
+ }
+
+ return extensionThis._origShouldAnimateActor.apply(this, [actor, types]);
+ };
+
+ // Make sure to remove any effects if requested by the window manager.
+ this._killEffectsSignal =
+ global.window_manager.connect('kill-window-effects', (wm, actor) => {
+ const shader = actor.get_effect('burn-my-windows-effect');
+ if (shader) {
+ shader.endAnimation();
+ }
+ });
+
+
+ // --------------------------------------------- fix window animations in the overview
+
+ // Some of the effects require that the window's actor is enlarged to provide a bigger
+ // canvas to draw the effects. Outside the overview we can simply increase the scale
+ // of the actor. However, if we are in the overview, we have to enlarge the clone of
+ // the window as well.
+ Workspace.prototype._addWindowClone = function(...params) {
+ const clone = extensionThis._origAddWindowClone.apply(this, params);
+ const container = clone.window_container;
+ const realWindow = params[0].get_compositor_private();
+
+ // Store the overview clone as temporary members of the real window actor. When we
+ // set up the effect, we will check for the existence of these and enlarge the clone
+ // as needed.
+ realWindow._bmwOverviewClone = clone;
+ realWindow._bmwOverviewCloneContainer = container;
+
+ // Remove the temporary members again once the clone is deleted.
+ container.connect('destroy', () => {
+ delete realWindow._bmwOverviewClone;
+ delete realWindow._bmwOverviewCloneContainer;
+ });
+
+ return clone;
+ };
+
+ // Usually, windows are faded in after the overview is completely hidden. We enable
+ // window-open animations by not waiting for this.
+ Main.wm._waitForOverviewToHide = async function() {
+ return Promise.resolve();
+ };
+
+ // These three method overrides are mega-hacky! Usually, windows are not faded when
+ // closed from the overview (why?). With these overrides we make sure that they are
+ // actually faded out. To do this, _windowRemoved and _doRemoveWindow now check
+ // whether there is a transition ongoing (via extensionThis._shouldDestroy). If that's
+ // the case, these methods do nothing.
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/workspace.js#L1258
+ Workspace.prototype._windowRemoved = function(ws, metaWin) {
+ if (extensionThis._shouldDestroy(this, metaWin)) {
+ extensionThis._origWindowRemoved.apply(this, [ws, metaWin]);
+ }
+ };
+
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/workspace.js#L1137
+ Workspace.prototype._doRemoveWindow = function(metaWin) {
+ if (extensionThis._shouldDestroy(this, metaWin)) {
+ extensionThis._origDoRemoveWindow.apply(this, [metaWin]);
+ }
+ };
+
+ // With the code below, we hide the window-overlay (icon, label, close button) in the
+ // overview once the close-animation is running.
+
+ // We will monkey-patch these methods.
+ this._origDeleteAll = WindowPreview.prototype._deleteAll;
+ this._origRestack = WindowPreview.prototype._restack;
+ this._origInit = WindowPreview.prototype._init;
+
+ // Whenever a WindowPreview is created, we connect to the referenced Meta.Window's
+ // 'unmanaged' signal to hide the overlay.
+ WindowPreview.prototype._init = function(...params) {
+ extensionThis._origInit.apply(this, params);
+
+ // Hide the window's icon, name, and close button.
+ const connectionID = this.metaWindow.connect('unmanaged', () => {
+ if (this.window_container) {
+ this.overlayEnabled = false;
+ this._icon.visible = false;
+ }
+ });
+
+ // Make sure to not call the callback above if the Meta.Window was not unmanaged
+ // before leaving the overview.
+ this.connect('destroy', () => {
+ this.metaWindow.disconnect(connectionID);
+ });
+ };
+
+ // The _deleteAll is called when the user clicks the X in the overview. We should
+ // not attempt to close windows twice. Due to the animation in the overview, the
+ // close button can be clicked twice which normally would lead to a crash.
+ WindowPreview.prototype._deleteAll = function() {
+ if (!this._closeRequested) {
+ extensionThis._origDeleteAll.apply(this);
+ }
+ };
+
+ // This is required, else WindowPreview's _restack() which is called by the
+ // "this.overlayEnabled = false", sometimes tries to access an already delete
+ // WindowPreview.
+ WindowPreview.prototype._restack = function() {
+ if (!this._closeRequested) {
+ extensionThis._origRestack.apply(this);
+ }
+ };
+ }
+
+ // This function could be called after the extension is uninstalled, disabled in GNOME
+ // Tweaks, when you log out or when the screen locks.
+ disable() {
+
+ // Free all effect resources.
+ this._ALL_EFFECTS = [];
+
+ // Unregister our resources.
+ Gio.resources_unregister(this._resources);
+
+ // Disable the window-picking D-Bus API.
+ this._windowPicker.unexport();
+
+ global.window_manager.disconnect(this._killEffectsSignal);
+
+ // Restore the original window-open and window-close animations.
+ Workspace.prototype._addWindowClone = this._origAddWindowClone;
+ Workspace.prototype._windowRemoved = this._origWindowRemoved;
+ Workspace.prototype._doRemoveWindow = this._origDoRemoveWindow;
+ Main.wm._shouldAnimateActor = this._origShouldAnimateActor;
+ Main.wm._waitForOverviewToHide = this._origWaitForOverviewToHide;
+
+ WindowPreview.prototype._deleteAll = this._origDeleteAll;
+ WindowPreview.prototype._restack = this._origRestack;
+ WindowPreview.prototype._init = this._origInit;
+
+ this._settings = null;
+ }
+
+ // ----------------------------------------------------------------------- private stuff
+
+ // This loads all effect profiles and assigns a priority to each profile. Whenever a
+ // window is opened or closed, the matching effect profile with the highest priority
+ // will be chosen.
+ // This method is called whenever the currently edited profile in the/ preferences
+ // dialog changes. This is most likely a bit too often, but it will also happen whenever
+ // a new profile is created and whenever an old profile is deleted.
+ _loadProfiles() {
+
+ // Get all currently available profiles.
+ const profileManager = new ProfileManager(this.metadata);
+ this._profiles = profileManager.getProfiles();
+
+ // Whenever the properties of a profile is changed in the settings, we may have to
+ // resort all profiles according to their priority.
+ const updatePriority = (p) => {
+ p.priority = profileManager.getProfilePriority(p.settings);
+ this._profiles.sort((a, b) => b.priority - a.priority);
+ };
+
+ // For each profile, assign an initial priority and update the priority whenever a
+ // related setting changes.
+ this._profiles.forEach(p => {
+ p.priority = profileManager.getProfilePriority(p.settings);
+ p.settings.connect('changed::profile-app', () => updatePriority(p));
+ p.settings.connect('changed::profile-animation-type', () => updatePriority(p));
+ p.settings.connect('changed::profile-window-type', () => updatePriority(p));
+ p.settings.connect('changed::profile-color-scheme', () => updatePriority(p));
+ p.settings.connect('changed::profile-power-mode', () => updatePriority(p));
+ p.settings.connect('changed::profile-power-profile', () => updatePriority(p));
+ p.settings.connect('changed::profile-high-priority', () => updatePriority(p));
+ });
+
+ // Sort all profiles initially according to their initial priority.
+ this._profiles.sort((a, b) => b.priority - a.priority);
+ }
+
+ // This method selects an effect profile matching the current circumstances. Then a
+ // random effect from its enabled effects will be selected. It returns null if no
+ // profile is currently applicable.
+ _chooseEffect(actor, forOpening) {
+
+ // For now, we only add effects to normal windows and dialog windows.
+ const isNormalWindow = actor.meta_window.window_type == Meta.WindowType.NORMAL;
+ const isDialogWindow =
+ actor.meta_window.window_type == Meta.WindowType.MODAL_DIALOG ||
+ actor.meta_window.window_type == Meta.WindowType.DIALOG;
+
+ if (!isNormalWindow && !isDialogWindow) {
+ return null;
+ }
+
+ // ----------------------------------------------- choose a profile and then an effect
+
+ // Usually, we use the effect profile with the highest priority which matches the
+ // current circumstances. From this profile, we choose a random effect. However, if an
+ // effect is to be previewed, we choose the currently edited profile regardless of the
+ // circumstances.
+ let profile = null;
+ let effect = null;
+
+ // Hence, we first check if an effect is to be previewed.
+ const previewNick = this._settings.get_string('preview-effect');
+
+ if (previewNick != '') {
+ const activeProfile = this._settings.get_string('active-profile');
+ effect =
+ this._ALL_EFFECTS.find(effect => effect.constructor.getNick() == previewNick);
+ profile = this._profiles.find(p => p.path == activeProfile);
+
+ // Only preview the effect until the preview window is closed.
+ if (!profile || !forOpening) {
+ this._settings.set_string('preview-effect', '');
+ }
+ }
+ // If no effect is previewed, we use the effect profile with the highest priority
+ // which matches the current circumstances. From this profile, we choose a random
+ // effect.
+ else {
+
+ // These numbers match the indices in the Gtk.StringLists defined in the UI files
+ // (e.g. resources/ui/adw/prefs.ui).
+ const animationType = forOpening ? 1 : 2;
+ const windowType = isNormalWindow ? 1 : 2;
+ const powerMode = this._upowerProxy.OnBattery ? 1 : 2;
+
+ // Get the first profile whose constraints match the circumstances. The list is
+ // sorted by priority, so we are good to take the first match.
+ profile = this._profiles.find(p => {
+ const profileApp = p.settings.get_string('profile-app');
+ const profileAnimationType = p.settings.get_int('profile-animation-type');
+ const profileWindowType = p.settings.get_int('profile-window-type');
+ const profilePowerMode = p.settings.get_int('profile-power-mode');
+ const profileColorScheme = p.settings.get_int('profile-color-scheme');
+ const profilePowerProfile = p.settings.get_int('profile-power-profile');
+
+ // First we check whether the animation type, window type, and power mode are
+ // matching.
+ let matches =
+ (profileAnimationType == 0 || profileAnimationType == animationType) &&
+ (profileWindowType == 0 || profileWindowType == windowType) &&
+ (profilePowerMode == 0 || profilePowerMode == powerMode);
+
+ // If that was the case, we also check the application name.
+ if (matches && profileApp != '') {
+ const wmClass = actor.meta_window.get_wm_class();
+
+ if (wmClass) {
+ const app = wmClass.toLowerCase();
+
+ // Split app names at |, remove any whitespace, and transform to lower case.
+ const profileApps =
+ profileApp.split('|').map(item => item.trim().toLowerCase());
+ matches = profileApps.includes(app);
+ } else {
+ matches = false;
+ }
+ }
+
+ // If the profile is still matching, we also check the color scheme.
+ if (matches && profileColorScheme != 0) {
+ const colorScheme = this._shellSettings.get_string('color-scheme');
+ matches &= (profileColorScheme == 1 && colorScheme == 'default') ||
+ (profileColorScheme == 2 && colorScheme == 'prefer-dark');
+ }
+
+ // Finally, we may also have to check the power profile.
+ if (matches && profilePowerProfile != 0 && this._powerProfilesProxy) {
+ const powerProfile = this._powerProfilesProxy.ActiveProfile;
+
+ // To understand the numbers, please refer to the indices in the Gtk.StringList
+ // of the profile-power-profile Adw.ComboRow in resources/ui/adw/prefs.ui.
+ if (powerProfile == 'power-saver') {
+ matches &= profilePowerProfile == 1 || profilePowerProfile == 4;
+ } else if (powerProfile == 'balanced') {
+ matches &= profilePowerProfile == 2 || profilePowerProfile == 4 ||
+ profilePowerProfile == 5;
+ } else {
+ matches &= profilePowerProfile == 3 || profilePowerProfile == 5;
+ }
+ }
+
+ return matches;
+ });
+
+ // If we found a matching profile, choose a random effect from it.
+ if (profile) {
+
+ // Create a list of all enabled effects of this profile.
+ const enabled = this._ALL_EFFECTS.filter(effect => {
+ return profile.settings.get_boolean(
+ `${effect.constructor.getNick()}-enable-effect`);
+ });
+
+ // And then choose a random effect.
+ if (enabled.length > 0) {
+ effect = enabled[Math.floor(Math.random() * enabled.length)];
+ }
+ }
+ }
+
+ // If nothing was enabled, we have to do nothing :)
+ if (!effect || !profile) {
+ return null;
+ }
+
+ return {effect: effect, profile: profile};
+ }
+
+ // This method adds the given effect using the settings from the given profile to the
+ // given actor.
+ _setupEffect(actor, forOpening, effect, profile) {
+
+ // There is the weird case where an animation is already ongoing. This happens when a
+ // window is closed which has been created before the session was started (e.g. when
+ // GNOME Shell has been restarted in the meantime).
+ const oldShader = actor.get_effect('burn-my-windows-effect');
+ if (oldShader) {
+ oldShader.endAnimation();
+ }
+
+ // If we are currently performing integration test, all animations are set to a fixed
+ // duration and show a fixed frame from the middle of the animation.
+ const testMode = this._settings.get_boolean('test-mode');
+
+ // The following is used to tweak the ongoing transitions of a window actor. Usually
+ // windows are faded in / out scaled up / down slightly by GNOME Shell. Here, we tweak
+ // the transitions so that nothing changes. The window stays opaque and is scaled to
+ // actorScale.
+ const actorScale =
+ effect.constructor.getActorScale(profile.settings, forOpening, actor);
+
+ // All scaling is relative to the window's center.
+ actor.set_pivot_point(0.5, 0.5);
+ actor.opacity = 255;
+ actor.scale_x = actorScale.x;
+ actor.scale_y = actorScale.y;
+
+ // If we are in the overview, we have to enlarge the window's clone as well. We also
+ // disable the clone's overlay (e.g. its icon, name, and close button) during the
+ // animation.
+ if (actor._bmwOverviewClone) {
+ actor._bmwOverviewClone.overlayEnabled = false;
+ actor._bmwOverviewCloneContainer.set_pivot_point(0.5, 0.5);
+ actor._bmwOverviewCloneContainer.scale_x = actorScale.x;
+ actor._bmwOverviewCloneContainer.scale_y = actorScale.y;
+ }
+
+ // Now add a cool shader to our window actor!
+ const shader = effect.shaderFactory.getShader();
+ actor.add_effect_with_name('burn-my-windows-effect', shader);
+
+ // At the end of the animation, we restore the scale of the overview clone (if any)
+ // and call the methods which would have been called by the original ease() calls at
+ // the end of the standard fade-in animation.
+ const endID = shader.connect('end-animation', () => {
+ shader.disconnect(endID);
+
+ if (actor._bmwOverviewClone) {
+ actor._bmwOverviewClone.overlayEnabled = true;
+ actor._bmwOverviewCloneContainer.scale_x = 1.0;
+ actor._bmwOverviewCloneContainer.scale_y = 1.0;
+ }
+
+ // Restore the original scale of the window actor.
+ actor.scale_x = 1.0;
+ actor.scale_y = 1.0;
+
+ // Remove the shader and mark it being re-usable for future animations.
+ actor.remove_effect(shader);
+ shader.returnToFactory();
+
+ // Finally, once the animation is done or interrupted, we call the methods which
+ // should have been called by the original ease() methods.
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1487
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1558.
+ if (forOpening) {
+ Main.wm._mapWindowDone(global.window_manager, actor);
+ } else {
+ Main.wm._destroyWindowDone(global.window_manager, actor);
+ }
+ });
+
+ // To make things deterministic during testing, we set the effect duration to 8
+ // seconds.
+ const duration = testMode ?
+ 8000 :
+ profile.settings.get_int(effect.constructor.getNick() + '-animation-time');
+
+ // Finally start the animation!
+ shader.beginAnimation(profile.settings, forOpening, testMode, duration, actor);
+ }
+
+ // This is required to enable window-close animations in the overview. See the comment
+ // for Workspace.prototype._windowRemoved above for an explanation.
+ _shouldDestroy(workspace, metaWindow) {
+ const index = workspace._lookupIndex(metaWindow);
+ if (index == -1) {
+ return true;
+ }
+
+ const actor = workspace._windows[index]._windowActor;
+ const shader = actor.get_effect('burn-my-windows-effect');
+
+ return shader == null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ar/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ar/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..6e95eca
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ar/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/az/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/az/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..891ad55
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/az/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/cs/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/cs/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..b8a86f8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/cs/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/da/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/da/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..df5adac
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/da/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/de/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/de/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..aef1fa2
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/de/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/el/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/el/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..8bbafe8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/el/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/eo/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/eo/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..b6378a0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/eo/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/es/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/es/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..6f5075f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/es/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/et/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/et/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..0c0acf3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/et/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fa/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fa/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..7774041
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fa/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fi/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fi/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..2cf5b4f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fi/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fr/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fr/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..8b6fb43
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/fr/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/he/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/he/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..987bdfd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/he/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hi/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hi/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..cbef7b2
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hi/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hu/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hu/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..f9792e4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/hu/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/id/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/id/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..c2a931c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/id/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/it/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/it/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..36fc404
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/it/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ja/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ja/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..f5565f1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ja/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/kab/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/kab/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..b4578fb
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/kab/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ko/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ko/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..18211c9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ko/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/lt/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/lt/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..f982815
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/lt/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nb_NO/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nb_NO/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..0a37a11
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nb_NO/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nl/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nl/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..2c51c6c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/nl/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ota/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ota/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..dbc336f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ota/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pl/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pl/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..a5700c1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pl/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..96ec9fe
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt_BR/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt_BR/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..30f375e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/pt_BR/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ro/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ro/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..11c5392
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ro/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ru/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ru/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..2fb0e3c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ru/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sk/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sk/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..61f221a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sk/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sv/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sv/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..a287302
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/sv/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ta/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ta/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..43fe2f7
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/ta/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/tr/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/tr/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..05ff048
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/tr/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/uk/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/uk/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..9d84a3f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/uk/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hans/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hans/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..8fdcc07
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hans/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hant/LC_MESSAGES/burn-my-windows.mo b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hant/LC_MESSAGES/burn-my-windows.mo
new file mode 100644
index 0000000..7c22512
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/locale/zh_Hant/LC_MESSAGES/burn-my-windows.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/metadata.json
new file mode 100644
index 0000000..57af505
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/metadata.json
@@ -0,0 +1,23 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Disintegrate your windows with style.",
+ "donations": {
+ "github": "schneegans",
+ "kofi": "schneegans",
+ "paypal": "simonschneegans"
+ },
+ "gettext-domain": "burn-my-windows",
+ "name": "Burn My Windows",
+ "settings-schema": "org.gnome.shell.extensions.burn-my-windows",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/Schneegans/Burn-My-Windows",
+ "uuid": "burn-my-windows@schneegans.github.com",
+ "version": 48
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/prefs.js
new file mode 100644
index 0000000..8476964
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/prefs.js
@@ -0,0 +1,941 @@
+//////////////////////////////////////////////////////////////////////////////////////////
+// ) ( //
+// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
+// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
+// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
+// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
+// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
+// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
+// |__/ //
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// SPDX-FileCopyrightText: Simon Schneegans <code@simonschneegans.de>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+'use strict';
+
+import Gio from 'gi://Gio';
+import Gtk from 'gi://Gtk';
+import GLib from 'gi://GLib';
+import Gdk from 'gi://Gdk';
+import Adw from 'gi://Adw';
+
+import * as utils from './src/utils.js';
+import {ProfileManager} from './src/ProfileManager.js';
+
+import Apparition from './src/effects/Apparition.js';
+import AuraGlow from './src/effects/AuraGlow.js';
+import BrokenGlass from './src/effects/BrokenGlass.js';
+import Doom from './src/effects/Doom.js';
+import EnergizeA from './src/effects/EnergizeA.js';
+import EnergizeB from './src/effects/EnergizeB.js';
+import Fire from './src/effects/Fire.js';
+import Focus from './src/effects/Focus.js';
+import Glide from './src/effects/Glide.js';
+import Glitch from './src/effects/Glitch.js';
+import Hexagon from './src/effects/Hexagon.js';
+import Incinerate from './src/effects/Incinerate.js';
+import Matrix from './src/effects/Matrix.js';
+import Mushroom from './src/effects/Mushroom.js';
+import PaintBrush from './src/effects/PaintBrush.js';
+import Pixelate from './src/effects/Pixelate.js';
+import PixelWheel from './src/effects/PixelWheel.js';
+import PixelWipe from './src/effects/PixelWipe.js';
+import Portal from './src/effects/Portal.js';
+import RGBWarp from './src/effects/RGBWarp.js';
+import SnapOfDisintegration from './src/effects/SnapOfDisintegration.js';
+import TeamRocket from './src/effects/TeamRocket.js';
+import TRexAttack from './src/effects/TRexAttack.js';
+import TVEffect from './src/effects/TVEffect.js';
+import TVGlitch from './src/effects/TVGlitch.js';
+import Wisps from './src/effects/Wisps.js';
+
+
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+// Currently, the extension supports only one set of UI files. In the past, there were
+// three different sets for GTK3, GTK4, and Adwaita. This method returns the name of the
+// current UI directory. It's still here since it might be useful in the future.
+function getUIDir() {
+ return 'adw';
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// The preferences dialog is organized in pages, each of which is loaded from a //
+// separate ui file. There's one page with general options, all other paged are loaded //
+// from the respective effects. //
+//////////////////////////////////////////////////////////////////////////////////////////
+
+export default class BurnMyWindowsPreferences extends ExtensionPreferences {
+
+ // ------------------------------------------------------------ constructor / destructor
+
+ fillPreferencesWindow(window) {
+ window.set_default_size(650, 750);
+
+ // New effects must be registered here and in extension.js.
+ this._ALL_EFFECTS = [
+ Apparition, AuraGlow, BrokenGlass,
+ Doom, EnergizeA, EnergizeB,
+ Fire, Focus, Glide,
+ Glitch, Hexagon, Incinerate,
+ Matrix, Mushroom, PaintBrush,
+ Pixelate, PixelWheel, PixelWipe,
+ Portal, RGBWarp, SnapOfDisintegration,
+ TeamRocket, TRexAttack, TVEffect,
+ TVGlitch, Wisps,
+ ];
+
+
+ // Load all of our resources.
+ this._resources =
+ Gio.Resource.load(this.path + '/resources/burn-my-windows.gresource');
+ Gio.resources_register(this._resources);
+
+ // Make sure custom icons are found.
+ Gtk.IconTheme.get_for_display(Gdk.Display.get_default()).add_resource_path('/img');
+
+ // Load the general user interface files.
+ this._builder = new Gtk.Builder();
+ this._builder.add_from_resource(`/ui/common/menus.ui`);
+ this._builder.add_from_resource(`/ui/${getUIDir()}/prefs.ui`);
+
+
+ // Store a reference to the general settings object.
+ this._settings = this.getSettings();
+
+ // This tracks all available effect profiles.
+ this._profileManager = new ProfileManager(this.metadata);
+
+ // This is used to track all connections to properties of the current effect profile.
+ // Whenever the current effect profile changes, all connections are disconnected.
+ this._profileConnections = [];
+
+ // Load the current effect profile. If there is none, we create a default profile.
+ this._settings.connect('changed::active-profile', () => {
+ this._loadActiveProfile();
+ });
+
+ // Check whether the power profiles daemon is available - if not, we hide the
+ // corresponding settings row.
+ let hasPowerProfiles = false;
+ try {
+ const PowerProfilesProxy = Gio.DBusProxy.makeProxyWrapper(
+ utils.getStringResource('/interfaces/net.hadess.PowerProfiles.xml'));
+ let powerProfilesProxy = new PowerProfilesProxy(
+ Gio.DBus.system, 'net.hadess.PowerProfiles', '/net/hadess/PowerProfiles');
+
+ hasPowerProfiles = powerProfilesProxy.get_name_owner() != null;
+ } catch (e) {
+ // Maybe the service is masked...
+ }
+
+
+ let powerProfileRow = this._builder.get_object('profile-power-profile');
+ powerProfileRow.set_visible(hasPowerProfiles);
+
+ // Wire up the window picker of the profile editor. Whenever the app-chooser button is
+ // clicked, we call a method of the extension which will initiate the window picking.
+ this._builder.get_object('profile-choose-app-button').connect('clicked', () => {
+ Gio.DBus.session.call('org.gnome.Shell',
+ '/org/gnome/shell/extensions/BurnMyWindows',
+ 'org.gnome.shell.extensions.BurnMyWindows', 'PickWindow',
+ null, null, Gio.DBusCallFlags.NO_AUTO_START, -1, null, null);
+ });
+
+ // If the window picking was successful, this D-Bus signal will be emitted. If a
+ // window was picked, we add it to the list of window name.
+ this._dbusConnection = Gio.DBus.session.signal_subscribe(
+ 'org.gnome.Shell', 'org.gnome.shell.extensions.BurnMyWindows', 'WindowPicked',
+ '/org/gnome/shell/extensions/BurnMyWindows', null, Gio.DBusSignalFlags.NONE,
+ (conn, sender, obj_path, iface, signal, params) => {
+ const val = params.get_child_value(0).get_string()[0];
+ if (val != 'window-not-found') {
+ const entry = this._builder.get_object('profile-app');
+
+ // Split the current value at each |, trim spaces, and remove empty components.
+ let apps = entry.text.split('|').map(item => item.trim()).filter(v => v != '');
+
+ // If the list is empty, we simply use the name of the picked window.
+ if (apps.length == 0) {
+ entry.text = val;
+ } else {
+
+ // Else we append the new application name, remove any duplicates, and sort
+ // the resulting list alphabetically.
+ apps.push(val);
+ apps = [...new Set(apps)].sort();
+ entry.text = apps.join(' | ');
+ }
+ }
+ });
+
+
+ // This is our top-level widget which we will return later.
+ this._widget = this._builder.get_object('general-prefs');
+
+ // Add the functionality to the choose-all and choose-none buttons.
+ this._builder.get_object('choose-all-effects-button').connect('clicked', () => {
+ this._ALL_EFFECTS.forEach(effect => {
+ this.getProfileSettings().set_boolean(`${effect.getNick()}-enable-effect`, true);
+ });
+ });
+
+ this._builder.get_object('choose-no-effects-button').connect('clicked', () => {
+ this._ALL_EFFECTS.forEach(effect => {
+ this.getProfileSettings().set_boolean(`${effect.getNick()}-enable-effect`, false);
+ });
+ });
+
+ // search for effect feature
+ this._searchEntry = this._builder.get_object('search_entry');
+ this._searchEntry.connect('search-changed', () => {
+ const query = this._searchEntry.get_text().toLowerCase();
+
+ // If only one effect is left, we will expand it automatically.
+ let lastRow = null;
+ let numRows = 0;
+
+ this._effectRows.forEach(row => {
+ row.set_expanded(false);
+
+ if (query === '') {
+ row.show(); // Show all effects if query is empty
+ } else {
+ // Show or hide each effect based on query match
+ const showEffect = row.name.toLowerCase().includes(query);
+
+ if (showEffect) {
+ row.show();
+ lastRow = row;
+ numRows++;
+ } else {
+ row.hide();
+ }
+ }
+ });
+
+ // If only one effect is left, we expand it automatically.
+ if (numRows === 1) {
+ lastRow.set_expanded(true);
+ }
+ });
+
+ // Then add a preferences group for the effect expander rows.
+ const group = this._builder.get_object('effects-group');
+
+ // This stores all expander rows for the effects. We use this to implement the
+ // accordion-like behavior of the effect settings.
+ this._effectRows = [];
+
+ // Now add all the rows.
+ this._ALL_EFFECTS.forEach(effect => {
+ const [minMajor, minMinor] = effect.getMinShellVersion();
+ if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
+
+ const uiFile = `/ui/${getUIDir()}/${effect.getNick()}.ui`;
+
+ // Is there a better way to test for the existence of a resource file?
+ let hasPrefs = false;
+ try {
+ Gio.resources_get_info(uiFile, 0);
+ hasPrefs = true;
+ } catch (e) {
+ // Nothing todo, there
+ }
+
+ // Add the settings page to the builder.
+ if (hasPrefs) {
+ this._builder.add_from_resource(uiFile);
+ }
+
+ // The preview button.
+ let previewButton = Gtk.Button.new_from_icon_name('bmw-preview-symbolic');
+ previewButton.get_style_context().add_class('circular');
+ previewButton.get_style_context().add_class('flat');
+ previewButton.set_tooltip_text(_('Preview this effect'));
+ previewButton.set_valign(Gtk.Align.CENTER);
+ previewButton.connect('clicked', () => {
+ this._previewEffect(effect);
+ });
+
+ // The toggle button for enabling and disabling the effect.
+ const button = Gtk.Switch.new();
+ button.set_valign(Gtk.Align.CENTER);
+ this._builder.expose_object(`${effect.getNick()}-enable-effect`, button);
+
+ let row;
+ if (hasPrefs) {
+
+ // Add the effect's preferences (if any).
+ row = this._builder.get_object(`${effect.getNick()}-prefs`);
+ } else {
+ row = Adw.ActionRow.new();
+ }
+
+ // On older versions of Adw (e.g. on GNOME Shell <43), the set_use_markup() does
+ // not yet exist.
+ if (row.set_use_markup) {
+ row.set_title('<b>' + effect.getLabel() + '</b>');
+ row.set_use_markup(true);
+ } else {
+ row.set_title(effect.getLabel());
+ }
+
+ // this is the fix for the merge issue when an effect doesn't have a discription
+ row.name = effect.getLabel();
+
+
+ // Un-expand any previously expanded effect row. This way we ensure that there
+ // is only one expanded row at any time.
+ if (hasPrefs) {
+ row.connect('notify::expanded', currentRow => {
+ if (currentRow.get_expanded()) {
+ this._effectRows.forEach(row => {
+ if (row != currentRow) {
+ row.set_expanded(false);
+ }
+ });
+ }
+ });
+ this._effectRows.push(row);
+ row.add_action(button);
+ } else {
+ row.add_suffix(button);
+ }
+
+ row.add_prefix(previewButton);
+
+ group.add(row);
+ }
+ });
+
+
+ // Some things can only be done once the widget is shown as we do not have access to
+ // the toplevel widget before.
+ this._widget.connect('realize', (widget) => {
+ const window = widget.get_root();
+
+ // Show the version number in the title bar.
+ window.set_title(`Burn-My-Windows ${this.metadata.version}`);
+
+ this._loadActiveProfile();
+ this._updateProfileMenu();
+
+ // Show an Adw.Toast or a Gtk.InfoBar whenever Burn-My-Windows was updated. We use a
+ // small timeout so that it is not shown instantaneously.
+ const lastVersion = this._settings.get_int('last-prefs-version');
+ if (lastVersion < this.metadata.version) {
+ this._showUpdateInfoTimeout =
+ GLib.timeout_add(GLib.PRIORITY_DEFAULT, 2000, () => {
+ this._showUpdateInfoTimeout = 0;
+
+ const toast = Adw.Toast.new(_('Burn-My-Windows has been updated!'));
+ toast.set_button_label(_('View Changelog'));
+ toast.set_action_name('prefs.changelog');
+ toast.set_timeout(0);
+
+ toast.connect(
+ 'dismissed',
+ () => this._settings.set_int('last-prefs-version', this.metadata.version));
+
+ window.add_toast(toast);
+
+ return false;
+ });
+ }
+
+ // Count the number of times the user has opened the preferences window. Every now
+ // and then, we show a dialog asking the user to support the extension. We connect
+ // to the notify::visible signal to ensure that the dialog can be a modal dialog.
+ window.connect('notify::visible', (window) => {
+ // Do not show the dialog when the window is hidden.
+ if (!window.get_visible()) {
+ return;
+ }
+
+ // Do not show the dialog when the user has disabled it.
+ if (!this._settings.get_boolean('show-support-dialog')) {
+ return;
+ }
+
+ const count = this._settings.get_int('prefs-open-count') + 1;
+ this._settings.set_int('prefs-open-count', count);
+
+ // Show the dialog every 10th time.
+ if (count % 10 == 0) {
+ const dialog = this._createMessageDialog(
+ '❤️ Do you love Burn-My-Windows?',
+ `Even the smallest donation can have a big impact! If just one of ten Burn-My-Windows users donated $1 per month, I could dedicate myself to creating awesome open-source projects full-time!
+
+Ko-fi: <a href='https://ko-fi.com/schneegans'>https://ko-fi.com/schneegans</a>
+GitHub: <a href='https://github.com/sponsors/schneegans'>https://github.com/sponsors/schneegans</a>`,
+ window, [
+ {
+ label: 'Do not show this again!',
+ destructive: true,
+ default: false,
+ action: () => {
+ this._settings.set_boolean('show-support-dialog', false);
+ }
+ },
+ {
+ label: 'Remind me later.',
+ destructive: false,
+ default: true,
+ }
+ ]);
+
+ dialog.show();
+ }
+ });
+
+ // Populate the menu with actions.
+ const group = Gio.SimpleActionGroup.new();
+ window.insert_action_group('prefs', group);
+
+ // Add the main menu to the title bar.
+ {
+ // Add the menu button to the title bar.
+ const menu = this._builder.get_object('menu-button');
+
+ // Starting with GNOME Shell 42, we have to hack our way through the widget tree
+ // of the Adw.PreferencesWindow...
+ const header = this._findWidgetByType(window.get_content(), Adw.HeaderBar);
+ header.pack_start(menu);
+ header.set_title_widget(this._builder.get_object('profile-button'));
+
+ // GNOME Shell extensions are forced to use a AdwPreferencesWindow. This already
+ // includes a Gtk.ScrolledWindow as well as an Adw.Clamp. For the profile
+ // editing, we want to use an Adw.Flap which reveals itself from the top. This
+ // needs to be inserted in the widget hierarchy above the Adw.Clamp, else it
+ // would look ugly. Therefore, we fiddle around with the internal widgets...
+ const flap = this._builder.get_object('profile-editor-flap');
+ const clamp = this._findWidgetByType(window.get_content(), Adw.Clamp);
+ const viewport = clamp.get_parent();
+ viewport.get_parent().set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER);
+ viewport.set_child(flap);
+
+ const scrolledWindow = Gtk.ScrolledWindow.new();
+ scrolledWindow.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+ scrolledWindow.set_propagate_natural_height(true);
+ scrolledWindow.set_vexpand(true);
+ scrolledWindow.set_child(clamp);
+
+ flap.set_content(scrolledWindow);
+
+ const addURIAction = (name, uri) => {
+ const action = Gio.SimpleAction.new(name, null);
+ action.connect('activate', () => Gtk.show_uri(null, uri, Gdk.CURRENT_TIME));
+ group.add_action(action);
+ };
+
+ // clang-format off
+ addURIAction('homepage', 'https://github.com/Schneegans/Burn-My-Windows');
+ addURIAction('bugs', 'https://github.com/Schneegans/Burn-My-Windows/issues');
+ addURIAction('new-effect', 'https://github.com/Schneegans/Burn-My-Windows/blob/main/docs/how-to-create-new-effects.md');
+ addURIAction('translate', 'https://hosted.weblate.org/engage/burn-my-windows/');
+ addURIAction('donate-kofi', 'https://ko-fi.com/schneegans');
+ addURIAction('donate-github', 'https://github.com/sponsors/Schneegans');
+ addURIAction('donate-paypal', 'https://www.paypal.me/simonschneegans');
+ addURIAction('donate-crypto', 'https://schneegans.cb.id');
+ addURIAction('show-sponsors', 'https://schneegans.github.io/sponsors');
+ addURIAction('wallpapers', 'https://github.com/Schneegans/ai-wallpapers');
+ addURIAction('profile-dir', `file://${GLib.get_user_config_dir()}/burn-my-windows/profiles`);
+ // clang-format on
+
+ // The changelog action is a bit different, as it may get activated when the
+ // view-changelog button is pressed which is shown in an Adw.Toast or a
+ // Gtk.InfoBar whenever Burn-My-Windows gets updated. To ensure that the
+ // notification is shown only once, we have to store the current version of
+ // Burn-My-Windows in the settings.
+ const changelogAction = Gio.SimpleAction.new('changelog', null);
+ changelogAction.connect('activate', () => {
+ Gtk.show_uri(
+ null,
+ 'https://github.com/Schneegans/Burn-My-Windows/blob/main/docs/changelog.md',
+ Gdk.CURRENT_TIME);
+ this._settings.set_int('last-prefs-version', this.metadata.version)
+ });
+ group.add_action(changelogAction);
+
+ // Add the about dialog.
+ const aboutAction = Gio.SimpleAction.new('about', null);
+ aboutAction.connect('activate', () => {
+ // The JSON report format from weblate is a bit weird. Here we extract all
+ // unique names from the translation report.
+ const translators = new Set();
+ this._getJSONResource('/credits/translators.json').forEach(i => {
+ for (const j of Object.values(i)) {
+ j.forEach(k => translators.add(k.full_name));
+ }
+ });
+
+ const dialog = new Adw.AboutWindow({transient_for: window, modal: true});
+ dialog.set_application_icon('burn-my-windows-symbolic');
+ dialog.set_application_name('Burn-My-Windows');
+ dialog.set_version(`${this.metadata.version}`);
+ dialog.set_developer_name('Simon Schneegans');
+ dialog.set_issue_url('https://github.com/Schneegans/Burn-My-Windows/issues');
+ dialog.set_translator_credits([...translators].join('\n'));
+ dialog.set_copyright('© 2023 Simon Schneegans');
+ dialog.set_website('https://github.com/Schneegans/Burn-My-Windows');
+ dialog.set_license_type(Gtk.License.GPL_3_0);
+
+ dialog.show();
+ });
+
+ group.add_action(aboutAction);
+ }
+
+ // Setup all the profile-related actions.
+ {
+ // Allow switching the current profile.
+ const activeProfileAction = this._settings.create_action('active-profile');
+ group.add_action(activeProfileAction);
+
+ // This action creates a new profile and makes it the active one.
+ const newProfileAction = Gio.SimpleAction.new('profile-new', null);
+ newProfileAction.connect('activate', () => {
+ const profile = this._profileManager.createProfile();
+ this._settings.set_string('active-profile', profile.path);
+
+ // Start editing the profile.
+ this._builder.get_object('edit-profile-button').active = true;
+
+ // The number of profiles has changed, so we have to update the profile
+ // selection menu.
+ this._updateProfileMenu();
+ });
+ group.add_action(newProfileAction);
+
+ // This action shows a confirmation dialog. Upon user approval, the currently
+ // active profile is deleted. We use the more beautiful Adw.MessageDialog if it is
+ // available (usually on GNOME 43 and beyond).
+ const deleteProfileDialog = this._createMessageDialog(
+ _('Delete this Profile?'),
+ _('The current effect profile with all its effect settings will be permanently lost.'),
+ window, [
+ {
+ label: _('Cancel'),
+ destructive: false,
+ default: true,
+ },
+ {
+ label: _('Delete'),
+ destructive: true,
+ default: false,
+ action: () => {
+ // Stop editing the current profile.
+ this._builder.get_object('edit-profile-button').active = false;
+
+ // Delete the currently active profile.
+ this._profileManager.deleteProfile(this._activeProfile.path);
+
+ // Select another one.
+ this._settings.set_string('active-profile',
+ this._profileManager.getProfiles()[0].path);
+
+ // The number of profiles has changed, so we have to update the profile
+ // selection menu.
+ this._updateProfileMenu();
+ }
+ }
+ ]);
+
+ // This action deletes the currently active profile.
+ const deleteProfileAction = Gio.SimpleAction.new('profile-delete', null);
+ deleteProfileAction.connect('activate', () => {
+ deleteProfileDialog.show();
+ });
+ group.add_action(deleteProfileAction);
+ }
+ });
+
+ // As we do not have something like a destructor, we just listen for the destroy
+ // signal of our main widget.
+ this._widget.connect('destroy', () => {
+ // Unregister our resources.
+ Gio.resources_unregister(this._resources);
+
+ // Disconnect from the window-picker API.
+ Gio.DBus.session.signal_unsubscribe(this._dbusConnection);
+
+ // If the settings dialog got closed too quickly, this may not have been shown.
+ if (this._showUpdateInfoTimeout > 0) {
+ GLib.source_remove(this._showUpdateInfoTimeout);
+ }
+ });
+
+ window.add(this._widget);
+ }
+
+ // -------------------------------------------------------------------- public interface
+
+ // Returns the internally used Gtk.Builder. Effects can use this to modify the UI of the
+ // preferences dialog.
+ getBuilder() {
+ return this._builder;
+ }
+
+ // Returns a Gio.Settings object for the current effect profile.
+ getProfileSettings() {
+ return this._activeProfile.settings;
+ }
+
+ // Connects an Adw.ComboRow (or anything else which has an 'selected' property) to a
+ // settings key. It also binds the corresponding reset button.
+ bindComboRow(settingsKey) {
+ this._bind(settingsKey, 'selected');
+ }
+
+ // Connects a Gtk.ComboBox (or anything else which has an 'active' property) to a
+ // settings key. It also binds the corresponding reset button.
+ bindComboBox(settingsKey) {
+ this._bind(settingsKey, 'active');
+ }
+
+ // Connects a Gtk.Entry (or anything else which has an 'text' property) to a
+ // settings key. It also binds the corresponding reset button.
+ bindEntry(settingsKey) {
+ this._bind(settingsKey, 'text');
+ }
+
+ // Connects a Gtk.Adjustment (or anything else which has a 'value' property) to a
+ // settings key. It also binds the corresponding reset button.
+ bindAdjustment(settingsKey) {
+ this._bind(settingsKey, 'value');
+ }
+
+ // Connects a Gtk.Switch (or anything else which has an 'active' property) to a settings
+ // key. It also binds the corresponding reset button.
+ bindSwitch(settingsKey) {
+ this._bind(settingsKey, 'active');
+ }
+
+ // Colors are stored as strings like 'rgb(1, 0.5, 0)'. As Gio.Settings.bind_with_mapping
+ // is not available yet, we need to do the color conversion manually. It also binds the
+ // corresponding reset button.
+ bindColorButton(settingsKey) {
+
+ const button = this._builder.get_object(settingsKey);
+
+ if (button) {
+
+ // Update the settings when the color is modified.
+ if (!button._isConnected) {
+ button.connect('color-set', () => {
+ this.getProfileSettings().set_string(settingsKey,
+ button.get_rgba().to_string());
+ });
+
+ button._isConnected = true;
+ }
+
+ // Update the button state when the settings change.
+ const settingSignalHandler = () => {
+ const rgba = new Gdk.RGBA();
+ rgba.parse(this.getProfileSettings().get_string(settingsKey));
+ button.rgba = rgba;
+ };
+
+ this._connectProfileSetting('changed::' + settingsKey, settingSignalHandler);
+
+ // Initialize the button with the state in the settings.
+ settingSignalHandler();
+ }
+
+ this._bindResetButton(settingsKey);
+ }
+
+ // ----------------------------------------------------------------------- private stuff
+
+ // This loads all settings from the profile given in the settings key 'active-profile'.
+ _loadActiveProfile() {
+
+ // Disconnect any connection to the previously active profile.
+ this._disconnectProfileSettings();
+
+ // Find the active profile in the list of all profiles.
+ const allProfiles = this._profileManager.getProfiles();
+ const path = this._settings.get_string('active-profile');
+ this._activeProfile = allProfiles.find(p => p.path == path);
+
+ // If, for whatever reason, it is not found, select the first one. Setting the
+ // settings key will trigger this method again.
+ if (!this._activeProfile) {
+ this._activeProfile = allProfiles[0];
+ this._settings.set_string('active-profile', this._activeProfile.path);
+ return;
+ }
+
+ // Connect all profile options.
+ const updateProfileButtonMenuIfChanged = (settingsKey) => {
+ this._connectProfileSetting('changed::' + settingsKey, () => {
+ this._updateProfileButton();
+ this._updateProfileMenu();
+ });
+ };
+
+ updateProfileButtonMenuIfChanged('profile-app');
+ updateProfileButtonMenuIfChanged('profile-animation-type');
+ updateProfileButtonMenuIfChanged('profile-window-type');
+ updateProfileButtonMenuIfChanged('profile-color-scheme');
+ updateProfileButtonMenuIfChanged('profile-power-mode');
+ updateProfileButtonMenuIfChanged('profile-power-profile');
+ updateProfileButtonMenuIfChanged('profile-high-priority');
+
+ this.bindEntry('profile-app');
+ this.bindComboRow('profile-animation-type');
+ this.bindComboRow('profile-window-type');
+ this.bindComboRow('profile-color-scheme');
+ this.bindComboRow('profile-power-mode');
+ this.bindComboRow('profile-power-profile');
+ this.bindSwitch('profile-high-priority');
+
+ // Connect all effect settings.
+ this._ALL_EFFECTS.forEach(effect => {
+ const [minMajor, minMinor] = effect.getMinShellVersion();
+ if (utils.shellVersionIsAtLeast(minMajor, minMinor)) {
+ this.bindSwitch(`${effect.getNick()}-enable-effect`);
+ effect.bindPreferences(this);
+ }
+ });
+
+ // Update the label in the profile-select button.
+ this._updateProfileButton();
+ }
+
+ // This updates the label of the profile-selection button according to the currently
+ // active profile. It's called whenever the active profile is changed or whenever a
+ // configuration option of the profile is changed.
+ _updateProfileButton() {
+ this._builder.get_object('choose-profile-button').label =
+ this._getProfileName(this.getProfileSettings());
+ }
+
+ // This updates the list of available profiles in the dropdown of the profile-selection
+ // button. It's called whenever one of the configuration option of the currently active
+ // profile is changed, or whenever a profile is added / removed.
+ _updateProfileMenu() {
+
+ // First, clear the menu section.
+ const profileSection = this._builder.get_object('profile-section');
+ profileSection.remove_all();
+
+ // Then add an entry for each available profile.
+ this._profileManager.getProfiles().forEach(profile => {
+ const label = this._getProfileName(profile.settings);
+ const item = Gio.MenuItem.new(label, 'prefs.active-profile');
+ item.set_action_and_target_value('prefs.active-profile',
+ GLib.Variant.new_string(profile.path));
+ profileSection.append_item(item);
+ });
+ }
+
+ // Profiles are named according to their configuration. This method returns a localized
+ // string describing the settings of the profile.
+ _getProfileName(settings) {
+ let items = [];
+
+ // If an app is configured, use it as first component for the profile's name.
+ const app = settings.get_string('profile-app');
+ if (app != '') {
+ items.push(app);
+ }
+
+ // Now add components to the name for each non-default profile option. Make sure that
+ // these are the same strings as used in the UI files!
+ const addComponent = (settingsKey, options) => {
+ const option = settings.get_int(settingsKey);
+ if (option > 0) {
+ items.push(options[option - 1]);
+ }
+ };
+
+ // clang-format off
+ addComponent('profile-animation-type', [_('Opening Windows'),
+ _('Closing Windows')]);
+ addComponent('profile-window-type', [_('Normal Windows'),
+ _('Dialog Windows')]);
+ addComponent('profile-color-scheme', [_('Default Color Scheme'),
+ _('Dark Color Scheme')]);
+ addComponent('profile-power-mode', [_('On Battery'),
+ _('Plugged In')]);
+ addComponent('profile-power-profile', [_('Power-Saver Mode'),
+ _('Balanced Mode'),
+ _('Performance Mode'),
+ _('Power Saver or Balanced'),
+ _('Balanced or Performance')]);
+ // clang-format on
+
+ // If the profile is a high-priority profile, also add this..
+ if (settings.get_boolean('profile-high-priority')) {
+ items.push(_('High Priority'));
+ }
+
+ let name = '';
+
+ if (items.length == 0) {
+ name = _('Standard Profile');
+ } else {
+ name = items.join(' · ');
+ }
+
+ return name;
+ }
+
+ // This wrapper connects a given callback to a profile settings key. Use this instead of
+ // the direct connect() call on the profile settings to make sure that the callback is
+ // disconnected when the active profile changes.
+ _connectProfileSetting(key, callback) {
+ this._profileConnections.push(this.getProfileSettings().connect(key, callback));
+ }
+
+ // This is called whenever the currently edited effect profile changes. It unbinds all
+ // callbacks bound with the above method before.
+ _disconnectProfileSettings() {
+ this._profileConnections.forEach(c => this.getProfileSettings().disconnect(c));
+ this._profileConnections = [];
+ }
+
+ // Searches for a reset button for the given settings key and make it reset the settings
+ // key when clicked.
+ _bindResetButton(settingsKey) {
+ const resetButton = this._builder.get_object('reset-' + settingsKey);
+ if (resetButton && !resetButton._isConnected) {
+ resetButton.connect('clicked', () => {
+ this.getProfileSettings().reset(settingsKey);
+ });
+ resetButton._isConnected = true;
+ }
+ }
+
+ // Connects any widget's property to a settings key. The widget must have the same ID as
+ // the settings key. It also binds the corresponding reset button.
+ _bind(settingsKey, property) {
+ const object = this._builder.get_object(settingsKey);
+
+ if (object) {
+ // Unbind any previous binding (potentially to a previously edited profile).
+ Gio.Settings.unbind(object, property);
+ this.getProfileSettings().bind(settingsKey, object, property,
+ Gio.SettingsBindFlags.DEFAULT);
+ }
+
+ this._bindResetButton(settingsKey);
+ }
+
+ // Reads the contents of a JSON file contained in the global resources archive. The data
+ // is parsed and returned as a JavaScript object / array.
+ _getJSONResource(path) {
+ return JSON.parse(utils.getStringResource(path));
+ }
+
+ // This traverses the widget tree below the given parent recursively and returns the
+ // first widget of the given type.
+ _findWidgetByType(parent, type) {
+ for (const child of [...parent]) {
+ if (child instanceof type) return child;
+
+ const match = this._findWidgetByType(child, type);
+ if (match) return match;
+ }
+
+ return null;
+ }
+
+ // Opens a modal window using the given effect.
+ _previewEffect(effect) {
+
+ // Set the to-be-previewed effect.
+ this._settings.set_string('preview-effect', effect.getNick());
+
+ // Make sure that the window.show() firther below "sees" this change.
+ Gio.Settings.sync();
+
+ // Create the preview-window.
+ const window = new Gtk.Window({
+ // Translators: %s will be replaced by the effect's name.
+ title: _('Preview for %s').replace('%s', effect.getLabel()),
+ default_width: 800,
+ default_height: 450,
+ modal: true,
+ transient_for: this._widget.get_root()
+ });
+
+ // Add a header bar to the window.
+ const header = Gtk.HeaderBar.new();
+ window.set_titlebar(header);
+
+ const box = new Gtk.Box({
+ orientation: Gtk.Orientation.VERTICAL,
+ valign: Gtk.Align.CENTER,
+ spacing: 10,
+ margin_start: 50,
+ margin_end: 50
+ });
+
+ const label = Gtk.Label.new(_('Close this Window to Preview the Effect!'));
+ label.wrap = true;
+ label.justify = Gtk.Justification.CENTER;
+ label.get_style_context().add_class('large-title');
+
+ const image = new Gtk.Image({
+ icon_name: 'burn-my-windows-symbolic',
+ pixel_size: 128,
+ });
+
+ box.append(image);
+ box.append(label);
+
+ window.set_child(box);
+ window.show();
+ }
+
+ // Helper function to show a message dialog. The dialog is modal and has a title, a
+ // message and a list of buttons. Each button is an object with a label, a default flag
+ // and a destructive flag. Each button object can also have an "action" callback that is
+ // called when the button is clicked.
+ // This method works on GTK3, GTK4, and libadwaita.
+ _createMessageDialog(title, message, window, buttons) {
+ let dialog = new Adw.MessageDialog({
+ heading: title,
+ body: message,
+ body_use_markup: true,
+ modal: true,
+ });
+
+ buttons.forEach((button, i) => {
+ const response = i.toString();
+ dialog.add_response(response, button.label);
+
+ if (button.default) {
+ dialog.set_default_response(response);
+ dialog.set_close_response(response);
+ }
+
+ if (button.destructive) {
+ dialog.set_response_appearance(response, Adw.ResponseAppearance.DESTRUCTIVE);
+ }
+ });
+
+ dialog.set_hide_on_close(true);
+ dialog.set_transient_for(window);
+
+ // If the dialog is closed or a button is clicked, we hide the dialog and call the
+ // button's action callback if it exists.
+ dialog.connect('response', (dialog, response) => {
+ const i = parseInt(response);
+
+ const button = buttons[i];
+ if (button && button.action) {
+ button.action();
+ }
+
+ dialog.hide();
+ });
+
+ return dialog;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/resources/burn-my-windows.gresource b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/resources/burn-my-windows.gresource
new file mode 100644
index 0000000..b52a7db
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/resources/burn-my-windows.gresource
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/gschemas.compiled
new file mode 100644
index 0000000..5fac657
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml
new file mode 100644
index 0000000..e6b87a4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows-profile.gschema.xml
@@ -0,0 +1,1119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+SPDX-FileCopyrightText: Simon Schneegans <code@simonschneegans.de>
+SPDX-License-Identifier: CC0-1.0
+-->
+
+<schemalist>
+
+ <schema path="/org/gnome/shell/extensions/burn-my-windows-profile/" id="org.gnome.shell.extensions.burn-my-windows-profile">
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Profile Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="profile-app" type="s">
+ <default>""</default>
+ <summary>Profile App</summary>
+ <description>The application name for which the current effect settings should be used.</description>
+ </key>
+
+ <key name="profile-animation-type" type="i">
+ <default>0</default>
+ <range min="0" max="2" />
+ <summary>Profile Animation Type</summary>
+ <description>The animation types for which the current effect settings should be used.</description>
+ </key>
+
+ <key name="profile-window-type" type="i">
+ <default>0</default>
+ <range min="0" max="2" />
+ <summary>Profile Window Type</summary>
+ <description>The window types for which the current effect settings should be used.</description>
+ </key>
+
+ <key name="profile-color-scheme" type="i">
+ <default>0</default>
+ <range min="0" max="2" />
+ <summary>Profile Color Scheme</summary>
+ <description>The desktop color schemes for which the current effect settings should be used.</description>
+ </key>
+
+ <key name="profile-power-mode" type="i">
+ <default>0</default>
+ <range min="0" max="2" />
+ <summary>Profile Power Mode</summary>
+ <description>The power mode for which the current effect settings should be used.</description>
+ </key>
+
+ <key name="profile-power-profile" type="i">
+ <default>0</default>
+ <range min="0" max="5" />
+ <summary>Profile Power Profile</summary>
+ <description>The power profiles in which the current effect settings should be used.</description>
+ </key>
+
+ <key name="profile-high-priority" type="b">
+ <default>false</default>
+ <summary>Profile High Priority</summary>
+ <description>Use this profile even if there are better matching profiles.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Apparition Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="apparition-enable-effect" type="b">
+ <default>false</default>
+ <summary>Apparition Enable Effect</summary>
+ <description>Use the apparition effect.</description>
+ </key>
+
+ <key name="apparition-animation-time" type="i">
+ <default>300</default>
+ <summary>Apparition Animation Time</summary>
+ <description>The time the apparition effect takes.</description>
+ </key>
+
+ <key name="apparition-randomness" type="d">
+ <default>0.5</default>
+ <summary>Apparition Randomness</summary>
+ <description>The amount of randomness of the apparition effect.</description>
+ </key>
+
+ <key name="apparition-twirl-intensity" type="d">
+ <default>5.0</default>
+ <summary>Apparition Twirl Intensity</summary>
+ <description>The amount of twirling of the apparition effect.</description>
+ </key>
+
+ <key name="apparition-shake-intensity" type="d">
+ <default>3.0</default>
+ <summary>Apparition Shake Intensity</summary>
+ <description>The amount of shaking of the apparition effect.</description>
+ </key>
+
+ <key name="apparition-suction-intensity" type="d">
+ <default>0.75</default>
+ <summary>Apparition Suction Intensity</summary>
+ <description>The amount of suction of the apparition effect.</description>
+ </key>
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Pixelate Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="pixelate-enable-effect" type="b">
+ <default>false</default>
+ <summary>Pixelate Enable Effect</summary>
+ <description>Use the Pixelate effect.</description>
+ </key>
+
+ <key name="pixelate-animation-time" type="i">
+ <default>500</default>
+ <summary>Pixelate Animation Time</summary>
+ <description>The time the Pixelate effect takes.</description>
+ </key>
+
+ <key name="pixelate-noise" type="d">
+ <default>40</default>
+ <summary>Pixelate Noise</summary>
+ <description>The noise of the effect.</description>
+ </key>
+
+ <key name="pixelate-pixel-size" type="i">
+ <default>10</default>
+ <summary>Pixelate Pixel Size</summary>
+ <description>The size of the maximum pixelation.</description>
+ </key>
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Pixel Wipe Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="pixel-wipe-enable-effect" type="b">
+ <default>false</default>
+ <summary>Pixel Wipe Enable Effect</summary>
+ <description>Use the Pixel Wipe effect.</description>
+ </key>
+
+ <key name="pixel-wipe-animation-time" type="i">
+ <default>1000</default>
+ <summary>Pixel Wipe Animation Time</summary>
+ <description>The time the Pixel Wipe effect takes.</description>
+ </key>
+
+ <key name="pixel-wipe-pixel-size" type="i">
+ <default>10</default>
+ <summary>Pixel Wipe Pixel Size</summary>
+ <description>The size of the maximum pixelation.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Pixel Wheel Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="pixel-wheel-enable-effect" type="b">
+ <default>false</default>
+ <summary>Pixel Wheel Enable Effect</summary>
+ <description>Use the Pixel Wheel effect.</description>
+ </key>
+
+ <key name="pixel-wheel-animation-time" type="i">
+ <default>300</default>
+ <summary>Pixel Wheel Animation Time</summary>
+ <description>The time the Pixel Wheel effect takes.</description>
+ </key>
+
+ <key name="pixel-wheel-spoke-count" type="i">
+ <default>12</default>
+ <summary>Pixel Wheel Spoke Count</summary>
+ <description>The number of sections in the wheel.</description>
+ </key>
+
+ <key name="pixel-wheel-pixel-size" type="d">
+ <default>10</default>
+ <summary>Pixel Wheel Pixel Size</summary>
+ <description>The size of the maximum pixelation.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Doom Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="doom-enable-effect" type="b">
+ <default>false</default>
+ <summary>Doom Enable Effect</summary>
+ <description>Use the Doom effect.</description>
+ </key>
+
+ <key name="doom-animation-time" type="i">
+ <default>1000</default>
+ <summary>Doom Animation Time</summary>
+ <description>The time the Doom effect takes.</description>
+ </key>
+
+ <key name="doom-horizontal-scale" type="d">
+ <default>10.0</default>
+ <summary>Doom Horizontal Scale</summary>
+ <description>The X scale of the effect.</description>
+ </key>
+
+ <key name="doom-vertical-scale" type="d">
+ <default>10.0</default>
+ <summary>Doom Vertical Scale</summary>
+ <description>The Y scale of the effect.</description>
+ </key>
+
+ <key name="doom-pixel-size" type="i">
+ <default>10</default>
+ <summary>Doom Pixel Size</summary>
+ <description>The size of the maximum pixelation.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Fire Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="fire-enable-effect" type="b">
+ <default>true</default>
+ <summary>Fire Enable Effect</summary>
+ <description>Use the fire effect.</description>
+ </key>
+
+ <key name="fire-animation-time" type="i">
+ <default>1500</default>
+ <summary>Fire Animation Time</summary>
+ <description>The time the fire effect takes.</description>
+ </key>
+
+ <key name="fire-movement-speed" type="d">
+ <default>0.5</default>
+ <summary>Fire Movement Speed</summary>
+ <description>The speed at which the fire moves.</description>
+ </key>
+
+ <key name="fire-scale" type="d">
+ <default>1.0</default>
+ <summary>Fire Scale</summary>
+ <description>The size of the fire.</description>
+ </key>
+
+ <key name="fire-random-color" type="b">
+ <default>false</default>
+ <summary>use a random color for the fire</summary>
+ <description>use a random color of the fire</description>
+ </key>
+
+ <key name="fire-3d-noise" type="b">
+ <default>false</default>
+ <summary>Fire 3D Noise</summary>
+ <description>Use a 3D noise for the fire.</description>
+ </key>
+
+ <key name="fire-color-1" type="s">
+ <default>"rgba(76, 51, 25, 0.0)"</default>
+ <summary>Fire Color 1</summary>
+ <description>The first color of the fire gradient.</description>
+ </key>
+
+ <key name="fire-color-2" type="s">
+ <default>"rgba(180, 55, 30, 0.7)"</default>
+ <summary>Fire Color 2</summary>
+ <description>The second color of the fire gradient.</description>
+ </key>
+
+ <key name="fire-color-3" type="s">
+ <default>"rgba(255, 76, 38, 0.9)"</default>
+ <summary>Fire Color 3</summary>
+ <description>The third color of the fire gradient.</description>
+ </key>
+
+ <key name="fire-color-4" type="s">
+ <default>"rgba(255, 166, 25, 1)"</default>
+ <summary>Fire Color 4</summary>
+ <description>The fourth color of the fire gradient.</description>
+ </key>
+
+ <key name="fire-color-5" type="s">
+ <default>"rgba(255, 255, 255, 1)"</default>
+ <summary>Fire Color 5</summary>
+ <description>The fifth color of the fire gradient.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Glide Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="glide-enable-effect" type="b">
+ <default>false</default>
+ <summary>Glide Enable Effect</summary>
+ <description>Use the glide effect.</description>
+ </key>
+
+ <key name="glide-animation-time" type="i">
+ <default>250</default>
+ <summary>Glide Animation Time</summary>
+ <description>The time the glide effect takes.</description>
+ </key>
+
+ <key name="glide-scale" type="d">
+ <default>0.95</default>
+ <summary>Glide Scale</summary>
+ <description>The scale of the window.</description>
+ </key>
+
+ <key name="glide-squish" type="d">
+ <default>0.15</default>
+ <summary>Glide Squish</summary>
+ <description>The amount of squishing.</description>
+ </key>
+
+ <key name="glide-tilt" type="d">
+ <default>-0.3</default>
+ <summary>Glide Tilt</summary>
+ <description>The amount of tilting.</description>
+ </key>
+
+ <key name="glide-shift" type="d">
+ <default>-0.05</default>
+ <summary>Glide Shift</summary>
+ <description>The amount of shifting.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Matrix Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="matrix-enable-effect" type="b">
+ <default>false</default>
+ <summary>Matrix Enable Effect</summary>
+ <description>Use the matrix effect.</description>
+ </key>
+
+ <key name="matrix-animation-time" type="i">
+ <default>1500</default>
+ <summary>Matrix Animation Time</summary>
+ <description>The time the matrix effect takes.</description>
+ </key>
+
+ <key name="matrix-scale" type="i">
+ <default>20</default>
+ <summary>Matrix Scale</summary>
+ <description>The size of the matrix letters.</description>
+ </key>
+
+ <key name="matrix-randomness" type="d">
+ <default>1.0</default>
+ <summary>Matrix Randomness</summary>
+ <description>The randomness of the matrix effect.</description>
+ </key>
+
+ <key name="matrix-tip-color" type="s">
+ <default>"rgb(255, 255, 255)"</default>
+ <summary>Matrix Tip Color</summary>
+ <description>The color of the matrix trail tips.</description>
+ </key>
+
+ <key name="matrix-trail-color" type="s">
+ <default>"rgb(25, 255, 89)"</default>
+ <summary>Matrix Trail Color</summary>
+ <description>The color matrix trails.</description>
+ </key>
+
+ <key name="matrix-overshoot" type="d">
+ <default>0.25</default>
+ <summary>Matrix Overshoot</summary>
+ <description>The vertical overshooting of the matrix effect.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- TV Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="tv-enable-effect" type="b">
+ <default>false</default>
+ <summary>TV Enable Effect</summary>
+ <description>Use the tv effect.</description>
+ </key>
+
+ <key name="tv-animation-time" type="i">
+ <default>400</default>
+ <summary>TV Animation Time</summary>
+ <description>The time the tv effect takes.</description>
+ </key>
+
+ <key name="tv-effect-color" type="s">
+ <default>"rgb(255, 255, 255)"</default>
+ <summary>TV Effect Color</summary>
+ <description>The color of the TV effect.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Paint-Brush Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="paint-brush-enable-effect" type="b">
+ <default>false</default>
+ <summary>Paint Brush Enable Effect</summary>
+ <description>Use the paint-brush effect.</description>
+ </key>
+
+ <key name="paint-brush-animation-time" type="i">
+ <default>1000</default>
+ <summary>Paint Brush Animation Time</summary>
+ <description>The time the paint-brush effect takes.</description>
+ </key>
+
+ <key name="paint-brush-size" type="i">
+ <default>3</default>
+ <summary>Paint Brush Size</summary>
+ <description>Affects the number of paint strokes.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- T-Rex Attack Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="trex-enable-effect" type="b">
+ <default>false</default>
+ <summary>T-Rex Enable Effect</summary>
+ <description>Use the trex effect.</description>
+ </key>
+
+ <key name="trex-animation-time" type="i">
+ <default>1000</default>
+ <summary>T-Rex Animation Time</summary>
+ <description>The time the trex effect takes.</description>
+ </key>
+
+ <key name="trex-scratch-color" type="s">
+ <default>"rgb(170, 0, 0)"</default>
+ <summary>Claw Scratch Color</summary>
+ <description>The color of the claw scratches.</description>
+ </key>
+
+ <key name="trex-scratch-scale" type="d">
+ <default>1.0</default>
+ <summary>Claw Scratch Scale</summary>
+ <description>Size of the claw scratches.</description>
+ </key>
+
+ <key name="trex-scratch-warp" type="d">
+ <default>0.15</default>
+ <summary>Claw Scratch Warp</summary>
+ <description>Intensity of the claw scratch warp effect.</description>
+ </key>
+
+ <key name="trex-scratch-count" type="i">
+ <default>5</default>
+ <summary>Claw Scratch Count</summary>
+ <description>Number of claw scratches drawn on top of each other.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Hexagon Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="hexagon-enable-effect" type="b">
+ <default>false</default>
+ <summary>Hexagon Enable Effect</summary>
+ <description>Use the Hexagon effect.</description>
+ </key>
+
+ <key name="hexagon-animation-time" type="i">
+ <default>1500</default>
+ <summary>Hexagon Animation Time</summary>
+ <description>The time the Hexagon effect takes.</description>
+ </key>
+
+ <key name="hexagon-scale" type="d">
+ <default>3.0</default>
+ <summary>Hexagon Scale</summary>
+ <description>The size of the effect.</description>
+ </key>
+
+ <key name="hexagon-line-width" type="d">
+ <default>2.0</default>
+ <summary>Hexagon Line Width</summary>
+ <description>The width of the lines.</description>
+ </key>
+
+ <key name="hexagon-line-color" type="s">
+ <default>"rgba(200, 255, 255, 0.5)"</default>
+ <summary>Hexagon Line Color</summary>
+ <description>The color of the lines.</description>
+ </key>
+
+ <key name="hexagon-glow-color" type="s">
+ <default>"rgba(20, 80, 255, 0.5)"</default>
+ <summary>Hexagon Glow Color</summary>
+ <description>The color of the glow effect.</description>
+ </key>
+
+ <key name="hexagon-additive-blending" type="b">
+ <default>true</default>
+ <summary>Hexagon Additive Blending</summary>
+ <description>Use additive blending for the line and the glow.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Incinerate Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="incinerate-enable-effect" type="b">
+ <default>false</default>
+ <summary>Incinerate Enable Effect</summary>
+ <description>Use the Incinerate effect.</description>
+ </key>
+
+ <key name="incinerate-animation-time" type="i">
+ <default>2000</default>
+ <summary>Incinerate Animation Time</summary>
+ <description>The time the Incinerate effect takes.</description>
+ </key>
+
+ <key name="incinerate-scale" type="d">
+ <default>1.0</default>
+ <summary>Incinerate Scale</summary>
+ <description>The scale of the effect.</description>
+ </key>
+
+ <key name="incinerate-turbulence" type="d">
+ <default>0.3</default>
+ <summary>Incinerate Turbulence</summary>
+ <description>The turbulence of the effect.</description>
+ </key>
+
+ <key name="incinerate-color" type="s">
+ <default>"rgb(255, 180, 127)"</default>
+ <summary>Incinerate Color</summary>
+ <description>The color of the effect.</description>
+ </key>
+
+ <key name="incinerate-use-pointer" type="b">
+ <default>true</default>
+ <summary>Incinerate Use Pointer</summary>
+ <description>Use the pointer location for the incinerate effect.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Energize A Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="energize-a-enable-effect" type="b">
+ <default>false</default>
+ <summary>Energize A Enable Effect</summary>
+ <description>Use the Energize A effect.</description>
+ </key>
+
+ <key name="energize-a-animation-time" type="i">
+ <default>2000</default>
+ <summary>Energize A Animation Time</summary>
+ <description>The time the Energize A effect takes.</description>
+ </key>
+
+ <key name="energize-a-scale" type="d">
+ <default>1.0</default>
+ <summary>Energize A Scale</summary>
+ <description>The size of the effect.</description>
+ </key>
+
+ <key name="energize-a-color" type="s">
+ <default>"rgb(255, 200, 50)"</default>
+ <summary>Energize A Color</summary>
+ <description>The color of the effect.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Energize B Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="energize-b-enable-effect" type="b">
+ <default>false</default>
+ <summary>Energize B Enable Effect</summary>
+ <description>Use the Energize B effect.</description>
+ </key>
+
+ <key name="energize-b-animation-time" type="i">
+ <default>1000</default>
+ <summary>Energize B Animation Time</summary>
+ <description>The time the Energize B effect takes.</description>
+ </key>
+
+ <key name="energize-b-scale" type="d">
+ <default>1.0</default>
+ <summary>Energize B Scale</summary>
+ <description>The size of the effect.</description>
+ </key>
+
+ <key name="energize-b-color" type="s">
+ <default>"rgb(160, 180, 255)"</default>
+ <summary>Energize B Color</summary>
+ <description>The color of the effect.</description>
+ </key>
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Glitch Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="glitch-enable-effect" type="b">
+ <default>false</default>
+ <summary>Glitch Enable Effect</summary>
+ <description>Use the Glitch effect.</description>
+ </key>
+
+ <key name="glitch-animation-time" type="i">
+ <default>750</default>
+ <summary>Glitch Animation Time</summary>
+ <description>The time the Glitch effect takes.</description>
+ </key>
+
+ <key name="glitch-scale" type="d">
+ <default>1.0</default>
+ <summary>Glitch Scale</summary>
+ <description>The size of the effect.</description>
+ </key>
+
+ <key name="glitch-strength" type="d">
+ <default>2.0</default>
+ <summary>Glitch Strength</summary>
+ <description>The intensity of the effect.</description>
+ </key>
+
+ <key name="glitch-speed" type="d">
+ <default>2.0</default>
+ <summary>Glitch Speed</summary>
+ <description>The speed of the effect.</description>
+ </key>
+
+ <key name="glitch-color" type="s">
+ <default>"rgb(100, 160, 255)"</default>
+ <summary>Glitch Color</summary>
+ <description>The color of the effect.</description>
+ </key>
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- TV Glitch Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="tv-glitch-enable-effect" type="b">
+ <default>false</default>
+ <summary>TV Glitch Enable Effect</summary>
+ <description>Use the TV Glitch effect.</description>
+ </key>
+
+ <key name="tv-glitch-animation-time" type="i">
+ <default>750</default>
+ <summary>TV Glitch Animation Time</summary>
+ <description>The time the TV Glitch effect takes.</description>
+ </key>
+
+ <key name="tv-glitch-scale" type="d">
+ <default>1.0</default>
+ <summary>TV Glitch Scale</summary>
+ <description>The size of the effect.</description>
+ </key>
+
+ <key name="tv-glitch-strength" type="d">
+ <default>2.0</default>
+ <summary>TV Glitch Strength</summary>
+ <description>The intensity of the effect.</description>
+ </key>
+
+ <key name="tv-glitch-speed" type="d">
+ <default>2.0</default>
+ <summary>TV Glitch Speed</summary>
+ <description>The speed of the effect.</description>
+ </key>
+
+ <key name="tv-glitch-color" type="s">
+ <default>"rgb(100, 160, 255)"</default>
+ <summary>TV Glitch Color</summary>
+ <description>The color of the effect.</description>
+ </key>
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Wisps Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="wisps-enable-effect" type="b">
+ <default>false</default>
+ <summary>Wisps Enable Effect</summary>
+ <description>Use the wisps effect.</description>
+ </key>
+
+ <key name="wisps-animation-time" type="i">
+ <default>1500</default>
+ <summary>Wisps Animation Time</summary>
+ <description>The time the wisps effect takes.</description>
+ </key>
+
+ <key name="wisps-scale" type="d">
+ <default>1.0</default>
+ <summary>Wisps Scale</summary>
+ <description>The size of the wisps.</description>
+ </key>
+
+ <key name="wisps-color-1" type="s">
+ <default>"rgb(255, 100, 200)"</default>
+ <summary>Wisps Effect Color 1</summary>
+ <description>A color of the wisps effect.</description>
+ </key>
+
+ <key name="wisps-color-2" type="s">
+ <default>"rgb(100, 100, 255)"</default>
+ <summary>Wisps Effect Color 2</summary>
+ <description>A color of the wisps effect.</description>
+ </key>
+
+ <key name="wisps-color-3" type="s">
+ <default>"rgb(100, 200, 255)"</default>
+ <summary>Wisps Effect Color 3</summary>
+ <description>A color of the wisps effect.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Broken Glass Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="broken-glass-enable-effect" type="b">
+ <default>false</default>
+ <summary>Broken Glass Enable Effect</summary>
+ <description>Use the broken glass effect.</description>
+ </key>
+
+ <key name="broken-glass-animation-time" type="i">
+ <default>1000</default>
+ <summary>Broken Glass Animation Time</summary>
+ <description>The time the broken glass effect takes.</description>
+ </key>
+
+ <key name="broken-glass-scale" type="d">
+ <default>1.0</default>
+ <summary>Broken Glass Scale</summary>
+ <description>The size of the broken glass effect.</description>
+ </key>
+
+ <key name="broken-glass-gravity" type="d">
+ <default>-1.0</default>
+ <summary>Broken Glass Gravity</summary>
+ <description>The gravity of the broken glass effect.</description>
+ </key>
+
+ <key name="broken-glass-blow-force" type="d">
+ <default>0.5</default>
+ <summary>Broken Glass Blow Force</summary>
+ <description>The blow force of the broken glass effect.</description>
+ </key>
+
+ <key name="broken-glass-use-pointer" type="b">
+ <default>false</default>
+ <summary>Broken Glass Use Pointer</summary>
+ <description>Use the pointer location for the broken glass effect.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Portal Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="portal-enable-effect" type="b">
+ <default>false</default>
+ <summary>Portal Enable Effect</summary>
+ <description>Use the Portal effect.</description>
+ </key>
+
+ <key name="portal-animation-time" type="i">
+ <default>1250</default>
+ <summary>Portal Animation Time</summary>
+ <description>The time the Portal effect takes.</description>
+ </key>
+
+ <key name="portal-color" type="s">
+ <default>"rgb(121, 233, 24)"</default>
+ <summary>Portal Color</summary>
+ <description>The color of the effect.</description>
+ </key>
+
+ <key name="portal-rotation-speed" type="d">
+ <default>0.3</default>
+ <summary>Portal Rotation Speed</summary>
+ <description>The angular speed of the portal.</description>
+ </key>
+
+ <key name="portal-whirling" type="d">
+ <default>1.0</default>
+ <summary>Portal Whirling</summary>
+ <description>The amount of distortion.</description>
+ </key>
+
+ <key name="portal-details" type="d">
+ <default>15.0</default>
+ <summary>Portal Details</summary>
+ <description>The amount of details in the portal.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Snap of Disintegration Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="snap-enable-effect" type="b">
+ <default>false</default>
+ <summary>Snap Enable Effect</summary>
+ <description>Use the snap effect.</description>
+ </key>
+
+ <key name="snap-animation-time" type="i">
+ <default>1500</default>
+ <summary>Snap Animation Time</summary>
+ <description>The time the snap effect takes.</description>
+ </key>
+
+ <key name="snap-scale" type="d">
+ <default>3.0</default>
+ <summary>Snap Scale</summary>
+ <description>The size of the snap effect.</description>
+ </key>
+
+ <key name="snap-color" type="s">
+ <default>"rgb(50, 40, 35)"</default>
+ <summary>Snap Effect Color</summary>
+ <description>The color of the dust.</description>
+ </key>
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Focus Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="focus-enable-effect" type="b">
+ <default>false</default>
+ <summary>Focus Enable Effect</summary>
+ <description>Use the Focus effect.</description>
+ </key>
+
+ <key name="focus-animation-time" type="i">
+ <default>500</default>
+ <summary>Focus Animation Time</summary>
+ <description>The time the Focus effect takes.</description>
+ </key>
+
+ <key name="focus-blur-amount" type="i">
+ <default>50</default>
+ <summary>Focus Blur Amount</summary>
+ <description>The Blur Amount before Focusing.</description>
+ </key>
+
+ <key name="focus-blur-quality" type="i">
+ <default>3</default>
+ <summary>Focus Blur Quality</summary>
+ <description>The Quality of the Blur (Setting this too high may increase GPU load and affect performance)</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Mushroom Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="mushroom-enable-effect" type="b">
+ <default>false</default>
+ <summary>mushroom Enable Effect</summary>
+ <description>Use the mushroom effect.</description>
+ </key>
+
+ <key name="mushroom-animation-time" type="i">
+ <default>1500</default>
+ <summary>mushroom Animation Time</summary>
+ <description>The time the mushroom effect takes.</description>
+ </key>
+
+ <!-- <key name="mushroom-8bit-enable" type="b">
+ <default>true</default>
+ <summary>mushroom 8Bit Animation</summary>
+ <description>use the old style.</description>
+ </key> -->
+
+ <key name="mushroom-scale-style" type="d">
+ <default>1.0</default>
+ <summary>8Bit or Smooth Scaling</summary>
+ <description>How the window scales</description>
+ </key>
+
+ <key name="mushroom-spark-count" type="i">
+ <default>4</default>
+ <summary>The Number of Sparks</summary>
+ <description>The Number of Sparks</description>
+ </key>
+
+ <key name="mushroom-spark-color" type="s">
+ <default>"rgba(255, 255, 255, 1.0)"</default>
+ <summary>The Sparks Color</summary>
+ <description>The Sparks Color</description>
+ </key>
+
+ <key name="mushroom-spark-rotation" type="d">
+ <default>0.3</default>
+ <summary>The Number of Rotations the Sparks do</summary>
+ <description>The Number of Rotations the Sparks do</description>
+ </key>
+
+ <key name="mushroom-rays-color" type="s">
+ <default>"rgba(255, 255, 255, 1.0)"</default>
+ <summary>The Rays Color</summary>
+ <description>The Rays Color</description>
+ </key>
+
+ <key name="mushroom-ring-count" type="i">
+ <default>3</default>
+ <summary>Number of Rings</summary>
+ <description>Number of Rings</description>
+ </key>
+
+ <key name="mushroom-ring-rotation" type="d">
+ <default>1.33</default>
+ <summary>The Number of Rotations/Cycles the Stars do</summary>
+ <description>The Number of Rotations/Cycles the Stars do</description>
+ </key>
+
+ <key name="mushroom-star-count" type="i">
+ <default>5</default>
+ <summary>The Number of Stars in each Ring</summary>
+ <description>The Number of Stars in each Ring</description>
+ </key>
+
+ <key name="mushroom-star-color-0" type="s">
+ <default>"rgba(233, 249, 1, 1.0)"</default>
+ <summary>Star Color 1</summary>
+ <description>The Nth color of the Star gradient.</description>
+ </key>
+
+ <key name="mushroom-star-color-1" type="s">
+ <default>"rgba(233, 249, 1, 1.0)"</default>
+ <summary>Star Color 1</summary>
+ <description>The Nth color of the Star gradient.</description>
+ </key>
+
+ <key name="mushroom-star-color-2" type="s">
+ <default>"rgba(91, 255, 1, 1.0)"</default>
+ <summary>Star Color 1</summary>
+ <description>The Nth color of the Star gradient.</description>
+ </key>
+
+ <key name="mushroom-star-color-3" type="s">
+ <default>"rgba(91, 255, 1, 1.0)"</default>
+ <summary>Star Color 1</summary>
+ <description>The Nth color of the Star gradient.</description>
+ </key>
+
+ <key name="mushroom-star-color-4" type="s">
+ <default>"rgba(0, 240, 236, 1.0)"</default>
+ <summary>Star Color 1</summary>
+ <description>The Nth color of the Star gradient.</description>
+ </key>
+
+ <key name="mushroom-star-color-5" type="s">
+ <default>"rgba(0, 240, 236, 1.0)"</default>
+ <summary>Star Color 1</summary>
+ <description>The Nth color of the Star gradient.</description>
+ </key>
+
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Aura Glow Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="aura-glow-enable-effect" type="b">
+ <default>false</default>
+ <summary>Aura Glow Enable Effect</summary>
+ <description>Use the Aura Glow effect.</description>
+ </key>
+
+ <key name="aura-glow-animation-time" type="i">
+ <default>750</default>
+ <summary>Aura Glow Animation Time</summary>
+ <description>The time the Aura Glow effect takes.</description>
+ </key>
+
+ <key name="aura-glow-random-color" type="b">
+ <default>false</default>
+ <summary>Aura Glow Random Color Offset</summary>
+ <description>Ramdomize the color offset</description>
+ </key>
+
+ <key name="aura-glow-start-hue" type="d">
+ <default>0.0</default>
+ <summary>Aura Glow Color Offset</summary>
+ <description>Changes the inital color of the glow</description>
+ </key>
+
+ <key name="aura-glow-speed" type="d">
+ <default>2.5</default>
+ <summary>Aura Glow Color Speed</summary>
+ <description>The Speed at which the color changes</description>
+ </key>
+
+ <key name="aura-glow-saturation" type="d">
+ <default>0.75</default>
+ <summary>Aura Glow Color Saturation</summary>
+ <description>Amount of color in the effect</description>
+ </key>
+
+ <key name="aura-glow-edge-size" type="d">
+ <default>0.75</default>
+ <summary>Aura Glow Size of the Color Edge</summary>
+ <description>Size of the Color Edge</description>
+ </key>
+
+ <key name="aura-glow-edge-hardness" type="d">
+ <default>0.25</default>
+ <summary>Aura Glow Edge Hardness</summary>
+ <description>The Hardness of the Edge</description>
+ </key>
+
+ <key name="aura-glow-blur" type="d">
+ <default>15.0</default>
+ <summary>Aura Glow Blur</summary>
+ <description>Changes the Blue Amount</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- RGBWarp Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="rgbwarp-enable-effect" type="b">
+ <default>false</default>
+ <summary>RGBWarp Enable Effect</summary>
+ <description>Use the RGBWarp.</description>
+ </key>
+
+ <key name="rgbwarp-animation-time" type="i">
+ <default>750</default>
+ <summary>RGBWarp Animation Time</summary>
+ <description>The time the RGBWarp effect takes.</description>
+ </key>
+
+ <key name="rgbwarp-brightness" type="d">
+ <default>1.0</default>
+ <summary>RGBWarp Brightness</summary>
+ <description>brightness of the effect.</description>
+ </key>
+
+ <key name="rgbwarp-speed-r" type="d">
+ <default>1.0</default>
+ <summary>RGBWarp Red Speed</summary>
+ <description>Red speed of the effect.</description>
+ </key>
+
+ <key name="rgbwarp-speed-g" type="d">
+ <default>0.90</default>
+ <summary>RGBWarp Green Speed</summary>
+ <description>Green speed of the effect.</description>
+ </key>
+
+ <key name="rgbwarp-speed-b" type="d">
+ <default>0.80</default>
+ <summary>RGBWarp Blue Speed</summary>
+ <description>Blue speed of the effect.</description>
+ </key>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- Team Rocket Effect Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="team-rocket-enable-effect" type="b">
+ <default>false</default>
+ <summary>Team Rocket Enable Effect</summary>
+ <description>Use the Team Rocket effect.</description>
+ </key>
+
+ <key name="team-rocket-animation-time" type="i">
+ <default>1500</default>
+ <summary>Team Rocket Animation Time</summary>
+ <description>The time the Team Rocket effect takes.</description>
+ </key>
+
+ <key name="team-rocket-animation-split" type="d">
+ <default>0.5</default>
+ <summary>Window vs Sparkle split</summary>
+ <description>Window vs Sparkle split</description>
+ </key>
+
+ <key name="team-rocket-horizontal-sparkle-position" type="d">
+ <default>0.0</default>
+ <summary>Team Rocket X</summary>
+ <description>X position</description>
+ </key>
+
+ <key name="team-rocket-vertical-sparkle-position" type="d">
+ <default>0.5</default>
+ <summary>Team Rocket Y</summary>
+ <description>Y position</description>
+ </key>
+
+ <key name="team-rocket-window-rotation" type="b">
+ <default>true</default>
+ <summary>Rotate the Window</summary>
+ <description>Rotate the Window</description>
+ </key>
+
+ <key name="team-rocket-sparkle-rot" type="d">
+ <default>0.33</default>
+ <summary>Sparkle Rotation</summary>
+ <description>Sparkle Rotation</description>
+ </key>
+
+ <key name="team-rocket-sparkle-size" type="d">
+ <default>0.75</default>
+ <summary>Sparkle Size</summary>
+ <description>Sparkle Size</description>
+ </key>
+
+
+ </schema>
+
+</schemalist>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
new file mode 100644
index 0000000..844fc0e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/burn-my-windows@schneegans.github.com/schemas/org.gnome.shell.extensions.burn-my-windows.gschema.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+SPDX-FileCopyrightText: Simon Schneegans <code@simonschneegans.de>
+SPDX-License-Identifier: CC0-1.0
+-->
+
+<schemalist>
+
+ <schema path="/org/gnome/shell/extensions/burn-my-windows/" id="org.gnome.shell.extensions.burn-my-windows">
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- General Options -->
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <key name="active-profile" type="s">
+ <default>""</default>
+ <summary>Active Profile</summary>
+ <description>The currently active effect profile.</description>
+ </key>
+
+ <key name="preview-effect" type="s">
+ <default>""</default>
+ <summary>Preview Effect</summary>
+ <description>The effect with this nick will be used for the next window animation.</description>
+ </key>
+
+ <key name="test-mode" type="b">
+ <default>false</default>
+ <summary>Test Mode</summary>
+ <description>If set to true, all animations will show only one still frame to allow for continuous integration tests.</description>
+ </key>
+
+ <key name="last-prefs-version" type="i">
+ <default>0</default>
+ <summary>Last Prefs Version</summary>
+ <description>Used to check whether the extension got updated from the preferences dialog.</description>
+ </key>
+
+ <key name="last-extension-version" type="i">
+ <default>0</default>
+ <summary>Last Extension Version</summary>
+ <description>Used to check whether the extension got updated from the extension side.</description>
+ </key>
+
+ <key name="prefs-open-count" type="i">
+ <default>0</default>
+ <summary>Prefs Open Count</summary>
+ <description>The number of times the settings dialog was openend.</description>
+ </key>
+
+ <key name="show-support-dialog" type="b">
+ <default>true</default>
+ <summary>Show Support Dialog</summary>
+ <description>If set to false, the ask-for-support dialog will never be shown.</description>
+ </key>
+
+ </schema>
+
+</schemalist> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/42_custom_reboot b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/42_custom_reboot
new file mode 100644
index 0000000..638d768
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/42_custom_reboot
@@ -0,0 +1,11 @@
+#! /bin/sh
+# This script allows the extension to skip grub's timeout when rebooting
+
+set -e
+
+cat << EOF
+if [ ! -z "\${boot_once}" ] ; then
+ set timeout_style=hidden
+ set timeout=0
+fi
+EOF \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/bootloader.js b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/bootloader.js
new file mode 100644
index 0000000..5913d9c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/bootloader.js
@@ -0,0 +1,36 @@
+import { EFIBootManager } from "./efibootmgr.js";
+import { SystemdBoot } from './systemdBoot.js';
+import { Grub } from './grub.js';
+
+export const BootLoaders = {
+ EFI: "EFI Boot Manager",
+ GRUB: "Grub",
+ SYSD: "Systemd Boot",
+ UNKNOWN: "Unknown Boot Loader"
+}
+
+export class Bootloader {
+ /**
+ * Gets the first available boot loader type on the current system
+ * @returns BootLoaders type. Can be "EFI", "SYSD", "GRUB", or "UNKNOWN"
+ */
+ static async GetUseableType(extension) {
+ const settings = extension.getSettings('org.gnome.shell.extensions.customreboot');
+
+ if (await EFIBootManager.IsUseable() && settings.get_boolean('use-efibootmgr')) return BootLoaders.EFI;
+ if (await Grub.IsUseable() && settings.get_boolean('use-grub')) return BootLoaders.GRUB;
+ if (await SystemdBoot.IsUseable() && settings.get_boolean('use-systemd-boot')) return BootLoaders.SYSD;
+ return BootLoaders.UNKNOWN;
+ }
+
+ /**
+ * Gets a instance of the provided boot loader
+ * @returns A boot loader if one is found otherwise undefined
+ */
+ static async GetUseable(type) {
+ if (type === BootLoaders.EFI) return EFIBootManager;
+ if (type === BootLoaders.SYSD) return SystemdBoot;
+ if (type === BootLoaders.GRUB) return Grub;
+ return undefined;
+ }
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/efibootmgr.js b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/efibootmgr.js
new file mode 100644
index 0000000..f34cec0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/efibootmgr.js
@@ -0,0 +1,101 @@
+import Gio from "gi://Gio";
+import { ExecCommand, Log, LogWarning } from './utils.js';
+
+/**
+ * Represents efibootmgr
+ */
+export class EFIBootManager {
+ /**
+ * Get's all available boot options
+ * @returns {[Map, string]} Map(title, id), defaultOption
+ */
+ static async GetBootOptions() {
+ const [status, stdout, stderr] = await ExecCommand(['efibootmgr'],);
+ const lines = stdout.split("\n");
+
+ let boot_first = "0000";
+
+ const boot_options = new Map();
+
+ for (let l = 0; l < lines.length; l++) {
+ const line = lines[l];
+ if (line.startsWith("BootOrder:")) {
+ boot_first = line.split(" ")[1].split(",")[0];
+ continue;
+ }
+
+ const regex = /(Boot[0-9]{4})/;
+ const vLine = regex.exec(line)
+ if (vLine && vLine.length) {
+ const option = line.replace("Boot", "").split("*");
+ const title = option[1];
+ if (title.includes("HD") || title.includes("RC")) {
+ const trimed_title = title.replace(/(?<=[\S\s]*)(HD|RC)([\s\S()]*|$)/, "").trim();
+ boot_options.set(trimed_title, option[0].trim());
+ }
+ else {
+ boot_options.set(option[1].trim(), option[0].trim());
+ }
+ }
+ }
+
+ return [boot_options, boot_first];
+ }
+
+ /**
+ * Set's the next boot option
+ * @param {string} id
+ * @returns True if the boot option was set, otherwise false
+ */
+ static async SetBootOption(id) {
+ if (!this.IsUseable()) return false;
+ const [status, stdout, stderr] = await ExecCommand(['/usr/bin/pkexec', 'efibootmgr', '-n', id],);
+ if (status === 0) {
+ Log(`Set boot option to ${id}`);
+ return true;
+ }
+ LogWarning("Unable to set boot option using efibootmgr");
+ return false;
+ }
+
+ /**
+ * Can we use this bootloader?
+ * @returns True if useable otherwise false
+ */
+ static async IsUseable() {
+ return await this.GetBinary() !== "";
+ }
+
+ /**
+ * Get's efibootmgr binary path
+ * @returns A string containing the location of the binary, if none is found returns a blank string
+ */
+ static async GetBinary() {
+ let paths = ["/usr/bin/efibootmgr", "/usr/sbin/efibootmgr"];
+
+ let file;
+
+ for (let i = 0; i < paths.length; i++) {
+ file = Gio.file_new_for_path(paths[i]);
+ if (file.query_exists(null)) {
+ return paths[i];
+ }
+ }
+
+ return "";
+ }
+
+ /**
+ * This boot loader cannot be quick rebooted
+ */
+ static async CanQuickReboot() {
+ return false;
+ }
+
+ /**
+ * This boot loader cannot be quick rebooted
+ */
+ static async QuickRebootEnabled() {
+ return false;
+ }
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/extension.js
new file mode 100644
index 0000000..035e019
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/extension.js
@@ -0,0 +1,170 @@
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as QuickSettings from 'resource:///org/gnome/shell/ui/quickSettings.js';
+
+
+import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
+import { getDefault } from "resource:///org/gnome/shell/misc/systemActions.js";
+import GObject from "gi://GObject";
+
+//import
+import { LogWarning, Log } from './utils.js';
+import { BootLoaders, Bootloader } from "./bootloader.js";
+
+
+const RebootQuickMenu = GObject.registerClass(
+class RebootQuickMenu extends QuickSettings.QuickMenuToggle {
+
+ _init(extension) {
+ const init_params = { iconName: 'system-reboot-symbolic', toggleMode: false };
+ init_params.title = "Reboot Into";
+ super._init(init_params);
+
+ // Set toggle to be unchecked
+ this.checked = false;
+
+ // Open menu and set toggle check to true
+ this.clickedID = this.connect("clicked", () => {
+ this.menu.open();
+ this.checked = true;
+ });
+
+ // Connect 'menu-closed' to update checked state
+ this.menuClosedID = this.menu.connect("menu-closed", () => {
+ this.checked = false;
+ });
+
+ // Add boot options to menu
+ try {
+ this.createBootMenu(extension);
+ }
+ catch (e) {
+ LogWarning(e);
+ }
+ }
+
+ cleanConns() {
+ // Disconnect all connections
+ this.disconnect(this.openStateID);
+ this.disconnect(this.clickedID);
+ }
+
+ async createBootMenu(extension) {
+ // Get boot options
+ const type = await Bootloader.GetUseableType(extension);
+
+
+ const header_title = `Boot Options - ${type}`;
+
+ // Set Menu Header
+ this.menu.setHeader('system-reboot-symbolic', header_title, 'Reboot into the selected entry');
+
+ const loader = await Bootloader.GetUseable(type);
+
+ if (loader === undefined) {
+ // Set Menu Header
+ this.menu.setHeader('system-reboot-symbolic', 'Error', 'The selected boot loader cannot be found...');
+
+ // Add reload option, to refresh extension menu without reloading GNOME or the extension
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.menu.addAction('Reload', () => {
+ this.menu.removeAll();
+ this.createBootMenu();
+ });
+
+ // Add button to open settings
+ this.menu.addAction('Settings', () => {
+ extension.openPreferences();
+ });
+
+ return;
+ }
+
+ loader.GetBootOptions().then(([bootOps, defaultOpt]) => {
+ if (bootOps !== undefined) {
+ this._itemsSection = new PopupMenu.PopupMenuSection();
+
+ for (let [title, id] of bootOps) {
+ Log(`${title} - ${id}`);
+ this._itemsSection.addAction(String(title), () => {
+ // Set boot option
+ loader.SetBootOption(String(id)).then(result => {
+ if (result) {
+ // On success trigger restart dialog
+ new getDefault().activateRestart();
+ }
+ });
+ }, (title === defaultOpt || id === defaultOpt)? "pan-end-symbolic" : undefined);
+ }
+
+ this.menu.addMenuItem(this._itemsSection);
+ }
+
+ // Add reload option, to refresh extension menu without reloading GNOME or the extension
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.menu.addAction('Reload', () => {
+ this.menu.removeAll();
+ this.createBootMenu();
+ });
+
+ // Add button to open settings
+ this.menu.addAction('Settings', () => {
+ extension.openPreferences();
+ });
+
+ loader.CanQuickReboot().then(async result => {
+ if (!result) return;
+ if (!await loader.QuickRebootEnabled()) {
+ this.menu.addAction('Enable Quick Reboot', async () => {
+ await loader.EnableQuickReboot(extension);
+ this.menu.removeAll();
+ this.createBootMenu();
+ });
+ }
+ else {
+ this.menu.addAction('Disable Quick Reboot', async () => {
+ await loader.DisableQuickReboot();
+ this.menu.removeAll();
+ this.createBootMenu();
+ });
+ }
+ });
+
+ }).catch((error) => {
+ LogWarning(error);
+ // Only do this if the current bootloader is grub
+ if (type === BootLoaders.GRUB)
+ {
+ // Only add this if all fails, giving user option to make the config readable
+ this.menu.addMenuItem(new PopupSeparatorMenuItem());
+ this.menu.addAction('Fix Grub.conf Perms', async () => {
+ await loader.SetReadable();
+ this.menu.removeAll();
+ this.createBootMenu(extension);
+ });
+ }
+ })
+ }
+});
+
+export default class CustomReboot extends Extension {
+
+ enable() {
+ // Add items to QuickSettingsMenu
+ this._indicator = new QuickSettings.SystemIndicator();
+ this.menu = new RebootQuickMenu(this);
+ this._indicator.quickSettingsItems.push(this.menu);
+ Main.panel.statusArea.quickSettings.addExternalIndicator(this._indicator, 1);
+ }
+
+ disable() {
+ this.menu.cleanConns();
+ this.menu = null;
+
+ this._indicator.quickSettingsItems.forEach(item => {
+ item.destroy();
+ });
+ this._indicator.destroy();
+ this._indicator = null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/grub.js b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/grub.js
new file mode 100644
index 0000000..6d67ff0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/grub.js
@@ -0,0 +1,205 @@
+import Gio from "gi://Gio";
+import { ExecCommand, Log, LogWarning } from './utils.js';
+
+/**
+ * Represents grub
+ */
+export class Grub {
+ /**
+ * Get's all available boot options
+ * @returns {[Map, string]} Map(title, id), defaultOption
+ */
+ static async GetBootOptions() {
+ try {
+ let cfgpath = await this.GetConfig();
+ if (cfgpath == "") {
+ throw new String("Failed to find grub config");
+ }
+
+ let bootOptions = new Map();
+
+ let defualtEn = "";
+
+ let file = Gio.file_new_for_path(cfgpath);
+ let [suc, content] = file.load_contents(null);
+ if (!suc) {
+ throw new String("Failed to load grub config");
+ }
+
+ let lines;
+ if (content instanceof Uint8Array) {
+ lines = new TextDecoder().decode(content);
+ }
+ else {
+ lines = content.toString();
+ }
+
+ let entryRx = /^menuentry ['"]([^'"]+)/;
+ let defaultRx = /(?<=set default=\")([A-Za-z- ()/0-9]*)(?=\")/
+ lines.split('\n').forEach(l => {
+ let res = entryRx.exec(l);
+ if (res && res.length) {
+ bootOptions.set(res[1], res[1]);
+ }
+ let def = defaultRx.exec(l);
+ if (def && def.length) {
+ defualtEn = def[0];
+ }
+ });
+
+ bootOptions.forEach((v, k) => {
+ Log(`${k} = ${v}`);
+ });
+
+ if (defualtEn == "") defualtEn = bootOptions.keys().next().value;
+
+ return [bootOptions, defualtEn];
+
+ } catch (e) {
+ LogWarning(e);
+ return undefined;
+ }
+ }
+
+ /**
+ * Set's the next boot option
+ * @param {string} id
+ * @returns True if the boot option was set, otherwise false
+ */
+ static async SetBootOption(id) {
+ try {
+ let [status, stdout, stderr] = await ExecCommand(
+ ['/usr/bin/pkexec', '/usr/sbin/grub-reboot', id],
+ );
+ Log(`Set boot option to ${id}: ${status}\n${stdout}\n${stderr}`);
+ return true;
+ } catch (e) {
+ LogWarning(e);
+ return false;
+ }
+ }
+
+ /**
+ * Can we use this bootloader?
+ * @returns True if useable otherwise false
+ */
+ static async IsUseable() {
+ return await this.GetConfig() !== "";
+ }
+
+ /**
+ * Get's grub config file
+ * @returns A string containing the location of the config file, if none is found returns a blank string
+ */
+ static async GetConfig() {
+ let paths = ["/boot/grub/grub.cfg", "/boot/grub2/grub.cfg"];
+
+ let file;
+
+ for (let i = 0; i < paths.length; i++) {
+ file = Gio.file_new_for_path(paths[i]);
+ if (file.query_exists(null)) {
+ return paths[i];
+ }
+ }
+
+ return "";
+ }
+
+ /**
+ * Copies a custom grub script to allow the extension to quickly reboot into another OS
+ * If anyone reads this: Idk how to combine these into one pkexec call, if you do please leave a commit fixing it
+ */
+ static async EnableQuickReboot(ext) {
+ try {
+ let [status, stdout, stderr] = await ExecCommand([
+ 'pkexec',
+ 'sh',
+ '-c',
+ `/usr/bin/cp ${ext.lookupByUUID('customreboot@nova1545').path()}/42_custom_reboot /etc/grub.d/42_custom_reboot && /usr/bin/chmod 755 /etc/grub.d/42_custom_reboot && /usr/sbin/update-grub`
+ ]);
+
+ if (status !== 0) {
+ return false;
+ }
+
+ return true;
+ }
+ catch (e) {
+ LogWarning(e);
+ return false;
+ }
+ }
+
+
+ /**
+ * Removes the script used to allow the extension to quickly reboot into another OS without waiting for grub's timeout
+ * If anyone reads this: Idk how to combine these into one pkexec call, if you do please leave a commit fixing it
+ */
+ static async DisableQuickReboot() {
+ try {
+
+ let [status, stdout, stderr] = await ExecCommand([
+ 'pkexec',
+ 'sh',
+ '-c',
+ '/usr/bin/rm /etc/grub.d/42_custom_reboot && /usr/sbin/update-grub'
+ ]);
+
+ if (status !== 0) {
+ return false;
+ }
+
+ return true;
+ }
+ catch (e) {
+ LogWarning(e);
+ return false;
+ }
+ }
+
+
+ /**
+ * This boot loader can be quick rebooted
+ */
+ static async CanQuickReboot() {
+ return true;
+ }
+
+ /**
+ * Checks if /etc/grub.d/42_custom_reboot exists
+ */
+ static async QuickRebootEnabled() {
+ try {
+ let [status, stdout, stderr] = await ExecCommand(['/usr/bin/cat', '/etc/grub.d/42_custom_reboot'],);
+ if (status !== 0) {
+ LogWarning(`/etc/grub.d/42_custom_reboot not found`);
+ return false;
+ }
+ Log(`/etc/grub.d/42_custom_reboot found`);
+
+ return true;
+ }
+ catch (e) {
+ LogWarning(e);
+ return false;
+ }
+ }
+
+ static async SetReadable() {
+ try {
+ const config = GetConfig();
+ let [status, stdout, stderr] = await ExecCommand(['/usr/bin/pkexec', '/usr/bin/chmod', '644', config],);
+ if (status !== 0) {
+ Log(`Failed to make ${config} readable`);
+ return false;
+ }
+ Log(`Made ${config} readable`);
+ return true;
+ }
+ catch (e) {
+ Log(e);
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/metadata.json
new file mode 100644
index 0000000..8cea11c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/metadata.json
@@ -0,0 +1,17 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Reboot into another OS directly from GNOME.\nA expansion of https://github.com/docquantum/gnome-shell-extension-customreboot",
+ "name": "Custom Reboot",
+ "original-author": "Nova1545",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/Nova1545/gnome-shell-extension-customreboot",
+ "uuid": "customreboot@nova1545",
+ "version": 11
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/prefs.js
new file mode 100644
index 0000000..f93539a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/prefs.js
@@ -0,0 +1,101 @@
+'use strict';
+
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+
+import Gio from "gi://Gio";
+import Adw from "gi://Adw";
+import Gtk from "gi://Gtk";
+import { EFIBootManager } from './efibootmgr.js';
+import { Grub } from './grub.js';
+import { SystemdBoot } from './systemdBoot.js';
+
+export default class MyExtensionPreferences extends ExtensionPreferences {
+ fillPreferencesWindow(window) {
+ const settings = this.getSettings('org.gnome.shell.extensions.customreboot');
+
+ // Create a preferences page and group
+ const page = new Adw.PreferencesPage();
+ const group = new Adw.PreferencesGroup();
+ page.add(group);
+
+ // Create row for efibootmgr
+ const efi = new Adw.ActionRow({ title: 'Use EFI Boot Manager (efibootmgr)' });
+
+ // Create row for grub
+ const grub = new Adw.ActionRow({ title: 'Use Grub'});
+
+ // Create row for systemd-boot
+ const sysd = new Adw.ActionRow({ title: 'Use Systemd Boot'});
+
+ // Add rows
+ group.add(efi);
+ group.add(grub);
+ group.add(sysd);
+
+ // Create switch for efibootmgr
+ const efi_switch = new Gtk.Switch({
+ active: settings.get_boolean('use-efibootmgr'),
+ valign: Gtk.Align.CENTER,
+ });
+
+ // Create switch for grub
+ const grub_switch = new Gtk.Switch({
+ active: settings.get_boolean('use-grub'),
+ valign: Gtk.Align.CENTER,
+ });
+
+ // Create switch for systemd-boot
+ const sysd_switch = new Gtk.Switch({
+ active: settings.get_boolean('use-systemd-boot'),
+ valign: Gtk.Align.CENTER,
+ });
+
+ // Bind settings for efibootmgr
+ settings.bind(
+ 'use-efibootmgr',
+ efi_switch,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ // Bind settings for grub
+ settings.bind(
+ 'use-grub',
+ grub_switch,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ // Bind settings for systemd-boot
+ settings.bind(
+ 'use-systemd-boot',
+ sysd_switch,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ // Add the switch for efibootmgr
+ efi.add_suffix(efi_switch);
+ efi.activatable_widget = efi_switch;
+
+ // Add the switch for grub
+ grub.add_suffix(grub_switch);
+ grub.activatable_widget = grub_switch;
+
+ // Add the switch for systemd-boot
+ sysd.add_suffix(sysd_switch);
+ sysd.activatable_widget = sysd_switch;
+
+ // Add our page to the window
+ window.add(page);
+
+ (async () => {
+ // Disable/enable switches in accordance to them being usable
+
+ efi_switch.set_sensitive(await EFIBootManager.IsUseable());
+ grub_switch.set_sensitive(await Grub.IsUseable());
+ sysd_switch.set_sensitive(await SystemdBoot.IsUseable());
+ })();
+ }
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/gschemas.compiled
new file mode 100644
index 0000000..cdce7f0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/org.gnome.extensions.customreboot.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/org.gnome.extensions.customreboot.gschema.xml
new file mode 100644
index 0000000..f56ca42
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/schemas/org.gnome.extensions.customreboot.gschema.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+ <schema id="org.gnome.shell.extensions.customreboot" path="/org/gnome/shell/extensions/customreboot/">
+ <!-- See also: https://docs.gtk.org/glib/gvariant-format-strings.html -->
+ <key name="use-efibootmgr" type="b">
+ <default>true</default>
+ </key>
+ <key name="use-grub" type="b">
+ <default>false</default>
+ </key>
+ <key name="use-systemd-boot" type="b">
+ <default>false</default>
+ </key>
+ </schema>
+</schemalist> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/systemdBoot.js b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/systemdBoot.js
new file mode 100644
index 0000000..51d4e6e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/systemdBoot.js
@@ -0,0 +1,121 @@
+import Gio from "gi://Gio";
+import { ExecCommand, Log, LogWarning } from './utils.js';
+
+/**
+ * Represents Systemdboot
+ */
+export class SystemdBoot {
+ /**
+ * Get's all available boot options
+ * @returns {[Map, string]} Map(title, id), defaultOption
+ */
+ static async GetBootOptions() {
+ let bootctl = await this.GetBinary();
+ if (bootctl == "") {
+ Log(`Failed to find bootctl binary`);
+ return undefined;
+ }
+
+ try {
+ let [status, stdout, stderr] = await ExecCommand([bootctl, "list"]);
+ if (status !== 0)
+ throw new Error(`Failed to get list from bootctl: ${status}\n${stdout}\n${stderr}`);
+ Log(`bootctl list: ${status}\n${stdout}\n${stderr}`);
+ let lines = String(stdout).split('\n');
+ let titleRx = /(?<=title:\s+).+/;
+ let idRx = /(?<=id:\s+).+/;
+ let defaultRx = /\(default\)/;
+ let titles = [];
+ let ids = []
+ let defaultOpt;
+ lines.forEach(l => {
+ let title = titleRx.exec(l);
+ let id = idRx.exec(l);
+ if (title && title.length) {
+ titles.push(String(title));
+ } else if (id && id.length) {
+ ids.push(String(id));
+ }
+ });
+ if (titles.length !== ids.length)
+ throw new Error("Number of titles and ids do not match!");
+ let bootOptions = new Map();
+ for (let i = 0; i < titles.length; i++) {
+ bootOptions.set(titles[i], ids[i])
+ }
+
+ bootOptions.forEach((id, title) => {
+ Log(`${id} = ${title}`);
+
+ let defaultRes = defaultRx.exec(title);
+
+ if (defaultRes) {
+ defaultOpt = title;
+ }
+ })
+
+ return [bootOptions, bootOptions.get(defaultOpt)];
+ } catch (e) {
+ LogWarning(e);
+ return undefined;
+ }
+ }
+
+ /**
+ * Set's the next boot option
+ * @param {string} id
+ * @returns True if the boot option was set, otherwise false
+ */
+ static async SetBootOption(id) {
+ if (!this.IsUseable()) return false;
+ const [status, stdout, stderr] = await ExecCommand(['/usr/bin/pkexec', '/usr/bin/bootctl', 'set-oneshot', id],);
+ if (status === 0) {
+ Log(`Set boot option to ${id}`);
+ return true;
+ }
+ LogWarning("Unable to set boot option using bootctl");
+ return false;
+ }
+
+
+ /**
+ * Can we use this bootloader?
+ * @returns True if useable otherwise false
+ */
+ static async IsUseable() {
+ return await this.GetBinary() !== "";
+ }
+
+ /**
+ * Get's bootctl binary path
+ * @returns A string containing the location of the binary, if none is found returns a blank string
+ */
+ static async GetBinary() {
+ let paths = ["/usr/sbin/bootctl", "/usr/bin/bootctl"];
+
+ let file;
+
+ for (let i = 0; i < paths.length; i++) {
+ file = Gio.file_new_for_path(paths[i]);
+ if (file.query_exists(null)) {
+ return paths[i];
+ }
+ }
+
+ return "";
+ }
+
+ /**
+ * This boot loader cannot be quick rebooted
+ */
+ static async CanQuickReboot() {
+ return false;
+ }
+
+ /**
+ * This boot loader cannot be quick rebooted
+ */
+ static async QuickRebootEnabled() {
+ return false;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/utils.js b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/utils.js
new file mode 100644
index 0000000..1c7514a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/customreboot@nova1545/utils.js
@@ -0,0 +1,89 @@
+/* utils.js
+ *
+ * Copyright (C) 2020
+ * Daniel Shchur (DocQuantum) <shchurgood@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+*/
+
+import Gio from "gi://Gio";
+
+
+var DEBUG = false;
+
+/**
+ * @param {String[]} argv
+ * @param {String} input
+ * @param {Gio.Cancellable} cancellable
+ * @returns {Promise} Function execution
+ * => @returns {[int, String, String]} [StatusCode, STDOUT, STDERR]
+ *
+ * Executes a command asynchronously.
+ */
+export async function ExecCommand(argv, input = null, cancellable = null) {
+ let flags = Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE;
+
+ if (input !== null)
+ flags |= Gio.SubprocessFlags.STDIN_PIPE;
+
+ let proc = new Gio.Subprocess({
+ argv: argv,
+ flags: flags
+ });
+ proc.init(cancellable);
+ return new Promise((resolve,reject) => {
+ proc.communicate_utf8_async(input, cancellable, (proc, res) => {
+ try {
+ resolve([(function() {
+ if(!proc.get_if_exited())
+ throw new Error("Subprocess failed to exit in time!");
+ return proc.get_exit_status()
+ })()].concat(proc.communicate_utf8_finish(res).slice(1)));
+ } catch (e) {
+ reject(e);
+ }
+ });
+ });
+}
+
+/**
+ * @param {bool} value
+ *
+ * Set's whether to debug or to not.
+ */
+export function SetDebug(value){
+ DEBUG = value;
+}
+
+/**
+ * @param {string} msg
+ *
+ * Logs general messages if debug is set to true.
+ */
+export function Log(msg) {
+ if(DEBUG)
+ console.log(`CustomReboot NOTE: ${msg}`);
+}
+
+/**
+ * @param {string} msg
+ *
+ * Logs warning messages if debug is set to true.
+ */
+export function LogWarning(msg) {
+ if(DEBUG)
+ console.warn(`CustomReboot WARN: ${msg}`);
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md
new file mode 100644
index 0000000..b0dc634
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/README.md
@@ -0,0 +1,53 @@
+# Dash to Dock
+![screenshot](https://github.com/micheleg/dash-to-dock/raw/master/media/screenshot.jpg)
+
+## A dock for the GNOME Shell
+This extension enhances the dash moving it out of the overview and transforming it in a dock for an easier launching of applications and a faster switching between windows and desktops without having to leave the desktop view.
+
+[<img src="https://micheleg.github.io/dash-to-dock/media/get-it-on-ego.png" height="100">](https://extensions.gnome.org/extension/307/dash-to-dock)
+
+For additional installation instructions and more information visit [https://micheleg.github.io/dash-to-dock/](https://micheleg.github.io/dash-to-dock/).
+
+## Installation from source
+
+The extension can be installed directly from source, either for the convenience of using git or to test the latest development version. Clone the desired branch with git
+
+### Build Dependencies
+
+To compile the stylesheet you'll need an implementation of SASS. Dash to Dock supports `dart-sass` (`sass`), `sassc`, and `ruby-sass`. Every distro should have at least one of these implementations, we recommend using `dart-sass` (`sass`) or `sassc` over `ruby-sass` as `ruby-sass` is deprecated.
+
+By default, Dash to Dock will attempt to build with `sassc`. To change this behavior set the `SASS` environment variable to either `dart` or `ruby`.
+
+```bash
+export SASS=dart
+# or...
+export SASS=ruby
+```
+
+### Building
+
+Clone the repository or download the branch from github. A simple Makefile is included.
+
+Next use `make` to install the extension into your home directory. A Shell reload is required <kbd>Alt</kbd> + <kbd>F2</kbd> <kbd>r</kbd> <kbd>Enter</kbd> under Xorg or under Wayland you may have to logout and login. The extension has to be enabled with *gnome-extensions-app* (GNOME Extensions) or with *dconf*.
+
+```bash
+git clone https://github.com/micheleg/dash-to-dock.git
+make -C dash-to-dock install
+```
+
+If `msgfmt` is not available on your system, you will see an error message like the following:
+
+```bash
+make: msgfmt: No such file or directory
+```
+
+In this case install the `gettext` package from your distribution's repository.
+
+
+## Bug Reporting
+
+Bugs should be reported to the Github bug tracker [https://github.com/micheleg/dash-to-dock/issues](https://github.com/micheleg/dash-to-dock/issues).
+
+## License
+Dash to Dock Gnome Shell extension is distributed under the terms of the GNU General Public License,
+version 2 or later. See the COPYING file for details.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui
new file mode 100644
index 0000000..b16ca59
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/Settings.ui
@@ -0,0 +1,3100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <object class="GtkAdjustment" id="animation_time_adjustment">
+ <property name="upper">1</property>
+ <property name="step-increment">0.050000000000000003</property>
+ <property name="page-increment">0.25</property>
+ </object>
+ <object class="GtkBox" id="box_middle_click_options">
+ <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>
+ <child>
+ <object class="GtkFrame" id="frame_middle_click_options">
+ <property name="can_focus">0</property>
+ <property name="child">
+ <object class="GtkListBox" id="listbox_middle_click_options">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow11">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme7">
+ <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="builtin_theme_description4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">When set to minimize, double clicking minimizes all the windows of the application.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</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="builtin_theme_label4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Shift+Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="shift_click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ <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="listboxrow_middle_click">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_middle_click">
+ <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="description_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Behavior for Middle-Click.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</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="label_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Middle-Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="middle_click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ <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="listboxrow_shift_middle_click">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_shift_middle_click">
+ <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="description_shift_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Behavior for Shift+Middle-Click.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</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="label_shift_middle_click">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Shift+Middle-Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="shift_middle_click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize window</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ <item translatable="yes">Quit</item>
+ </items>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="custom_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10</property>
+ </object>
+ <object class="GtkAdjustment" id="dock_size_adjustment">
+ <property name="lower">0.33</property>
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10</property>
+ </object>
+ <object class="GtkAdjustment" id="dot_border_width_adjustment">
+ <property name="upper">10</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">5</property>
+ </object>
+ <object class="GtkBox" id="running_dots_advance_settings_box">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <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>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox7">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow10">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox" id="dot_style_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="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="unity_backlit_items_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="label" translatable="yes">Enable Unity7 like glossy backlit items</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="unity_backlit_items_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="apply_gloss_effect_checkbutton">
+ <property name="label" translatable="yes">Apply glossy effect.</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <child>
+ <object class="GtkLabel" id="dominant_color_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Use dominant color</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="dominant_color_switch"/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="can_focus">0</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSwitch" id="dot_style_switch">
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Customize indicator style</property>
+ <property name="justify">fill</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="dot_style_settings_box">
+ <property name="can_focus">0</property>
+ <property name="margin_bottom">1</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkBox" id="dot_color_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_color_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Color</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkColorButton" id="dot_color_colorbutton">
+ <property name="receives_default">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="border_color_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_border_color_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Border color</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkColorButton" id="dot_border_color_colorbutton">
+ <property name="receives_default">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_boder_width_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dot_border_width_label">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Border width</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="dot_border_width_spin_button">
+ <property name="adjustment">dot_border_width_adjustment</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="hide_timeout_adjustment">
+ <property name="upper">1</property>
+ <property name="step-increment">0.050000000000000003</property>
+ <property name="page-increment">0.25</property>
+ </object>
+ <object class="GtkAdjustment" id="icon_size_adjustment">
+ <property name="lower">16</property>
+ <property name="upper">128</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="preview_size_adjustment">
+ <property name="lower">0.0</property>
+ <property name="upper">1</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.1</property>
+ </object>
+ <object class="GtkNotebook" id="settings_notebook">
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="child">
+ <object class="GtkBox" id="position_and_size">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="dock_display">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="dock_display_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="dock_monitor_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="dock_monitor_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="dock_monitor_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock on</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="dock_monitor_combo">
+ <property name="can_focus">0</property>
+ <property name="width_request">400</property>
+ <property name="valign">center</property>
+ <signal name="changed" handler="dock_display_combo_changed_cb" />
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="multi_monitor_button">
+ <property name="label" translatable="yes">Show on all monitors</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="dock_position_listboxrow">
+ <property name="width_request">100</property>
+ <property name="child">
+ <object class="GtkBox" id="dock_position_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="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="dock_position_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Position on screen</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="dock_position_butttons_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</property>
+ <child>
+ <object class="GtkCheckButton" id="position_left_button">
+ <property name="label" translatable="yes">Left</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+
+ <signal name="toggled" handler="position_left_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="position_bottom_button">
+ <property name="label" translatable="yes">Bottom</property>
+ <property name="halign">center</property>
+
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_bottom_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="position_top_button">
+ <property name="label" translatable="yes">Top</property>
+ <property name="halign">center</property>
+
+
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_top_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="position_right_button">
+ <property name="label" translatable="yes">Right</property>
+ <property name="valign">center</property>
+
+ <property name="group">position_left_button</property>
+ <signal name="toggled" handler="position_right_button_toggled_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="intelligent_autohide_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox3">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow14">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="intelligent_autohide_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="builtin_theme_description7">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Hide the dock when it obstructs a window of the current application. More refined settings are available.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="builtin_theme_label8">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Intelligent autohide</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="intelligent_autohide_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="intelligent_autohide_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="size_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="size_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="dock_size_listboxrow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="dock_size_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="dock_size_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Dock size limit</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="dock_size_scale">
+ <property name="draw-value">1</property>
+ <property name="valign">baseline</property>
+ <property name="hexpand">1</property>
+ <property name="adjustment">dock_size_adjustment</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="dock_size_scale_value_changed_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="dock_size_extend_checkbutton">
+ <property name="label" translatable="yes">Panel mode: extend to the screen edge</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="dock_center_icons_check">
+ <property name="label" translatable="yes">Place icons to the center</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="icon_size_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="icon_size_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="builtin_theme_label6">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Icon size limit</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="icon_size_scale">
+ <property name="draw-value">1</property>
+ <property name="valign">baseline</property>
+ <property name="hexpand">1</property>
+ <property name="adjustment">icon_size_adjustment</property>
+ <property name="round_digits">1</property>
+ <property name="digits">0</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="icon_size_scale_value_changed_cb" />
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="icon_size_fixed_checkbutton">
+ <property name="label" translatable="yes">Fixed icon size: scroll to reveal other icons</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="preview_size_listboxrow">
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkGrid" id="preview_size_grid">
+ <property name="can_focus">False</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="builtin_theme_label7">
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Preview size scale</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="preview_size_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">preview_size_adjustment</property>
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <!-- <signal name="format-value" handler="preview_size_scale_format_value_cb" /> -->
+ <signal name="value-changed" handler="preview_size_scale_value_changed_cb" />
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <property name="tab">
+ <object class="GtkLabel" id="general_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Position and size</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">1</property>
+ <property name="child">
+ <object class="GtkBox" id="apps">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="customize_theme1">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox9">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow6">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash1">
+ <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="show_favorite_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label1">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show pinned applications</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow16">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash2">
+ <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="show_running_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show running applications</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_isolation_button">
+ <property name="label" translatable="yes">Isolate workspaces</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_urgent_button">
+ <property name="label" translatable="yes">Show urgent windows despite current workspace</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_monitor_isolation_button">
+ <property name="label" translatable="yes">Isolate monitors</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">4</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="windows_preview_button">
+ <property name="margin_top">3</property>
+ <property name="label" translatable="yes">Show open windows' previews</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="child">
+ <object class="GtkGrid">
+ <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="scroll_to_icon_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Keep the focused application always visible in the dash</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow17">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash3">
+ <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="show_applications_button_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_description1">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">If disabled, these settings are accessible from gnome-tweak-tool or the extension website.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="shrink_dash_label3">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show &lt;i&gt;Applications&lt;/i&gt; icon</property>
+ <property name="use_markup">1</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_first_button">
+ <property name="label" translatable="yes">Move at beginning of the dock</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="application_button_animation_button">
+ <style>
+ <class name="text-button"/>
+ </style>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Animate &lt;i&gt;Show Applications&lt;/i&gt;</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_apps_always_in_the_edge">
+ <style>
+ <class name="text-button"/>
+ </style>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Put &lt;i&gt;Show Applications&lt;/i&gt; in a dock edge when using Panel mode</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">4</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow18">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash4">
+ <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="show_trash_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label4">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show trash can</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow19">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash5">
+ <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="show_mounts_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_label5">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show volumes and devices</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_only_mounted_devices_check">
+ <property name="label" translatable="yes">Only if mounted</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_network_volumes_check">
+ <property name="label" translatable="yes">Include network volumes</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="isolate_locations_row">
+ <property name="child">
+ <object class="GtkGrid">
+ <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="isolate_locations_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Isolate volumes, devices and trash windows from file manager</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="wiggle_urgent_applications_row">
+ <property name="child">
+ <object class="GtkGrid">
+ <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="wiggle_urgent_applications_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Wiggle urgent applications</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="hide_tooltip_row">
+ <property name="child">
+ <object class="GtkGrid">
+ <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="hide_tooltip_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Hide application tooltip</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="child">
+ <object class="GtkGrid">
+ <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="show_icons_emblems_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show icons emblems</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">When enabled application icons will show notification counters and progress-bars (if Unity API is used).</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkCheckButton" id="notifications_counter_check">
+ <property name="label" translatable="yes">Show the number of unread notifications</property>
+ <property name="halign">start</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="applications_override_counter">
+ <style>
+ <class name="text-button"/>
+ </style>
+ <property name="margin_top">3</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Application-provided counter overrides the notifications counter</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</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="launchers_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Launchers</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">2</property>
+ <property name="child">
+ <object class="GtkBox" id="behaviour">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="hot_keys_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="hot_keys_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="hot_keys_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="hot_keys_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="hot_keys_description">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Enable Super+(0-9) as shortcuts to activate apps. It can also be used together with Shift and Ctrl.</property>
+ <property name="use_markup">1</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="hot_keys_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Use keyboard shortcuts to activate apps</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="overlay_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="overlay_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ <child>
+ <object class="GtkImage" id="image_overlay">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="hot_keys_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame3">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox6">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow9">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme5">
+ <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="builtin_theme_description5">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Behaviour when clicking on the icon of a running application.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="builtin_theme_label5">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Click action</property>
+ <property name="xalign">0</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="click_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="middle_click_options_button">
+ <property name="receives_default">1</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage" id="middle_click_image">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="click_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Raise window</item>
+ <item translatable="yes">Minimize</item>
+ <item translatable="yes">Launch new instance</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Minimize or overview</item>
+ <item translatable="yes">Show window previews</item>
+ <item translatable="yes">Minimize or show previews</item>
+ <item translatable="yes">Focus or show previews</item>
+ <item translatable="yes">Focus or app spread</item>
+ <item translatable="yes">Focus, minimize or show previews</item>
+ <item translatable="yes">Focus, minimize or app spread</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame_scroll">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox5">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_scroll">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme_scroll">
+ <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="builtin_theme_description_scroll">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Behaviour when scrolling on the icon of an application.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="builtin_theme_label_scroll">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Scroll action</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="click_box_scroll">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkComboBoxText" id="scroll_action_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item translatable="yes">Do nothing</item>
+ <item translatable="yes">Cycle through windows</item>
+ <item translatable="yes">Switch workspace</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</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="behaviour_label">
+ <property name="can_focus">0</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Behavior</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">3</property>
+ <property name="child">
+ <object class="GtkBox" id="appearance">
+ <property name="can_focus">0</property>
+ <property name="margin_start">24</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">24</property>
+ <child>
+ <object class="GtkFrame" id="built_in_theme_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox1">
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow2">
+ <property name="child">
+ <object class="GtkGrid" id="shrink_dash">
+ <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="shrink_dash_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shrink_dash_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Save space reducing padding and border radius.</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="shrink_dash_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Shrink the dash</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_rnd_border">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</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="force_straight_corner_label">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Force straight corner</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="force_straight_corner_switch">
+ <property name="valign">center</property>
+ <property name="margin_start">3</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ <object class="GtkListBoxRow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox">
+ <property name="can_focus">0</property>
+ <property name="spacing">32</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">
+ <property name="hexpand">1</property>
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show overview on startup</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="show_overview_on_startup_switch">
+ <property name="valign">center</property>
+ <property name="margin_start">3</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme">
+ <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="builtin_theme_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Few customizations meant to integrate the dock with the default GNOME theme. Alternatively, specific options can be enabled below.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="builtin_theme_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Use built-in theme</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="builtin_theme_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <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="GtkListBox" id="customize_theme">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow3">
+ <property name="child">
+ <object class="GtkGrid" id="running_dots">
+ <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="running_dots_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Customize windows counter indicators</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="running_indicators_advance_settings_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="running_indicators_combo">
+ <property name="can_focus">0</property>
+ <items>
+ <item translatable="yes">Default</item>
+ <item translatable="yes">Dots</item>
+ <item translatable="yes">Squares</item>
+ <item translatable="yes">Dashes</item>
+ <item translatable="yes">Segmented</item>
+ <item translatable="yes">Solid</item>
+ <item translatable="yes">Ciliora</item>
+ <item translatable="yes">Metro</item>
+ <item translatable="yes">Binary</item>
+ <item translatable="yes">Dot</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="custom_background_color_listboxrow">
+ <property name="child">
+ <object class="GtkGrid" id="custom_background_color_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="custom_background_color_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Set the background color for the dash.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="custom_background_color_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Customize the dash color</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="custom_background_color_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkColorButton" id="custom_background_color">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="custom_background_color_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ <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="listboxrow4">
+ <property name="child">
+ <object class="GtkBox" id="box2">
+ <property name="can_focus">0</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkGrid" id="customize_opacity">
+ <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="customize_opacity_description">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Tune the dash background opacity.</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="customize_opacity_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Customize opacity</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="customize_opacity_box">
+ <property name="can_focus">0</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="dynamic_opacity_button">
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <child>
+ <object class="GtkImage" id="dynamic_opacity_image">
+ <property name="can_focus">0</property>
+ <property name="icon_name">emblem-system-symbolic</property>
+ </object>
+ </child>
+ <style>
+ <class name="circular"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="customize_opacity_combo">
+ <property name="can_focus">0</property>
+ <property name="valign">center</property>
+ <items>
+ <item id="0" translatable="yes">Default</item>
+ <item id="1" translatable="yes">Fixed</item>
+ <item id="3" translatable="yes">Dynamic</item>
+ </items>
+ </object>
+ </child>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="custom_opacity">
+ <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="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="custom_opacity_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Opacity</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="custom_opacity_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">custom_opacity_adjustement</property>
+
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="custom_opacity_scale_value_changed_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ <property name="tab">
+ <object class="GtkLabel" id="appearance_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Appearance</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkNotebookPage">
+ <property name="position">4</property>
+ <property name="child">
+ <object class="GtkBox" id="about">
+ <property name="visible">0</property>
+ <property name="can_focus">0</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</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">./media/logo.svg</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="extension_name">
+ <property name="can_focus">0</property>
+ <property name="label">&lt;b&gt;Dash to Dock&lt;/b&gt;</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="can_focus">0</property>
+ <property name="halign">center</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">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="GtkLabel" id="extension_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Moves the dash out of the overview transforming it in a dock</property>
+ <property name="justify">center</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box10">
+ <property name="can_focus">0</property>
+ <property name="halign">center</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Created by</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="label">Michele (&lt;a href=&quot;mailto:micxgx@gmail.com&quot;&gt;micxgx@gmail.com&lt;/a&gt;)</property>
+ <property name="use_markup">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="homepage_link">
+ <property name="label" translatable="yes">Webpage</property>
+ <property name="receives_default">1</property>
+ <property name="halign">center</property>
+
+ <property name="uri">https://micheleg.github.io/dash-to-dock/</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="vexpand">1</property>
+ <property name="valign">end</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/old-licenses/gpl-2.0.html&quot;&gt;GNU General Public License, version 2 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>
+ <property name="xalign">0</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>
+ <object class="GtkAdjustment" id="max_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="min_opacity_adjustement">
+ <property name="upper">1</property>
+ <property name="step-increment">0.01</property>
+ <property name="page-increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkBox" id="advanced_transparency_dialog">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <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>
+ <child>
+ <object class="GtkFrame" id="advanced_transparency_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="advanced_transparency_listbox">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="advanced_transparency_listboxrow">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkBox" id="advanced_transparency_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="GtkBox" id="min_alpha_box_1">
+ <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="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="customize_alphas_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Customize minimum and maximum opacity values</property>
+ <property name="justify">fill</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="customize_alphas_switch">
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="min_alpha_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="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="min_alpha_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Minimum opacity</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="min_alpha_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">min_opacity_adjustement</property>
+
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="min_opacity_scale_value_changed_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="max_alpha_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="spacing">32</property>
+ <child>
+ <object class="GtkLabel" id="max_alpha_label">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Maximum opacity</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="max_alpha_scale">
+ <property name="hexpand">1</property>
+ <property name="draw-value">1</property>
+ <property name="adjustment">max_opacity_adjustement</property>
+
+ <property name="restrict_to_fill_level">0</property>
+ <property name="fill_level">0</property>
+ <property name="round_digits">0</property>
+ <property name="digits">2</property>
+ <property name="value_pos">right</property>
+ <signal name="value-changed" handler="max_opacity_scale_value_changed_cb" />
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="pressure_threshold_adjustment">
+ <property name="upper">100</property>
+ <property name="step-increment">5</property>
+ <property name="page-increment">25</property>
+ </object>
+ <object class="GtkAdjustment" id="shortcut_time_adjustment">
+ <property name="upper">10</property>
+ <property name="step-increment">0.25</property>
+ <property name="page-increment">1</property>
+ </object>
+ <object class="GtkBox" id="box_overlay_shortcut">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</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>
+ <child>
+ <object class="GtkFrame" id="frame_overlay_show_dock">
+ <property name="child">
+ <object class="GtkListBox" id="listbox_overlay_shortcut">
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_overlay_shortcut">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_overlay">
+ <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="overlay_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="overlay_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Number overlay</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="overlay_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Temporarily show the application numbers over the icons, corresponding to the shortcut.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</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>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_show_dock">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_show_dock">
+ <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="show_dock_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_dock_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock if it is hidden</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_dock_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">If using autohide, the dock will appear for a short time when triggering the shortcut.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</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>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_extra_shortcut">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="child">
+ <object class="GtkGrid" id="grid_shortcut">
+ <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="GtkEntry" id="shortcut_entry">
+ <property name="valign">center</property>
+ <property name="width_chars">12</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Shortcut for the options above</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_description">
+ <property name="can_focus">0</property>
+ <property name="label" translatable="yes">Syntax: &lt;Shift&gt;, &lt;Ctrl&gt;, &lt;Alt&gt;, &lt;Super&gt;</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</property>
+ <property name="max_width_chars">40</property>
+
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_timeout">
+ <property name="child">
+ <object class="GtkGrid" id="grid_timeout">
+ <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="hexpand">1</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSpinButton" id="timeout_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">shortcut_time_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="shortcut_timeout_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Hide timeout (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <object class="GtkAdjustment" id="show_timeout_adjustment">
+ <property name="upper">1</property>
+ <property name="step-increment">0.050000000000000003</property>
+ <property name="page-increment">0.25</property>
+ </object>
+ <object class="GtkBox" id="intelligent_autohide_advanced_settings_box">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <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>
+ <child>
+ <object class="GtkFrame" id="intelligent_autohide_advanced_settings_frame">
+ <property name="can_focus">0</property>
+
+ <property name="child">
+ <object class="GtkListBox" id="listbox4">
+ <property name="can_focus">0</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow8">
+ <property name="child">
+ <object class="GtkGrid" id="buitin_theme2">
+ <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="builtin_theme_description2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock by mouse hover on the screen edge.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="builtin_theme_label2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Autohide</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="autohide_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="require_pressure_checkbutton">
+ <property name="label" translatable="yes">Push to show: require pressure to show the dock</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="autohide_enable_in_fullscreen_checkbutton">
+ <property name="label" translatable="yes">Enable in fullscreen mode</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="show_dock_urgent_notify_checkbutton">
+ <property name="label" translatable="yes">Show dock for urgent notifications</property>
+ <property name="margin_top">12</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">4</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow15">
+ <property name="child">
+ <object class="GtkGrid" id="intellihide_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="builtin_theme_description3">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Show the dock when it doesn&apos;t obstruct application windows.</property>
+ <property name="wrap">1</property>
+ <property name="xalign">0</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="builtin_theme_label3">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Dodge windows</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="intellihide_switch">
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="intellihide_mode_box">
+ <property name="can_focus">0</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="all_windows_radio_button">
+ <property name="label" translatable="yes">All windows</property>
+ <property name="margin_top">12</property>
+
+ <signal name="toggled" handler="all_windows_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="focus_application_windows_radio_button">
+ <property name="label" translatable="yes">Only focused application&apos;s windows</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="focus_application_windows_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="maximized_windows_radio_button">
+ <property name="label" translatable="yes">Only maximized windows</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="maximized_windows_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="always_on_top_radio_button">
+ <property name="label" translatable="yes">Always on top</property>
+ <property name="group">all_windows_radio_button</property>
+ <signal name="toggled" handler="always_on_top_radio_button_toggled_cb" />
+ </object>
+ </child>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow5">
+ <property name="child">
+ <object class="GtkGrid" id="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="hexpand">1</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">32</property>
+ <child>
+ <object class="GtkSpinButton" id="animation_duration_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">animation_time_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Animation duration (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="hide_timeout_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">hide_timeout_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="show_timeout_spinbutton">
+ <property name="halign">end</property>
+ <property name="adjustment">show_timeout_adjustment</property>
+ <property name="digits">3</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="pressure_threshold_spinbutton">
+ <property name="text">0.000</property>
+ <property name="adjustment">pressure_threshold_adjustment</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Hide timeout (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="show_timeout_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Show timeout (s)</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="pressure_threshold_label">
+ <property name="can_focus">0</property>
+ <property name="hexpand">1</property>
+ <property name="label" translatable="yes">Pressure threshold</property>
+
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </property>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js
new file mode 100644
index 0000000..d981c0f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconIndicators.js
@@ -0,0 +1,1305 @@
+import {
+ Clutter,
+ Cogl,
+ GdkPixbuf,
+ Gio,
+ GObject,
+ Pango,
+ St,
+} from './dependencies/gi.js';
+
+import {Main} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {cairo: Cairo} = imports;
+
+const RunningIndicatorStyle = Object.freeze({
+ DEFAULT: 0,
+ DOTS: 1,
+ SQUARES: 2,
+ DASHES: 3,
+ SEGMENTED: 4,
+ SOLID: 5,
+ CILIORA: 6,
+ METRO: 7,
+ BINARY: 8,
+ DOT: 9,
+});
+
+const MAX_WINDOWS_CLASSES = 4;
+
+
+/*
+ * This is the main indicator class to be used. The desired behavior is
+ * obtained by composing the desired classes below based on the settings.
+ *
+ */
+export class AppIconIndicator {
+ constructor(source) {
+ this._indicators = [];
+
+ // Choose the style for the running indicators
+ let runningIndicator = null;
+ let runningIndicatorStyle;
+
+ const {settings} = Docking.DockManager;
+ if (settings.applyCustomTheme)
+ runningIndicatorStyle = RunningIndicatorStyle.DOTS;
+ else
+ ({runningIndicatorStyle} = settings);
+
+ if (settings.showIconsEmblems &&
+ !Docking.DockManager.getDefault().notificationsMonitor.dndMode) {
+ const unityIndicator = new UnityIndicator(source);
+ this._indicators.push(unityIndicator);
+ }
+
+ switch (runningIndicatorStyle) {
+ case RunningIndicatorStyle.DEFAULT:
+ runningIndicator = new RunningIndicatorDefault(source);
+ break;
+
+ case RunningIndicatorStyle.DOTS:
+ runningIndicator = new RunningIndicatorDots(source);
+ break;
+
+ case RunningIndicatorStyle.SQUARES:
+ runningIndicator = new RunningIndicatorSquares(source);
+ break;
+
+ case RunningIndicatorStyle.DASHES:
+ runningIndicator = new RunningIndicatorDashes(source);
+ break;
+
+ case RunningIndicatorStyle.SEGMENTED:
+ runningIndicator = new RunningIndicatorSegmented(source);
+ break;
+
+ case RunningIndicatorStyle.SOLID:
+ runningIndicator = new RunningIndicatorSolid(source);
+ break;
+
+ case RunningIndicatorStyle.CILIORA:
+ runningIndicator = new RunningIndicatorCiliora(source);
+ break;
+
+ case RunningIndicatorStyle.METRO:
+ runningIndicator = new RunningIndicatorMetro(source);
+ break;
+
+ case RunningIndicatorStyle.BINARY:
+ runningIndicator = new RunningIndicatorBinary(source);
+ break;
+
+ case RunningIndicatorStyle.DOT:
+ runningIndicator = new RunningIndicatorDot(source);
+ break;
+
+ default:
+ runningIndicator = new RunningIndicatorBase(source);
+ }
+
+ this._indicators.push(runningIndicator);
+ }
+
+ update() {
+ for (let i = 0; i < this._indicators.length; i++) {
+ const indicator = this._indicators[i];
+ indicator.update();
+ }
+ }
+
+ destroy() {
+ for (let i = 0; i < this._indicators.length; i++) {
+ const indicator = this._indicators[i];
+ indicator.destroy();
+ }
+ }
+}
+
+/*
+ * Base class to be inherited by all indicators of any kind
+*/
+class IndicatorBase {
+ constructor(source) {
+ this._source = source;
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this._source);
+ }
+
+ update() {
+ }
+
+ destroy() {
+ this._source = null;
+ this._signalsHandler.destroy();
+ this._signalsHandler = null;
+ }
+}
+
+/*
+ * A base indicator class for running style, from which all other RunningIndicators should derive,
+ * providing some basic methods, variables definitions and their update, css style classes handling.
+ *
+ */
+class RunningIndicatorBase extends IndicatorBase {
+ constructor(source) {
+ super(source);
+
+ this._side = Utils.getPosition();
+ this._dominantColorExtractor = new DominantColorExtractor(this._source.app);
+ this._signalsHandler.add(this._source, 'notify::running', () => this.update());
+ this._signalsHandler.add(this._source, 'notify::focused', () => this.update());
+ this._signalsHandler.add(this._source, 'notify::windows-count', () => this._updateCounterClass());
+ this.update();
+ }
+
+ get _number() {
+ return Math.min(this._source.windowsCount, MAX_WINDOWS_CLASSES);
+ }
+
+ update() {
+ this._updateCounterClass();
+ this._updateDefaultDot();
+ }
+
+ _updateCounterClass() {
+ for (let i = 1; i <= MAX_WINDOWS_CLASSES; i++) {
+ const className = `running${i}`;
+ if (i !== this._number)
+ this._source.remove_style_class_name(className);
+ else
+ this._source.add_style_class_name(className);
+ }
+ }
+
+ _updateDefaultDot() {
+ if (this._source.running)
+ this._source._dot.show();
+ else
+ this._source._dot.hide();
+ }
+
+ _hideDefaultDot() {
+ // I use opacity to hide the default dot because the show/hide function
+ // are used by the parent class.
+ this._source._dot.opacity = 0;
+ }
+
+ _restoreDefaultDot() {
+ this._source._dot.opacity = 255;
+ }
+
+ _enableBacklight() {
+ const colorPalette = this._dominantColorExtractor._getColorPalette();
+
+ // Fallback
+ if (!colorPalette) {
+ this._source._iconContainer.set_style(
+ 'border-radius: 5px;' +
+ 'background-gradient-direction: vertical;' +
+ 'background-gradient-start: #e0e0e0;' +
+ 'background-gradient-end: darkgray;'
+ );
+
+ return;
+ }
+
+ this._source._iconContainer.set_style(
+ `${'border-radius: 5px;' +
+ 'background-gradient-direction: vertical;' +
+ 'background-gradient-start: '}${colorPalette.original};` +
+ `background-gradient-end: ${colorPalette.darker};`
+ );
+ }
+
+ _disableBacklight() {
+ this._source._iconContainer.set_style(null);
+ }
+
+ destroy() {
+ this._disableBacklight();
+ // Remove glossy background if the children still exists
+ if (this._source._iconContainer.get_children().length > 1)
+ this._source._iconContainer.get_children()[1].set_style(null);
+ this._restoreDefaultDot();
+
+ super.destroy();
+ }
+}
+
+// We add a css class so third parties themes can limit their indicator customization
+// to the case we do nothing
+class RunningIndicatorDefault extends RunningIndicatorBase {
+ constructor(source) {
+ super(source);
+ this._source.add_style_class_name('default');
+ }
+
+ destroy() {
+ this._source.remove_style_class_name('default');
+ super.destroy();
+ }
+}
+
+const IndicatorDrawingArea = GObject.registerClass(
+class IndicatorDrawingArea extends St.DrawingArea {
+ vfunc_allocate(box) {
+ if (box.x1 !== 0 || box.y1 !== 0)
+ return super.vfunc_allocate(box);
+
+ // We assume that the are is a rectangle in the operations below:
+ const size = Math.min(box.get_width(), box.get_height());
+ box.x2 = size;
+ box.y2 = size;
+ this.set_allocation(box);
+
+ return super.vfunc_allocate(box);
+ }
+});
+
+class RunningIndicatorDots extends RunningIndicatorBase {
+ constructor(source) {
+ super(source);
+
+ this._hideDefaultDot();
+
+ this._area = new IndicatorDrawingArea({
+ x_expand: true,
+ y_expand: true,
+ });
+
+ // We draw for the bottom case and rotate the canvas for other placements
+ // set center of rotations to the center
+ this._area.set_pivot_point(0.5, 0.5);
+
+ switch (this._side) {
+ case St.Side.TOP:
+ this._area.rotation_angle_z = 180;
+ break;
+
+ case St.Side.BOTTOM:
+ // nothing
+ break;
+
+ case St.Side.LEFT:
+ this._area.rotation_angle_z = 90;
+ break;
+
+ case St.Side.RIGHT:
+ this._area.rotation_angle_z = -90;
+ break;
+ }
+
+ this._area.connectObject('repaint', this._updateIndicator.bind(this), this);
+ this._source._iconContainer.add_child(this._area);
+
+ const keys = ['custom-theme-running-dots-color',
+ 'custom-theme-running-dots-border-color',
+ 'custom-theme-running-dots-border-width',
+ 'custom-theme-customize-running-dots',
+ 'unity-backlit-items',
+ 'apply-glossy-effect',
+ 'running-indicator-dominant-color'];
+
+ keys.forEach(function (key) {
+ this._signalsHandler.add(
+ Docking.DockManager.settings,
+ `changed::${key}`,
+ this.update.bind(this)
+ );
+ }, this);
+
+ // Apply glossy background
+ // TODO: move to enable/disableBacklit to apply it only to the running apps?
+ // TODO: move to css class for theming support
+ const {extension} = Docking.DockManager;
+ this._glossyBackgroundStyle = `background-image: url('${extension.path}/media/glossy.svg');` +
+ 'background-size: contain;';
+ }
+
+ update() {
+ super.update();
+
+ // Enable / Disable the backlight of running apps
+ if (!Docking.DockManager.settings.applyCustomTheme &&
+ Docking.DockManager.settings.unityBacklitItems) {
+ const [icon] = this._source._iconContainer.get_children();
+ icon.set_style(
+ Docking.DockManager.settings.applyGlossyEffect
+ ? this._glossyBackgroundStyle : null);
+ if (this._source.running)
+ this._enableBacklight();
+ else
+ this._disableBacklight();
+ } else {
+ this._disableBacklight();
+ this._source._iconContainer.get_children()[1].set_style(null);
+ }
+
+ if (this._area)
+ this._area.queue_repaint();
+ }
+
+ _computeStyle() {
+ const [width, height] = this._area.get_surface_size();
+ this._width = height;
+ this._height = width;
+
+ // By default re-use the style - background color, and border width and color -
+ // of the default dot
+ const themeNode = this._source._dot.get_theme_node();
+ this._borderColor = themeNode.get_border_color(this._side);
+ this._borderWidth = themeNode.get_border_width(this._side);
+ this._bodyColor = themeNode.get_background_color();
+
+ const {settings} = Docking.DockManager;
+ if (!settings.applyCustomTheme) {
+ // Adjust for the backlit case
+ const Color = Clutter.Color ?? Cogl.Color;
+
+ if (settings.unityBacklitItems) {
+ // Use dominant color for dots too if the backlit is enables
+ const colorPalette = this._dominantColorExtractor._getColorPalette();
+
+ // Slightly adjust the styling
+ this._borderWidth = 2;
+
+ if (colorPalette) {
+ [, this._borderColor] = Color.from_string(colorPalette.lighter);
+ [, this._bodyColor] = Color.from_string(colorPalette.darker);
+ } else {
+ // Fallback
+ [, this._borderColor] = Color.from_string('white');
+ [, this._bodyColor] = Color.from_string('gray');
+ }
+ }
+
+ // Apply dominant color if requested
+ if (settings.runningIndicatorDominantColor) {
+ const colorPalette = this._dominantColorExtractor._getColorPalette();
+ if (colorPalette)
+ [, this._bodyColor] = Color.from_string(colorPalette.original);
+ else
+ // Fallback
+ [, this._bodyColor] = Color.from_string(settings.customThemeRunningDotsColor);
+ }
+
+ // Finally, use customize style if requested
+ if (settings.customThemeCustomizeRunningDots) {
+ [, this._borderColor] = Color.from_string(settings.customThemeRunningDotsBorderColor);
+ this._borderWidth = settings.customThemeRunningDotsBorderWidth;
+ [, this._bodyColor] = Color.from_string(settings.customThemeRunningDotsColor);
+ }
+ }
+
+ // Define the radius as an arbitrary size, but keep large enough to account
+ // for the drawing of the border.
+ this._radius = Math.max(this._width / 22, this._borderWidth / 2);
+ this._padding = 0; // distance from the margin
+ this._spacing = this._radius + this._borderWidth; // separation between the dots
+ }
+
+ _updateIndicator() {
+ const cr = this._area.get_context();
+
+ this._computeStyle();
+ this._drawIndicator(cr);
+ cr.$dispose();
+ }
+
+ _drawIndicator(cr) {
+ // Draw the required numbers of dots
+ const n = this._number;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ // draw for the bottom case:
+ cr.translate(
+ (this._width - (2 * n) * this._radius - (n - 1) * this._spacing) / 2,
+ this._height - this._padding);
+
+ for (let i = 0; i < n; i++) {
+ cr.newSubPath();
+ cr.arc((2 * i + 1) * this._radius + i * this._spacing,
+ -this._radius - this._borderWidth / 2,
+ this._radius, 0, 2 * Math.PI);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+
+ destroy() {
+ this._area.destroy();
+ super.destroy();
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorCiliora extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ const spacing = size; // separation between the dots
+ const lineLength = this._width - (size * (this._number - 1)) - (spacing * (this._number - 1));
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ cr.newSubPath();
+ cr.rectangle(0, 0, lineLength, size);
+ for (let i = 1; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(lineLength + (i * spacing) + ((i - 1) * size), 0, size, size);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorSegmented extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ const spacing = Math.ceil(this._width / 18); // separation between the dots
+ const dashLength = Math.ceil((this._width - ((this._number - 1) * spacing)) / this._number);
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ for (let i = 0; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(i * dashLength + i * spacing, 0, dashLength, size);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorSolid extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ let padding = this._borderWidth;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(0, yOffset);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width, size);
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorSquares extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 11, this._borderWidth);
+ const padding = this._borderWidth;
+ const spacing = Math.ceil(this._width / 18); // separation between the dots
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(
+ Math.floor((this._width - this._number * size - (this._number - 1) * spacing) / 2),
+ yOffset);
+
+ for (let i = 0; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(i * size + i * spacing, 0, size, size);
+ }
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorDashes extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ const padding = this._borderWidth;
+ const spacing = Math.ceil(this._width / 18); // separation between the dots
+ const dashLength = Math.floor(this._width / 4) - spacing;
+ const yOffset = this._height - padding - size;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(
+ Math.floor((this._width - this._number * dashLength - (this._number - 1) * spacing) / 2),
+ yOffset);
+
+ for (let i = 0; i < this._number; i++) {
+ cr.newSubPath();
+ cr.rectangle(i * dashLength + i * spacing, 0, dashLength, size);
+ }
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+// Adapted from dash-to-panel by Jason DeRose
+// https://github.com/jderose9/dash-to-panel
+class RunningIndicatorMetro extends RunningIndicatorDots {
+ constructor(source) {
+ super(source);
+ this._source.add_style_class_name('metro');
+ }
+
+ destroy() {
+ this._source.remove_style_class_name('metro');
+ super.destroy();
+ }
+
+ _drawIndicator(cr) {
+ if (this._source.running) {
+ const size = Math.max(this._width / 20, this._borderWidth);
+ let padding = 0;
+ // For the backlit case here we don't want the outer border visible
+ if (Docking.DockManager.settings.unityBacklitItems &&
+ !Docking.DockManager.settings.customThemeCustomizeRunningDots)
+ padding = 0;
+ const yOffset = this._height - padding - size;
+
+ const n = this._number;
+ if (n <= 1) {
+ cr.translate(0, yOffset);
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width, size);
+ cr.fill();
+ } else {
+ // need to scale with the SVG for the stacked highlight
+ const blackenedLength = (1 / 48) * this._width;
+ const darkenedLength = this._source.focused
+ ? (2 / 48) * this._width : (10 / 48) * this._width;
+ const blackenedColor = this._bodyColor.shade(.3);
+ const darkenedColor = this._bodyColor.shade(.7);
+
+ cr.translate(0, yOffset);
+
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.newSubPath();
+ cr.rectangle(0, 0, this._width - darkenedLength - blackenedLength, size);
+ cr.fill();
+ Utils.cairoSetSourceColor(cr, blackenedColor);
+ cr.newSubPath();
+ cr.rectangle(this._width - darkenedLength - blackenedLength, 0, 1, size);
+ cr.fill();
+ Utils.cairoSetSourceColor(cr, darkenedColor);
+ cr.newSubPath();
+ cr.rectangle(this._width - darkenedLength, 0, darkenedLength, size);
+ cr.fill();
+ }
+ }
+ }
+}
+
+class RunningIndicatorBinary extends RunningIndicatorDots {
+ _drawIndicator(cr) {
+ // Draw the required numbers of dots
+ const n = Math.min(15, this._source.windowsCount);
+
+ if (this._source.running) {
+ const size = Math.max(this._width / 11, this._borderWidth);
+ const spacing = Math.ceil(this._width / 18);
+ const yOffset = this._height - size;
+ const binaryValue = String(`0000${(n >>> 0).toString(2)}`).slice(-4);
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ cr.translate(Math.floor((this._width - 4 * size - (4 - 1) * spacing) / 2), yOffset);
+ for (let i = 0; i < binaryValue.length; i++) {
+ if (binaryValue[i] === '1') {
+ cr.newSubPath();
+ cr.arc((2 * i + 1) * this._radius + i * spacing,
+ -this._radius - this._borderWidth / 2,
+ this._radius, 0, 2 * Math.PI);
+ } else {
+ cr.newSubPath();
+ cr.rectangle(i * size + i * spacing,
+ -this._radius - this._borderWidth / 2 - size / 5,
+ size, size / 3);
+ }
+ }
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+ }
+}
+
+class RunningIndicatorDot extends RunningIndicatorDots {
+ _computeStyle() {
+ super._computeStyle();
+
+ this._radius = Math.max(this._width / 26, this._borderWidth / 2);
+ }
+
+ _drawIndicator(cr) {
+ if (!this._source.running)
+ return;
+
+ cr.setLineWidth(this._borderWidth);
+ Utils.cairoSetSourceColor(cr, this._borderColor);
+
+ // draw from the bottom case:
+ cr.translate(
+ (this._width - 2 * this._radius) / 2,
+ this._height - this._padding);
+ cr.newSubPath();
+ cr.arc(this._radius,
+ -this._radius - this._borderWidth / 2,
+ this._radius, 0, 2 * Math.PI);
+
+ cr.strokePreserve();
+ Utils.cairoSetSourceColor(cr, this._bodyColor);
+ cr.fill();
+ }
+}
+
+/*
+ * Unity like notification and progress indicators
+ */
+export class UnityIndicator extends IndicatorBase {
+ static defaultProgressBar = {
+ // default values for the progress bar itself
+ background: {
+ colorStart: {red: 204, green: 204, blue: 204, alpha: 255},
+ colorEnd: null,
+ },
+ border: {
+ colorStart: {red: 230, green: 230, blue: 230, alpha: 255},
+ colorEnd: null,
+ },
+ };
+
+ static defaultProgressBarTrack = {
+ // default values for the progress bar track
+ background: {
+ colorStart: {red: 64, green: 64, blue: 64, alpha: 255},
+ colorEnd: {red: 89, green: 89, blue: 89, alpha: 255},
+ offsetStart: 0.4,
+ offsetEnd: 0.9,
+ },
+ border: {
+ colorStart: {red: 128, green: 128, blue: 128, alpha: 26},
+ colorEnd: {red: 204, green: 204, blue: 204, alpha: 102},
+ offsetStart: 0.5,
+ offsetEnd: 0.9,
+ },
+ };
+
+ static notificationBadgeSignals = Symbol('notification-badge-signals');
+
+ constructor(source) {
+ super(source);
+
+ const {remoteModel, notificationsMonitor} = Docking.DockManager.getDefault();
+ const remoteEntry = remoteModel.lookupById(this._source.app.id);
+ this._remoteEntry = remoteEntry;
+
+ this._signalsHandler.add([
+ remoteEntry,
+ ['count-changed', 'count-visible-changed'],
+ () => this._updateNotificationsCount(),
+ ], [
+ remoteEntry,
+ ['progress-changed', 'progress-visible-changed'],
+ (sender, {progress, progress_visible: progressVisible}) =>
+ this.setProgress(progressVisible ? progress : -1),
+ ], [
+ remoteEntry,
+ 'urgent-changed',
+ (sender, {urgent}) => this.setUrgent(urgent),
+ ], [
+ remoteEntry,
+ 'updating-changed',
+ (sender, {updating}) => this.setUpdating(updating),
+ ], [
+ notificationsMonitor,
+ 'changed',
+ () => this._updateNotificationsCount(),
+ ], [
+ this._source,
+ 'style-changed',
+ () => this._updateIconStyle(),
+ ]);
+
+ this._updateNotificationsCount();
+ this.setProgress(this._remoteEntry.progress_visible
+ ? this._remoteEntry.progress : -1);
+ this.setUrgent(this._remoteEntry.urgent);
+ this.setUpdating(this._remoteEntry.updating);
+ }
+
+ destroy() {
+ this._notificationBadgeBin?.destroy();
+ this._notificationBadgeBin = null;
+ this._hideProgressOverlay();
+ this.setUrgent(false);
+ this.setUpdating(false);
+ this._remoteEntry = null;
+
+ super.destroy();
+ }
+
+ _updateNotificationBadgeStyle() {
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+ const fontDesc = themeContext.get_font();
+ const defaultFontSize = fontDesc.get_size() / 1024;
+ let fontSize = defaultFontSize * 0.9;
+ const {iconSize} = Main.overview.dash;
+ const defaultIconSize = Docking.DockManager.settings.get_default_value(
+ 'dash-max-icon-size').unpack();
+
+ if (!fontDesc.get_size_is_absolute()) {
+ // fontSize was expressed in points, so convert to pixel
+ fontSize /= 0.75;
+ }
+
+ let sizeMultiplier;
+ if (iconSize < defaultIconSize) {
+ sizeMultiplier = Math.max(24, Math.min(iconSize +
+ iconSize * 0.3, defaultIconSize)) / defaultIconSize;
+ } else {
+ sizeMultiplier = iconSize / defaultIconSize;
+ }
+
+ fontSize = Math.round(sizeMultiplier * fontSize);
+ const leftMargin = Math.round(sizeMultiplier * 3);
+
+ this._notificationBadgeBin.child.set_style(
+ `font-size: ${fontSize}px;` +
+ `margin-left: ${leftMargin}px`
+ );
+ }
+
+ _notificationBadgeCountToText(count) {
+ if (count <= 9999) {
+ return count.toString();
+ } else if (count < 1e5) {
+ const thousands = count / 1e3;
+ return `${thousands.toFixed(1).toString()}k`;
+ } else if (count < 1e6) {
+ const thousands = count / 1e3;
+ return `${thousands.toFixed(0).toString()}k`;
+ } else if (count < 1e8) {
+ const millions = count / 1e6;
+ return `${millions.toFixed(1).toString()}M`;
+ } else if (count < 1e9) {
+ const millions = count / 1e6;
+ return `${millions.toFixed(0).toString()}M`;
+ } else {
+ const billions = count / 1e9;
+ return `${billions.toFixed(1).toString()}B`;
+ }
+ }
+
+ _updateNotificationsCount() {
+ const remoteCount = this._remoteEntry['count-visible']
+ ? this._remoteEntry.count ?? 0 : 0;
+
+ if (remoteCount > 0 &&
+ Docking.DockManager.settings.applicationCounterOverridesNotifications) {
+ this.setNotificationCount(remoteCount);
+ return;
+ }
+
+ const {notificationsMonitor} = Docking.DockManager.getDefault();
+ const notificationsCount = notificationsMonitor.getAppNotificationsCount(
+ this._source.app.id);
+
+ this.setNotificationCount(remoteCount + notificationsCount);
+ }
+
+ _updateNotificationsBadge(text) {
+ if (this._notificationBadgeBin) {
+ this._notificationBadgeBin.child.text = text;
+ return;
+ }
+
+ this._notificationBadgeBin = new St.Bin({
+ child: new St.Label({
+ styleClass: 'notification-badge',
+ text,
+ }),
+ xAlign: Clutter.ActorAlign.END,
+ yAlign: Clutter.ActorAlign.START,
+ xExpand: true,
+ yExpand: true,
+ });
+ this._notificationBadgeBin.child.clutterText.ellipsize =
+ Pango.EllipsizeMode.MIDDLE;
+
+ this._source._iconContainer.add_child(this._notificationBadgeBin);
+ this._updateNotificationBadgeStyle();
+
+ const themeContext = St.ThemeContext.get_for_stage(global.stage);
+ this._signalsHandler.addWithLabel(UnityIndicator.notificationBadgeSignals, [
+ themeContext,
+ 'changed',
+ () => this._updateNotificationBadgeStyle(),
+ ], [
+ themeContext,
+ 'notify::scale-factor',
+ () => this._updateNotificationBadgeStyle(),
+ ], [
+ this._source._iconContainer,
+ 'notify::size',
+ () => this._updateNotificationBadgeStyle(),
+ ]);
+ }
+
+ setNotificationCount(count) {
+ if (count > 0) {
+ const text = this._notificationBadgeCountToText(count);
+ this._updateNotificationsBadge(text);
+ } else if (this._notificationBadgeBin) {
+ this._signalsHandler.removeWithLabel(UnityIndicator.notificationBadgeSignals);
+ this._notificationBadgeBin.destroy();
+ this._notificationBadgeBin = null;
+ }
+ }
+
+ _showProgressOverlay() {
+ if (this._progressOverlayArea) {
+ this._updateProgressOverlay();
+ return;
+ }
+
+ this._progressOverlayArea = new St.DrawingArea({x_expand: true, y_expand: true});
+ this._progressOverlayArea.add_style_class_name('progress-bar');
+ this._progressOverlayArea.connect('repaint', () => {
+ this._drawProgressOverlay(this._progressOverlayArea);
+ });
+
+ this._source._iconContainer.add_child(this._progressOverlayArea);
+ this._updateProgressOverlay();
+ }
+
+ _hideProgressOverlay() {
+ this._progressOverlayArea?.destroy();
+ this._progressOverlayArea = null;
+ }
+
+ _updateProgressOverlay() {
+ this._progressOverlayArea?.queue_repaint();
+ }
+
+ _readGradientData(node, elementName, defaultValues) {
+ const output = {
+ colorStart: defaultValues.colorStart,
+ colorEnd: defaultValues.colorEnd,
+ offsetStart: defaultValues.offsetStart ?? 0.0,
+ offsetEnd: defaultValues.offsetEnd ?? 1.0,
+ };
+
+ const [hasElementName, elementNameValue] = node.lookup_color(elementName, false);
+ if (hasElementName) {
+ output.colorStart = elementNameValue;
+ output.colorEnd = null;
+ } else {
+ const [hasColorStart, colorStartValue] = node.lookup_color(`${elementName}-color-start`, false);
+ const [hasColorEnd, colorEndValue] = node.lookup_color(`${elementName}-color-end`, false);
+ if (hasColorStart && hasColorEnd) {
+ output.colorStart = colorStartValue;
+ output.colorEnd = colorEndValue;
+ }
+ }
+
+ const [hasOffsetStart, offsetStartValue] = node.lookup_color(`${elementName}-offset-start`, false);
+ if (hasOffsetStart)
+ output.offsetStart = offsetStartValue;
+
+ const [hasOffsetEnd, offsetEndValue] = node.lookup_color(`${elementName}-offset-end`, false);
+ if (hasOffsetEnd)
+ output.offsetEnd = offsetEndValue;
+
+ return output;
+ }
+
+ _readThemeDoubleValue(node, elementName, defaultValue) {
+ const [hasValue, value] = node.lookup_double(elementName, false);
+ return hasValue ? value : defaultValue;
+ }
+
+ _readElementData(node, elementName, defaultValues) {
+ return {
+ background: this._readGradientData(node, `${elementName}-background`, defaultValues.background),
+ border: this._readGradientData(node, `${elementName}-border`, defaultValues.border),
+ lineWidth: this._readThemeDoubleValue(node, `${elementName}-line-width`,
+ defaultValues.lineWidth ?? 1.0),
+ };
+ }
+
+ _createGradient(values, x0, y0, x1, y1) {
+ if (values.colorEnd) {
+ const gradient = new Cairo.LinearGradient(x0, y0, x1, y1);
+ gradient.addColorStopRGBA(values.offsetStart,
+ values.colorStart.red / 255,
+ values.colorStart.green / 255,
+ values.colorStart.blue / 255,
+ values.colorStart.alpha / 255);
+ gradient.addColorStopRGBA(values.offsetEnd,
+ values.colorEnd.red / 255,
+ values.colorEnd.green / 255,
+ values.colorEnd.blue / 255,
+ values.colorEnd.alpha / 255);
+ return gradient;
+ } else {
+ const gradient = Cairo.SolidPattern.createRGBA(values.colorStart.red / 255,
+ values.colorStart.green / 255,
+ values.colorStart.blue / 255,
+ values.colorStart.alpha / 255);
+ return gradient;
+ }
+ }
+
+ _drawProgressOverlay(area) {
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const [surfaceWidth, surfaceHeight] = area.get_surface_size();
+ const cr = area.get_context();
+ const node = this._progressOverlayArea.get_theme_node();
+ const iconSize = this._source.icon.iconSize * scaleFactor;
+
+ let x = Math.floor((surfaceWidth - iconSize) / 2);
+ let y = Math.floor((surfaceHeight - iconSize) / 2);
+
+ const readThemeValue = element =>
+ this._readThemeDoubleValue(node, `-progress-bar-${element}`);
+
+ y = readThemeValue('top-offset') ?? y;
+
+ const baseLineWidth = Math.floor(Number(scaleFactor));
+ const horizontalPadding = iconSize *
+ Utils.clampDouble(readThemeValue('horizontal-padding') ?? 0.05);
+ const verticalPadding = iconSize *
+ Utils.clampDouble(readThemeValue('vertical-padding') ?? 0.05);
+ const heightFactor =
+ Utils.clampDouble(readThemeValue('height-factor') ?? 0.20);
+
+ let width = iconSize - 2.0 * horizontalPadding;
+ let height = Math.floor(Math.min(18.0 * scaleFactor, heightFactor * iconSize));
+ x += horizontalPadding;
+
+ const valign = Utils.clampDouble(readThemeValue('valign') ?? 1);
+ y += (iconSize - height - verticalPadding) * valign;
+
+ const progressBarTrack = this._readElementData(node,
+ '-progress-bar-track',
+ UnityIndicator.defaultProgressBarTrack);
+
+ const progressBar = this._readElementData(node,
+ '-progress-bar',
+ UnityIndicator.defaultProgressBar);
+
+ // Draw the track
+ let lineWidth = baseLineWidth * progressBarTrack.lineWidth;
+ cr.setLineWidth(lineWidth);
+
+ x += lineWidth;
+ y += lineWidth;
+ width -= 2.0 * lineWidth;
+ height -= 2.0 * lineWidth;
+
+ let fill = this._createGradient(progressBarTrack.background, 0, y, 0, y + height);
+ let stroke = this._createGradient(progressBarTrack.border, 0, y, 0, y + height);
+ Utils.drawRoundedLine(cr, x + lineWidth / 2.0,
+ y + lineWidth / 2.0, width, height, true, true, stroke, fill);
+
+ // Draw the finished bar
+ lineWidth = baseLineWidth * progressBar.lineWidth;
+ cr.setLineWidth(lineWidth);
+
+ x += lineWidth;
+ y += lineWidth;
+ width -= 2.0 * lineWidth;
+ height -= 2.0 * lineWidth;
+
+ const finishedWidth = Math.ceil(this._progress * width);
+ fill = this._createGradient(progressBar.background, 0, y, 0, y + height);
+ stroke = this._createGradient(progressBar.border, 0, y, 0, y + height);
+
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
+ Utils.drawRoundedLine(cr,
+ x + lineWidth / 2.0 + width - finishedWidth, y + lineWidth / 2.0,
+ finishedWidth, height, true, true, stroke, fill);
+ } else {
+ Utils.drawRoundedLine(cr, x + lineWidth / 2.0, y + lineWidth / 2.0,
+ finishedWidth, height, true, true, stroke, fill);
+ }
+
+ cr.$dispose();
+ }
+
+ setProgress(progress) {
+ if (progress < 0) {
+ this._hideProgressOverlay();
+ } else {
+ this._progress = Math.min(progress, 1.0);
+ this._showProgressOverlay();
+ }
+ }
+
+ setUrgent(urgent) {
+ if (urgent || this._isUrgent !== undefined)
+ this._source.urgent = urgent;
+
+ if (urgent)
+ this._isUrgent = urgent;
+ else
+ delete this._isUrgent;
+ }
+
+ setUpdating(updating) {
+ this._source.updating = updating;
+ }
+
+ _updateIconStyle() {
+ const opacity = this._readThemeDoubleValue(this._source.get_theme_node(),
+ 'opacity') ?? (this._source.updating ? 0.5 : 1);
+ this._source.icon.set_opacity(255 * opacity);
+ }
+}
+
+
+// Global icon cache. Used for Unity7 styling.
+const iconCacheMap = new Map();
+// Max number of items to store
+// We don't expect to ever reach this number, but let's put an hard limit to avoid
+// even the remote possibility of the cached items to grow indefinitely.
+const MAX_CACHED_ITEMS = 1000;
+// When the size exceed it, the oldest 'n' ones are deleted
+const BATCH_SIZE_TO_DELETE = 50;
+// The icon size used to extract the dominant color
+const DOMINANT_COLOR_ICON_SIZE = 64;
+
+// Compute dominant color from the app icon.
+// The color is cached for efficiency.
+class DominantColorExtractor {
+ constructor(app) {
+ this._app = app;
+ }
+
+ /**
+ * Try to get the pixel buffer for the current icon, if not fail gracefully
+ */
+ _getIconPixBuf() {
+ let iconTexture = this._app.create_icon_texture(16);
+ const themeLoader = Docking.DockManager.iconTheme;
+
+ // Unable to load the icon texture, use fallback
+ if (iconTexture instanceof St.Icon === false)
+ return null;
+
+
+ iconTexture = iconTexture.get_gicon();
+
+ // Unable to load the icon texture, use fallback
+ if (!iconTexture)
+ return null;
+
+ if (iconTexture instanceof Gio.FileIcon) {
+ // Use GdkPixBuf to load the pixel buffer from the provided file path
+ return GdkPixbuf.Pixbuf.new_from_file(iconTexture.get_file().get_path());
+ } else if (iconTexture instanceof Gio.ThemedIcon) {
+ // Get the first pixel buffer available in the icon theme
+ const iconNames = iconTexture.get_names();
+ const iconInfo = themeLoader.choose_icon(iconNames, DOMINANT_COLOR_ICON_SIZE, 0);
+
+ if (iconInfo)
+ return iconInfo.load_icon();
+ else
+ return null;
+ }
+
+ // Use GdkPixBuf to load the pixel buffer from memory
+ // iconTexture.load is available unless iconTexture is not an instance of Gio.LoadableIcon
+ // this means that iconTexture is an instance of Gio.EmblemedIcon,
+ // which may be converted to a normal icon via iconTexture.get_icon?
+ const [iconBuffer] = iconTexture.load(DOMINANT_COLOR_ICON_SIZE, null);
+ return GdkPixbuf.Pixbuf.new_from_stream(iconBuffer, null);
+ }
+
+ /**
+ * The backlight color choosing algorithm was mostly ported to javascript from the
+ * Unity7 C++ source of Canonicals:
+ * https://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/launcher/LauncherIcon.cpp
+ * so it more or less works the same way.
+ */
+ _getColorPalette() {
+ if (iconCacheMap.get(this._app.get_id())) {
+ // We already know the answer
+ return iconCacheMap.get(this._app.get_id());
+ }
+
+ const pixBuf = this._getIconPixBuf();
+ if (!pixBuf)
+ return null;
+
+ let pixels = pixBuf.get_pixels();
+
+ let total = 0,
+ rTotal = 0,
+ gTotal = 0,
+ bTotal = 0;
+
+ let resampleX = 1;
+ let resampleY = 1;
+
+ // Resampling of large icons
+ // We resample icons larger than twice the desired size, as the resampling
+ // to a size s
+ // DOMINANT_COLOR_ICON_SIZE < s < 2*DOMINANT_COLOR_ICON_SIZE,
+ // most of the case exactly DOMINANT_COLOR_ICON_SIZE as the icon size is
+ // typically a multiple of it.
+ const width = pixBuf.get_width();
+ const height = pixBuf.get_height();
+
+ // Resample
+ if (height >= 2 * DOMINANT_COLOR_ICON_SIZE)
+ resampleY = Math.floor(height / DOMINANT_COLOR_ICON_SIZE);
+
+ if (width >= 2 * DOMINANT_COLOR_ICON_SIZE)
+ resampleX = Math.floor(width / DOMINANT_COLOR_ICON_SIZE);
+
+ if (resampleX !== 1 || resampleY !== 1)
+ pixels = this._resamplePixels(pixels, resampleX, resampleY);
+
+ // computing the limit outside the for (where it would be repeated at each iteration)
+ // for performance reasons
+ const limit = pixels.length;
+ for (let offset = 0; offset < limit; offset += 4) {
+ const r = pixels[offset],
+ g = pixels[offset + 1],
+ b = pixels[offset + 2],
+ a = pixels[offset + 3];
+
+ const saturation = Math.max(r, g, b) - Math.min(r, g, b);
+ const relevance = 0.1 * 255 * 255 + 0.9 * a * saturation;
+
+ rTotal += r * relevance;
+ gTotal += g * relevance;
+ bTotal += b * relevance;
+
+ total += relevance;
+ }
+
+ total *= 255;
+
+ const r = rTotal / total,
+ g = gTotal / total,
+ b = bTotal / total;
+
+ const hsv = Utils.ColorUtils.RGBtoHSV(r * 255, g * 255, b * 255);
+
+ if (hsv.s > 0.15)
+ hsv.s = 0.65;
+ hsv.v = 0.90;
+
+ const rgb = Utils.ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v);
+
+ // Cache the result.
+ const backgroundColor = {
+ lighter: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0.2),
+ original: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0),
+ darker: Utils.ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, -0.5),
+ };
+
+ if (iconCacheMap.size >= MAX_CACHED_ITEMS) {
+ // delete oldest cached values (which are in order of insertions)
+ let ctr = 0;
+ for (const key of iconCacheMap.keys()) {
+ if (++ctr > BATCH_SIZE_TO_DELETE)
+ break;
+ iconCacheMap.delete(key);
+ }
+ }
+
+ iconCacheMap.set(this._app.get_id(), backgroundColor);
+
+ return backgroundColor;
+ }
+
+ /**
+ * Downscale large icons before scanning for the backlight color to
+ * improve performance.
+ *
+ * @param pixBuf
+ * @param pixels
+ * @param resampleX
+ * @param resampleY
+ *
+ * @returns [];
+ */
+ _resamplePixels(pixels, resampleX, resampleY) {
+ const resampledPixels = [];
+ // computing the limit outside the for (where it would be repeated at each iteration)
+ // for performance reasons
+ const limit = pixels.length / (resampleX * resampleY) / 4;
+ for (let i = 0; i < limit; i++) {
+ const pixel = i * resampleX * resampleY;
+
+ resampledPixels.push(pixels[pixel * 4]);
+ resampledPixels.push(pixels[pixel * 4 + 1]);
+ resampledPixels.push(pixels[pixel * 4 + 2]);
+ resampledPixels.push(pixels[pixel * 4 + 3]);
+ }
+
+ return resampledPixels;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js
new file mode 100644
index 0000000..6b0ac24
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIcons.js
@@ -0,0 +1,1635 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ Gio,
+ GLib,
+ GObject,
+ Meta,
+ Mtk,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ AppDisplay,
+ AppFavorites,
+ BoxPointer,
+ Dash,
+ Main,
+ PopupMenu,
+} from './dependencies/shell/ui.js';
+
+import {
+ ParentalControlsManager,
+ Util,
+} from './dependencies/shell/misc.js';
+
+import {
+ AppIconIndicators,
+ DBusMenuUtils,
+ Docking,
+ Locations,
+ Theming,
+ Utils,
+ WindowPreview,
+} from './imports.js';
+
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// Use __ () and N__() for the extension gettext domain, and reuse
+// the shell domain with the default _() and N_()
+const {gettext: __, ngettext} = Extension;
+
+const DBusMenu = await DBusMenuUtils.haveDBusMenu();
+
+const tracker = Shell.WindowTracker.get_default();
+
+const Labels = Object.freeze({
+ ISOLATE_MONITORS: Symbol('isolate-monitors'),
+ ISOLATE_WORKSPACES: Symbol('isolate-workspaces'),
+ URGENT_WINDOWS: Symbol('urgent-windows'),
+});
+
+const clickAction = Object.freeze({
+ SKIP: 0,
+ MINIMIZE: 1,
+ LAUNCH: 2,
+ CYCLE_WINDOWS: 3,
+ MINIMIZE_OR_OVERVIEW: 4,
+ PREVIEWS: 5,
+ MINIMIZE_OR_PREVIEWS: 6,
+ FOCUS_OR_PREVIEWS: 7,
+ FOCUS_OR_APP_SPREAD: 8,
+ FOCUS_MINIMIZE_OR_PREVIEWS: 9,
+ FOCUS_MINIMIZE_OR_APP_SPREAD: 10,
+ QUIT: 11,
+});
+
+const scrollAction = Object.freeze({
+ DO_NOTHING: 0,
+ CYCLE_WINDOWS: 1,
+ SWITCH_WORKSPACE: 2,
+});
+
+// module "Dash" did not export DASH_ITEM_LABEL_SHOW_TIME, so let's define it.
+const DASH_ITEM_LABEL_SHOW_TIME = Dash.DASH_ITEM_LABEL_SHOW_TIME ?? 150;
+
+let recentlyClickedAppLoopId = 0;
+let recentlyClickedApp = null;
+let recentlyClickedAppWindows = null;
+let recentlyClickedAppIndex = 0;
+let recentlyClickedAppMonitor = -1;
+
+/**
+ * Extend AppIcon
+ *
+ * - Apply a css class based on the number of windows of each application (#N);
+ * - Customized indicators for running applications in place of the default "dot" style which is hidden (#N);
+ * a class of the form "running#N" is applied to the AppWellIcon actor.
+ * like the original .running one.
+ * - Add a .focused style to the focused app
+ * - Customize click actions.
+ * - Update minimization animation target
+ * - Update menu if open on windows change
+ */
+const DockAbstractAppIcon = GObject.registerClass({
+ GTypeFlags: GObject.TypeFlags.ABSTRACT,
+ Properties: {
+ 'focused': GObject.ParamSpec.boolean(
+ 'focused', 'focused', 'focused',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'running': GObject.ParamSpec.boolean(
+ 'running', 'running', 'running',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'urgent': GObject.ParamSpec.boolean(
+ 'urgent', 'urgent', 'urgent',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'updating': GObject.ParamSpec.boolean(
+ 'updating', 'updating', 'updating',
+ GObject.ParamFlags.READWRITE,
+ false),
+ 'windows-count': GObject.ParamSpec.uint(
+ 'windows-count', 'windows-count', 'windows-count',
+ GObject.ParamFlags.READWRITE,
+ 0, GLib.MAXUINT32, 0),
+ },
+}, class DockAbstractAppIcon extends Dash.DashIcon {
+ // settings are required inside.
+ _init(app, monitorIndex, iconAnimator) {
+ super._init(app);
+
+ // a prefix is required to avoid conflicting with the parent class variable
+ this.monitorIndex = monitorIndex;
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this.iconAnimator = iconAnimator;
+ this._indicator = new AppIconIndicators.AppIconIndicator(this);
+ this._urgentWindows = new Set();
+
+ // Monitor windows-changes instead of app state.
+ // Keep using the same Id and function callback (that is extended)
+ if (this._stateChangedId > 0) {
+ this.app.disconnect(this._stateChangedId);
+ this._stateChangedId = 0;
+ }
+
+ this._signalsHandler.add(this.app, 'windows-changed', () => this._updateWindows());
+ this._signalsHandler.add(this.app, 'notify::state', () => this._updateRunningState());
+ this._signalsHandler.add(global.display, 'window-demands-attention', (_dpy, window) =>
+ this._onWindowDemandsAttention(window));
+ this._signalsHandler.add(global.display, 'window-marked-urgent', (_dpy, window) =>
+ this._onWindowDemandsAttention(window));
+
+ // In Wayland sessions, this signal is needed to track the state of windows dragged
+ // from one monitor to another. As this is triggered quite often (whenever a new
+ // window of any application opened or moved to a different desktop),
+ // we restrict this signal to the case when Labels.ISOLATE_MONITORS is true,
+ // and if there are at least 2 monitors.
+ if (Docking.DockManager.settings.isolateMonitors &&
+ Main.layoutManager.monitors.length > 1) {
+ this._signalsHandler.addWithLabel(Labels.ISOLATE_MONITORS,
+ global.display,
+ 'window-entered-monitor',
+ this._onWindowEntered.bind(this));
+ }
+
+ this.connect('notify::running', () => {
+ if (this.running)
+ this.add_style_class_name('running');
+ else
+ this.remove_style_class_name('running');
+ });
+ this.notify('running');
+
+ this.connect('notify::focused', () => {
+ if (this.focused)
+ this.add_style_class_name('focused');
+ else
+ this.remove_style_class_name('focused');
+ });
+ this.notify('focused');
+
+ this.connect('notify::updating', () => {
+ if (this.updating)
+ this.add_style_class_name('updating');
+ else
+ this.remove_style_class_name('updating');
+ });
+ this.notify('updating');
+
+ const {notificationsMonitor} = Docking.DockManager.getDefault();
+
+ this.connect('notify::urgent', () => {
+ const icon = this.icon._iconBin;
+ this._signalsHandler.removeWithLabel(Labels.URGENT_WINDOWS);
+ if (this.urgent) {
+ if (Docking.DockManager.settings.danceUrgentApplications &&
+ notificationsMonitor.enabled) {
+ icon.set_pivot_point(0.5, 0.5);
+ this.iconAnimator.addAnimation(icon, 'wiggle');
+ }
+ if (this.running && !this._urgentWindows.size) {
+ const urgentWindows = this.getInterestingWindows();
+ urgentWindows.forEach(w => (w._manualUrgency = true));
+ this._updateUrgentWindows(urgentWindows);
+ }
+ } else {
+ this.iconAnimator.removeAnimation(icon, 'wiggle');
+ icon.rotation_angle_z = 0;
+ this._urgentWindows.forEach(w => delete w._manualUrgency);
+ this._updateUrgentWindows();
+ }
+ });
+ this.notify('urgent');
+
+ this._progressOverlayArea = null;
+ this._progress = 0;
+
+ [
+ 'apply-custom-theme',
+ 'running-indicator-style',
+ 'show-icons-emblems',
+ 'show-icons-notifications-counter',
+ 'application-counter-overrides-notifications',
+ ].forEach(key => {
+ this._signalsHandler.add(
+ Docking.DockManager.settings,
+ `changed::${key}`, () => {
+ this._indicator.destroy();
+ this._indicator = new AppIconIndicators.AppIconIndicator(this);
+ }
+ );
+ });
+
+ this._signalsHandler.add(notificationsMonitor, 'state-changed', () => {
+ this._indicator.destroy();
+ this._indicator = new AppIconIndicators.AppIconIndicator(this);
+ });
+
+ this._updateState();
+ this._numberOverlay();
+
+ this._previewMenuManager = null;
+ this._previewMenu = null;
+ }
+
+ _onDestroy() {
+ super._onDestroy();
+
+ // This is necessary due to an upstream bug
+ // https://bugzilla.gnome.org/show_bug.cgi?id=757556
+ // It can be safely removed once it get solved upstream.
+ this._menu?.close(false);
+ delete this._menu;
+ }
+
+ ownsWindow(window) {
+ return this.app === tracker.get_window_app(window);
+ }
+
+ _onWindowEntered(metaScreen, monitorIndex, metaWin) {
+ if (this.ownsWindow(metaWin))
+ this._updateWindows();
+ }
+
+ vfunc_scroll_event(scrollEvent) {
+ const {settings} = Docking.DockManager;
+ const isEnabled = settings.scrollAction === scrollAction.CYCLE_WINDOWS;
+ if (!isEnabled)
+ return Clutter.EVENT_PROPAGATE;
+
+ // We only activate windows of running applications, i.e. we never open new windows
+ // We check if the app is running, and that the # of windows is > 0 in
+ // case we use workspace isolation,
+ if (!this.running)
+ return Clutter.EVENT_PROPAGATE;
+
+ if (this._optionalScrollCycleWindowsDeadTimeId) {
+ return Clutter.EVENT_PROPAGATE;
+ } else {
+ this._optionalScrollCycleWindowsDeadTimeId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 250, () => {
+ this._optionalScrollCycleWindowsDeadTimeId = 0;
+ });
+ }
+
+ let direction = null;
+
+ switch (scrollEvent.direction) {
+ case Clutter.ScrollDirection.UP:
+ direction = Meta.MotionDirection.UP;
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ direction = Meta.MotionDirection.DOWN;
+ break;
+ case Clutter.ScrollDirection.SMOOTH: {
+ const [, dy] = Clutter.get_current_event().get_scroll_delta();
+ if (dy < 0)
+ direction = Meta.MotionDirection.UP;
+ else if (dy > 0)
+ direction = Meta.MotionDirection.DOWN;
+ }
+ break;
+ }
+
+ if (!Main.overview.visible) {
+ const reversed = direction === Meta.MotionDirection.UP;
+ if (this.focused && !this._urgentWindows.size) {
+ this._cycleThroughWindows(reversed);
+ } else {
+ // Activate the first window
+ const windows = this.getInterestingWindows();
+ if (windows.length > 0) {
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ }
+ } else {
+ this.app.activate();
+ }
+ return Clutter.EVENT_STOP;
+ }
+
+ _updateWindows() {
+ if (this._menu && this._menu.isOpen)
+ this._menu.update();
+
+ this._updateState();
+ this.updateIconGeometry();
+ }
+
+ _updateState() {
+ this._urgentWindows.clear();
+ const interestingWindows = this.getInterestingWindows();
+ this.windowsCount = interestingWindows.length;
+ this._updateRunningState();
+ this._updateFocusState();
+ this._updateUrgentWindows(interestingWindows);
+
+ if (Docking.DockManager.settings.isolateWorkspaces) {
+ this._signalsHandler.removeWithLabel(Labels.ISOLATE_WORKSPACES);
+ interestingWindows.forEach(window =>
+ this._signalsHandler.addWithLabel(Labels.ISOLATE_WORKSPACES,
+ window, 'workspace-changed', () => this._updateWindows()));
+ }
+ }
+
+ _updateRunningState() {
+ this.running = (this.app.state === Shell.AppState.RUNNING) && this.windowsCount;
+ }
+
+ _updateFocusState() {
+ this.focused = tracker.focus_app === this.app && this.running;
+ }
+
+ _updateUrgentWindows(interestingWindows) {
+ this._signalsHandler.removeWithLabel(Labels.URGENT_WINDOWS);
+ this._urgentWindows.clear();
+ if (interestingWindows === undefined)
+ interestingWindows = this.getInterestingWindows();
+ interestingWindows.filter(isWindowUrgent).forEach(win => this._addUrgentWindow(win));
+ this.urgent = !!this._urgentWindows.size;
+ }
+
+ _onWindowDemandsAttention(window) {
+ if (this.ownsWindow(window) && isWindowUrgent(window))
+ this._addUrgentWindow(window);
+ }
+
+ _updateDotStyle() {
+ super._updateDotStyle();
+ const themeNode = this._dot.get_theme_node();
+ this._dot.translationX = themeNode.get_length('offset-x');
+ }
+
+ _addUrgentWindow(window) {
+ if (this._urgentWindows.has(window))
+ return;
+
+ if (window._manualUrgency && window.has_focus()) {
+ delete window._manualUrgency;
+ return;
+ }
+
+ this._urgentWindows.add(window);
+ this.urgent = true;
+
+ const onDemandsAttentionChanged = () => {
+ if (!isWindowUrgent(window))
+ this._updateUrgentWindows();
+ };
+
+ if (window.demandsAttention) {
+ this._signalsHandler.addWithLabel(Labels.URGENT_WINDOWS, window,
+ 'notify::demands-attention', () => onDemandsAttentionChanged());
+ }
+ if (window.urgent) {
+ this._signalsHandler.addWithLabel(Labels.URGENT_WINDOWS, window,
+ 'notify::urgent', () => onDemandsAttentionChanged());
+ }
+ if (window._manualUrgency) {
+ this._signalsHandler.addWithLabel(Labels.URGENT_WINDOWS, window,
+ 'focus', () => {
+ delete window._manualUrgency;
+ onDemandsAttentionChanged();
+ });
+ }
+ }
+
+ /**
+ * Update target for minimization animation
+ */
+ updateIconGeometry() {
+ // If (for unknown reason) the actor is not on the stage the reported size
+ // and position are random values, which might exceeds the integer range
+ // resulting in an error when assigned to the a rect. This is a more like
+ // a workaround to prevent flooding the system with errors.
+ if (!this.get_stage())
+ return;
+
+ const rect = new Mtk.Rectangle();
+
+ [rect.x, rect.y] = this.get_transformed_position();
+ [rect.width, rect.height] = this.get_transformed_size();
+
+ let windows = this.getWindows();
+ if (Docking.DockManager.settings.multiMonitor) {
+ const {monitorIndex} = this;
+ windows = windows.filter(w => w.get_monitor() === monitorIndex);
+ }
+ windows.forEach(w => w.set_icon_geometry(rect));
+ }
+
+ _updateRunningStyle() {
+ // The logic originally in this function has been moved to
+ // AppIconIndicatorBase._updateDefaultDot(). However it cannot be removed as
+ // it called by the parent constructor.
+ }
+
+ popupMenu() {
+ this._removeMenuTimeout?.();
+ this.fake_release();
+ this._draggable.fakeRelease?.();
+
+ if (!this._menu) {
+ this._menu = new DockAppIconMenu(this);
+ this._menu.connect('activate-window', (menu, window) => {
+ if (window) {
+ Main.activateWindow(window);
+ } else {
+ Main.overview.hide();
+ Main.panel.closeCalendar();
+ }
+ });
+ this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp) {
+ this._onMenuPoppedDown();
+ } else {
+ // Setting the max-height is s useful if part of the menu is
+ // scrollable so the minimum height is smaller than the natural height.
+ const monitorIndex = Main.layoutManager.findIndexForActor(this);
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
+ const position = Utils.getPosition();
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const isHorizontal = position === St.Side.TOP || position === St.Side.BOTTOM;
+ // If horizontal also remove the height of the dash
+ const {dockFixed: fixedDock} = Docking.DockManager.settings;
+ const additionalMargin = isHorizontal && !fixedDock ? Main.overview.dash.height : 0;
+ const verticalMargins = this._menu.actor.margin_top + this._menu.actor.margin_bottom;
+ const maxMenuHeight = workArea.height - additionalMargin - verticalMargins;
+ // Also set a max width to the menu, so long labels (long windows title) get truncated
+ this._menu.actor.style = 'max-width: 400px; ' +
+ `max-height: ${Math.round(maxMenuHeight / scaleFactor)}px;`;
+ }
+ });
+ const id = Main.overview.connect('hiding', () => {
+ this._menu.close();
+ });
+ this._menu.actor.connect('destroy', () => {
+ Main.overview.disconnect(id);
+ });
+
+ this._menuManager.addMenu(this._menu);
+ }
+
+ this.emit('menu-state-changed', true);
+
+ this.set_hover(true);
+ this._menu.popup();
+ // Removed in GNOME 50.
+ this._menuManager.ignoreRelease?.();
+ this.emit('sync-tooltip');
+
+ return false;
+ }
+
+ activate(button) {
+ const event = Clutter.get_current_event();
+ let modifiers = event ? event.get_state() : 0;
+
+ // Only consider SHIFT and CONTROL as modifiers (exclude SUPER, CAPS-LOCK, etc.)
+ modifiers &= Clutter.ModifierType.SHIFT_MASK | Clutter.ModifierType.CONTROL_MASK;
+
+ // We don't change the CTRL-click behavior: in such case we just chain
+ // up the parent method and return.
+ if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
+ // Keep default behavior: launch new window
+ // By calling the parent method I make it compatible
+ // with other extensions tweaking ctrl + click
+ super.activate(button);
+ return;
+ }
+
+ // We check what type of click we have and if the modifier SHIFT is
+ // being used. We then define what buttonAction should be for this
+ // event.
+ let buttonAction = 0;
+ const {settings} = Docking.DockManager;
+ if (button && button === 2) {
+ if (modifiers & Clutter.ModifierType.SHIFT_MASK)
+ buttonAction = settings.shiftMiddleClickAction;
+ else
+ buttonAction = settings.middleClickAction;
+ } else if (button && button === 1) {
+ if (modifiers & Clutter.ModifierType.SHIFT_MASK)
+ buttonAction = settings.shiftClickAction;
+ else
+ buttonAction = settings.clickAction;
+ }
+
+ switch (buttonAction) {
+ case clickAction.FOCUS_OR_APP_SPREAD:
+ if (!Docking.DockManager.getDefault().appSpread.supported)
+ buttonAction = clickAction.FOCUS_OR_PREVIEWS;
+ break;
+
+ case clickAction.FOCUS_MINIMIZE_OR_APP_SPREAD:
+ if (!Docking.DockManager.getDefault().appSpread.supported)
+ buttonAction = clickAction.FOCUS_MINIMIZE_OR_PREVIEWS;
+ break;
+ }
+
+ // We check if the app is running, and that the # of windows is > 0 in
+ // case we use workspace isolation.
+ const windows = this.getInterestingWindows();
+
+ // Some action modes (e.g. MINIMIZE_OR_OVERVIEW) require overview to remain open
+ // This variable keeps track of this
+ let shouldHideOverview = true;
+
+ // We customize the action only when the application is already running
+ if (this.running) {
+ const hasUrgentWindows = !!this._urgentWindows.size;
+ const singleOrUrgentWindows = windows.length === 1 || hasUrgentWindows;
+ switch (buttonAction) {
+ case clickAction.MINIMIZE:
+ // In overview just activate the app, unless the action is explicitly
+ // requested with a keyboard modifier
+ if (!Main.overview.visible || modifiers) {
+ // If we have button=2 or a modifier, allow minimization even if
+ // the app is not focused
+ if (this.focused && !hasUrgentWindows || button === 2 ||
+ modifiers & Clutter.ModifierType.SHIFT_MASK) {
+ // minimize all windows on double click and always in
+ // the case of primary click without additional modifiers
+ let clickCount = 0;
+ if (Clutter.EventType.CLUTTER_BUTTON_PRESS)
+ clickCount = event.get_click_count();
+ const allWindows = (button === 1 && !modifiers) || clickCount > 1;
+ this._minimizeWindow(allWindows);
+ } else {
+ this._activateAllWindows();
+ }
+ } else {
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ break;
+
+ case clickAction.MINIMIZE_OR_OVERVIEW:
+ // When a single window is present, toggle minimization
+ // If only one windows is present toggle minimization, but
+ // only when triggered with the simple click action
+ // (no modifiers, no middle click).
+ if (singleOrUrgentWindows && !modifiers && button === 1) {
+ const [w] = windows;
+ if (this.focused) {
+ if (buttonAction !== clickAction.FOCUS_OR_APP_SPREAD) {
+ // Window is raised, minimize it
+ this._minimizeWindow(w);
+ }
+ } else {
+ // Window is minimized, raise it
+ Main.activateWindow(w);
+ }
+ // Launch overview when multiple windows are present
+ // TODO: only show current app windows when gnome shell API will allow it
+ } else {
+ shouldHideOverview = false;
+ Main.overview.toggle();
+ }
+ break;
+
+ case clickAction.CYCLE_WINDOWS:
+ if (!Main.overview.visible) {
+ if (this.focused && !hasUrgentWindows) {
+ this._cycleThroughWindows();
+ } else {
+ // Activate the first window
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ } else {
+ this.app.activate();
+ }
+ break;
+
+ case clickAction.FOCUS_OR_PREVIEWS:
+ if (this.focused && !hasUrgentWindows &&
+ (windows.length > 1 || modifiers || button !== 1)) {
+ this._windowPreviews();
+ } else {
+ // Activate the first window
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ break;
+
+ case clickAction.FOCUS_MINIMIZE_OR_PREVIEWS:
+ if (this.focused && !hasUrgentWindows) {
+ if (windows.length > 1 || modifiers || button !== 1)
+ this._windowPreviews();
+ else if (!Main.overview.visible)
+ this._minimizeWindow();
+ } else {
+ // Activate the first window
+ const [w] = windows;
+ Main.activateWindow(w);
+ }
+ break;
+
+ case clickAction.LAUNCH:
+ this.launchNewWindow();
+ break;
+
+ case clickAction.PREVIEWS:
+ if (!Main.overview.visible) {
+ // If only one windows is present just switch to it,
+ // but only when triggered with the simple click action
+ // (no modifiers, no middle click).
+ if (singleOrUrgentWindows && !modifiers && button === 1) {
+ const [w] = windows;
+ Main.activateWindow(w);
+ } else {
+ this._windowPreviews();
+ }
+ } else {
+ this.app.activate();
+ }
+ break;
+
+ case clickAction.MINIMIZE_OR_PREVIEWS:
+ // When a single window is present, toggle minimization
+ // If only one windows is present toggle minimization, but only
+ // when triggered with the standard click action (no modifiers,
+ // no middle click).
+ if (!Main.overview.visible) {
+ if (singleOrUrgentWindows && !modifiers && button === 1) {
+ const [w] = windows;
+ if (this.focused) {
+ // Window is raised, minimize it
+ this._minimizeWindow(w);
+ } else {
+ // Window is minimized, raise it
+ Main.activateWindow(w);
+ }
+ } else {
+ // Launch previews when multiple windows are present
+ this._windowPreviews();
+ }
+ } else {
+ this.app.activate();
+ }
+ break;
+
+ case clickAction.FOCUS_OR_APP_SPREAD:
+ if (this.focused && !singleOrUrgentWindows && !modifiers && button === 1) {
+ shouldHideOverview = false;
+ Docking.DockManager.getDefault().appSpread.toggle(this.app);
+ } else {
+ // Activate the first window
+ Main.activateWindow(windows[0]);
+ }
+ break;
+
+ case clickAction.FOCUS_MINIMIZE_OR_APP_SPREAD:
+ if (this.focused && !singleOrUrgentWindows && !modifiers && button === 1) {
+ shouldHideOverview = false;
+ Docking.DockManager.getDefault().appSpread.toggle(this.app);
+ } else if (!this.focused) {
+ // Activate the first window
+ Main.activateWindow(windows[0]);
+ } else {
+ this._minimizeWindow();
+ }
+ break;
+
+ case clickAction.QUIT:
+ this.closeAllWindows();
+ break;
+
+ case clickAction.SKIP:
+ Main.activateWindow(windows[0]);
+ break;
+ }
+ } else {
+ this.launchNewWindow();
+ }
+
+ // Hide overview except when action mode requires it
+ if (shouldHideOverview)
+ Main.overview.hide();
+ }
+
+ shouldShowTooltip() {
+ return super.shouldShowTooltip() && !this._previewMenu?.isOpen &&
+ !Docking.DockManager.settings.hideTooltip;
+ }
+
+ _windowPreviews() {
+ if (!this._previewMenu) {
+ this._previewMenuManager = new PopupMenu.PopupMenuManager(this);
+
+ this._previewMenu = new WindowPreview.WindowPreviewMenu(this);
+
+ this._previewMenuManager.addMenu(this._previewMenu);
+
+ this._previewMenu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ const id = Main.overview.connect('hiding', () => {
+ this._previewMenu.close();
+ });
+ this._previewMenu.actor.connect('destroy', () => {
+ Main.overview.disconnect(id);
+ });
+ }
+
+ this.emit('menu-state-changed', !this._previewMenu.isOpen);
+
+ if (this._previewMenu.isOpen)
+ this._previewMenu.close();
+ else
+ this._previewMenu.popup();
+
+ return false;
+ }
+
+ // Try to do the right thing when attempting to launch a new window of an app. In
+ // particular, if the application doesn't allow to launch a new window, activate
+ // the existing window instead.
+ launchNewWindow() {
+ if (this.updating) {
+ const icon = Gio.Icon.new_for_string('action-unavailable-symbolic');
+ Main.osdWindowManager.show(-1, icon,
+ _('%s is updating, try again later').format(this.name),
+ null);
+ return;
+ }
+
+ if (this.app.state === Shell.AppState.RUNNING &&
+ this.app.can_open_new_window()) {
+ this.animateLaunch();
+ this.app.open_new_window(-1);
+ } else {
+ // Try to manually activate the first window. Otherwise, when the
+ // app is activated by switching to a different workspace, a launch
+ // spinning icon is shown and disappears only after a timeout.
+ const windows = this.getWindows();
+ if (windows.length > 0) {
+ Main.activateWindow(windows[0]);
+ } else {
+ this.app.activate();
+ this.animateLaunch();
+ }
+ }
+ }
+
+ _numberOverlay() {
+ // Add label for a Hot-Key visual aid
+ this._numberOverlayLabel = new St.Label();
+ this._numberOverlayBin = new St.Bin({
+ child: this._numberOverlayLabel,
+ x_align: Clutter.ActorAlign.START,
+ y_align: Clutter.ActorAlign.START,
+ x_expand: true, y_expand: true,
+ });
+ this._numberOverlayLabel.add_style_class_name('number-overlay');
+ this._numberOverlayOrder = -1;
+ this._numberOverlayBin.hide();
+
+ this._iconContainer.add_child(this._numberOverlayBin);
+ }
+
+ updateNumberOverlay() {
+ // We apply an overall scale factor that might come from a HiDPI monitor.
+ // Clutter dimensions are in physical pixels, but CSS measures are in logical
+ // pixels, so make sure to consider the scale.
+ const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ // Set the font size to something smaller than the whole icon so it is
+ // still visible. The border radius is large to make the shape circular
+ const [minWidth_, natWidth] = this._iconContainer.get_preferred_width(-1);
+ const fontSize = Math.round(Math.max(12, 0.3 * natWidth) / scaleFactor);
+ const size = Math.round(fontSize * 1.2);
+ this._numberOverlayLabel.set_style(
+ `font-size: ${fontSize}px;` +
+ `border-radius: ${this.icon.iconSize}px;` +
+ `width: ${size}px; height: ${size}px;`
+ );
+ }
+
+ setNumberOverlay(number) {
+ this._numberOverlayOrder = number;
+ this._numberOverlayLabel.set_text(number.toString());
+ }
+
+ toggleNumberOverlay(activate) {
+ if (activate && this._numberOverlayOrder > -1) {
+ this.updateNumberOverlay();
+ this._numberOverlayBin.show();
+ } else {
+ this._numberOverlayBin.hide();
+ }
+ }
+
+ _minimizeWindow(param) {
+ // Param true make all app windows minimize
+ const windows = this.getInterestingWindows();
+ const currentWorkspace = global.workspace_manager.get_active_workspace();
+ for (let i = 0; i < windows.length; i++) {
+ const w = windows[i];
+ if (w.get_workspace() === currentWorkspace && w.showing_on_its_workspace()) {
+ w.minimize();
+ // Just minimize one window. By specification it should be the
+ // focused window on the current workspace.
+ if (!param)
+ break;
+ }
+ }
+ }
+
+ // By default only non minimized windows are activated.
+ // This activates all windows in the current workspace.
+ _activateAllWindows() {
+ // First activate first window so workspace is switched if needed.
+ // We don't do this if isolation is on!
+ if (!Docking.DockManager.settings.isolateWorkspaces &&
+ !Docking.DockManager.settings.isolateMonitors) {
+ if (!this.running)
+ this.animateLaunch();
+ this.app.activate();
+ }
+
+ // then activate all other app windows in the current workspace
+ const windows = this.getInterestingWindows();
+ const activeWorkspace = global.workspace_manager.get_active_workspace_index();
+
+ if (windows.length <= 0)
+ return;
+
+ for (let i = windows.length - 1; i >= 0; i--) {
+ if (windows[i].get_workspace()?.index() === activeWorkspace)
+ Main.activateWindow(windows[i]);
+ }
+ }
+
+ // This closes all windows of the app.
+ closeAllWindows() {
+ const windows = this.getInterestingWindows();
+ const time = global.get_current_time();
+ windows.forEach(w => w.delete(time));
+ }
+
+ _cycleThroughWindows(reversed) {
+ // Store for a little amount of time last clicked app and its windows
+ // since the order changes upon window interaction
+ const MEMORY_TIME = 3000;
+
+ const appWindows = this.getInterestingWindows();
+
+ if (appWindows.length < 1)
+ return;
+
+ if (recentlyClickedAppLoopId > 0)
+ GLib.source_remove(recentlyClickedAppLoopId);
+ recentlyClickedAppLoopId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, MEMORY_TIME, this._resetRecentlyClickedApp);
+
+ // If there isn't already a list of windows for the current app,
+ // or the stored list is outdated, use the current windows list.
+ const monitorIsolation = Docking.DockManager.settings.isolateMonitors;
+ if (!recentlyClickedApp ||
+ recentlyClickedApp.get_id() !== this.app.get_id() ||
+ recentlyClickedAppWindows.length !== appWindows.length ||
+ (recentlyClickedAppMonitor !== this.monitorIndex && monitorIsolation)) {
+ recentlyClickedApp = this.app;
+ recentlyClickedAppWindows = appWindows;
+ recentlyClickedAppMonitor = this.monitorIndex;
+ recentlyClickedAppIndex = 0;
+ }
+
+ if (reversed) {
+ recentlyClickedAppIndex--;
+ if (recentlyClickedAppIndex < 0)
+ recentlyClickedAppIndex = recentlyClickedAppWindows.length - 1;
+ } else {
+ recentlyClickedAppIndex++;
+ }
+ const index = recentlyClickedAppIndex % recentlyClickedAppWindows.length;
+ const window = recentlyClickedAppWindows[index];
+
+ Main.activateWindow(window);
+ }
+
+ _resetRecentlyClickedApp() {
+ if (recentlyClickedAppLoopId > 0)
+ GLib.source_remove(recentlyClickedAppLoopId);
+ recentlyClickedAppLoopId = 0;
+ recentlyClickedApp = null;
+ recentlyClickedAppWindows = null;
+ recentlyClickedAppIndex = 0;
+ recentlyClickedAppMonitor = -1;
+
+ return false;
+ }
+
+ getWindows() {
+ return this.app.get_windows();
+ }
+
+ // Filter out unnecessary windows, for instance
+ // nautilus desktop window.
+ getInterestingWindows() {
+ const interestingWindows = getInterestingWindows(this.getWindows(),
+ this.monitorIndex);
+
+ if (!this._urgentWindows.size)
+ return interestingWindows;
+
+ return [...new Set([...this._urgentWindows, ...interestingWindows])];
+ }
+
+ getSnapName() {
+ return this.app.appInfo?.get_string('X-SnapInstanceName');
+ }
+});
+
+const DockAppIcon = GObject.registerClass({
+}, class DockAppIcon extends DockAbstractAppIcon {
+ _init(app, monitorIndex, iconAnimator) {
+ super._init(app, monitorIndex, iconAnimator);
+
+ this._signalsHandler.add(tracker, 'notify::focus-app', () => this._updateFocusState());
+ }
+});
+
+const DockLocationAppIcon = GObject.registerClass({
+}, class DockLocationAppIcon extends DockAbstractAppIcon {
+ _init(app, monitorIndex, iconAnimator) {
+ if (!(app.appInfo instanceof Locations.LocationAppInfo))
+ throw new Error('Provided application %s is not a Location'.format(app));
+
+ super._init(app, monitorIndex, iconAnimator);
+
+ if (Docking.DockManager.settings.isolateLocations) {
+ this._signalsHandler.add(tracker, 'notify::focus-app', () => this._updateFocusState());
+ } else {
+ this._signalsHandler.add(global.display, 'notify::focus-window',
+ () => this._updateFocusState());
+ }
+
+ this._signalsHandler.add(this.app, 'notify::icon', () => this.icon.update());
+ }
+
+ get location() {
+ return this.app.location;
+ }
+
+ _updateFocusState() {
+ if (Docking.DockManager.settings.isolateLocations) {
+ super._updateFocusState();
+ return;
+ }
+
+ this.focused = this.app.isFocused && this.running;
+ }
+});
+
+/**
+ * @param app
+ * @param monitorIndex
+ * @param iconAnimator
+ */
+export function makeAppIcon(app, monitorIndex, iconAnimator) {
+ if (app.appInfo instanceof Locations.LocationAppInfo)
+ return new DockLocationAppIcon(app, monitorIndex, iconAnimator);
+
+ return new DockAppIcon(app, monitorIndex, iconAnimator);
+}
+
+/**
+ * DockAppIconMenu
+ *
+ * - set popup arrow side based on dash orientation
+ * - Add close windows option based on quitfromdash extension
+ * (https://github.com/deuill/shell-extension-quitfromdash)
+ * - Add open windows thumbnails instead of list
+ * - update menu when application windows change
+ */
+const DockAppIconMenu = class DockAppIconMenu extends PopupMenu.PopupMenu {
+ constructor(source) {
+ super(source, 0.5, Utils.getPosition());
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+
+ // We want to keep the item hovered while the menu is up
+ this.blockSourceEvents = true;
+
+ this.actor.add_style_class_name('app-menu');
+ this.actor.add_style_class_name('dock-app-menu');
+
+ // Chain our visibility and lifecycle to that of the source
+ this._signalsHandler.add(source, 'notify::mapped', () => {
+ if (!source.mapped)
+ this.close();
+ });
+ source.connect('destroy', () => this.destroy());
+
+ Main.uiGroup.add_child(this.actor);
+
+ const {remoteModel} = Docking.DockManager.getDefault();
+ const remoteModelApp = remoteModel?.lookupById(this.sourceActor?.app?.id);
+ if (remoteModelApp && DBusMenu) {
+ const [onQuickList, onDynamicSection] = Utils.splitHandler((sender,
+ {quicklist}, dynamicSection) => {
+ dynamicSection.removeAll();
+ if (quicklist) {
+ quicklist.get_children().forEach(remoteItem =>
+ dynamicSection.addMenuItem(
+ DBusMenuUtils.makePopupMenuItem(remoteItem, false)));
+ }
+ });
+
+ this._signalsHandler.add([
+ remoteModelApp,
+ 'quicklist-changed',
+ onQuickList,
+ ], [
+ this,
+ 'dynamic-section-changed',
+ onDynamicSection,
+ ]);
+ }
+ }
+
+ destroy() {
+ super.destroy();
+ delete this.sourceActor;
+ delete this._signalsHandler;
+ }
+
+ _appendSeparator() {
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ }
+
+ _appendMenuItem(labelText, params) {
+ const item = new PopupMenu.PopupMenuItem(labelText, params);
+ this.addMenuItem(item);
+ return item;
+ }
+
+ popup(_activatingButton) {
+ this._rebuildMenu();
+ this.open(BoxPointer.PopupAnimation.FULL);
+ }
+
+ removeAll() {
+ super.removeAll();
+
+ delete this._allWindowsMenuItem;
+ delete this._quitMenuItem;
+ }
+
+ _rebuildMenu() {
+ this.removeAll();
+
+ const appItemLabel = this.sourceActor.updating
+ ? _('%s is being updated…').format(this.sourceActor.name)
+ : this.sourceActor.name;
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(appItemLabel));
+
+ const {app} = this.sourceActor;
+
+ if (Docking.DockManager.settings.showWindowsPreview) {
+ // Display the app windows menu items and the separator between windows
+ // of the current desktop and other windows.
+ const windows = this.sourceActor.getInterestingWindows();
+
+ this._allWindowsMenuItem = new PopupMenu.PopupSubMenuMenuItem(__('All Windows'), false);
+ if (this._allWindowsMenuItem.menu?.actor)
+ this._allWindowsMenuItem.menu.actor.overlayScrollbars = true;
+ this._allWindowsMenuItem.hide();
+ if (windows.length > 0)
+ this.addMenuItem(this._allWindowsMenuItem);
+ } else {
+ const windows = this.sourceActor.getInterestingWindows();
+
+ if (windows.length > 0) {
+ this.addMenuItem(
+ /* Translators: This is the heading of a list of open windows */
+ new PopupMenu.PopupSeparatorMenuItem(_('Open Windows')));
+ }
+
+ windows.forEach(window => {
+ const title = window.title ? window.title : app.get_name();
+ const item = this._appendMenuItem(title);
+ item.connect('activate', () => {
+ this.emit('activate-window', window);
+ });
+ });
+ }
+
+ if (!app.is_window_backed()) {
+ this._appendSeparator();
+
+ const appInfo = app.get_app_info();
+ const actions = this.sourceActor.updating ? [] : appInfo.list_actions();
+ if (!this.sourceActor.updating &&
+ app.can_open_new_window() &&
+ actions.indexOf('new-window') === -1) {
+ const newMenuItem = this._appendMenuItem(_('New Window'));
+ newMenuItem.connect('activate', () => {
+ if (app.state === Shell.AppState.STOPPED)
+ this.sourceActor.animateLaunch();
+
+ app.open_new_window(-1);
+ this.emit('activate-window', null);
+ });
+ this._appendSeparator();
+ }
+
+ if (!this.sourceActor.updating &&
+ Docking.DockManager.getDefault().discreteGpuAvailable &&
+ app.state === Shell.AppState.STOPPED) {
+ const appPrefersNonDefaultGPU = appInfo.get_boolean('PrefersNonDefaultGPU');
+ const gpuPref = appPrefersNonDefaultGPU
+ ? Shell.AppLaunchGpu.DEFAULT
+ : Shell.AppLaunchGpu.DISCRETE;
+ const gpuMenuItem = this._appendMenuItem(appPrefersNonDefaultGPU
+ ? _('Launch using Integrated Graphics Card')
+ : _('Launch using Discrete Graphics Card'));
+ gpuMenuItem.connect('activate', () => {
+ this.sourceActor.animateLaunch();
+ app.launch(0, -1, gpuPref);
+ this.emit('activate-window', null);
+ });
+ }
+
+ for (let i = 0; i < actions.length; i++) {
+ const action = actions[i];
+ const item = this._appendMenuItem(appInfo.get_action_name(action));
+ item.sensitive = !appInfo.busy;
+ item.connect('activate', (emitter, event) => {
+ app.launch_action(action, event.get_time(), -1);
+ this.emit('activate-window', null);
+ });
+ }
+
+ const canFavorite = global.settings.is_writable('favorite-apps') &&
+ (this.sourceActor instanceof DockAppIcon) &&
+ ParentalControlsManager.getDefault().shouldShowApp(app.appInfo);
+
+ if (canFavorite) {
+ this._appendSeparator();
+
+ const isFavorite = AppFavorites.getAppFavorites().isFavorite(app.get_id());
+ if (isFavorite) {
+ const item = this._appendMenuItem(_('Unpin'));
+ item.connect('activate', () => {
+ const favs = AppFavorites.getAppFavorites();
+ favs.removeFavorite(app.get_id());
+ });
+ } else {
+ const item = this._appendMenuItem(__('Pin to Dock'));
+ item.connect('activate', () => {
+ const favs = AppFavorites.getAppFavorites();
+ favs.addFavorite(app.get_id());
+ });
+ }
+ }
+
+ if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop') &&
+ this.sourceActor instanceof DockAppIcon &&
+ !this.sourceActor.getSnapName()) {
+ this._appendSeparator();
+ const item = this._appendMenuItem(_('App Details'));
+ item.connect('activate', () => {
+ const id = app.get_id();
+ const args = GLib.Variant.new('(ss)', [id, '']);
+ Gio.DBus.get(Gio.BusType.SESSION, null,
+ (o, res) => {
+ const bus = Gio.DBus.get_finish(res);
+ bus.call('org.gnome.Software',
+ '/org/gnome/Software',
+ 'org.gtk.Actions', 'Activate',
+ GLib.Variant.new('(sava{sv})',
+ ['details', [args], null]),
+ null, 0, -1, null, null);
+ Main.overview.hide();
+ });
+ });
+ }
+
+ if (this.sourceActor instanceof DockAppIcon) {
+ const snapName = this.sourceActor.getSnapName();
+ const snapStore = snapName
+ ? Shell.AppSystem.get_default().lookup_app(
+ 'snap-store_snap-store.desktop') : null;
+
+ if (snapStore) {
+ this._appendSeparator();
+ const item = this._appendMenuItem(_('App Details'));
+ item.connect('activate', (_, event) => {
+ snapStore.activate_full(-1, event.get_time());
+ Util.spawnApp(
+ [...snapStore.appInfo.get_commandline().split(' '), snapName]);
+ Main.overview.hide();
+ });
+ }
+ }
+ }
+
+ // dynamic menu
+ const items = this._getMenuItems();
+ let i = items.length;
+ if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop'))
+ i -= 2;
+
+ if (global.settings.is_writable('favorite-apps'))
+ i -= 2;
+
+ if (i < 0)
+ i = 0;
+
+ const dynamicSection = new PopupMenu.PopupMenuSection();
+ this.addMenuItem(dynamicSection, i);
+ this.emit('dynamic-section-changed', dynamicSection);
+
+ // quit menu
+ this._appendSeparator();
+ this._quitMenuItem = this._appendMenuItem(_('Quit'));
+ this._quitMenuItem.connect('activate', () => this.sourceActor.closeAllWindows());
+
+ this.update();
+ }
+
+ // update menu content when application windows change. This is desirable as actions
+ // acting on windows (closing) are performed while the menu is shown.
+ update() {
+ // update, show or hide the quit menu
+ if (this.sourceActor.windowsCount > 0) {
+ if (this.sourceActor.windowsCount === 1) {
+ this._quitMenuItem.label.set_text(_('Quit'));
+ } else {
+ this._quitMenuItem.label.set_text(ngettext(
+ 'Quit %d Window', 'Quit %d Windows', this.sourceActor.windowsCount).format(
+ this.sourceActor.windowsCount));
+ }
+
+ this._quitMenuItem.actor.show();
+ } else {
+ this._quitMenuItem.actor.hide();
+ }
+
+ if (Docking.DockManager.settings.showWindowsPreview) {
+ const windows = this.sourceActor.getInterestingWindows();
+
+ // update, show, or hide the allWindows menu
+ // Check if there are new windows not already displayed. In such case,
+ // repopulate the allWindows menu. Windows removal is already handled
+ // by each preview being connected to the destroy signal
+ const oldWindows = this._allWindowsMenuItem.menu._getMenuItems().map(item => {
+ return item._window;
+ });
+
+ const newWindows = windows.filter(w =>
+ oldWindows.indexOf(w) < 0);
+ if (newWindows.length > 0) {
+ this._populateAllWindowMenu(windows);
+
+ // Try to set the width to that of the submenu.
+ // TODO: can't get the actual size, getting a bit less.
+ // Temporary workaround: add 15px to compensate
+ this._allWindowsMenuItem.width = this._allWindowsMenuItem.menu.actor.width + 15;
+ }
+
+ // The menu is created hidden and never hidden after being shown.
+ // Instead, a signal connected to its items destroy will set is
+ // insensitive if no more windows preview are shown.
+ if (windows.length > 0) {
+ this._allWindowsMenuItem.show();
+ this._allWindowsMenuItem.setSensitive(true);
+
+ if (Docking.DockManager.settings.defaultWindowsPreviewToOpen)
+ this._allWindowsMenuItem.menu.open();
+ }
+ }
+
+ // Update separators
+ this._getMenuItems().forEach(item => {
+ if ('label' in item)
+ this._updateSeparatorVisibility(item);
+ });
+ }
+
+ _populateAllWindowMenu(windows) {
+ this._allWindowsMenuItem.menu.removeAll();
+
+ if (windows.length > 0) {
+ const activeWorkspace = global.workspace_manager.get_active_workspace();
+ let separatorShown = windows[0].get_workspace() !== activeWorkspace;
+
+ for (let i = 0; i < windows.length; i++) {
+ const window = windows[i];
+ if (!separatorShown && window.get_workspace() !== activeWorkspace) {
+ this._allWindowsMenuItem.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ separatorShown = true;
+ }
+
+ const item = new WindowPreview.WindowPreviewMenuItem(window,
+ St.Side.LEFT);
+ this._allWindowsMenuItem.menu.addMenuItem(item);
+ item.connect('activate', () => {
+ this.emit('activate-window', window);
+ });
+
+ // This is to achieve a more graceful transition when the last
+ // window is closed.
+ item.connect('destroy', () => {
+ // It's still counting the item just going to be destroyed
+ if (this._allWindowsMenuItem.menu._getMenuItems().length === 1)
+ this._allWindowsMenuItem.setSensitive(false);
+ });
+ }
+ }
+ }
+};
+
+/**
+ * @param w
+ */
+function isWindowUrgent(w) {
+ return w.urgent || w.demandsAttention || w._manualUrgency;
+}
+
+/**
+ * Filter out unnecessary windows, for instance
+ * nautilus desktop window.
+ *
+ * @param windows
+ * @param monitorIndex
+ */
+export function getInterestingWindows(windows, monitorIndex) {
+ const {settings} = Docking.DockManager;
+
+ // When using workspace isolation, we filter out windows
+ // that are neither in the current workspace nor marked urgent
+ if (settings.isolateWorkspaces) {
+ const showUrgent = settings.workspaceAgnosticUrgentWindows;
+ const activeWorkspace = global.workspace_manager.get_active_workspace();
+ windows = windows.filter(w => {
+ const inWorkspace = w.get_workspace() === activeWorkspace;
+ return inWorkspace || (showUrgent && isWindowUrgent(w));
+ });
+ }
+
+ if (settings.isolateMonitors && monitorIndex >= 0) {
+ windows = windows.filter(w => {
+ return w.get_monitor() === monitorIndex;
+ });
+ }
+
+ return windows.filter(w => !w.skipTaskbar);
+}
+
+/**
+ * A ShowAppsIcon improved class.
+ *
+ * - set label position based on dash orientation
+ * Note: we are am reusing most machinery of the appIcon class.
+ * - implement a popupMenu based on the AppIcon code
+ * Note: we are reusing most machinery of the appIcon class)
+ *
+ */
+
+export const DockShowAppsIcon = GObject.registerClass({
+ Signals: {
+ 'menu-state-changed': {param_types: [GObject.TYPE_BOOLEAN]},
+ 'sync-tooltip': {},
+ },
+}
+, class DockShowAppsIcon extends Dash.ShowAppsIcon {
+ _init(position) {
+ super._init();
+
+ // Re-use appIcon methods
+ const {prototype: appIconPrototype} = AppDisplay.AppIcon;
+ this.toggleButton.y_expand = false;
+ this.toggleButton.connect('popup-menu', () =>
+ appIconPrototype._onKeyboardPopupMenu.call(this));
+ this.toggleButton.connect('clicked', () =>
+ this._removeMenuTimeout());
+
+ this.reactive = true;
+ this.toggleButton.popupMenu = (...args) =>
+ this.popupMenu(...args);
+ this.toggleButton._setPopupTimeout = (...args) =>
+ this._setPopupTimeout(...args);
+ this.toggleButton._removeMenuTimeout = (...args) =>
+ this._removeMenuTimeout(...args);
+
+ this.label?.add_style_class_name(Theming.PositionStyleClass[position]);
+ if (Docking.DockManager.settings.customThemeShrink)
+ this.label?.add_style_class_name('shrink');
+
+ this._menu = null;
+ this._menuManager = new PopupMenu.PopupMenuManager(this);
+ this._menuTimeoutId = 0;
+
+ this._maybeEnablePopupGestures();
+ }
+
+ _createIcon(size) {
+ this._iconActor = super._createIcon(size);
+ this._iconActor.fallbackIconName = this._iconActor.iconName;
+ this._iconActor.fallbackGicon = this._iconActor.gicon;
+ this._iconActor.iconName = `view-app-grid-${Main.sessionMode.currentMode}-symbolic`;
+ return this._iconActor;
+ }
+
+ vfunc_leave_event(...args) {
+ return AppDisplay.AppIcon.prototype.vfunc_leave_event.call(
+ this.toggleButton, ...args);
+ }
+
+ vfunc_button_press_event(...args) {
+ try {
+ // TODO: Drop this when not supporting GNOME 48 (and older) anymore.
+ return AppDisplay.AppIcon.prototype.vfunc_button_press_event.call(
+ this.toggleButton, ...args);
+ } catch {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ }
+
+ vfunc_touch_event(...args) {
+ try {
+ // TODO: Drop this when not supporting GNOME 48 (and older) anymore.
+ return AppDisplay.AppIcon.prototype.vfunc_touch_event.call(
+ this.toggleButton, ...args);
+ } catch {
+ return Clutter.EVENT_PROPAGATE;
+ }
+ }
+
+ showLabel(...args) {
+ itemShowLabel.call(this, ...args);
+ }
+
+ setForcedHighlight(...args) {
+ AppDisplay.AppIcon.prototype.setForcedHighlight.call(this, ...args);
+ }
+
+ _onMenuPoppedDown(...args) {
+ AppDisplay.AppIcon.prototype._onMenuPoppedDown.call(this, ...args);
+ }
+
+ _setPopupTimeout(...args) {
+ AppDisplay.AppIcon.prototype._setPopupTimeout.call(this, ...args);
+ }
+
+ _removeMenuTimeout(...args) {
+ AppDisplay.AppIcon.prototype._removeMenuTimeout?.call(this, ...args);
+ }
+
+ _hasPopupMenu() {
+ return Docking.DockManager.extension.uuid !== 'ubuntu-dock@ubuntu.com';
+ }
+
+ _maybeEnablePopupGestures() {
+ if (!this._hasPopupMenu())
+ return;
+
+ if (!Clutter.LongPressGesture || !Clutter.ClickGesture)
+ return;
+
+ const longPressGesture = new Clutter.LongPressGesture();
+ longPressGesture.connect('recognize', () => this.popupMenu());
+ this.add_action(longPressGesture);
+
+ const rightClickGesture = new Clutter.ClickGesture({
+ required_button: Clutter.BUTTON_SECONDARY,
+ recognize_on_press: true,
+ });
+ rightClickGesture.connect('recognize', () => this.popupMenu());
+ this.add_action(rightClickGesture);
+ }
+
+ popupMenu() {
+ if (!this._hasPopupMenu())
+ return false;
+
+ this._removeMenuTimeout();
+ this.toggleButton.fake_release();
+
+ if (!this._menu) {
+ this._menu = new DockShowAppsIconMenu(this);
+ this._menu.connect('open-state-changed', (menu, isPoppedUp) => {
+ if (!isPoppedUp)
+ this._onMenuPoppedDown();
+ });
+ const id = Main.overview.connect('hiding', () => {
+ this._menu.close();
+ });
+ this._menu.actor.connect('destroy', () => {
+ Main.overview.disconnect(id);
+ });
+ this._menuManager.addMenu(this._menu);
+ }
+
+ this.emit('menu-state-changed', true);
+
+ this.toggleButton.set_hover(true);
+ this._menu.popup();
+ // Removed in GNOME 50.
+ this._menuManager.ignoreRelease?.();
+ this.emit('sync-tooltip');
+
+ return false;
+ }
+});
+
+
+/**
+ * A menu for the showAppsIcon
+ */
+class DockShowAppsIconMenu extends DockAppIconMenu {
+ _rebuildMenu() {
+ this.removeAll();
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem(__('Dash to Dock')));
+
+ const item = this._appendMenuItem(_('Settings'));
+ item.connect('activate', () =>
+ Docking.DockManager.extension.openPreferences());
+ }
+}
+
+/**
+ * This function is used for both DockShowAppsIcon and DockDashItemContainer
+ */
+export function itemShowLabel() {
+ /* eslint-disable no-invalid-this */
+ // Check if the label is still present at all. When switching workspace, the
+ // item might have been destroyed in between.
+ if (!this._labelText || !this.label.get_stage())
+ return;
+
+ this.label.set_text(this._labelText);
+ this.label.opacity = 0;
+ this.label.show();
+
+ const [stageX, stageY] = this.get_transformed_position();
+ const node = this.label.get_theme_node();
+
+ const itemWidth = this.allocation.x2 - this.allocation.x1;
+ const itemHeight = this.allocation.y2 - this.allocation.y1;
+
+ const labelWidth = this.label.get_width();
+ const labelHeight = this.label.get_height();
+
+ let x, y, xOffset, yOffset;
+
+ const position = Utils.getPosition();
+ const labelOffset = node.get_length('-x-offset');
+
+ switch (position) {
+ case St.Side.LEFT:
+ yOffset = Math.floor((itemHeight - labelHeight) / 2);
+ y = stageY + yOffset;
+ xOffset = labelOffset;
+ x = stageX + this.get_width() + xOffset;
+ break;
+ case St.Side.RIGHT:
+ yOffset = Math.floor((itemHeight - labelHeight) / 2);
+ y = stageY + yOffset;
+ xOffset = labelOffset;
+ x = Math.round(stageX) - labelWidth - xOffset;
+ break;
+ case St.Side.TOP:
+ y = stageY + labelOffset + itemHeight;
+ xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ x = stageX + xOffset;
+ break;
+ case St.Side.BOTTOM:
+ yOffset = labelOffset;
+ y = stageY - labelHeight - yOffset;
+ xOffset = Math.floor((itemWidth - labelWidth) / 2);
+ x = stageX + xOffset;
+ break;
+ }
+
+ // keep the label inside the screen border
+ // Only needed fot the x coordinate.
+
+ // Leave a few pixel gap
+ const gap = 5;
+ const monitor = Main.layoutManager.findMonitorForActor(this);
+ if (x - monitor.x < gap)
+ x += monitor.x - x + labelOffset;
+ else if (x + labelWidth > monitor.x + monitor.width - gap)
+ x -= x + labelWidth - (monitor.x + monitor.width) + gap;
+
+ this.label.remove_all_transitions();
+ this.label.set_position(x, y);
+ this.label.ease({
+ opacity: 255,
+ duration: DASH_ITEM_LABEL_SHOW_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ /* eslint-enable no-invalid-this */
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js
new file mode 100644
index 0000000..89afa3e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appIconsDecorator.js
@@ -0,0 +1,168 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+
+import {
+ Docking,
+ AppIconIndicators,
+ Utils,
+} from './imports.js';
+
+import {
+ Gio,
+} from './dependencies/gi.js';
+
+import {
+ AppMenu,
+ AppDisplay,
+ Main,
+ PopupMenu,
+} from './dependencies/shell/ui.js';
+
+const Labels = Object.freeze({
+ GENERIC: Symbol('generic'),
+ ICONS: Symbol('icons'),
+});
+
+export class AppIconsDecorator {
+ constructor() {
+ this._signals = new Utils.GlobalSignalsHandler();
+ this._methodInjections = new Utils.InjectionsHandler();
+ this._propertyInjections = new Utils.PropertyInjectionsHandler(
+ null, {allowNewProperty: true});
+ this._indicators = new Set();
+
+ this._patchAppIcons();
+ this._decorateIcons();
+ }
+
+ destroy() {
+ this._signals?.destroy();
+ delete this._signals;
+ this._methodInjections?.destroy();
+ delete this._methodInjections;
+ this._propertyInjections?.destroy();
+ delete this._propertyInjections;
+ this._indicators?.forEach(i => i.destroy());
+ this._indicators?.clear();
+ delete this._indicators;
+ }
+
+ _decorateIcon(parentIcon, signalLabel = Labels.GENERIC) {
+ const indicator = new AppIconIndicators.UnityIndicator(parentIcon);
+ this._indicators.add(indicator);
+ this._signals.addWithLabel(signalLabel, parentIcon, 'destroy', () => {
+ this._indicators.delete(indicator);
+ indicator.destroy();
+ });
+ return indicator;
+ }
+
+ _decorateIcons() {
+ const {appDisplay} = Docking.DockManager.getDefault().overviewControls;
+
+ const decorateAppIcons = () => {
+ this._indicators.forEach(i => i.destroy());
+ this._indicators.clear();
+ this._signals.removeWithLabel(Labels.ICONS);
+
+ const decorateViewIcons = view => {
+ const items = view.getAllItems();
+ items.forEach(i => {
+ if (i instanceof AppDisplay.AppIcon) {
+ this._decorateIcon(i, Labels.ICONS);
+ } else if (i instanceof AppDisplay.FolderIcon) {
+ decorateViewIcons(i.view);
+ this._signals.addWithLabel(Labels.ICONS, i.view,
+ 'view-loaded', () => decorateAppIcons());
+ }
+ });
+ };
+ decorateViewIcons(appDisplay);
+ };
+
+ this._signals.add(appDisplay, 'view-loaded', () => decorateAppIcons());
+ decorateAppIcons();
+ }
+
+ _patchAppIcons() {
+ const self = this;
+
+ this._methodInjections.add(AppDisplay.AppSearchProvider.prototype,
+ 'createResultObject', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ const result = originalFunction.call(this, ...args);
+ if (result instanceof AppDisplay.AppIcon)
+ self._decorateIcon(result);
+ return result;
+ /* eslint-enable no-invalid-this */
+ });
+
+ this._methodInjections.add(AppDisplay.AppIcon.prototype,
+ 'activate', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (this.updating) {
+ const icon = Gio.Icon.new_for_string('action-unavailable-symbolic');
+ Main.osdWindowManager.show(-1, icon,
+ _('%s is updating, try again later').format(this.name),
+ null);
+ return;
+ }
+
+ originalFunction.call(this, ...args);
+ /* eslint-enable no-invalid-this */
+ });
+
+ const appIconsTypes = [
+ AppDisplay.AppSearchProvider,
+ AppDisplay.AppIcon,
+ ];
+ appIconsTypes.forEach(type =>
+ this._propertyInjections.add(type.prototype, 'updating', {
+ get() {
+ return !!this.__d2dUpdating;
+ },
+ set(updating) {
+ if (this.updating === updating)
+ return;
+ this.__d2dUpdating = updating;
+ if (updating)
+ this.add_style_class_name('updating');
+ else
+ this.remove_style_class_name('updating');
+ },
+ }));
+
+ this._methodInjections.add(AppMenu.AppMenu.prototype,
+ 'open', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (!this.sourceActor.updating) {
+ originalFunction.call(this, ...args);
+ return;
+ }
+
+ if (this.isOpen)
+ return;
+
+ if (this.isEmpty())
+ return;
+
+ // Temporarily hide all the menu items a part the Pinning and
+ // the details one while we're updating.
+ const validItems = [
+ this._toggleFavoriteItem,
+ this._detailsItem,
+ ];
+ const items = this._getMenuItems().filter(
+ i => !validItems.includes(i)).map(i =>
+ i instanceof PopupMenu.PopupMenuBase ? i.actor : i);
+ const itemsVisibility = items.map(i => i.visible);
+ items.forEach(i => (i.visible = false));
+ const menuClosedId = this.connect('menu-closed', () => {
+ this.disconnect(menuClosedId);
+ items.forEach((i, idx) => (i.visible = itemsVisibility[idx]));
+ });
+ originalFunction.call(this, ...args);
+ /* eslint-enable no-invalid-this */
+ });
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js
new file mode 100644
index 0000000..790ef64
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/appSpread.js
@@ -0,0 +1,235 @@
+import {Atk, Clutter} from './dependencies/gi.js';
+
+import {
+ Main,
+ SearchController,
+ Workspace,
+ WorkspaceThumbnail,
+} from './dependencies/shell/ui.js';
+
+import {Utils} from './imports.js';
+
+const APP_SPREAD_RESTORE_ACTION = 'dock-app-spread-restore';
+
+export class AppSpread {
+ constructor() {
+ this.app = null;
+ this.supported = true;
+ this.windows = [];
+
+ // fail early and do nothing, if mandatory gnome shell functions are missing
+ if (Main.overview.isDummy ||
+ !Workspace?.Workspace?.prototype._isOverviewWindow ||
+ !WorkspaceThumbnail?.WorkspaceThumbnail?.prototype._isOverviewWindow) {
+ log('Dash to dock: Unable to temporarily replace shell functions ' +
+ 'for app spread - using previews instead');
+ this.supported = false;
+ return;
+ }
+
+ this._signalHandlers = new Utils.GlobalSignalsHandler();
+ this._methodInjections = new Utils.InjectionsHandler();
+ this._vfuncInjections = new Utils.VFuncInjectionsHandler();
+ }
+
+ get isInAppSpread() {
+ return !!this.app;
+ }
+
+ destroy() {
+ if (!this.supported)
+ return;
+ this._hideAppSpread();
+ this._signalHandlers.destroy();
+ this._methodInjections.destroy();
+ this._vfuncInjections.destroy();
+ }
+
+ toggle(app) {
+ const newApp = this.app !== app;
+ if (this.app)
+ Main.overview.hide(); // also triggers hook 'hidden'
+
+ if (app && newApp)
+ this._showAppSpread(app);
+ }
+
+ _updateWindows() {
+ this.windows = this.app.get_windows();
+ }
+
+ _restoreDefaultWindows() {
+ const {workspaceManager} = global;
+
+ for (let i = 0; i < workspaceManager.nWorkspaces; i++) {
+ const metaWorkspace = workspaceManager.get_workspace_by_index(i);
+ metaWorkspace.list_windows().forEach(w => metaWorkspace.emit('window-added', w));
+ }
+ }
+
+ _filterWindows() {
+ const {workspaceManager} = global;
+
+ for (let i = 0; i < workspaceManager.nWorkspaces; i++) {
+ const metaWorkspace = workspaceManager.get_workspace_by_index(i);
+ metaWorkspace.list_windows().filter(w => !this.windows.includes(w)).forEach(
+ w => metaWorkspace.emit('window-removed', w));
+ }
+ }
+
+ _restoreDefaultOverview() {
+ this._hideAppSpread();
+ this._restoreDefaultWindows();
+ }
+
+ _showAppSpread(app) {
+ if (this.isInAppSpread)
+ return;
+
+ // Checked in overview "hide" event handler _hideAppSpread
+ this.app = app;
+ this._updateWindows();
+
+ // we need to hook into overview 'hidden' like this, in case app spread
+ // overview is hidden by choosing another app it should then do its
+ // cleanup too
+ this._signalHandlers.add(Main.overview, 'hidden', () => this._hideAppSpread());
+
+ const appSpread = this;
+ this._methodInjections.add([
+ // Filter workspaces to only show current app windows
+ Workspace.Workspace.prototype, '_isOverviewWindow',
+ function (originalMethod, window) {
+ /* eslint-disable no-invalid-this */
+ const isOverviewWindow = originalMethod.call(this, window);
+ return isOverviewWindow && appSpread.windows.includes(window);
+ /* eslint-enable no-invalid-this */
+ },
+ ],
+ [
+ // Filter thumbnails to only show current app windows
+ WorkspaceThumbnail.WorkspaceThumbnail.prototype, '_isOverviewWindow',
+ function (originalMethod, windowActor) {
+ /* eslint-disable no-invalid-this */
+ const isOverviewWindow = originalMethod.call(this, windowActor);
+ return isOverviewWindow && appSpread.windows.includes(windowActor.metaWindow);
+ /* eslint-enable no-invalid-this */
+ },
+ ]);
+
+ const activitiesButton = Main.panel.statusArea?.activities;
+
+ if (activitiesButton) {
+ this._signalHandlers.add(Main.overview, 'showing', () => {
+ activitiesButton.remove_style_pseudo_class('overview');
+ activitiesButton.remove_accessible_state(Atk.StateType.CHECKED);
+ });
+
+ let hasEventVFunc = false;
+ try {
+ hasEventVFunc = !!activitiesButton.constructor.prototype.vfunc_event;
+ } catch {}
+
+ if (hasEventVFunc) {
+ this._vfuncInjections.add([
+ activitiesButton.constructor.prototype,
+ 'event',
+ function (event) {
+ if (event.type() === Clutter.EventType.TOUCH_END ||
+ event.type() === Clutter.EventType.BUTTON_RELEASE) {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ },
+ ]);
+ } else {
+ /* Shell >= 50 uses gestures on the activities button */
+ const click = new Clutter.ClickGesture();
+ click.set_recognize_on_press(true);
+ click.set_enabled(true);
+ click.connect('recognize', () => {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ });
+ activitiesButton.add_action_with_name(APP_SPREAD_RESTORE_ACTION, click);
+ }
+
+ this._vfuncInjections.add([
+ activitiesButton.constructor.prototype,
+ 'key_release_event',
+ function (keyEvent) {
+ const keyval = keyEvent.get_key_symbol?.() ?? keyEvent.keyval;
+ if (keyval === Clutter.KEY_Return || keyval === Clutter.KEY_space) {
+ if (Main.overview.shouldToggleByCornerOrButton())
+ appSpread._restoreDefaultOverview();
+ }
+ return Clutter.EVENT_PROPAGATE;
+ },
+ ]);
+ }
+
+ this._signalHandlers.add(Main.overview.dash.showAppsButton, 'notify::checked', () => {
+ if (Main.overview.dash.showAppsButton.checked)
+ this._restoreDefaultOverview();
+ });
+
+ // If closing windows in AppSpread, and only one window left:
+ // exit app spread and focus remaining window (handled in _hideAppSpread)
+ this._signalHandlers.add(this.app, 'windows-changed', () => {
+ this._updateWindows();
+
+ if (this.windows.length <= 1)
+ Main.overview.hide();
+ });
+
+ this._disableSearch();
+
+ Main.overview.show();
+ }
+
+ _hideAppSpread() {
+ if (!this.isInAppSpread)
+ return;
+
+ if (Main.overview.visible) {
+ Main.panel.statusArea?.activities.add_style_pseudo_class('overview');
+ Main.panel.statusArea?.activities.add_accessible_state(Atk.StateType.CHECKED);
+ }
+
+ // Restore original behaviour
+ this.app = null;
+ this._enableSearch();
+ this._methodInjections.clear();
+ this._signalHandlers.clear();
+ this._vfuncInjections.clear();
+ Main.panel.statusArea?.activities.remove_action_by_name(APP_SPREAD_RESTORE_ACTION);
+
+ // Check reason for leaving AppSpread was closing app windows and only one window left...
+ if (this.windows.length === 1)
+ Main.activateWindow(this.windows[0]);
+
+ this.windows = [];
+ }
+
+ _disableSearch() {
+ if (Main.overview.searchEntry) {
+ Main.overview.searchEntry.opacity = 0;
+ Main.overview.searchEntry.reactive = false;
+ }
+
+ if (!SearchController.SearchController.prototype._shouldTriggerSearch)
+ return;
+
+ this._methodInjections.add(
+ SearchController.SearchController.prototype,
+ '_shouldTriggerSearch', () => false);
+ }
+
+ _enableSearch() {
+ if (Main.overview.searchEntry) {
+ Main.overview.searchEntry.opacity = 255;
+ Main.overview.searchEntry.reactive = true;
+ }
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js
new file mode 100644
index 0000000..956bbeb
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dash.js
@@ -0,0 +1,1177 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ Gio,
+ GLib,
+ GObject,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ AppFavorites,
+ Dash,
+ DND,
+ Main,
+} from './dependencies/shell/ui.js';
+
+import {
+ Util,
+} from './dependencies/shell/misc.js';
+
+import {
+ AppIcons,
+ Docking,
+ Theming,
+ Utils,
+} from './imports.js';
+
+// module "Dash" did not export DASH_ANIMATION_TIME in old versions
+// so we just define it like it is defined in Dash;
+// taken from https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/dash.js
+const DASH_ANIMATION_TIME = Dash.DASH_ANIMATION_TIME ?? 200;
+const DASH_VISIBILITY_TIMEOUT = 3;
+
+const Labels = Object.freeze({
+ SHOW_MOUNTS: Symbol('show-mounts'),
+ FIRST_LAST_CHILD_WORKAROUND: Symbol('first-last-child-workaround'),
+});
+
+/**
+ * Extend DashItemContainer
+ *
+ * - set label position based on dash orientation
+ *
+ */
+const DockDashItemContainer = GObject.registerClass(
+class DockDashItemContainer extends Dash.DashItemContainer {
+ _init(position) {
+ super._init();
+
+ this.label?.add_style_class_name(Theming.PositionStyleClass[position]);
+ if (Docking.DockManager.settings.customThemeShrink)
+ this.label?.add_style_class_name('shrink');
+ }
+
+ showLabel() {
+ return AppIcons.itemShowLabel.call(this);
+ }
+
+ // we override the method show taken from:
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/dash.js
+ // in order to apply a little modification at the end of the animation
+ // which makes sure that the icon background is not blurry
+ show(animate) {
+ if (this.child == null)
+ return;
+
+ this.ease({
+ scale_x: 1,
+ scale_y: 1,
+ opacity: 255,
+ duration: animate ? DASH_ANIMATION_TIME : 0,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ // when the animation is ended, we simulate
+ // a hover to gain back focus and unblur the
+ // background
+ this.set_hover(true);
+ },
+ });
+ }
+});
+
+const DockDashIconsVerticalLayout = GObject.registerClass(
+ class DockDashIconsVerticalLayout extends Clutter.BoxLayout {
+ _init() {
+ super._init({
+ orientation: Clutter.Orientation.VERTICAL,
+ });
+ }
+
+ vfunc_get_preferred_height(container, forWidth) {
+ const [natHeight] = super.vfunc_get_preferred_height(container, forWidth);
+ return [natHeight, 0];
+ }
+ });
+
+
+const baseIconSizes = [16, 22, 24, 32, 48, 64, 96, 128];
+
+/**
+ * This class is a fork of the upstream dash class (ui.dash.js)
+ *
+ * Summary of changes:
+ * - disconnect global signals adding a destroy method;
+ * - play animations even when not in overview mode
+ * - set a maximum icon size
+ * - show running and/or favorite applications
+ * - hide showApps label when the custom menu is shown.
+ * - add scrollview
+ * ensure actor is visible on keyfocus inseid the scrollview
+ * - add 128px icon size, might be useful for hidpi display
+ * - sync minimization application target position.
+ * - keep running apps ordered.
+ */
+export const DockDash = GObject.registerClass({
+ Properties: {
+ 'requires-visibility': GObject.ParamSpec.boolean(
+ 'requires-visibility', 'requires-visibility', 'requires-visibility',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+ Signals: {
+ 'menu-opened': {},
+ 'menu-closed': {},
+ 'icon-size-changed': {},
+ },
+}, class DockDash extends St.Widget {
+ _init(monitorIndex) {
+ // Initialize icon variables and size
+ super._init({
+ name: 'dash',
+ offscreen_redirect: Clutter.OffscreenRedirect.ALWAYS,
+ layout_manager: new Clutter.BinLayout(),
+ });
+
+ this._maxWidth = -1;
+ this._maxHeight = -1;
+ this.iconSize = Docking.DockManager.settings.dashMaxIconSize;
+ this._availableIconSizes = baseIconSizes;
+ this._shownInitially = false;
+ this._initializeIconSize(this.iconSize);
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+
+ this._separator = null;
+
+ this._monitorIndex = monitorIndex;
+ this._position = Utils.getPosition();
+ this._isHorizontal = (this._position === St.Side.TOP) ||
+ (this._position === St.Side.BOTTOM);
+
+ this._dragPlaceholder = null;
+ this._dragPlaceholderPos = -1;
+ this._animatingPlaceholdersCount = 0;
+ this._showLabelTimeoutId = 0;
+ this._resetHoverTimeoutId = 0;
+ this._labelShowing = false;
+
+ this._dashContainer = new St.BoxLayout({
+ name: 'dashtodockDashContainer',
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ vertical: !this._isHorizontal,
+ y_expand: this._isHorizontal,
+ x_expand: !this._isHorizontal,
+ });
+
+ this._scrollView = new St.ScrollView({
+ name: 'dashtodockDashScrollview',
+ hscrollbar_policy: this._isHorizontal ? St.PolicyType.EXTERNAL : St.PolicyType.NEVER,
+ vscrollbar_policy: this._isHorizontal ? St.PolicyType.NEVER : St.PolicyType.EXTERNAL,
+ x_expand: this._isHorizontal,
+ y_expand: !this._isHorizontal,
+ enable_mouse_scrolling: false,
+ });
+
+ this._scrollView.connect('scroll-event', this._onScrollEvent.bind(this));
+
+ this._boxContainer = new St.BoxLayout({
+ name: 'dashtodockBoxContainer',
+ x_align: Clutter.ActorAlign.FILL,
+ y_align: Clutter.ActorAlign.FILL,
+ vertical: !this._isHorizontal,
+ });
+ this._boxContainer.add_style_class_name(Theming.PositionStyleClass[this._position]);
+
+ const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+ this._box = new St.BoxLayout({
+ vertical: !this._isHorizontal,
+ clip_to_allocation: false,
+ ...!this._isHorizontal ? {layout_manager: new DockDashIconsVerticalLayout()} : {},
+ x_align: rtl ? Clutter.ActorAlign.END : Clutter.ActorAlign.START,
+ y_align: this._isHorizontal ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START,
+ y_expand: !this._isHorizontal,
+ x_expand: this._isHorizontal,
+ });
+ this._box._delegate = this;
+ this._boxContainer.add_child(this._box);
+ Utils.addActor(this._scrollView, this._boxContainer);
+ this._dashContainer.add_child(this._scrollView);
+
+ this._showAppsIcon = new AppIcons.DockShowAppsIcon(this._position);
+ this._showAppsIcon.show(false);
+ this._showAppsIcon.icon.setIconSize(this.iconSize);
+ this._showAppsIcon.x_expand = false;
+ this._showAppsIcon.y_expand = false;
+ this.showAppsButton.connect('notify::hover', a => {
+ if (this._showAppsIcon.get_parent() === this._boxContainer)
+ this._ensureItemVisibility(a);
+ });
+ if (!this._isHorizontal)
+ this._showAppsIcon.y_align = Clutter.ActorAlign.START;
+ this._hookUpLabel(this._showAppsIcon);
+ this._showAppsIcon.connect('menu-state-changed', (_icon, opened) => {
+ this._itemMenuStateChanged(this._showAppsIcon, opened);
+ });
+ this.updateShowAppsButton();
+
+ this._background = new St.Widget({
+ style_class: 'dash-background',
+ y_expand: this._isHorizontal,
+ x_expand: !this._isHorizontal,
+ });
+
+ const sizerBox = new Clutter.Actor();
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: this._isHorizontal ? this._showAppsIcon.icon : this._dashContainer,
+ coordinate: Clutter.BindCoordinate.HEIGHT,
+ }));
+ sizerBox.add_constraint(new Clutter.BindConstraint({
+ source: this._isHorizontal ? this._dashContainer : this._showAppsIcon.icon,
+ coordinate: Clutter.BindCoordinate.WIDTH,
+ }));
+ this._background.add_child(sizerBox);
+
+ this.add_child(this._background);
+ this.add_child(this._dashContainer);
+
+ this._workId = Main.initializeDeferredWork(this._box, this._redisplay.bind(this));
+
+ this._shellSettings = new Gio.Settings({
+ schema_id: 'org.gnome.shell',
+ });
+
+ this._appSystem = Shell.AppSystem.get_default();
+
+ this.iconAnimator = new Docking.IconAnimator(this);
+
+ this._signalsHandler.add([
+ this._appSystem,
+ 'installed-changed',
+ () => {
+ AppFavorites.getAppFavorites().reload();
+ this._queueRedisplay();
+ },
+ ], [
+ AppFavorites.getAppFavorites(),
+ 'changed',
+ this._queueRedisplay.bind(this),
+ ], [
+ this._appSystem,
+ 'app-state-changed',
+ this._queueRedisplay.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-begin',
+ this._onItemDragBegin.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ this._onItemDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ this._onItemDragCancelled.bind(this),
+ ], [
+ Main.overview,
+ 'window-drag-begin',
+ this._onWindowDragBegin.bind(this),
+ ], [
+ Main.overview,
+ 'window-drag-cancelled',
+ this._onWindowDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'window-drag-end',
+ this._onWindowDragEnd.bind(this),
+ ]);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ const [minHeight, natHeight] = super.vfunc_get_preferred_height.call(this, forWidth);
+ if (!this._isHorizontal && this._maxHeight !== -1 && natHeight > this._maxHeight)
+ return [minHeight, this._maxHeight];
+ else
+ return [minHeight, natHeight];
+ }
+
+ vfunc_get_preferred_width(forHeight) {
+ const [minWidth, natWidth] = super.vfunc_get_preferred_width.call(this, forHeight);
+ if (this._isHorizontal && this._maxWidth !== -1 && natWidth > this._maxWidth)
+ return [minWidth, this._maxWidth];
+ else
+ return [minWidth, natWidth];
+ }
+
+ get _container() {
+ return this._dashContainer;
+ }
+
+ _onDestroy() {
+ this.iconAnimator.destroy();
+
+ if (this._requiresVisibilityTimeout) {
+ GLib.source_remove(this._requiresVisibilityTimeout);
+ delete this._requiresVisibilityTimeout;
+ }
+
+ if (this._ensureActorVisibilityTimeoutId) {
+ GLib.source_remove(this._ensureActorVisibilityTimeoutId);
+ delete this._ensureActorVisibilityTimeoutId;
+ }
+ }
+
+
+ _onItemDragBegin(...args) {
+ return Dash.Dash.prototype._onItemDragBegin.call(this, ...args);
+ }
+
+ _onItemDragCancelled(...args) {
+ return Dash.Dash.prototype._onItemDragCancelled.call(this, ...args);
+ }
+
+ _onItemDragEnd(...args) {
+ return Dash.Dash.prototype._onItemDragEnd.call(this, ...args);
+ }
+
+ _endItemDrag(...args) {
+ return Dash.Dash.prototype._endItemDrag.call(this, ...args);
+ }
+
+ _onItemDragMotion(...args) {
+ return Dash.Dash.prototype._onItemDragMotion.call(this, ...args);
+ }
+
+ _appIdListToHash(...args) {
+ return Dash.Dash.prototype._appIdListToHash.call(this, ...args);
+ }
+
+ _queueRedisplay(...args) {
+ return Dash.Dash.prototype._queueRedisplay.call(this, ...args);
+ }
+
+ _hookUpLabel(...args) {
+ return Dash.Dash.prototype._hookUpLabel.call(this, ...args);
+ }
+
+ _syncLabel(...args) {
+ return Dash.Dash.prototype._syncLabel.call(this, ...args);
+ }
+
+ _clearDragPlaceholder(...args) {
+ return Dash.Dash.prototype._clearDragPlaceholder.call(this, ...args);
+ }
+
+ _clearEmptyDropTarget(...args) {
+ return Dash.Dash.prototype._clearEmptyDropTarget.call(this, ...args);
+ }
+
+ handleDragOver(source, actor, x, y, time) {
+ let ret;
+ if (this._isHorizontal) {
+ ret = Dash.Dash.prototype.handleDragOver.call(this, source, actor, x, y, time);
+
+ if (ret === DND.DragMotionResult.CONTINUE)
+ return ret;
+ } else {
+ const propertyInjections = new Utils.PropertyInjectionsHandler();
+ propertyInjections.add(this._box, 'width', {
+ get: () => this._box.get_children().reduce((a, c) => a + c.height, 0),
+ });
+
+ if (this._dragPlaceholder) {
+ propertyInjections.add(this._dragPlaceholder, 'width', {
+ get: () => this._dragPlaceholder.height,
+ });
+ }
+
+ ret = Dash.Dash.prototype.handleDragOver.call(this, source, actor, y, x, time);
+ propertyInjections.destroy();
+
+ if (ret === DND.DragMotionResult.CONTINUE)
+ return ret;
+
+ if (this._dragPlaceholder) {
+ this._dragPlaceholder.child.set_width(this.iconSize / 2);
+ this._dragPlaceholder.child.set_height(this.iconSize);
+
+ let pos = this._dragPlaceholderPos;
+ if (this._isHorizontal &&
+ Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
+ pos = this._box.get_children() - 1 - pos;
+
+ if (pos !== this._dragPlaceholderPos) {
+ this._dragPlaceholderPos = pos;
+ this._box.set_child_at_index(this._dragPlaceholder,
+ this._dragPlaceholderPos);
+ }
+ }
+ }
+
+ if (this._dragPlaceholder) {
+ // Ensure the next and previous icon are visible when moving the
+ // placeholder (we're assuming there's room for both of them)
+ const children = this._box.get_children();
+ if (this._dragPlaceholderPos > 0) {
+ ensureActorVisibleInScrollView(this._scrollView,
+ children[this._dragPlaceholderPos - 1]);
+ }
+
+ if (this._dragPlaceholderPos >= -1 &&
+ this._dragPlaceholderPos < children.length - 1) {
+ ensureActorVisibleInScrollView(this._scrollView,
+ children[this._dragPlaceholderPos + 1]);
+ }
+ }
+
+ return ret;
+ }
+
+ acceptDrop(...args) {
+ return Dash.Dash.prototype.acceptDrop.call(this, ...args);
+ }
+
+ _onWindowDragBegin(...args) {
+ return Dash.Dash.prototype._onWindowDragBegin.call(this, ...args);
+ }
+
+ _onWindowDragEnd(...args) {
+ return Dash.Dash.prototype._onWindowDragEnd.call(this, ...args);
+ }
+
+ _onScrollEvent(actor, event) {
+ // If scroll is not used because the icon is resized, let the scroll event propagate.
+ if (!Docking.DockManager.settings.iconSizeFixed)
+ return Clutter.EVENT_PROPAGATE;
+
+ // reset timeout to avid conflicts with the mousehover event
+ this._ensureItemVisibility(null);
+
+ // Skip to avoid double events mouse
+ if (event.get_scroll_direction() !== Clutter.ScrollDirection.SMOOTH)
+ return Clutter.EVENT_STOP;
+
+
+ let adjustment, delta = 0;
+
+ if (this._isHorizontal) {
+ adjustment = this._scrollView.get_hadjustment
+ ? this._scrollView.get_hadjustment()
+ : this._scrollView.get_hscroll_bar().get_adjustment();
+ } else {
+ adjustment = this._scrollView.get_vadjustment
+ ? this._scrollView.get_vadjustment()
+ : this._scrollView.get_vscroll_bar().get_adjustment();
+ }
+
+ const increment = adjustment.step_increment;
+ const [dx, dy] = event.get_scroll_delta();
+
+ if (this._isHorizontal)
+ delta = (Math.abs(dx) > Math.abs(dy) ? dx : dy) * increment;
+ else
+ delta = dy * increment;
+
+ const value = adjustment.get_value();
+
+ // TODO: Remove this if possible.
+ if (Number.isNaN(value))
+ adjustment.set_value(delta);
+ else
+ adjustment.set_value(value + delta);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _ensureItemVisibility(actor) {
+ if (actor?.hover) {
+ const destroyId =
+ actor.connect('destroy', () => this._ensureItemVisibility(null));
+ this._ensureActorVisibilityTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 100, () => {
+ actor.disconnect(destroyId);
+ ensureActorVisibleInScrollView(this._scrollView, actor);
+ this._ensureActorVisibilityTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ } else if (this._ensureActorVisibilityTimeoutId) {
+ GLib.source_remove(this._ensureActorVisibilityTimeoutId);
+ this._ensureActorVisibilityTimeoutId = 0;
+ }
+ }
+
+ _createAppItem(app) {
+ const appIcon = new AppIcons.makeAppIcon(app, this._monitorIndex, this.iconAnimator);
+
+ if (appIcon._draggable) {
+ appIcon._draggable.connect('drag-begin', () => {
+ appIcon.opacity = 50;
+ });
+ appIcon._draggable.connect('drag-end', () => {
+ appIcon.opacity = 255;
+ });
+ }
+
+ appIcon.connectObject('menu-state-changed', (_, opened) => {
+ this._itemMenuStateChanged(item, opened);
+ }, this);
+
+ const item = new DockDashItemContainer(this._position);
+ item.setChild(appIcon);
+
+ appIcon.connectObject('notify::hover', a => this._ensureItemVisibility(a), this);
+ appIcon.connectObject('clicked', actor => {
+ ensureActorVisibleInScrollView(this._scrollView, actor);
+ }, this);
+
+ appIcon.connectObject('key-focus-in', actor => {
+ const [xShift, yShift] = ensureActorVisibleInScrollView(this._scrollView, actor);
+
+ // This signal is triggered also by mouse click. The popup menu is opened at the original
+ // coordinates. Thus correct for the shift which is going to be applied to the scrollview.
+ if (appIcon._menu) {
+ appIcon._menu._boxPointer.xOffset = -xShift;
+ appIcon._menu._boxPointer.yOffset = -yShift;
+ }
+ }, this);
+
+ appIcon.connectObject('notify::focused', () => {
+ const {settings} = Docking.DockManager;
+ if (appIcon.focused && settings.scrollToFocusedApplication)
+ ensureActorVisibleInScrollView(this._scrollView, item);
+ }, this);
+
+ appIcon.connectObject('notify::urgent', () => {
+ if (appIcon.urgent) {
+ ensureActorVisibleInScrollView(this._scrollView, item);
+ if (Docking.DockManager.settings.showDockUrgentNotify)
+ this._requireVisibility();
+ }
+ }, this);
+
+ // Override default AppIcon label_actor, now the
+ // accessible_name is set at DashItemContainer.setLabelText
+ appIcon.label_actor = null;
+ item.setLabelText(app.get_name());
+
+ appIcon.icon.setIconSize(this.iconSize);
+ this._hookUpLabel(item, appIcon);
+
+ item.connectObject('notify::position', () => appIcon.updateIconGeometry(), appIcon);
+ item.connectObject('notify::size', () => appIcon.updateIconGeometry(), appIcon);
+
+ return item;
+ }
+
+ _requireVisibility() {
+ this.requiresVisibility = true;
+
+ if (this._requiresVisibilityTimeout)
+ GLib.source_remove(this._requiresVisibilityTimeout);
+
+ this._requiresVisibilityTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
+ DASH_VISIBILITY_TIMEOUT, () => {
+ this._requiresVisibilityTimeout = 0;
+ this.requiresVisibility = false;
+ });
+ }
+
+ /**
+ * Return an array with the "proper" appIcons currently in the dash
+ */
+ getAppIcons() {
+ // Only consider children which are "proper"
+ // icons (i.e. ignoring drag placeholders) and which are not
+ // animating out (which means they will be destroyed at the end of
+ // the animation)
+ const iconChildren = this._box.get_children().filter(actor => {
+ return actor.child &&
+ !!actor.child.icon &&
+ !actor.animatingOut;
+ });
+
+ const appIcons = iconChildren.map(actor => {
+ return actor.child;
+ });
+
+ return appIcons;
+ }
+
+ _itemMenuStateChanged(item, opened) {
+ Dash.Dash.prototype._itemMenuStateChanged.call(this, item, opened);
+
+ if (opened) {
+ this.emit('menu-opened');
+ } else {
+ // I want to listen from outside when a menu is closed. I used to
+ // add a custom signal to the appIcon, since gnome 3.8 the signal
+ // calling this callback was added upstream.
+ this.emit('menu-closed');
+ }
+ }
+
+ _adjustIconSize() {
+ // For the icon size, we only consider children which are "proper"
+ // icons (i.e. ignoring drag placeholders) and which are not
+ // animating out (which means they will be destroyed at the end of
+ // the animation)
+ const iconChildren = this._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.icon &&
+ !actor.animatingOut;
+ });
+
+ iconChildren.push(this._showAppsIcon);
+
+ if (this._maxWidth === -1 && this._maxHeight === -1)
+ return;
+
+ // Check if the container is present in the stage. This avoids critical
+ // errors when unlocking the screen
+ if (!this._container.get_stage())
+ return;
+
+ const themeNode = this._dashContainer.get_theme_node();
+ const maxAllocation = new Clutter.ActorBox({
+ x1: 0,
+ y1: 0,
+ x2: this._isHorizontal ? this._maxWidth : 42 /* whatever */,
+ y2: this._isHorizontal ? 42 : this._maxHeight,
+ });
+ const maxContent = themeNode.get_content_box(maxAllocation);
+ let availSpace;
+ if (this._isHorizontal)
+ availSpace = maxContent.get_width();
+ else
+ availSpace = maxContent.get_height();
+
+ const spacing = themeNode.get_length('spacing');
+
+ const [{child: firstButton}] = iconChildren;
+ const {child: firstIcon} = firstButton?.icon ?? {child: null};
+
+ // if no icons there's nothing to adjust
+ if (!firstIcon)
+ return;
+
+ // Enforce valid spacings during the size request
+ firstIcon.ensure_style();
+ const [, , iconWidth, iconHeight] = firstIcon.get_preferred_size();
+ const [, , buttonWidth, buttonHeight] = firstButton.get_preferred_size();
+
+ if (this._isHorizontal) {
+ // Subtract icon padding and box spacing from the available width
+ availSpace -= iconChildren.length * (buttonWidth - iconWidth) +
+ (iconChildren.length - 1) * spacing;
+
+ if (this._separator) {
+ const [, , separatorWidth] = this._separator.get_preferred_size();
+ availSpace -= separatorWidth + spacing;
+ }
+ } else {
+ // Subtract icon padding and box spacing from the available height
+ availSpace -= iconChildren.length * (buttonHeight - iconHeight) +
+ (iconChildren.length - 1) * spacing;
+
+ if (this._separator) {
+ const [, , , separatorHeight] = this._separator.get_preferred_size();
+ availSpace -= separatorHeight + spacing;
+ }
+ }
+
+ const maxIconSize = availSpace / iconChildren.length;
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+ const iconSizes = this._availableIconSizes.map(s => s * scaleFactor);
+
+ let [newIconSize] = this._availableIconSizes;
+ for (let i = 0; i < iconSizes.length; i++) {
+ if (iconSizes[i] <= maxIconSize)
+ newIconSize = this._availableIconSizes[i];
+ }
+
+ if (newIconSize === this.iconSize)
+ return;
+
+ const oldIconSize = this.iconSize;
+ this.iconSize = newIconSize;
+ this.emit('icon-size-changed');
+
+ const scale = oldIconSize / newIconSize;
+ for (let i = 0; i < iconChildren.length; i++) {
+ const {icon} = iconChildren[i].child._delegate;
+
+ // Set the new size immediately, to keep the icons' sizes
+ // in sync with this.iconSize
+ icon.setIconSize(this.iconSize);
+
+ // Don't animate the icon size change when the overview
+ // is transitioning, not visible or when initially filling
+ // the dash
+ if (!Main.overview.visible || Main.overview.animationInProgress ||
+ !this._shownInitially)
+ continue;
+
+ const [targetWidth, targetHeight] = icon.icon.get_size();
+
+ // Scale the icon's texture to the previous size and
+ // tween to the new size
+ icon.icon.set_size(icon.icon.width * scale,
+ icon.icon.height * scale);
+
+ icon.icon.ease({
+ width: targetWidth,
+ height: targetHeight,
+ duration: DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+
+ if (this._separator) {
+ const animateProperties = this._isHorizontal
+ ? {height: this.iconSize} : {width: this.iconSize};
+
+ this._separator.ease({
+ ...animateProperties,
+ duration: DASH_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+
+ _redisplay() {
+ const favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+ let running = this._appSystem.get_running();
+ const dockManager = Docking.DockManager.getDefault();
+ const {settings} = dockManager;
+
+ this._scrollView.set({
+ xAlign: Clutter.ActorAlign.FILL,
+ yAlign: Clutter.ActorAlign.FILL,
+ });
+ if (dockManager.settings.dockExtended) {
+ if (!this._isHorizontal) {
+ this._scrollView.yAlign = dockManager.settings.alwaysCenterIcons
+ ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START;
+ } else {
+ this._scrollView.xAlign = dockManager.settings.alwaysCenterIcons
+ ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START;
+ }
+ }
+
+ if (settings.isolateWorkspaces ||
+ settings.isolateMonitors) {
+ // When using isolation, we filter out apps that have no windows in
+ // the current workspace
+ const monitorIndex = this._monitorIndex;
+ running = running.filter(app =>
+ AppIcons.getInterestingWindows(app.get_windows(), monitorIndex).length);
+ }
+
+ const children = this._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child._delegate &&
+ actor.child._delegate.app;
+ });
+ // Apps currently in the dash
+ let oldApps = children.map(actor => actor.child._delegate.app);
+ // Apps supposed to be in the dash
+ const newApps = [];
+
+ const {showFavorites} = settings;
+ if (showFavorites)
+ newApps.push(...Object.values(favorites));
+
+ if (settings.showRunning) {
+ // We reorder the running apps so that they don't change position on the
+ // dash with every redisplay() call
+
+ // First: add the apps from the oldApps list that are still running
+ oldApps.forEach(oldApp => {
+ const index = running.indexOf(oldApp);
+ if (index > -1) {
+ const [app] = running.splice(index, 1);
+ if (!showFavorites || !(app.get_id() in favorites))
+ newApps.push(app);
+ }
+ });
+
+ // Second: add the new apps
+ running.forEach(app => {
+ if (!showFavorites || !(app.get_id() in favorites))
+ newApps.push(app);
+ });
+ }
+
+ this._signalsHandler.removeWithLabel(Labels.SHOW_MOUNTS);
+ if (dockManager.removables) {
+ this._signalsHandler.addWithLabel(Labels.SHOW_MOUNTS,
+ dockManager.removables, 'changed', this._queueRedisplay.bind(this));
+ dockManager.removables.getApps().forEach(removable => {
+ if (!newApps.includes(removable))
+ newApps.push(removable);
+ });
+ } else {
+ oldApps = oldApps.filter(app => !app.location || app.isTrash);
+ }
+
+ if (dockManager.trash) {
+ const trashApp = dockManager.trash.getApp();
+ if (!newApps.includes(trashApp))
+ newApps.push(trashApp);
+ } else {
+ oldApps = oldApps.filter(app => !app.isTrash);
+ }
+
+ // Temporary remove the separator so that we don't compute to position icons
+ const oldSeparatorPos = this._box.get_children().indexOf(this._separator);
+ if (this._separator)
+ this._box.remove_child(this._separator);
+
+ // Figure out the actual changes to the list of items; we iterate
+ // over both the list of items currently in the dash and the list
+ // of items expected there, and collect additions and removals.
+ // Moves are both an addition and a removal, where the order of
+ // the operations depends on whether we encounter the position
+ // where the item has been added first or the one from where it
+ // was removed.
+ // There is an assumption that only one item is moved at a given
+ // time; when moving several items at once, everything will still
+ // end up at the right position, but there might be additional
+ // additions/removals (e.g. it might remove all the launchers
+ // and add them back in the new order even if a smaller set of
+ // additions and removals is possible).
+ // If above assumptions turns out to be a problem, we might need
+ // to use a more sophisticated algorithm, e.g. Longest Common
+ // Subsequence as used by diff.
+
+ const addedItems = [];
+ const removedActors = [];
+
+ let newIndex = 0;
+ let oldIndex = 0;
+ while (newIndex < newApps.length || oldIndex < oldApps.length) {
+ const oldApp = oldApps.length > oldIndex ? oldApps[oldIndex] : null;
+ const newApp = newApps.length > newIndex ? newApps[newIndex] : null;
+
+ // No change at oldIndex/newIndex
+ if (oldApp === newApp) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // App removed at oldIndex
+ if (oldApp && !newApps.includes(oldApp)) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // App added at newIndex
+ if (newApp && !oldApps.includes(newApp)) {
+ addedItems.push({
+ app: newApp,
+ item: this._createAppItem(newApp),
+ pos: newIndex,
+ });
+ newIndex++;
+ continue;
+ }
+
+ // App moved
+ const nextApp = newApps.length > newIndex + 1
+ ? newApps[newIndex + 1] : null;
+ const insertHere = nextApp && nextApp === oldApp;
+ const alreadyRemoved = removedActors.reduce((result, actor) => {
+ const removedApp = actor.child._delegate.app;
+ return result || removedApp === newApp;
+ }, false);
+
+ if (insertHere || alreadyRemoved) {
+ const newItem = this._createAppItem(newApp);
+ addedItems.push({
+ app: newApp,
+ item: newItem,
+ pos: newIndex + removedActors.length,
+ });
+ newIndex++;
+ } else {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ }
+ }
+
+ for (let i = 0; i < addedItems.length; i++) {
+ this._box.insert_child_at_index(addedItems[i].item,
+ addedItems[i].pos);
+ }
+
+ for (let i = 0; i < removedActors.length; i++) {
+ const item = removedActors[i];
+
+ // Don't animate item removal when the overview is transitioning
+ // or hidden
+ if (!Main.overview.animationInProgress)
+ item.animateOutAndDestroy();
+ else
+ item.destroy();
+ }
+
+ // Update separator
+ const nFavorites = Object.keys(favorites).length;
+ const nIcons = children.length + addedItems.length - removedActors.length;
+ if (nFavorites > 0 && nFavorites < nIcons) {
+ if (!this._separator) {
+ this._separator = new St.Widget({
+ style_class: 'dash-separator',
+ x_align: this._isHorizontal
+ ? Clutter.ActorAlign.FILL : Clutter.ActorAlign.CENTER,
+ y_align: this._isHorizontal
+ ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.FILL,
+ width: this._isHorizontal ? -1 : this.iconSize,
+ height: this._isHorizontal ? this.iconSize : -1,
+ reactive: true,
+ track_hover: true,
+ });
+ this._separator.connect('notify::hover', a => this._ensureItemVisibility(a));
+ }
+ let pos = nFavorites + this._animatingPlaceholdersCount;
+ if (this._dragPlaceholder)
+ pos++;
+ const removedFavorites = removedActors.filter(a =>
+ children.indexOf(a) < oldSeparatorPos);
+ pos += removedFavorites.length;
+ this._box.insert_child_at_index(this._separator, pos);
+ } else if (this._separator) {
+ this._separator.destroy();
+ this._separator = null;
+ }
+
+ this._adjustIconSize();
+
+ // Skip animations on first run when adding the initial set
+ // of items, to avoid all items zooming in at once
+ const animate = this._shownInitially &&
+ !Main.layoutManager._startingUp;
+
+ if (!this._shownInitially)
+ this._shownInitially = true;
+
+ addedItems.forEach(({item}) => item.show(animate));
+
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
+ // Without it, StBoxLayout may use a stale size cache
+ this._box.queue_relayout();
+
+ // This will update the size, and the corresponding number for each icon
+ this._updateNumberOverlay();
+
+ this.updateShowAppsButton();
+ }
+
+ _updateNumberOverlay() {
+ const appIcons = this.getAppIcons();
+ let counter = 1;
+ appIcons.forEach(icon => {
+ if (counter < 10) {
+ icon.setNumberOverlay(counter);
+ counter++;
+ } else if (counter === 10) {
+ icon.setNumberOverlay(0);
+ counter++;
+ } else {
+ // No overlay after 10
+ icon.setNumberOverlay(-1);
+ }
+ icon.updateNumberOverlay();
+ });
+ }
+
+ toggleNumberOverlay(activate) {
+ const appIcons = this.getAppIcons();
+ appIcons.forEach(icon => {
+ icon.toggleNumberOverlay(activate);
+ });
+ }
+
+ _initializeIconSize(maxSize) {
+ const maxAllowed = baseIconSizes[baseIconSizes.length - 1];
+ maxSize = Math.min(maxSize, maxAllowed);
+
+ if (Docking.DockManager.settings.iconSizeFixed) {
+ this._availableIconSizes = [maxSize];
+ } else {
+ this._availableIconSizes = baseIconSizes.filter(val => {
+ return val < maxSize;
+ });
+ this._availableIconSizes.push(maxSize);
+ }
+ }
+
+ setIconSize(maxSize, doNotAnimate) {
+ this._initializeIconSize(maxSize);
+
+ if (doNotAnimate)
+ this._shownInitially = false;
+
+ this._queueRedisplay();
+ }
+
+ /**
+ * Reset the displayed apps icon to maintain the correct order when changing
+ * show favorites/show running settings
+ */
+ resetAppIcons() {
+ const children = this._box.get_children().filter(actor => {
+ return actor.child &&
+ !!actor.child.icon;
+ });
+ for (let i = 0; i < children.length; i++) {
+ const item = children[i];
+ item.destroy();
+ }
+
+ // to avoid ugly animations, just suppress them like when dash is first loaded.
+ this._shownInitially = false;
+ this._redisplay();
+ }
+
+ get showAppsButton() {
+ return this._showAppsIcon.toggleButton;
+ }
+
+ showShowAppsButton() {
+ this._showAppsIcon.visible = true;
+ this._showAppsIcon.show(true);
+ this.updateShowAppsButton();
+ }
+
+ hideShowAppsButton() {
+ this._showAppsIcon.visible = false;
+ }
+
+ setMaxSize(maxWidth, maxHeight) {
+ if (this._maxWidth === maxWidth &&
+ this._maxHeight === maxHeight)
+ return;
+
+ this._maxWidth = maxWidth;
+ this._maxHeight = maxHeight;
+ this._queueRedisplay();
+ }
+
+ updateShowAppsButton() {
+ if (this._showAppsIcon.get_parent() && !this._showAppsIcon.visible)
+ return;
+
+ const {settings} = Docking.DockManager;
+ const notifiedProperties = [];
+ const showAppsContainer = settings.showAppsAlwaysInTheEdge || !settings.dockExtended
+ ? this._dashContainer : this._boxContainer;
+
+ this._signalsHandler.addWithLabel(Labels.FIRST_LAST_CHILD_WORKAROUND,
+ showAppsContainer, 'notify',
+ (_obj, pspec) => notifiedProperties.push(pspec.name));
+
+ if (this._showAppsIcon.get_parent() !== showAppsContainer) {
+ this._showAppsIcon.get_parent()?.remove_child(this._showAppsIcon);
+
+ if (Docking.DockManager.settings.showAppsAtTop)
+ showAppsContainer.insert_child_below(this._showAppsIcon, null);
+ else
+ showAppsContainer.insert_child_above(this._showAppsIcon, null);
+ } else if (settings.showAppsAtTop) {
+ showAppsContainer.set_child_below_sibling(this._showAppsIcon, null);
+ } else {
+ showAppsContainer.set_child_above_sibling(this._showAppsIcon, null);
+ }
+
+ this._signalsHandler.removeWithLabel(Labels.FIRST_LAST_CHILD_WORKAROUND);
+
+ // This is indeed ugly, but we need to ensure that the last and first
+ // visible widgets are re-computed by St, that is buggy because of a
+ // mutter issue that is being fixed:
+ // https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2047
+ if (!notifiedProperties.includes('first-child'))
+ showAppsContainer.notify('first-child');
+ if (!notifiedProperties.includes('last-child'))
+ showAppsContainer.notify('last-child');
+ }
+});
+
+
+/**
+ * This is a copy of the same function in utils.js, but also adjust horizontal scrolling
+ * and perform few further checks on the current value to avoid changing the values when
+ * it would be clamp to the current one in any case.
+ * Return the amount of shift applied
+ *
+ * @param scrollView
+ * @param actor
+ */
+function ensureActorVisibleInScrollView(scrollView, actor) {
+ // access to scrollView.[hv]scroll was deprecated in gnome 46
+ // instead, adjustment can be accessed directly
+ // keep old way for backwards compatibility (gnome <= 45)
+ const vAdjustment = scrollView.vadjustment ?? scrollView.vscroll.adjustment;
+ const hAdjustment = scrollView.hadjustment ?? scrollView.hscroll.adjustment;
+ const {value: vValue0, pageSize: vPageSize, upper: vUpper} = vAdjustment;
+ const {value: hValue0, pageSize: hPageSize, upper: hUpper} = hAdjustment;
+ let [hValue, vValue] = [hValue0, vValue0];
+ let vOffset = 0;
+ let hOffset = 0;
+
+ const fade = scrollView.get_effect('fade');
+ if (fade) {
+ vOffset = fade.fade_margins.top;
+ hOffset = fade.fade_margins.left;
+ }
+
+ const box = actor.get_allocation_box();
+ let {y1} = box, {y2} = box, {x1} = box, {x2} = box;
+
+ let parent = actor.get_parent();
+ while (parent !== scrollView) {
+ if (!parent)
+ throw new Error('Actor not in scroll view');
+
+ const parentBox = parent.get_allocation_box();
+ y1 += parentBox.y1;
+ y2 += parentBox.y1;
+ x1 += parentBox.x1;
+ x2 += parentBox.x1;
+ parent = parent.get_parent();
+ }
+
+ if (y1 < vValue + vOffset)
+ vValue = Math.max(0, y1 - vOffset);
+ else if (vValue < vUpper - vPageSize && y2 > vValue + vPageSize - vOffset)
+ vValue = Math.min(vUpper - vPageSize, y2 + vOffset - vPageSize);
+
+ if (x1 < hValue + hOffset)
+ hValue = Math.max(0, x1 - hOffset);
+ else if (hValue < hUpper - hPageSize && x2 > hValue + hPageSize - hOffset)
+ hValue = Math.min(hUpper - hPageSize, x2 + hOffset - hPageSize);
+
+ if (vValue !== vValue0) {
+ vAdjustment.ease(vValue, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: Util.SCROLL_TIME,
+ });
+ }
+
+ if (hValue !== hValue0) {
+ hAdjustment.ease(hValue, {
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ duration: Util.SCROLL_TIME,
+ });
+ }
+
+ return [hValue - hValue0, vValue - vValue0];
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js
new file mode 100644
index 0000000..d992b1a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dbusmenuUtils.js
@@ -0,0 +1,280 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Atk,
+ Clutter,
+ Gio,
+ GLib,
+ St,
+} from './dependencies/gi.js';
+
+import {PopupMenu} from './dependencies/shell/ui.js';
+
+import {Utils} from './imports.js';
+
+// Dbusmenu features not (yet) supported:
+//
+// * The CHILD_DISPLAY property
+//
+// This seems to have only one possible value in the Dbusmenu API, so
+// there's little point in depending on it--the code in libdbusmenu sets it
+// if and only if an item has children, so for our purposes it's simpler
+// and more intuitive to just check children.length. (This does ignore the
+// possibility of a program not using libdbusmenu and setting CHILD_DISPLAY
+// independently, perhaps to indicate that an childless menu item should
+// nevertheless be displayed like a submenu.)
+//
+// * Children more than two levels deep
+//
+// PopupMenu doesn't seem to support submenus in submenus.
+//
+// * Shortcut keys
+//
+// If these keys are supposed to be installed as global shortcuts, we'd
+// have to query these aggressively and not wait for the DBus menu to be
+// mapped to a popup menu. A shortcut key that only works once the popup
+// menu is open and has key focus is possibly of marginal value.
+
+/**
+ *
+ */
+export async function haveDBusMenu() {
+ try {
+ const {default: DBusMenu} = await import('gi://Dbusmenu');
+ return DBusMenu;
+ } catch (e) {
+ log(`Failed to import DBusMenu, quicklists are not available: ${e}`);
+ return null;
+ }
+}
+
+const DBusMenu = await haveDBusMenu();
+
+/**
+ * @param dbusmenuItem
+ * @param deep
+ */
+export function makePopupMenuItem(dbusmenuItem, deep) {
+ // These are the only properties guaranteed to be available when the root
+ // item is first announced. Other properties might be loaded already, but
+ // be sure to connect to Dbusmenu.MENUITEM_SIGNAL_PROPERTY_CHANGED to get
+ // the most up-to-date values in case they aren't.
+ const itemType = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_TYPE);
+ const label = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_LABEL);
+ const visible = dbusmenuItem.property_get_bool(DBusMenu.MENUITEM_PROP_VISIBLE);
+ const enabled = dbusmenuItem.property_get_bool(DBusMenu.MENUITEM_PROP_ENABLED);
+ const accessibleDesc = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_ACCESSIBLE_DESC);
+ // const childDisplay = dbusmenuItem.property_get(Dbusmenu.MENUITEM_PROP_CHILD_DISPLAY);
+
+ let item;
+ const signalsHandler = new Utils.GlobalSignalsHandler();
+ const wantIcon = itemType === DBusMenu.CLIENT_TYPES_IMAGE;
+
+ // If the basic type of the menu item needs to change, call this.
+ const recreateItem = () => {
+ const newItem = makePopupMenuItem(dbusmenuItem, deep);
+ const parentMenu = item._parent;
+ parentMenu.addMenuItem(newItem);
+ // Reminder: Clutter thinks of later entries in the child list as
+ // "above" earlier ones, so "above" here means "below" in terms of the
+ // menu's vertical order.
+ parentMenu.actor.set_child_above_sibling(newItem.actor, item.actor);
+ if (newItem.menu)
+ parentMenu.actor.set_child_above_sibling(newItem.menu.actor, newItem.actor);
+
+ parentMenu.actor.remove_child(item.actor);
+ item.destroy();
+ item = null;
+ };
+
+ const updateDisposition = () => {
+ const disposition = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_DISPOSITION);
+ let iconName = null;
+ switch (disposition) {
+ case DBusMenu.MENUITEM_DISPOSITION_ALERT:
+ case DBusMenu.MENUITEM_DISPOSITION_WARNING:
+ iconName = 'dialog-warning-symbolic';
+ break;
+ case DBusMenu.MENUITEM_DISPOSITION_INFORMATIVE:
+ iconName = 'dialog-information-symbolic';
+ break;
+ }
+ if (iconName) {
+ item._dispositionIcon = new St.Icon({
+ icon_name: iconName,
+ style_class: 'popup-menu-icon',
+ y_align: Clutter.ActorAlign.CENTER,
+ y_expand: true,
+ });
+ let expander;
+ for (let child = item.label.get_next_sibling(); ; child = child.get_next_sibling()) {
+ if (!child) {
+ expander = new St.Bin({
+ style_class: 'popup-menu-item-expander',
+ x_expand: true,
+ });
+ item.actor.add_child(expander);
+ break;
+ } else if (child instanceof St.Widget &&
+ child.has_style_class_name('popup-menu-item-expander')) {
+ expander = child;
+ break;
+ }
+ }
+ item.actor.insert_child_above(item._dispositionIcon, expander);
+ } else if (item._dispositionIcon) {
+ item.actor.remove_child(item._dispositionIcon);
+ item._dispositionIcon = null;
+ }
+ };
+
+ const updateIcon = () => {
+ if (!wantIcon)
+ return;
+
+ const iconData = dbusmenuItem.property_get_byte_array(DBusMenu.MENUITEM_PROP_ICON_DATA);
+ const iconName = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_ICON_NAME);
+ if (iconName)
+ item.icon.icon_name = iconName;
+ else if (iconData.length)
+ item.icon.gicon = Gio.BytesIcon.new(iconData);
+ };
+
+ const updateOrnament = () => {
+ const toggleType = dbusmenuItem.property_get(DBusMenu.MENUITEM_PROP_TOGGLE_TYPE);
+ switch (toggleType) {
+ case DBusMenu.MENUITEM_TOGGLE_CHECK:
+ item.actor.accessible_role = Atk.Role.CHECK_MENU_ITEM;
+ break;
+ case DBusMenu.MENUITEM_TOGGLE_RADIO:
+ item.actor.accessible_role = Atk.Role.RADIO_MENU_ITEM;
+ break;
+ default:
+ item.actor.accessible_role = Atk.Role.MENU_ITEM;
+ }
+ let ornament = PopupMenu.Ornament.NONE;
+ const state = dbusmenuItem.property_get_int(DBusMenu.MENUITEM_PROP_TOGGLE_STATE);
+ if (state === DBusMenu.MENUITEM_TOGGLE_STATE_UNKNOWN) {
+ // PopupMenu doesn't natively support an "unknown" ornament, but we
+ // can hack one in:
+ item.setOrnament(ornament);
+ item.actor.add_accessible_state(Atk.StateType.INDETERMINATE);
+ item._ornamentLabel.text = '\u2501';
+ item.actor.remove_style_pseudo_class('checked');
+ } else {
+ item.actor.remove_accessible_state(Atk.StateType.INDETERMINATE);
+ if (state === DBusMenu.MENUITEM_TOGGLE_STATE_CHECKED) {
+ if (toggleType === DBusMenu.MENUITEM_TOGGLE_CHECK)
+ ornament = PopupMenu.Ornament.CHECK;
+ else if (toggleType === DBusMenu.MENUITEM_TOGGLE_RADIO)
+ ornament = PopupMenu.Ornament.DOT;
+
+ item.actor.add_style_pseudo_class('checked');
+ } else {
+ item.actor.remove_style_pseudo_class('checked');
+ }
+ item.setOrnament(ornament);
+ }
+ };
+
+ const onPropertyChanged = (_, name, value) => {
+ // `value` is null when a property is cleared, so handle those cases
+ // with sensible defaults.
+ switch (name) {
+ case DBusMenu.MENUITEM_PROP_TYPE:
+ recreateItem();
+ break;
+ case DBusMenu.MENUITEM_PROP_ENABLED:
+ item.setSensitive(value ? value.unpack() : false);
+ break;
+ case DBusMenu.MENUITEM_PROP_LABEL:
+ item.label.text = value ? value.unpack() : '';
+ break;
+ case DBusMenu.MENUITEM_PROP_VISIBLE:
+ item.actor.visible = value ? value.unpack() : false;
+ break;
+ case DBusMenu.MENUITEM_PROP_DISPOSITION:
+ updateDisposition();
+ break;
+ case DBusMenu.MENUITEM_PROP_ACCESSIBLE_DESC:
+ item.actor.get_accessible().accessible_description = value && value.unpack() || '';
+ break;
+ case DBusMenu.MENUITEM_PROP_ICON_DATA:
+ case DBusMenu.MENUITEM_PROP_ICON_NAME:
+ updateIcon();
+ break;
+ case DBusMenu.MENUITEM_PROP_TOGGLE_TYPE:
+ case DBusMenu.MENUITEM_PROP_TOGGLE_STATE:
+ updateOrnament();
+ break;
+ }
+ };
+
+
+ // Start actually building the menu item.
+ const children = dbusmenuItem.get_children();
+ if (children.length && !deep) {
+ // Make a submenu.
+ item = new PopupMenu.PopupSubMenuMenuItem(label, wantIcon);
+ const updateChildren = () => {
+ const itemChildren = dbusmenuItem.get_children();
+ if (!itemChildren.length) {
+ recreateItem();
+ return;
+ }
+
+ item.menu.removeAll();
+ itemChildren.forEach(remoteChild =>
+ item.menu.addMenuItem(makePopupMenuItem(remoteChild, true)));
+ };
+ updateChildren();
+ signalsHandler.add(
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_ADDED, updateChildren],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_MOVED, updateChildren],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_REMOVED, updateChildren]);
+ } else {
+ // Don't make a submenu.
+ if (!deep) {
+ // We only have the potential to get a submenu if we aren't deep.
+ signalsHandler.add(
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_ADDED, recreateItem],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_MOVED, recreateItem],
+ [dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_CHILD_REMOVED, recreateItem]);
+ }
+
+ if (itemType === DBusMenu.CLIENT_TYPES_SEPARATOR) {
+ item = new PopupMenu.PopupSeparatorMenuItem();
+ } else if (wantIcon) {
+ item = new PopupMenu.PopupImageMenuItem(label, null);
+ item.icon = item._icon;
+ } else {
+ item = new PopupMenu.PopupMenuItem(label);
+ }
+ }
+
+ // Set common initial properties.
+ item.actor.visible = visible;
+ item.setSensitive(enabled);
+ if (accessibleDesc)
+ item.actor.get_accessible().accessible_description = accessibleDesc;
+
+ updateDisposition();
+ updateIcon();
+ updateOrnament();
+
+ // Prevent an initial resize flicker.
+ if (wantIcon)
+ item.icon.icon_size = 16;
+
+
+ signalsHandler.add(dbusmenuItem, DBusMenu.MENUITEM_SIGNAL_PROPERTY_CHANGED, onPropertyChanged);
+
+ // Connections on item will be lost when item is disposed; there's no need
+ // to add them to signalsHandler.
+ item.connect('activate', () =>
+ dbusmenuItem.handle_event(DBusMenu.MENUITEM_EVENT_ACTIVATED,
+ new GLib.Variant('i', 0), Math.floor(Date.now() / 1000)));
+ item.connect('destroy', () => signalsHandler.destroy());
+
+ return item;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js
new file mode 100644
index 0000000..c4b5385
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/gi.js
@@ -0,0 +1,14 @@
+export {default as Atk} from 'gi://Atk';
+export {default as Clutter} from 'gi://Clutter';
+export {default as Cogl} from 'gi://Cogl';
+export {default as GLib} from 'gi://GLib';
+export {default as GObject} from 'gi://GObject';
+export {default as GdkPixbuf} from 'gi://GdkPixbuf';
+export {default as Gio} from 'gi://Gio';
+export {default as GioUnix} from 'gi://GioUnix';
+export {default as Meta} from 'gi://Meta';
+export {default as Mtk} from 'gi://Mtk';
+export {default as Pango} from 'gi://Pango';
+export {default as Shell} from 'gi://Shell';
+export {default as St} from 'gi://St';
+
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js
new file mode 100644
index 0000000..bd9db98
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/extensions/extension.js
@@ -0,0 +1 @@
+export * as Extension from 'resource:///org/gnome/shell/extensions/extension.js';
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js
new file mode 100644
index 0000000..cfd4b95
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/misc.js
@@ -0,0 +1,5 @@
+export * as AnimationUtils from 'resource:///org/gnome/shell/misc/animationUtils.js';
+export * as Config from 'resource:///org/gnome/shell/misc/config.js';
+export * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js';
+export * as ParentalControlsManager from 'resource:///org/gnome/shell/misc/parentalControlsManager.js';
+export * as Util from 'resource:///org/gnome/shell/misc/util.js';
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js
new file mode 100644
index 0000000..33f1082
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/dependencies/shell/ui.js
@@ -0,0 +1,18 @@
+export * as AppDisplay from 'resource:///org/gnome/shell/ui/appDisplay.js';
+export * as AppMenu from 'resource:///org/gnome/shell/ui/appMenu.js';
+export * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js';
+export * as BoxPointer from 'resource:///org/gnome/shell/ui/boxpointer.js';
+export * as DND from 'resource:///org/gnome/shell/ui/dnd.js';
+export * as Dash from 'resource:///org/gnome/shell/ui/dash.js';
+export * as Layout from 'resource:///org/gnome/shell/ui/layout.js';
+export * as Main from 'resource:///org/gnome/shell/ui/main.js';
+export * as Overview from 'resource:///org/gnome/shell/ui/overview.js';
+export * as OverviewControls from 'resource:///org/gnome/shell/ui/overviewControls.js';
+export * as PointerWatcher from 'resource:///org/gnome/shell/ui/pointerWatcher.js';
+export * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+export * as SearchController from 'resource:///org/gnome/shell/ui/searchController.js';
+export * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
+export * as Workspace from 'resource:///org/gnome/shell/ui/workspace.js';
+export * as WorkspaceSwitcherPopup from 'resource:///org/gnome/shell/ui/workspaceSwitcherPopup.js';
+export * as WorkspaceThumbnail from 'resource:///org/gnome/shell/ui/workspaceThumbnail.js';
+export * as WorkspacesView from 'resource:///org/gnome/shell/ui/workspacesView.js';
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js
new file mode 100644
index 0000000..ca07747
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/desktopIconsIntegration.js
@@ -0,0 +1,170 @@
+/*
+ * The code in this file is distributed under a "1-clause BSD license",
+ * which makes it compatible with GPLv2 and GPLv3 too, and others.
+ *
+ * License text:
+ *
+ * Copyright (C) 2021 Sergio Costas (rastersoft@gmail.com)
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 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 OR CONTRIBUTORS 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.
+ */
+
+/**
+ * Integration class
+ *
+ * This class must be added to other extensions in order to integrate
+ * them with Desktop Icons NG. It allows an extension to notify how much margin
+ * it uses in each side of each monitor.
+ *
+ * DON'T SEND PATCHES TO THIS FILE TO THE EXTENSION MAINTAINER. SEND THEM TO
+ * DESKTOP ICONS NG MAINTAINER: https://gitlab.com/rastersoft/desktop-icons-ng
+ *
+ * In the *enable()* function, create a *DesktopIconsUsableAreaClass()*
+ * object with
+ *
+ * new DesktopIconsIntegration.DesktopIconsUsableAreaClass(object);
+ *
+ * Now, in the *disable()* function just call to the *destroy()* method before
+ * nullifying the pointer. You must create a new object in enable() the next
+ * time the extension is enabled.
+ *
+ * In your code, every time you change the margins, you should call first to
+ * *resetMargins()* method to clear the current margins, and then call to
+ * *setMargins(...)* method as many times as you need to set the margins in each
+ * monitor. You don't need to call it for all the monitors, only for those where
+ * you are painting something. If you don't set values for a monitor, they will
+ * be considered zero.
+ *
+ * The margins values are relative to the monitor border.
+ *
+ *******************************************************************************/
+
+import GLib from 'gi://GLib';
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js';
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+const IDENTIFIER_UUID = '130cbc66-235c-4bd6-8571-98d2d8bba5e2';
+
+export class DesktopIconsUsableAreaClass {
+ _checkIfExtensionIsEnabled(extension) {
+ return (extension?.state === ExtensionUtils.ExtensionState.ENABLED) ||
+ (extension?.state === ExtensionUtils.ExtensionState.ACTIVE);
+ }
+
+ constructor() {
+ const Me = Extension.lookupByURL(import.meta.url);
+ this._UUID = Me.uuid;
+ this._extensionManager = Main.extensionManager;
+ this._timedMarginsID = 0;
+ this._margins = {};
+ this._emID = this._extensionManager.connect('extension-state-changed', (_obj, extension) => {
+ if (!extension)
+ return;
+
+ // If an extension is being enabled and lacks the
+ // DesktopIconsUsableArea object, we can avoid launching a refresh
+ if (this._checkIfExtensionIsEnabled(extension)) {
+ this._sendMarginsToExtension(extension);
+ return;
+ }
+ // if the extension is being disabled, we must do a full refresh,
+ // because if there were other extensions originally
+ // loaded after that extension, those extensions will be disabled
+ // and enabled again without notification
+ this._changedMargins();
+ });
+ }
+
+ /**
+ * Sets or updates the top, bottom, left and right margins for a
+ * monitor. Values are measured from the monitor border (and NOT from
+ * the workspace border).
+ *
+ * @param {int} monitor Monitor number to which set the margins.
+ * A negative value means "the primary monitor".
+ * @param {int} top Top margin in pixels
+ * @param {int} bottom Bottom margin in pixels
+ * @param {int} left Left margin in pixels
+ * @param {int} right Right margin in pixels
+ */
+ setMargins(monitor, top, bottom, left, right) {
+ this._margins[monitor] = {
+ top,
+ bottom,
+ left,
+ right,
+ };
+ this._changedMargins();
+ }
+
+ /**
+ * Clears the current margins. Must be called before configuring the monitors
+ * margins with setMargins().
+ */
+ resetMargins() {
+ this._margins = {};
+ this._changedMargins();
+ }
+
+ /**
+ * Disconnects all the signals and removes the margins.
+ */
+ destroy() {
+ if (this._emID) {
+ this._extensionManager.disconnect(this._emID);
+ this._emID = 0;
+ }
+ if (this._timedMarginsID) {
+ GLib.source_remove(this._timedMarginsID);
+ this._timedMarginsID = 0;
+ }
+ this._margins = null;
+ this._changedMargins();
+ }
+
+ _changedMargins() {
+ if (this._timedMarginsID)
+ GLib.source_remove(this._timedMarginsID);
+
+ this._timedMarginsID = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 100, () => {
+ this._sendMarginsToAll();
+ this._timedMarginsID = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _sendMarginsToAll() {
+ this._extensionManager.getUuids().forEach(uuid =>
+ this._sendMarginsToExtension(this._extensionManager.lookup(uuid)));
+ }
+
+ _sendMarginsToExtension(extension) {
+ // check that the extension is an extension that has the logic to accept
+ // working margins
+ if (!this._checkIfExtensionIsEnabled(extension))
+ return;
+
+ const usableArea = extension?.stateObj?.DesktopIconsUsableArea;
+ if (usableArea?.uuid === IDENTIFIER_UUID) {
+ usableArea.setMarginsForExtension(
+ this._UUID, this._margins);
+ }
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js
new file mode 100644
index 0000000..393b1de
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/docking.js
@@ -0,0 +1,2698 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ GLib,
+ Gio,
+ GObject,
+ Meta,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ AppMenu,
+ AppDisplay,
+ Layout,
+ Main,
+ OverviewControls,
+ PointerWatcher,
+ Workspace,
+ WorkspacesView,
+ WorkspaceSwitcherPopup,
+} from './dependencies/shell/ui.js';
+
+import {
+ AnimationUtils,
+} from './dependencies/shell/misc.js';
+
+import {
+ AppIconsDecorator,
+ AppSpread,
+ DockDash,
+ DesktopIconsIntegration,
+ FileManager1API,
+ Intellihide,
+ LauncherAPI,
+ Locations,
+ NotificationsMonitor,
+ Theming,
+ Utils,
+} from './imports.js';
+
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// Use __ () and N__() for the extension gettext domain, and reuse
+// the shell domain with the default _() and N_()
+const {gettext: __} = Extension;
+
+const {signals: Signals} = imports;
+
+const DOCK_DWELL_CHECK_INTERVAL = 100;
+const ICON_ANIMATOR_DURATION = 3000;
+const STARTUP_ANIMATION_TIME = 500;
+
+export const State = Object.freeze({
+ HIDDEN: 0,
+ SHOWING: 1,
+ SHOWN: 2,
+ HIDING: 3,
+});
+
+const scrollAction = Object.freeze({
+ DO_NOTHING: 0,
+ CYCLE_WINDOWS: 1,
+ SWITCH_WORKSPACE: 2,
+});
+
+const Labels = Object.freeze({
+ INITIALIZE: Symbol('initialize'),
+ ISOLATION: Symbol('isolation'),
+ LOCATIONS: Symbol('locations'),
+ MAIN_DASH: Symbol('main-dash'),
+ OLD_DASH_CHANGES: Symbol('old-dash-changes'),
+ SETTINGS: Symbol('settings'),
+ STARTUP_ANIMATION: Symbol('startup-animation'),
+ WORKSPACE_SWITCH_SCROLL: Symbol('workspace-switch-scroll'),
+});
+
+/**
+ * A simple St.Widget with one child whose allocation takes into account the
+ * slide out of its child via the slide-x property ([0:1]).
+ *
+ * Required since I want to track the input region of this container which is
+ * based on its allocation even if the child overflows the parent actor. By doing
+ * this the region of the dash that is slide-out is not stealing anymore the input
+ * regions making the extension usable when the primary monitor is the right one.
+ *
+ * The slide-x parameter can be used to directly animate the sliding. The parent
+ * must have a WEST (SOUTH) anchor_point to achieve the sliding to the RIGHT (BOTTOM)
+ * side.
+ */
+const DashSlideContainer = GObject.registerClass({
+ Properties: {
+ 'monitor-index': GObject.ParamSpec.uint(
+ 'monitor-index', 'monitor-index', 'monitor-index',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ 0, GLib.MAXUINT32, 0),
+ 'side': GObject.ParamSpec.enum(
+ 'side', 'side', 'side',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ St.Side, St.Side.LEFT),
+ 'slide-x': GObject.ParamSpec.double(
+ 'slide-x', 'slide-x', 'slide-x',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
+ 0, 1, 1),
+ },
+}, class DashSlideContainer extends St.Bin {
+ _init(params = {}) {
+ super._init(params);
+
+ this._slideoutSize = 0; // minimum size when slided out
+ this.connect('notify::slide-x', () => this.queue_relayout());
+
+ if (this.side === St.Side.TOP && DockManager.settings.dockFixed) {
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._signalsHandler.add(Main.panel, 'notify::height',
+ () => this.queue_relayout());
+ }
+ }
+
+ vfunc_allocate(box) {
+ const contentBox = this.get_theme_node().get_content_box(box);
+
+ this.set_allocation(box);
+
+ if (!this.child)
+ return;
+
+ const availWidth = contentBox.x2 - contentBox.x1;
+ let availHeight = contentBox.y2 - contentBox.y1;
+ const [, , natChildWidth, natChildHeight] =
+ this.child.get_preferred_size();
+
+ const childWidth = natChildWidth;
+ const childHeight = natChildHeight;
+
+ const childBox = new Clutter.ActorBox();
+
+ const slideoutSize = this._slideoutSize;
+
+ if (this.side === St.Side.LEFT) {
+ childBox.x1 = (this.slideX - 1) * (childWidth - slideoutSize);
+ childBox.x2 = slideoutSize + this.slideX * (childWidth - slideoutSize);
+ childBox.y1 = 0;
+ childBox.y2 = childBox.y1 + childHeight;
+ } else if ((this.side === St.Side.RIGHT) || (this.side === St.Side.BOTTOM)) {
+ childBox.x1 = 0;
+ childBox.x2 = childWidth;
+ childBox.y1 = 0;
+ childBox.y2 = childBox.y1 + childHeight;
+ } else if (this.side === St.Side.TOP) {
+ const monitor = Main.layoutManager.monitors[this.monitorIndex];
+ let yOffset = 0;
+ if (Main.panel.x === monitor.x && Main.panel.y === monitor.y &&
+ DockManager.settings.dockFixed)
+ yOffset = Main.panel.height;
+ childBox.x1 = 0;
+ childBox.x2 = childWidth;
+ childBox.y1 = (this.slideX - 1) * (childHeight - slideoutSize) + yOffset;
+ childBox.y2 = slideoutSize + this.slideX * (childHeight - slideoutSize) + yOffset;
+ availHeight += yOffset;
+ }
+
+ this.child.allocate(childBox);
+
+ this.child.set_clip(-childBox.x1, -childBox.y1,
+ -childBox.x1 + availWidth, -childBox.y1 + availHeight);
+ }
+
+ /**
+ * Just the child width but taking into account the slided out part
+ *
+ * @param forHeight
+ */
+ vfunc_get_preferred_width(forHeight) {
+ let [minWidth, natWidth] = super.vfunc_get_preferred_width(forHeight || 0);
+ if ((this.side === St.Side.LEFT) || (this.side === St.Side.RIGHT)) {
+ minWidth = (minWidth - this._slideoutSize) * this.slideX + this._slideoutSize;
+ natWidth = (natWidth - this._slideoutSize) * this.slideX + this._slideoutSize;
+ }
+ return [minWidth, natWidth];
+ }
+
+ /**
+ * Just the child height but taking into account the slided out part
+ *
+ * @param forWidth
+ */
+ vfunc_get_preferred_height(forWidth) {
+ let [minHeight, natHeight] = super.vfunc_get_preferred_height(forWidth || 0);
+ if ((this.side === St.Side.TOP) || (this.side === St.Side.BOTTOM)) {
+ minHeight = (minHeight - this._slideoutSize) * this.slideX + this._slideoutSize;
+ natHeight = (natHeight - this._slideoutSize) * this.slideX + this._slideoutSize;
+
+ if (this.side === St.Side.TOP && DockManager.settings.dockFixed) {
+ const monitor = Main.layoutManager.monitors[this.monitorIndex];
+ if (Main.panel.x === monitor.x && Main.panel.y === monitor.y) {
+ minHeight += Main.panel.height;
+ natHeight += Main.panel.height;
+ }
+ }
+ }
+ return [minHeight, natHeight];
+ }
+});
+
+const DockedDash = GObject.registerClass({
+ Properties: {
+ 'is-main': GObject.ParamSpec.boolean(
+ 'is-main', 'is-main', 'is-main',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ false),
+ 'monitor-index': GObject.ParamSpec.uint(
+ 'monitor-index', 'monitor-index', 'monitor-index',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ 0, GLib.MAXUINT32, 0),
+ },
+ Signals: {
+ 'showing': {},
+ 'hiding': {},
+ },
+}, class DashToDock extends St.Bin {
+ _init(params) {
+ this._position = Utils.getPosition();
+
+ // This is the centering actor
+ super._init({
+ ...params,
+ name: 'dashtodockContainer',
+ reactive: false,
+ style_class: Theming.PositionStyleClass[this._position],
+ });
+
+ if (this.monitorIndex === undefined) {
+ // Hello turkish locale, gjs has instead defined this.monitorIndex
+ // See: https://gitlab.gnome.org/GNOME/gjs/-/merge_requests/742
+ this.monitorIndex = this.monitor_index;
+ }
+
+ this._rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
+
+ // Load settings
+ const {settings} = DockManager;
+ this._isHorizontal = (this._position === St.Side.TOP) || (this._position === St.Side.BOTTOM);
+
+ // Temporary ignore hover events linked to autohide for whatever reason
+ this._ignoreHover = false;
+ this._oldIgnoreHover = null;
+ // This variables are linked to the settings regardles of autohide or intellihide
+ // being temporary disable. Get set by _updateVisibilityMode;
+ this._autohideIsEnabled = null;
+ this._intellihideIsEnabled = null;
+
+ // This variable marks if _disableUnredirect() is called
+ // to help restore the original state when intelihide is disabled.
+ this._unredirectDisabled = false;
+
+ // Create intellihide object to monitor windows overlapping
+ this._intellihide = new Intellihide.Intellihide(this.monitorIndex);
+
+ // initialize dock state
+ this._dockState = State.HIDDEN;
+
+ // Put dock on the required monitor
+ this._monitor = Main.layoutManager.monitors[this.monitorIndex];
+
+ // this store size and the position where the dash is shown;
+ // used by intellihide module to check window overlap.
+ this.staticBox = new Clutter.ActorBox();
+
+ // Initialize pressure barrier variables
+ this._canUsePressure = false;
+ this._pressureBarrier = null;
+ this._barrier = null;
+ this._removeBarrierTimeoutId = 0;
+
+ // Initialize dwelling system variables
+ this._dockDwelling = false;
+ this._dockWatch = null;
+ this._dockDwellUserTime = 0;
+ this._dockDwellTimeoutId = 0;
+
+ // Create a new dash object
+ this.dash = new DockDash.DockDash(this.monitorIndex);
+
+ if (Main.overview.isDummy || !settings.showShowAppsButton)
+ this.dash.hideShowAppsButton();
+
+ // Create the containers for sliding in and out and
+ // centering, turn on track hover
+ // This is the sliding actor whose allocation is to be tracked for input regions
+ this._slider = new DashSlideContainer({
+ monitor_index: this._monitor.index,
+ side: this._position,
+ slide_x: Main.layoutManager._startingUp ? 0 : 1,
+ ...this._isHorizontal ? {
+ x_align: Clutter.ActorAlign.CENTER,
+ } : {
+ y_align: Clutter.ActorAlign.CENTER,
+ },
+ });
+
+ // This is the actor whose hover status us tracked for autohide
+ this._box = new St.BoxLayout({
+ name: 'dashtodockBox',
+ reactive: true,
+ track_hover: true,
+ });
+ this._box.connect('notify::hover', this._hoverChanged.bind(this));
+
+ // Connect global signals
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._bindSettingsChanges();
+ this._signalsHandler.add([
+ // update when workarea changes, for instance if other extensions modify the struts
+ // (like moving th panel at the bottom)
+ global.display,
+ 'workareas-changed',
+ this._resetPosition.bind(this),
+ ], [
+ global.display,
+ 'in-fullscreen-changed',
+ this._updateBarrier.bind(this),
+ ], [
+ // Monitor windows overlapping
+ this._intellihide,
+ 'status-changed',
+ this._updateDashVisibility.bind(this),
+ ], [
+ this.dash,
+ 'menu-opened',
+ () => {
+ this._onMenuOpened();
+ },
+ ], [
+ // sync hover after a popupmenu is closed
+ this.dash,
+ 'menu-closed',
+ () => {
+ this._onMenuClosed();
+ },
+ ], [
+ this.dash,
+ 'notify::requires-visibility',
+ () => this._updateDashVisibility(),
+ ]);
+
+ if (!Main.overview.isDummy) {
+ this._signalsHandler.add([
+ Main.overview,
+ 'item-drag-begin',
+ this._onDragStart.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-end',
+ this._onDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'item-drag-cancelled',
+ this._onDragEnd.bind(this),
+ ], [
+ Main.overview,
+ 'showing',
+ this._onOverviewShowing.bind(this),
+ ], [
+ Main.overview,
+ 'hiding',
+ this._onOverviewHiding.bind(this),
+ ],
+ [
+ Main.overview,
+ 'hidden',
+ this._onOverviewHidden.bind(this),
+ ]);
+ }
+
+ this._themeManager = new Theming.ThemeManager(this);
+ this._signalsHandler.add(this._themeManager, 'updated',
+ () => this.dash.resetAppIcons());
+
+ this._signalsHandler.add(DockManager.iconTheme, 'changed',
+ () => this.dash.resetAppIcons());
+
+ // Since the actor is not a topLevel child and its parent is now not added to the Chrome,
+ // the allocation change of the parent container (slide in and slideout) doesn't trigger
+ // anymore an update of the input regions. Force the update manually.
+ this.connect('notify::allocation',
+ Main.layoutManager._queueUpdateRegions.bind(Main.layoutManager));
+
+
+ // Since Clutter has no longer ClutterAllocationFlags,
+ // "allocation-changed" signal has been removed. MR !1245
+ this.dash._container.connect('notify::allocation', this._updateStaticBox.bind(this));
+ this._slider.connect(this._isHorizontal ? 'notify::x' : 'notify::y',
+ this._updateStaticBox.bind(this));
+
+ // Load optional features that need to be activated for one dock only
+ if (this.isMain)
+ this._enableExtraFeatures();
+ // Load optional features that need to be activated once per dock
+ this._optionalScrollWorkspaceSwitch();
+
+ // Delay operations that require the shell to be fully loaded and with
+ // user theme applied.
+
+ this._signalsHandler.addWithLabel(Labels.INITIALIZE, global.stage,
+ 'after-paint', () => this._initialize());
+
+ // Add dash container actor and the container to the Chrome.
+ this.set_child(this._slider);
+ this._slider.set_child(this._box);
+ this._box.add_child(this.dash);
+
+ // Add aligning container without tracking it for input region
+ this._trackDock();
+
+ // Create and apply height/width constraint to the dash.
+ if (this._isHorizontal) {
+ this.connect('notify::width', () => {
+ this.dash.setMaxSize(this.width, this.height);
+ });
+ } else {
+ this.connect('notify::height', () => {
+ this.dash.setMaxSize(this.width, this.height);
+ });
+ }
+
+ if (this._position === St.Side.RIGHT) {
+ this.connect('notify::width', () =>
+ (this.translation_x = -this.width));
+ } else if (this._position === St.Side.BOTTOM) {
+ this.connect('notify::height', () =>
+ (this.translation_y = -this.height));
+ }
+
+ // Set initial position
+ this._resetPosition();
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ get position() {
+ return this._position;
+ }
+
+ get isHorizontal() {
+ return this._isHorizontal;
+ }
+
+ _untrackDock() {
+ Main.layoutManager.untrackChrome(this);
+ }
+
+ _trackDock() {
+ if (DockManager.settings.dockFixed) {
+ if (this.get_parent())
+ Main.layoutManager.removeChrome(this);
+ Main.layoutManager.addChrome(this, {
+ trackFullscreen: true,
+ affectsStruts: true,
+ });
+ } else {
+ if (this.get_parent())
+ Main.layoutManager.removeChrome(this);
+ Main.layoutManager.addChrome(this);
+ }
+ }
+
+ _initialize() {
+ this._signalsHandler.removeWithLabel(Labels.INITIALIZE);
+
+ // Apply custom css class according to the settings
+ this._themeManager.updateCustomTheme();
+
+ this._updateVisibilityMode();
+
+ // In case we are already inside the overview when the extension is loaded,
+ // for instance on unlocking the screen if it was locked with the overview open.
+ if (Main.overview.visibleTarget)
+ this._onOverviewShowing();
+
+ this._updateAutoHideBarriers();
+ }
+
+ _onDestroy() {
+ // The dash, intellihide and themeManager have global signals as well internally
+ this.dash.destroy();
+ this._intellihide.destroy();
+ this._themeManager.destroy();
+
+ if (this._marginLater) {
+ Utils.laterRemove(this._marginLater);
+ delete this._marginLater;
+ }
+
+ if (this._triggerTimeoutId)
+ GLib.source_remove(this._triggerTimeoutId);
+
+ this._restoreUnredirect();
+
+ // Remove barrier timeout
+ if (this._removeBarrierTimeoutId > 0)
+ GLib.source_remove(this._removeBarrierTimeoutId);
+
+ // Remove existing barrier
+ this._removeBarrier();
+
+ // Remove pointer watcher
+ if (this._dockWatch) {
+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
+ this._dockWatch = null;
+ }
+ }
+
+ _updateAutoHideBarriers() {
+ // Remove pointer watcher
+ if (this._dockWatch) {
+ PointerWatcher.getPointerWatcher()._removeWatch(this._dockWatch);
+ this._dockWatch = null;
+ }
+
+ // Setup pressure barrier (GS38+ only)
+ this._updatePressureBarrier();
+ this._updateBarrier();
+
+ // setup dwelling system if pressure barriers are not available
+ this._setupDockDwellIfNeeded();
+ }
+
+ _bindSettingsChanges() {
+ const {settings} = DockManager;
+ this._signalsHandler.add([
+ settings,
+ 'changed::scroll-action',
+ () => {
+ this._optionalScrollWorkspaceSwitch();
+ },
+ ], [
+ settings,
+ 'changed::dash-max-icon-size',
+ () => {
+ this.dash.setIconSize(settings.dashMaxIconSize);
+ },
+ ], [
+ settings,
+ 'changed::icon-size-fixed',
+ () => {
+ this.dash.setIconSize(settings.dashMaxIconSize);
+ },
+ ], [
+ settings,
+ 'changed::show-favorites',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ ], [
+ settings,
+ 'changed::show-trash',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::show-mounts',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::isolate-locations',
+ () => this.dash.resetAppIcons(),
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::dance-urgent-applications',
+ () => this.dash.resetAppIcons(),
+ Utils.SignalsHandlerFlags.CONNECT_AFTER,
+ ], [
+ settings,
+ 'changed::show-running',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ ], [
+ settings,
+ 'changed::show-apps-always-in-the-edge',
+ () => {
+ this.dash.updateShowAppsButton();
+ },
+ ], [
+ settings,
+ 'changed::show-apps-at-top',
+ () => {
+ this.dash.updateShowAppsButton();
+ },
+ ], [
+ settings,
+ 'changed::show-show-apps-button',
+ () => {
+ if (!Main.overview.isDummy &&
+ settings.showShowAppsButton)
+ this.dash.showShowAppsButton();
+ else
+ this.dash.hideShowAppsButton();
+ },
+ ], [
+ settings,
+ 'changed::dock-fixed',
+ () => {
+ this._untrackDock();
+ this._trackDock();
+
+ this._resetPosition();
+ this._updateAutoHideBarriers();
+ this._updateVisibilityMode();
+ },
+ ], [
+ settings,
+ 'changed::manualhide',
+ () => {
+ this._updateVisibilityMode();
+ },
+ ], [
+ settings,
+ 'changed::intellihide',
+ () => {
+ this._updateVisibilityMode();
+ this._updateVisibleDesktop();
+ },
+ ], [
+ settings,
+ 'changed::intellihide-mode',
+ () => {
+ this._intellihide.forceUpdate();
+ },
+ ], [
+ settings,
+ 'changed::autohide',
+ () => {
+ this._updateVisibilityMode();
+ this._updateAutoHideBarriers();
+ },
+ ], [
+ settings,
+ 'changed::autohide-in-fullscreen',
+ this._updateBarrier.bind(this),
+ ], [
+ settings,
+ 'changed::show-dock-urgent-notify',
+ () => {
+ this.dash.resetAppIcons();
+ },
+ ],
+ [
+ settings,
+ 'changed::extend-height',
+ this._resetPosition.bind(this),
+ ], [
+ settings,
+ 'changed::height-fraction',
+ this._resetPosition.bind(this),
+ ], [
+ settings,
+ 'changed::always-center-icons',
+ () => this.dash.resetAppIcons(),
+ ], [
+ settings,
+ 'changed::require-pressure-to-show',
+ () => this._updateAutoHideBarriers(),
+ ], [
+ settings,
+ 'changed::pressure-threshold',
+ () => {
+ this._updatePressureBarrier();
+ this._updateBarrier();
+ },
+ ]);
+ }
+
+ _disableUnredirect() {
+ if (!this._unredirectDisabled) {
+ if (Meta.disable_unredirect_for_display !== undefined)
+ Meta.disable_unredirect_for_display(global.display);
+ else if (global.compositor.disable_unredirect !== undefined)
+ global.compositor.disable_unredirect();
+ this._unredirectDisabled = true;
+ }
+ }
+
+ _restoreUnredirect() {
+ if (this._unredirectDisabled) {
+ if (Meta.enable_unredirect_for_display !== undefined)
+ Meta.enable_unredirect_for_display(global.display);
+ else if (global.compositor.enable_unredirect !== undefined)
+ global.compositor.enable_unredirect();
+ this._unredirectDisabled = false;
+ }
+ }
+
+ /**
+ * This is call when visibility settings change
+ */
+ _updateVisibilityMode() {
+ const {settings} = DockManager;
+ if (DockManager.settings.dockFixed || DockManager.settings.manualhide) {
+ this._autohideIsEnabled = false;
+ this._intellihideIsEnabled = false;
+ } else {
+ this._autohideIsEnabled = settings.autohide;
+ this._intellihideIsEnabled = settings.intellihide;
+ }
+
+ if (this._autohideIsEnabled)
+ this.add_style_class_name('autohide');
+ else
+ this.remove_style_class_name('autohide');
+
+ if (this._intellihideIsEnabled) {
+ this._intellihide.enable();
+ } else {
+ this._intellihide.disable();
+ this._restoreUnredirect();
+ }
+
+ this._updateDashVisibility();
+ }
+
+ /**
+ * Show/hide dash based on, in order of priority:
+ * overview visibility
+ * fixed mode
+ * intellihide
+ * autohide
+ * overview visibility
+ */
+ _updateDashVisibility() {
+ if (DockManager.settings.manualhide) {
+ this._ignoreHover = true;
+ this._removeAnimations();
+ this._animateOut(0, 0);
+ return;
+ }
+
+ if (Main.overview.visibleTarget)
+ return;
+
+ const {settings} = DockManager;
+
+ if (DockManager.settings.dockFixed) {
+ this._removeAnimations();
+ this._animateIn(settings.animationTime, 0);
+ } else if (this._intellihideIsEnabled) {
+ if (!this.dash.requiresVisibility && this._intellihide.getOverlapStatus()) {
+ this._ignoreHover = false;
+ // Do not hide if autohide is enabled and mouse is hover
+ if (!this._box.hover || !this._autohideIsEnabled)
+ this._animateOut(settings.animationTime, 0);
+ } else {
+ this._ignoreHover = true;
+ this._removeAnimations();
+ this._animateIn(settings.animationTime, 0);
+ }
+ } else if (this._autohideIsEnabled) {
+ this._ignoreHover = false;
+
+ if (this._box.hover || this.dash.requiresVisibility)
+ this._animateIn(settings.animationTime, 0);
+ else
+ this._animateOut(settings.animationTime, 0);
+ } else {
+ this._animateOut(settings.animationTime, 0);
+ }
+ }
+
+ _onOverviewShowing() {
+ this.add_style_class_name('overview');
+
+ this._ignoreHover = true;
+ this._intellihide.disable();
+ this._removeAnimations();
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+
+ _onOverviewHiding() {
+ this._intellihide.enable();
+ this._updateDashVisibility();
+ }
+
+ _onOverviewHidden() {
+ this.remove_style_class_name('overview');
+ this._updateDashVisibility();
+ }
+
+ _onMenuOpened() {
+ this._ignoreHover = true;
+ }
+
+ _onMenuClosed() {
+ this._ignoreHover = false;
+ this._box.sync_hover();
+ this._updateDashVisibility();
+ }
+
+ _hoverChanged() {
+ if (!this._ignoreHover) {
+ // Skip if dock is not in autohide mode for instance because it is shown
+ // by intellihide.
+ if (this._autohideIsEnabled) {
+ if (this._box.hover || Main.overview.visible)
+ this._show();
+ else
+ this._hide();
+ }
+ }
+ }
+
+ getDockState() {
+ return this._dockState;
+ }
+
+ _show() {
+ this._delayedHide = false;
+ if ((this._dockState === State.HIDDEN) || (this._dockState === State.HIDING)) {
+ if (this._dockState === State.HIDING)
+ // suppress all potential queued transitions - i.e. added but not started,
+ // always give priority to show
+ this._removeAnimations();
+
+ this.emit('showing');
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+ }
+
+ _hide() {
+ // If no hiding animation is running or queued
+ if ((this._dockState === State.SHOWN) || (this._dockState === State.SHOWING)) {
+ const {settings} = DockManager;
+ const delay = settings.hideDelay;
+
+ if (this._dockState === State.SHOWING) {
+ // if a show already started, let it finish; queue hide without removing the show.
+ // to obtain this, we wait for the animateIn animation to be completed
+ this._delayedHide = true;
+ return;
+ }
+
+ this.emit('hiding');
+ this._animateOut(settings.animationTime, delay);
+ }
+ }
+
+ _animateIn(time, delay) {
+ if (this._intellihideIsEnabled)
+ this._disableUnredirect();
+ this._dockState = State.SHOWING;
+ this.dash.iconAnimator.start();
+ this._delayedHide = false;
+
+ this._slider.ease_property('slide-x', 1, {
+ duration: time * 1000,
+ delay: delay * 1000,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._dockState = State.SHOWN;
+ // Remove barrier so that mouse pointer is released and can
+ // monitors on other side of dock.
+ // NOTE: Delay needed to keep mouse from moving past dock and
+ // re-hiding dock immediately. This gives users an opportunity
+ // to hover over the dock
+ if (this._removeBarrierTimeoutId > 0)
+ GLib.source_remove(this._removeBarrierTimeoutId);
+
+ if (!this._delayedHide) {
+ this._removeBarrierTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 100, this._removeBarrier.bind(this));
+ } else {
+ this._hide();
+ }
+ },
+ });
+ }
+
+ _animateOut(time, delay) {
+ this._dockState = State.HIDING;
+
+ this._slider.ease_property('slide-x', 0, {
+ duration: time * 1000,
+ delay: delay * 1000,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ onComplete: () => {
+ this._dockState = State.HIDDEN;
+ if (this._intellihideIsEnabled)
+ this._restoreUnredirect();
+ // Remove queued barrier removal timeout if any
+ if (this._removeBarrierTimeoutId > 0)
+ GLib.source_remove(this._removeBarrierTimeoutId);
+ this._updateBarrier();
+ this.dash.iconAnimator.pause();
+ },
+ });
+ }
+
+ /**
+ * Dwelling system based on the GNOME Shell 3.14 messageTray code.
+ */
+ _setupDockDwellIfNeeded() {
+ // If we don't have extended barrier features, then we need
+ // to support the old tray dwelling mechanism.
+ if (this._autohideIsEnabled &&
+ (!Utils.supportsExtendedBarriers() ||
+ !DockManager.settings.requirePressureToShow)) {
+ const pointerWatcher = PointerWatcher.getPointerWatcher();
+ this._dockWatch = pointerWatcher.addWatch(
+ DOCK_DWELL_CHECK_INTERVAL, this._checkDockDwell.bind(this));
+ this._dockDwelling = false;
+ this._dockDwellUserTime = 0;
+ }
+ }
+
+ _checkDockDwell(x, y) {
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(this._monitor.index);
+ let shouldDwell;
+ // Check for the correct screen edge, extending the sensitive area to the whole workarea,
+ // minus 1 px to avoid conflicting with other active corners.
+ if (this._position === St.Side.LEFT) {
+ shouldDwell = (x === this._monitor.x) && (y > workArea.y) &&
+ (y < workArea.y + workArea.height);
+ } else if (this._position === St.Side.RIGHT) {
+ shouldDwell = (x === this._monitor.x + this._monitor.width - 1) &&
+ (y > workArea.y) && (y < workArea.y + workArea.height);
+ } else if (this._position === St.Side.TOP) {
+ shouldDwell = (y === this._monitor.y) && (x > workArea.x) &&
+ (x < workArea.x + workArea.width);
+ } else if (this._position === St.Side.BOTTOM) {
+ shouldDwell = (y === this._monitor.y + this._monitor.height - 1) &&
+ (x > workArea.x) && (x < workArea.x + workArea.width);
+ }
+
+ if (shouldDwell) {
+ // We only set up dwell timeout when the user is not hovering over the dock
+ // already (!this._box.hover).
+ // The _dockDwelling variable is used so that we only try to
+ // fire off one dock dwell - if it fails (because, say, the user has the mouse down),
+ // we don't try again until the user moves the mouse up and down again.
+ if (!this._dockDwelling && !this._box.hover && (this._dockDwellTimeoutId === 0)) {
+ // Save the interaction timestamp so we can detect user input
+ const focusWindow = global.display.focus_window;
+ this._dockDwellUserTime = focusWindow ? focusWindow.user_time : 0;
+
+ this._dockDwellTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ DockManager.settings.showDelay * 1000,
+ this._dockDwellTimeout.bind(this));
+ GLib.Source.set_name_by_id(this._dockDwellTimeoutId,
+ '[dash-to-dock] this._dockDwellTimeout');
+ }
+ this._dockDwelling = true;
+ } else {
+ this._cancelDockDwell();
+ this._dockDwelling = false;
+ }
+ }
+
+ _cancelDockDwell() {
+ if (this._dockDwellTimeoutId !== 0) {
+ GLib.source_remove(this._dockDwellTimeoutId);
+ this._dockDwellTimeoutId = 0;
+ }
+ }
+
+ _dockDwellTimeout() {
+ this._dockDwellTimeoutId = 0;
+
+ if (!DockManager.settings.autohideInFullscreen &&
+ this._monitor.inFullscreen)
+ return GLib.SOURCE_REMOVE;
+
+ // We don't want to open the tray when a modal dialog
+ // is up, so we check the modal count for that. When we are in the
+ // overview we have to take the overview's modal push into account
+ if (Main.modalCount > (Main.overview.visible ? 1 : 0))
+ return GLib.SOURCE_REMOVE;
+
+ // If the user interacted with the focus window since we started the tray
+ // dwell (by clicking or typing), don't activate the message tray
+ const focusWindow = global.display.focus_window;
+ const currentUserTime = focusWindow ? focusWindow.user_time : 0;
+ if (currentUserTime !== this._dockDwellUserTime)
+ return GLib.SOURCE_REMOVE;
+
+ // Reuse the pressure version function, the logic is the same
+ this._onPressureSensed();
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _updatePressureBarrier() {
+ const {settings} = DockManager;
+ this._canUsePressure = Utils.supportsExtendedBarriers();
+ const {pressureThreshold} = settings;
+
+ // Remove existing pressure barrier
+ if (this._pressureBarrier) {
+ this._pressureBarrier.destroy();
+ this._pressureBarrier = null;
+ }
+
+ if (this._barrier) {
+ this._barrier.destroy();
+ this._barrier = null;
+ }
+
+ // Create new pressure barrier based on pressure threshold setting
+ if (this._canUsePressure && this._autohideIsEnabled &&
+ DockManager.settings.requirePressureToShow) {
+ this._pressureBarrier = new Layout.PressureBarrier(
+ pressureThreshold, settings.showDelay * 1000,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW);
+ this._pressureBarrier.connect('trigger', _barrier => {
+ if (!settings.autohideInFullscreen && this._monitor.inFullscreen)
+ return;
+ this._onPressureSensed();
+ });
+ }
+ }
+
+ /**
+ * handler for mouse pressure sensed
+ */
+ _onPressureSensed() {
+ if (Main.overview.visibleTarget)
+ return;
+
+ if (this._triggerTimeoutId)
+ GLib.source_remove(this._triggerTimeoutId);
+
+ // In case the mouse move away from the dock area before hovering it,
+ // in such case the leave event would never be triggered and the dock
+ // would stay visible forever.
+ this._triggerTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 250, () => {
+ const [x, y, mods_] = global.get_pointer();
+ let shouldHide = true;
+ switch (this._position) {
+ case St.Side.LEFT:
+ if (x <= this.staticBox.x2 &&
+ x >= this._monitor.x &&
+ y >= this._monitor.y &&
+ y <= this._monitor.y + this._monitor.height)
+ shouldHide = false;
+
+ break;
+ case St.Side.RIGHT:
+ if (x >= this.staticBox.x1 &&
+ x <= this._monitor.x + this._monitor.width &&
+ y >= this._monitor.y &&
+ y <= this._monitor.y + this._monitor.height)
+ shouldHide = false;
+
+ break;
+ case St.Side.TOP:
+ if (x >= this._monitor.x &&
+ x <= this._monitor.x + this._monitor.width &&
+ y <= this.staticBox.y2 &&
+ y >= this._monitor.y)
+ shouldHide = false;
+
+ break;
+ case St.Side.BOTTOM:
+ if (x >= this._monitor.x &&
+ x <= this._monitor.x + this._monitor.width &&
+ y >= this.staticBox.y1 &&
+ y <= this._monitor.y + this._monitor.height)
+ shouldHide = false;
+ }
+ if (shouldHide) {
+ this._triggerTimeoutId = 0;
+ this._hoverChanged();
+ return GLib.SOURCE_REMOVE;
+ } else {
+ return GLib.SOURCE_CONTINUE;
+ }
+ });
+
+ this._show();
+ }
+
+ /**
+ * Remove pressure barrier
+ */
+ _removeBarrier() {
+ if (this._barrier) {
+ if (this._pressureBarrier)
+ this._pressureBarrier.removeBarrier(this._barrier);
+ this._barrier.destroy();
+ this._barrier = null;
+ }
+ this._removeBarrierTimeoutId = 0;
+ return false;
+ }
+
+ /**
+ * Update pressure barrier size
+ */
+ _updateBarrier() {
+ // Remove existing barrier
+ this._removeBarrier();
+
+ // The barrier needs to be removed in fullscreen with autohide disabled
+ // otherwise the mouse can get trapped on monitor.
+ if (this._monitor.inFullscreen &&
+ !DockManager.settings.autohideInFullscreen)
+ return;
+
+ // Manually reset pressure barrier
+ // This is necessary because we remove the pressure barrier when it is
+ // triggered to show the dock
+ if (this._pressureBarrier) {
+ this._pressureBarrier._reset();
+ this._pressureBarrier._isTriggered = false;
+ }
+
+ // Create new barrier
+ // The barrier extends to the whole workarea, minus 1 px to avoid
+ // conflicting with other active corners
+ // Note: dash in fixed position doesn't use pressure barrier.
+ if (this._canUsePressure && this._autohideIsEnabled &&
+ DockManager.settings.requirePressureToShow) {
+ let x1, x2, y1, y2, direction;
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(
+ this._monitor.index);
+
+ if (this._position === St.Side.LEFT) {
+ x1 = this._monitor.x + 1;
+ x2 = x1;
+ y1 = workArea.y + 1;
+ y2 = workArea.y + workArea.height - 1;
+ direction = Meta.BarrierDirection.POSITIVE_X;
+ } else if (this._position === St.Side.RIGHT) {
+ x1 = this._monitor.x + this._monitor.width - 1;
+ x2 = x1;
+ y1 = workArea.y + 1;
+ y2 = workArea.y + workArea.height - 1;
+ direction = Meta.BarrierDirection.NEGATIVE_X;
+ } else if (this._position === St.Side.TOP) {
+ x1 = workArea.x + 1;
+ x2 = workArea.x + workArea.width - 1;
+ y1 = this._monitor.y;
+ y2 = y1;
+ direction = Meta.BarrierDirection.POSITIVE_Y;
+ } else if (this._position === St.Side.BOTTOM) {
+ x1 = workArea.x + 1;
+ x2 = workArea.x + workArea.width - 1;
+ y1 = this._monitor.y + this._monitor.height;
+ y2 = y1;
+ direction = Meta.BarrierDirection.NEGATIVE_Y;
+ }
+
+ if (this._pressureBarrier && this._dockState === State.HIDDEN) {
+ this._barrier = new Meta.Barrier({
+ backend: global.backend,
+ x1,
+ x2,
+ y1,
+ y2,
+ directions: direction,
+ });
+ this._pressureBarrier.addBarrier(this._barrier);
+ }
+ }
+ }
+
+ _isPrimaryMonitor() {
+ return this.monitorIndex === Main.layoutManager.primaryIndex;
+ }
+
+ _resetPosition() {
+ // Ensure variables linked to settings are updated.
+ this._updateVisibilityMode();
+
+ const {dockFixed: fixedIsEnabled, dockExtended: extendHeight} = DockManager.settings;
+
+ if (fixedIsEnabled)
+ this.add_style_class_name('fixed');
+ else
+ this.remove_style_class_name('fixed');
+
+ // Note: do not use the workarea coordinates in the direction on which the dock is placed,
+ // to avoid a loop [position change -> workArea change -> position change] with
+ // fixed dock.
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(this.monitorIndex);
+
+ let fraction = DockManager.settings.heightFraction;
+ if (extendHeight)
+ fraction = 1;
+ else if ((fraction < 0) || (fraction > 1))
+ fraction = 0.95;
+
+ if (this._isHorizontal) {
+ this.width = Math.round(fraction * workArea.width);
+
+ let posY = this._monitor.y;
+ if (this._position === St.Side.BOTTOM)
+ posY += this._monitor.height;
+
+ this.x = workArea.x + Math.round((1 - fraction) / 2 * workArea.width);
+ this.y = posY;
+
+ if (extendHeight) {
+ this.dash._container.set_width(this.width);
+ this.add_style_class_name('extended');
+ } else {
+ this.dash._container.set_width(-1);
+ this.remove_style_class_name('extended');
+ }
+ } else {
+ this.height = Math.round(fraction * workArea.height);
+
+ let posX = this._monitor.x;
+ if (this._position === St.Side.RIGHT)
+ posX += this._monitor.width;
+
+ this.x = posX;
+ this.y = workArea.y + Math.round((1 - fraction) / 2 * workArea.height);
+
+ if (extendHeight) {
+ this.dash._container.set_height(this.height);
+ this.add_style_class_name('extended');
+ } else {
+ this.dash._container.set_height(-1);
+ this.remove_style_class_name('extended');
+ }
+ }
+ }
+
+ _updateVisibleDesktop() {
+ if (!this._intellihideIsEnabled)
+ return;
+
+ const {desktopIconsUsableArea} = DockManager.getDefault();
+ if (this._position === St.Side.BOTTOM)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, 0, this._box.height, 0, 0);
+ else if (this._position === St.Side.TOP)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, this._box.height, 0, 0, 0);
+ else if (this._position === St.Side.RIGHT)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, 0, 0, 0, this._box.width);
+ else if (this._position === St.Side.LEFT)
+ desktopIconsUsableArea.setMargins(this.monitorIndex, 0, 0, this._box.width, 0);
+ }
+
+ _updateStaticBox() {
+ this.staticBox.init_rect(
+ this.x + this._slider.x - (this._position === St.Side.RIGHT ? this._box.width : 0),
+ this.y + this._slider.y - (this._position === St.Side.BOTTOM ? this._box.height : 0),
+ this._box.width,
+ this._box.height
+ );
+
+ this._intellihide.updateTargetBox(this.staticBox);
+ this._updateVisibleDesktop();
+ }
+
+ _removeAnimations() {
+ this._slider.remove_all_transitions();
+ }
+
+ _onDragStart() {
+ this._oldIgnoreHover = this._ignoreHover;
+ this._ignoreHover = true;
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+
+ _onDragEnd() {
+ if (this._oldIgnoreHover)
+ this._ignoreHover = this._oldIgnoreHover;
+ this._oldIgnoreHover = null;
+ this._box.sync_hover();
+ this._updateDashVisibility();
+ }
+
+ /**
+ * Show dock and give key focus to it
+ */
+ _onAccessibilityFocus() {
+ this._box.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ this._animateIn(DockManager.settings.animationTime, 0);
+ }
+
+ // Optional features to be enabled only for the main Dock
+ _enableExtraFeatures() {
+ // Restore dash accessibility
+ Main.ctrlAltTabManager.addGroup(
+ this.dash, _('Dash'), 'user-bookmarks-symbolic',
+ {focusCallback: this._onAccessibilityFocus.bind(this)});
+ }
+
+ /**
+ * Switch workspace by scrolling over the dock
+ */
+ _optionalScrollWorkspaceSwitch() {
+ const isEnabled = () =>
+ DockManager.settings.scrollAction === scrollAction.SWITCH_WORKSPACE;
+
+ const enable = () => {
+ this._signalsHandler.removeWithLabel(Labels.WORKSPACE_SWITCH_SCROLL);
+
+ this._signalsHandler.addWithLabel(Labels.WORKSPACE_SWITCH_SCROLL,
+ this._box, 'scroll-event', (_, e) => onScrollEvent(e));
+ };
+
+ const disable = () => {
+ this._signalsHandler.removeWithLabel(Labels.WORKSPACE_SWITCH_SCROLL);
+
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId) {
+ GLib.source_remove(this._optionalScrollWorkspaceSwitchDeadTimeId);
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
+ }
+ };
+
+ DockManager.settings.connect('changed::scroll-action', () => {
+ if (isEnabled())
+ enable();
+ else
+ disable();
+ });
+
+ if (isEnabled())
+ enable();
+
+ // This was inspired to desktop-scroller@obsidien.github.com
+ const onScrollEvent = event => {
+ // When in overview change workspace only in windows view
+ if (Main.overview.visible)
+ return false;
+
+ const activeWs = global.workspace_manager.get_active_workspace();
+ let direction = null;
+
+ let prevDirection, nextDirection;
+ if (global.workspace_manager.layout_columns > global.workspace_manager.layout_rows) {
+ prevDirection = Meta.MotionDirection.UP;
+ nextDirection = Meta.MotionDirection.DOWN;
+ } else {
+ prevDirection = Meta.MotionDirection.LEFT;
+ nextDirection = Meta.MotionDirection.RIGHT;
+ }
+
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP:
+ direction = prevDirection;
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ direction = nextDirection;
+ break;
+ case Clutter.ScrollDirection.SMOOTH: {
+ const [dx_, dy] = event.get_scroll_delta();
+ if (dy < 0)
+ direction = prevDirection;
+ else if (dy > 0)
+ direction = nextDirection;
+ }
+ break;
+ }
+
+ if (direction) {
+ // Prevent scroll events from triggering too many workspace switches
+ // by adding a 250ms dead time between each scroll event.
+ // Useful on laptops when using a touch pad.
+
+ // During the deadtime do nothing
+ if (this._optionalScrollWorkspaceSwitchDeadTimeId) {
+ return false;
+ } else {
+ this._optionalScrollWorkspaceSwitchDeadTimeId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, 250, () => {
+ this._optionalScrollWorkspaceSwitchDeadTimeId = 0;
+ });
+ }
+
+ let ws;
+
+ ws = activeWs.get_neighbor(direction);
+
+ if (!Main.wm._workspaceSwitcherPopup) {
+ // Support Workspace Grid extension showing their custom
+ // Grid Workspace Switcher
+ if (global.workspace_manager.workspace_grid !== undefined) {
+ Main.wm._workspaceSwitcherPopup =
+ global.workspace_manager.workspace_grid.getWorkspaceSwitcherPopup();
+ } else {
+ Main.wm._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup();
+ }
+ }
+ // Set the actor non reactive, so that it doesn't prevent the
+ // clicks events from reaching the dash actor. I can't see a reason
+ // why it should be reactive.
+ Main.wm._workspaceSwitcherPopup.reactive = false;
+ Main.wm._workspaceSwitcherPopup.connect('destroy', () => {
+ Main.wm._workspaceSwitcherPopup = null;
+ });
+
+ // If Workspace Grid is installed, let them handle the scroll behavior.
+ if (global.workspace_manager.workspace_grid !== undefined)
+ ws = global.workspace_manager.workspace_grid.actionMoveWorkspace(direction);
+ else
+ Main.wm.actionMoveWorkspace(ws);
+
+ // Do not show workspaceSwitcher in overview
+ if (!Main.overview.visible)
+ Main.wm._workspaceSwitcherPopup.display(ws.index());
+
+ return true;
+ } else {
+ return false;
+ }
+ };
+ }
+
+ _activateApp(appIndex) {
+ const children = this.dash._box.get_children().filter(actor => {
+ return actor.child &&
+ actor.child.app;
+ });
+
+ // Apps currently in the dash
+ const apps = children.map(actor => {
+ return actor.child;
+ });
+
+ // Activate with button = 1, i.e. same as left click
+ const button = 1;
+ if (appIndex < apps.length)
+ apps[appIndex].activate(button);
+ }
+});
+
+/*
+ * Handle keyboard shortcuts
+ */
+const NUM_HOTKEYS = 10;
+
+const KeyboardShortcuts = class DashToDockKeyboardShortcuts {
+ constructor() {
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ this._hotKeysEnabled = false;
+ if (DockManager.settings.hotKeys)
+ this._enableHotKeys();
+
+ this._signalsHandler.add([
+ DockManager.settings,
+ 'changed::hot-keys',
+ () => {
+ if (DockManager.settings.hotKeys)
+ this._enableHotKeys.bind(this)();
+ else
+ this._disableHotKeys.bind(this)();
+ },
+ ]);
+
+ this._optionalNumberOverlay();
+ }
+
+ destroy() {
+ DockManager.allDocks.forEach(dock => {
+ if (dock._numberOverlayTimeoutId) {
+ GLib.source_remove(dock._numberOverlayTimeoutId);
+ dock._numberOverlayTimeoutId = 0;
+ }
+ });
+
+ // Remove keybindings
+ this._disableHotKeys();
+ this._disableExtraShortcut();
+ this._signalsHandler.destroy();
+ }
+
+ _enableHotKeys() {
+ if (this._hotKeysEnabled)
+ return;
+
+ // Setup keyboard bindings for dash elements
+ const keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
+ const {mainDock} = DockManager.getDefault();
+ keys.forEach(function (key) {
+ for (let i = 0; i < NUM_HOTKEYS; i++) {
+ const appNum = i;
+ Main.wm.addKeybinding(key + (i + 1), DockManager.settings,
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ () => {
+ mainDock._activateApp(appNum);
+ this._showOverlay();
+ });
+ }
+ }, this);
+
+ this._hotKeysEnabled = true;
+ }
+
+ _disableHotKeys() {
+ if (!this._hotKeysEnabled)
+ return;
+
+ const keys = ['app-hotkey-', 'app-shift-hotkey-', 'app-ctrl-hotkey-'];
+ keys.forEach(key => {
+ for (let i = 0; i < NUM_HOTKEYS; i++)
+ Main.wm.removeKeybinding(key + (i + 1));
+ }, this);
+
+ this._hotKeysEnabled = false;
+ }
+
+ _optionalNumberOverlay() {
+ const {settings} = DockManager;
+ this._shortcutIsSet = false;
+ // Enable extra shortcut if either 'overlay' or 'show-dock' are true
+ if (settings.hotKeys &&
+ (settings.hotkeysOverlay || settings.hotkeysShowDock))
+ this._enableExtraShortcut();
+
+ this._signalsHandler.add([
+ settings,
+ 'changed::hot-keys',
+ this._checkHotkeysOptions.bind(this),
+ ], [
+ settings,
+ 'changed::hotkeys-overlay',
+ this._checkHotkeysOptions.bind(this),
+ ], [
+ settings,
+ 'changed::hotkeys-show-dock',
+ this._checkHotkeysOptions.bind(this),
+ ]);
+ }
+
+ _checkHotkeysOptions() {
+ const {settings} = DockManager;
+
+ if (settings.hotKeys &&
+ (settings.hotkeysOverlay || settings.hotkeysShowDock))
+ this._enableExtraShortcut();
+ else
+ this._disableExtraShortcut();
+ }
+
+ _enableExtraShortcut() {
+ if (!this._shortcutIsSet) {
+ Main.wm.addKeybinding('shortcut', DockManager.settings,
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ this._showOverlay.bind(this));
+ this._shortcutIsSet = true;
+ }
+ }
+
+ _disableExtraShortcut() {
+ if (this._shortcutIsSet) {
+ Main.wm.removeKeybinding('shortcut');
+ this._shortcutIsSet = false;
+ }
+ }
+
+ _showOverlay() {
+ for (const dock of DockManager.allDocks) {
+ if (DockManager.settings.hotkeysOverlay)
+ dock.dash.toggleNumberOverlay(true);
+
+ // Restart the counting if the shortcut is pressed again
+ if (dock._numberOverlayTimeoutId) {
+ GLib.source_remove(dock._numberOverlayTimeoutId);
+ dock._numberOverlayTimeoutId = 0;
+ }
+
+ // Hide the overlay/dock after the timeout
+ const timeout = DockManager.settings.shortcutTimeout * 1000;
+ dock._numberOverlayTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, timeout, () => {
+ dock._numberOverlayTimeoutId = 0;
+ dock.dash.toggleNumberOverlay(false);
+ // Hide the dock again if necessary
+ dock._updateDashVisibility();
+ });
+
+ // Show the dock if it is hidden
+ if (DockManager.settings.hotkeysShowDock) {
+ const showDock = dock._intellihideIsEnabled || dock._autohideIsEnabled;
+ if (showDock)
+ dock._show();
+ }
+ }
+ }
+};
+
+/**
+ * Isolate overview to open new windows for inactive apps
+ * Note: the future implementation is not fully contained here.
+ * Some bits are around in other methods of other classes.
+ * This class just take care of enabling/disabling the option.
+ */
+const WorkspaceIsolation = class DashToDockWorkspaceIsolation {
+ constructor() {
+ const {settings} = DockManager;
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._injectionsHandler = new Utils.InjectionsHandler();
+
+ const updateAllDocks = () => {
+ DockManager.allDocks.forEach(dock =>
+ dock.dash.resetAppIcons());
+ if (settings.isolateWorkspaces ||
+ settings.isolateMonitors)
+ this._enable.bind(this)();
+ else
+ this._disable.bind(this)();
+ };
+ this._signalsHandler.add(
+ [settings, 'changed::isolate-workspaces', updateAllDocks],
+ [settings, 'changed::workspace-agnostic-urgent-windows', updateAllDocks],
+ [settings, 'changed::isolate-monitors', updateAllDocks]
+ );
+
+ if (settings.isolateWorkspaces ||
+ settings.isolateMonitors)
+ this._enable();
+ }
+
+ _enable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this._disable();
+
+ DockManager.allDocks.forEach(dock => {
+ this._signalsHandler.addWithLabel(
+ Labels.ISOLATION,
+ [global.display, 'restacked', () => dock.dash._queueRedisplay()],
+ [global.display, 'window-marked-urgent', () => dock.dash._queueRedisplay()],
+ [global.display, 'window-demands-attention', () => dock.dash._queueRedisplay()],
+ [global.window_manager, 'switch-workspace', () => dock.dash._queueRedisplay()]
+ );
+
+ // This last signal is only needed for monitor isolation, as windows
+ // might migrate from one monitor to another without triggering 'restacked'
+ if (DockManager.settings.isolateMonitors) {
+ this._signalsHandler.addWithLabel(Labels.ISOLATION,
+ global.display,
+ 'window-entered-monitor',
+ dock.dash._queueRedisplay.bind(dock.dash));
+ }
+ }, this);
+
+ /**
+ * here this is the Shell.App
+ */
+ function IsolatedOverview() {
+ // These lines take care of Nautilus for icons on Desktop
+ const activeWorkspaceIndex =
+ global.workspaceManager.get_active_workspace_index();
+ const windows = this.get_windows().filter(w =>
+ !w.skipTaskbar && w.get_workspace().index() === activeWorkspaceIndex);
+
+ if (windows.length)
+ return Main.activateWindow(windows[0]);
+ return this.open_new_window(-1);
+ }
+
+ this._injectionsHandler.addWithLabel(Labels.ISOLATION,
+ Shell.App.prototype,
+ 'activate',
+ IsolatedOverview);
+ }
+
+ _disable() {
+ this._signalsHandler.removeWithLabel(Labels.ISOLATION);
+ this._injectionsHandler.removeWithLabel(Labels.ISOLATION);
+ }
+
+ destroy() {
+ this._signalsHandler.destroy();
+ this._injectionsHandler.destroy();
+ }
+};
+
+
+export class DockManager {
+ constructor(extension) {
+ if (DockManager._singleton)
+ throw new Error('DashToDock has been already initialized');
+ DockManager._singleton = this;
+ this._extension = extension;
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._methodInjections = new Utils.InjectionsHandler(this);
+ this._vfuncInjections = new Utils.VFuncInjectionsHandler(this);
+ this._propertyInjections = new Utils.PropertyInjectionsHandler(this);
+ this._settings = this._extension.getSettings(
+ 'org.gnome.shell.extensions.dash-to-dock');
+ this._appSwitcherSettings = new Gio.Settings({schema_id: 'org.gnome.shell.app-switcher'});
+ this._mapSettingsValues();
+
+ this._iconTheme = new St.IconTheme();
+
+ this._desktopIconsUsableArea = new DesktopIconsIntegration.DesktopIconsUsableAreaClass();
+ this._oldDash = Main.overview.isDummy ? null : Main.overview.dash;
+ this._discreteGpuAvailable = AppDisplay.discreteGpuAvailable;
+ this._appSpread = new AppSpread.AppSpread();
+ this._notificationsMonitor = new NotificationsMonitor.NotificationsMonitor();
+
+ const needsRemoteModel = () =>
+ !this._notificationsMonitor.dndMode && this._settings.showIconsEmblems;
+
+ const ensureRemoteModel = () => {
+ if (needsRemoteModel && !this._remoteModel) {
+ this._remoteModel = new LauncherAPI.LauncherEntryRemoteModel();
+ this._appIconsDecorator = new AppIconsDecorator.AppIconsDecorator();
+ } else if (!needsRemoteModel) {
+ this._remoteModel?.destroy();
+ delete this._remoteModel;
+ this._appIconsDecorator?.destroy();
+ delete this._appIconsDecorator;
+ }
+ };
+ ensureRemoteModel();
+
+ this._notificationsMonitor.connect('changed', ensureRemoteModel);
+ this._settings.connect('changed::show-icons-emblems', ensureRemoteModel);
+
+ if (this._discreteGpuAvailable === undefined) {
+ const updateDiscreteGpuAvailable = () => {
+ const switcherooProxy = global.get_switcheroo_control();
+ if (switcherooProxy) {
+ const prop = switcherooProxy.get_cached_property('HasDualGpu');
+ this._discreteGpuAvailable = prop?.unpack() ?? false;
+ } else {
+ this._discreteGpuAvailable = false;
+ }
+ };
+ this._signalsHandler.add(global, 'notify::switcheroo-control',
+ () => updateDiscreteGpuAvailable());
+ updateDiscreteGpuAvailable();
+ }
+
+ // Connect relevant signals to the toggling function
+ this._bindSettingsChanges();
+
+ this._ensureLocations();
+
+ /* Array of all the docks created */
+ this._allDocks = [];
+ this._createDocks();
+
+ this._overrideAppMenus();
+
+ // status variable: true when the overview is shown through the dash
+ // applications button.
+ this._forcedOverview = false;
+ }
+
+ static getDefault() {
+ return DockManager._singleton;
+ }
+
+ static get allDocks() {
+ return DockManager.getDefault()._allDocks;
+ }
+
+ static get extension() {
+ return DockManager.getDefault().extension;
+ }
+
+ static get settings() {
+ return DockManager.getDefault().settings;
+ }
+
+ get extension() {
+ return this._extension;
+ }
+
+ get settings() {
+ return this._settings;
+ }
+
+ static get iconTheme() {
+ return DockManager.getDefault().iconTheme;
+ }
+
+ get settings() { // eslint-disable-line no-dupe-class-members
+ return this._settings;
+ }
+
+ get iconTheme() {
+ return this._iconTheme;
+ }
+
+ get fm1Client() {
+ return this._fm1Client;
+ }
+
+ get remoteModel() {
+ return this._remoteModel;
+ }
+
+ get mainDock() {
+ return this._allDocks.length ? this._allDocks[0] : null;
+ }
+
+ get removables() {
+ return this._removables;
+ }
+
+ get trash() {
+ return this._trash;
+ }
+
+ get desktopIconsUsableArea() {
+ return this._desktopIconsUsableArea;
+ }
+
+ get discreteGpuAvailable() {
+ return AppDisplay.discreteGpuAvailable || this._discreteGpuAvailable;
+ }
+
+ get appSpread() {
+ return this._appSpread;
+ }
+
+ get notificationsMonitor() {
+ return this._notificationsMonitor;
+ }
+
+ getDockByMonitor(monitorIndex) {
+ return this._allDocks.find(d => d.monitorIndex === monitorIndex);
+ }
+
+ _ensureLocations() {
+ const {showMounts, showTrash} = this.settings;
+
+ if (showTrash || showMounts) {
+ if (!this._fm1Client)
+ this._fm1Client = new FileManager1API.FileManager1Client();
+ } else if (this._fm1Client) {
+ this._fm1Client.destroy();
+ this._fm1Client = null;
+ }
+
+ if (showMounts && !this._removables) {
+ this._removables = new Locations.Removables();
+ } else if (!showMounts && this._removables) {
+ this._removables.destroy();
+ this._removables = null;
+ }
+
+ if (showTrash && !this._trash) {
+ this._trash = new Locations.Trash();
+ } else if (!showTrash && this._trash) {
+ this._trash.destroy();
+ this._trash = null;
+ }
+
+ Locations.unWrapFileManagerApp();
+ [this._methodInjections, this._propertyInjections].forEach(
+ injections => injections.removeWithLabel(Labels.LOCATIONS));
+
+ if (showMounts || showTrash) {
+ if (this.settings.isolateLocations) {
+ const fileManagerApp = Locations.wrapFileManagerApp();
+
+ this._methodInjections.addWithLabel(Labels.LOCATIONS, [
+ Shell.AppSystem.prototype, 'get_running',
+ function (originalMethod, ...args) {
+ /* eslint-disable no-invalid-this */
+ const runningApps = originalMethod.call(this, ...args);
+ const locationApps = Locations.getRunningApps();
+ if (!locationApps.length)
+ return runningApps;
+
+ const fileManagerIdx = runningApps.indexOf(fileManagerApp);
+ if (fileManagerIdx > -1 && fileManagerApp?.state !== Shell.AppState.RUNNING)
+ runningApps.splice(fileManagerIdx, 1);
+
+ return [...runningApps, ...locationApps].sort(Utils.shellAppCompare);
+ /* eslint-enable no-invalid-this */
+ },
+ ],
+ [
+ Shell.WindowTracker.prototype, 'get_window_app',
+ function (originalMethod, window) {
+ /* eslint-disable no-invalid-this */
+ const locationApp = Locations.getRunningApps().find(a =>
+ a.get_windows().includes(window));
+ return locationApp ?? originalMethod.call(this, window);
+ /* eslint-enable no-invalid-this */
+ },
+ ],
+ [
+ Shell.WindowTracker.prototype, 'get_app_from_pid',
+ function (originalMethod, pid) {
+ /* eslint-disable no-invalid-this */
+ const locationApp = Locations.getRunningApps().find(a =>
+ a.get_pids().includes(pid));
+ return locationApp ?? originalMethod.call(this, pid);
+ /* eslint-enable no-invalid-this */
+ },
+ ]);
+
+ const {get: defaultFocusAppGetter} = Object.getOwnPropertyDescriptor(
+ Shell.WindowTracker.prototype, 'focus_app');
+ this._propertyInjections.addWithLabel(Labels.LOCATIONS,
+ Shell.WindowTracker.prototype, 'focus_app', {
+ get() {
+ const locationApp = Locations.getRunningApps().find(a => a.isFocused);
+ return locationApp ?? defaultFocusAppGetter.call(this);
+ },
+ });
+ }
+ }
+ }
+
+ _toggle() {
+ if (this._toggleLater)
+ return;
+
+ this._toggleLater = Utils.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => {
+ delete this._toggleLater;
+ this._restoreDash();
+ this._deleteDocks();
+ this._createDocks();
+ this.emit('toggled');
+ });
+ }
+
+ _mapExternalSetting(settings, key, mappedKey, mapValueFunction) {
+ const camelMappedKey = mappedKey.replace(/-([a-z\d])/g, k => k[1].toUpperCase());
+
+ const dockPropertyDesc = Object.getOwnPropertyDescriptor(this.settings, camelMappedKey);
+
+ if (!dockPropertyDesc)
+ throw new Error('Setting %s not found in dock'.format(mappedKey));
+
+ const mappedValue = () => mapValueFunction(settings.get_value(key).recursiveUnpack());
+ Object.defineProperty(this.settings, camelMappedKey, {
+ get: () => mappedValue() ?? dockPropertyDesc.value,
+ set: value => {
+ if (mappedValue())
+ dockPropertyDesc.value = value;
+ },
+ });
+
+ this._signalsHandler.addWithLabel(Labels.SETTINGS, settings,
+ 'changed::%s'.format(key), () => {
+ this._signalsHandler.blockWithLabel(Labels.SETTINGS);
+ this.settings.emit('changed::%s'.format(mappedKey), mappedKey);
+ this._signalsHandler.unblockWithLabel(Labels.SETTINGS);
+ });
+ }
+
+ _mapSettingsValues() {
+ this.settings.settingsSchema.list_keys().forEach(key => {
+ const camelKey = key.replace(/-([a-z\d])/g, k => k[1].toUpperCase());
+ const updateSetting = () => {
+ const schemaKey = this.settings.settingsSchema.get_key(key);
+ if (schemaKey.get_range().deepUnpack()[0] === 'enum')
+ this.settings[camelKey] = this.settings.get_enum(key);
+ else
+ this.settings[camelKey] = this.settings.get_value(key).recursiveUnpack();
+ };
+ updateSetting();
+ this._signalsHandler.addWithLabel(Labels.SETTINGS, this.settings,
+ `changed::${key}`, updateSetting);
+ if (key !== camelKey) {
+ Object.defineProperty(this.settings, key,
+ {get: () => this.settings[camelKey]});
+ }
+ });
+ Object.defineProperties(this.settings, {
+ dockExtended: {get: () => this.settings.extendHeight},
+ });
+ }
+
+ _bindSettingsChanges() {
+ // Connect relevant signals to the toggling function
+ this._signalsHandler.addWithLabel(Labels.SETTINGS, [
+ Utils.getMonitorManager(),
+ 'monitors-changed',
+ this._toggle.bind(this),
+ ], [
+ Main.sessionMode,
+ 'updated',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::multi-monitor',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::preferred-monitor',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::preferred-monitor-by-connector',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::dock-position',
+ this._toggle.bind(this),
+ ], [
+ this._settings,
+ 'changed::extend-height',
+ () => this._adjustPanelCorners(),
+ ], [
+ this._settings,
+ 'changed::dock-fixed',
+ () => this._adjustPanelCorners(),
+ ], [
+ this._settings,
+ 'changed::show-trash',
+ () => this._ensureLocations(),
+ ], [
+ this._settings,
+ 'changed::show-mounts',
+ () => this._ensureLocations(),
+ ], [
+ this._settings,
+ 'changed::isolate-locations',
+ () => this._ensureLocations(),
+ ], [
+ this._settings,
+ 'changed::intellihide',
+ () => {
+ if (!this._settings.intellihide)
+ this._desktopIconsUsableArea.resetMargins();
+ },
+ ]);
+
+ this._mapExternalSetting(this._appSwitcherSettings, 'current-workspace-only',
+ 'isolate-workspaces', value => value || undefined);
+ }
+
+ _createDocks() {
+ // If there are no monitors (headless configurations, but it can also
+ // happen temporary while disconnecting and reconnecting monitors), just
+ // do nothing. When a monitor will be connected we we'll be notified and
+ // and thus create the docks. This prevents pointing trying to access
+ // monitors throughout the code, were we are assuming that at least the
+ // primary monitor is present.
+ if (Main.layoutManager.monitors.length <= 0)
+ return;
+
+
+ this._preferredMonitorIndex = this.settings.preferredMonitor;
+ if (this._preferredMonitorIndex === -2) {
+ const monitorManager = Utils.getMonitorManager();
+ this._preferredMonitorIndex = monitorManager.get_monitor_for_connector(
+ this.settings.preferredMonitorByConnector);
+ } else if (this._preferredMonitorIndex >= 0) {
+ // Primary monitor used to be always 0 in Gdk, but the shell has a different
+ // concept (where the order depends on mutter order).
+ // So even if now the extension settings may use the same logic of the shell
+ // we prefer not to break the previously configured systems, and so we still
+ // assume that the gsettings monitor numbering follows the old strategy.
+ // This ensure the indexing in the settings and in the shell are matched,
+ // i.e. that we start counting from the primaryMonitorIndex
+ this._preferredMonitorIndex =
+ (Main.layoutManager.primaryIndex + this._preferredMonitorIndex) %
+ Main.layoutManager.monitors.length;
+ }
+
+ // In case of multi-monitor, we consider the dock on the primary monitor
+ // to be the preferred (main) one regardless of the settings the dock
+ // goes on the primary monitor also if the settings are inconsistent
+ // (e.g. desired monitor not connected).
+ if (this.settings.multiMonitor ||
+ this._preferredMonitorIndex < 0 ||
+ this._preferredMonitorIndex > Main.layoutManager.monitors.length - 1)
+ this._preferredMonitorIndex = Main.layoutManager.primaryIndex;
+
+
+ // First we create the main Dock, to get the extra features to bind to this one
+ let dock = new DockedDash({
+ monitorIndex: this._preferredMonitorIndex,
+ isMain: true,
+ });
+ this._allDocks.push(dock);
+
+ // connect app icon into the view selector
+ dock.dash.showAppsButton.connect('notify::checked',
+ this._onShowAppsButtonToggled.bind(this));
+
+ // Make the necessary changes to Main.overview.dash
+ this._prepareMainDash();
+
+ // Adjust corners if necessary
+ this._adjustPanelCorners();
+
+ if (this.settings.multiMonitor) {
+ const nMon = Main.layoutManager.monitors.length;
+ for (let iMon = 0; iMon < nMon; iMon++) {
+ if (iMon === this._preferredMonitorIndex)
+ continue;
+ dock = new DockedDash({monitorIndex: iMon});
+ this._allDocks.push(dock);
+ // connect app icon into the view selector
+ dock.dash.showAppsButton.connect('notify::checked',
+ this._onShowAppsButtonToggled.bind(this));
+ }
+ }
+
+ // Load optional features. We load *after* the docks are created, since
+ // we need to connect the signals to all dock instances.
+ this._workspaceIsolation = new WorkspaceIsolation();
+ this._keyboardShortcuts = new KeyboardShortcuts();
+
+ this.emit('docks-ready');
+ }
+
+ _prepareStartupAnimation() {
+ DockManager.allDocks.forEach(dock => {
+ const {dash} = dock;
+
+ dock.opacity = 255;
+ dash.set({
+ opacity: 0,
+ translation_x: 0,
+ translation_y: 0,
+ });
+ });
+ }
+
+ _runStartupAnimation() {
+ DockManager.allDocks.forEach(dock => {
+ const {dash} = dock;
+
+ switch (dock.position) {
+ case St.Side.LEFT:
+ dash.translation_x = -dash.width;
+ break;
+ case St.Side.RIGHT:
+ dash.translation_x = dash.width;
+ break;
+ case St.Side.BOTTOM:
+ dash.translation_y = dash.height;
+ break;
+ case St.Side.TOP:
+ dash.translation_y = -dash.height;
+ break;
+ }
+
+ dash.ease({
+ opacity: 255,
+ translation_x: 0,
+ translation_y: 0,
+ duration: STARTUP_ANIMATION_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ });
+ }
+
+ _prepareMainDash() {
+ // Ensure Main.overview.dash is set to our dash in dummy mode
+ // while just use the default getter otherwise.
+ // The getter must be dynamic and not set only when we've a dummy
+ // overview because the mode can change dynamically.
+ this._propertyInjections.removeWithLabel(Labels.MAIN_DASH);
+ const defaultDashGetter = Object.getOwnPropertyDescriptor(
+ Main.overview.constructor.prototype, 'dash').get;
+ this._propertyInjections.addWithLabel(Labels.MAIN_DASH, Main.overview, 'dash', {
+ get: () => Main.overview.isDummy
+ ? this.mainDock.dash : defaultDashGetter.call(Main.overview),
+ });
+
+ if (Main.overview.isDummy)
+ return;
+
+ // Hide usual Dash
+ this._oldDash.hide();
+
+ // Also set dash width to 1, so it's almost not taken into account by code
+ // calculating the reserved space in the overview. The reason to keep it at 1 is
+ // to allow its visibility change to trigger an allocation of the appGrid which
+ // in turn is triggering the appsIcon spring animation, required when no other
+ // actors has this effect, i.e in horizontal mode and without the workspaceThumbnails
+ // 1 static workspace only)
+ this._oldDash.set_height(1);
+
+ this._signalsHandler.addWithLabel(Labels.OLD_DASH_CHANGES, [
+ this._oldDash,
+ 'notify::visible',
+ () => this._oldDash.hide(),
+ ], [
+ this._oldDash,
+ 'notify::height',
+ () => this._oldDash.set_height(1),
+ ]);
+
+ // Pretend I'm the dash: meant to make app grid swarm animation come from
+ // the right position of the appShowButton.
+ this.overviewControls.dash = this.mainDock.dash;
+ this.searchController._showAppsButton = this.mainDock.dash.showAppsButton;
+
+ // We also need to ignore max-size changes
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash,
+ 'setMaxSize', () => {});
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash,
+ 'allocate', () => {});
+ // And to return the preferred height depending on the state
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, this._oldDash,
+ 'get_preferred_height', (_originalMethod, ...args) => {
+ if (this.mainDock.isHorizontal && !this.settings.dockFixed)
+ return this.mainDock.get_preferred_height(...args);
+ return [0, 0];
+ });
+
+ // FIXME: https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2890
+ // const { ControlsManagerLayout } = OverviewControls;
+ const ControlsManagerLayout = this.overviewControls.layout_manager.constructor;
+
+ const maybeAdjustBoxSize = (state, box, spacing) => {
+ // ensure that an undefined value will be converted into a valid one
+ spacing = spacing ?? 0;
+
+ if (state === OverviewControls.ControlsState.WINDOW_PICKER) {
+ const searchBox = this.overviewControls._searchEntry.get_allocation_box();
+ const {shouldShow: wsThumbnails} = this.overviewControls._thumbnailsBox;
+
+ if (!wsThumbnails) {
+ box.y1 += spacing;
+ box.y2 -= spacing;
+ }
+
+ box.y2 -= searchBox.get_height() + 2 * spacing;
+ }
+
+ return box;
+ };
+
+ const maybeAdjustBoxToDock = (state, box, spacing) => {
+ maybeAdjustBoxSize(state, box, spacing);
+
+ if (this.mainDock.isHorizontal || this.settings.dockFixed)
+ return box;
+
+ const [, preferredWidth] = this.mainDock.get_preferred_width(
+ box.get_height());
+
+ if (this.mainDock.position === St.Side.LEFT)
+ box.x1 += preferredWidth;
+ else if (this.mainDock.position === St.Side.RIGHT)
+ box.x2 -= preferredWidth;
+
+ return box;
+ };
+
+ this._vfuncInjections.addWithLabel(Labels.MAIN_DASH, ControlsManagerLayout.prototype,
+ 'allocate', function (container) {
+ /* eslint-disable no-invalid-this */
+ const oldPostAllocation = this._runPostAllocation;
+ this._runPostAllocation = () => {};
+
+ const monitor = Main.layoutManager.findMonitorForActor(container);
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(monitor.index);
+ const startX = workArea.x - monitor.x;
+ const startY = workArea.y - monitor.y;
+ const workAreaBox = new Clutter.ActorBox();
+ workAreaBox.set_origin(startX, startY);
+ workAreaBox.set_size(workArea.width, workArea.height);
+
+ // GNOME 46 changes "spacing" to "_spacing".
+ const spacing = this.spacing ?? this._spacing;
+
+ maybeAdjustBoxToDock(undefined, workAreaBox, spacing);
+ const oldStartY = workAreaBox.y1;
+
+ const propertyInjections = new Utils.PropertyInjectionsHandler();
+ propertyInjections.add(Main.layoutManager.panelBox, 'height', {value: startY});
+
+ if (Main.layoutManager.panelBox.y === Main.layoutManager.primaryMonitor.y)
+ workAreaBox.y1 -= oldStartY;
+
+ this.vfunc_allocate(container, workAreaBox);
+
+ propertyInjections.destroy();
+ workAreaBox.y1 = oldStartY;
+
+ const adjustActorHorizontalAllocation = actor => {
+ if (!actor.visible || !workAreaBox.x1)
+ return;
+
+ const contentBox = actor.get_allocation_box();
+ contentBox.set_size(workAreaBox.get_width(), contentBox.get_height());
+ contentBox.set_origin(workAreaBox.x1, contentBox.y1);
+ actor.allocate(contentBox);
+ };
+
+ [this._searchEntry, this._workspacesThumbnails, this._searchController].forEach(
+ actor => adjustActorHorizontalAllocation(actor));
+
+ this._runPostAllocation = oldPostAllocation;
+ this._runPostAllocation();
+ /* eslint-enable no-invalid-this */
+ });
+
+ /**
+ * This can be removed or bypassed when GNOME/gnome-shell!1892 will be merged
+ *
+ * @param originalFunction
+ * @param state
+ * @param workAreaBox
+ * @param {...any} args
+ */
+ function workspaceBoxOriginFixer(originalFunction, state, workAreaBox, ...args) {
+ /* eslint-disable no-invalid-this */
+ const workspaceBox = originalFunction.call(this, state, workAreaBox, ...args);
+ workspaceBox.set_origin(workAreaBox.x1, workspaceBox.y1);
+ return workspaceBox;
+ /* eslint-enable no-invalid-this */
+ }
+
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, [
+ ControlsManagerLayout.prototype,
+ '_computeWorkspacesBoxForState',
+ function (originalFunction, state, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (state === OverviewControls.ControlsState.HIDDEN)
+ return originalFunction.call(this, state, ...args);
+
+ const box = workspaceBoxOriginFixer.call(this, originalFunction, state, ...args);
+ // GNOME 46 changes "spacing" to "_spacing".
+ const spacing = this.spacing ?? this._spacing;
+ const dock = DockManager.getDefault().getDockByMonitor(Main.layoutManager.primaryIndex);
+ if (!dock)
+ return box;
+ else
+ return maybeAdjustBoxSize(state, box, spacing);
+ /* eslint-enable no-invalid-this */
+ },
+ ], [
+ WorkspacesView.SecondaryMonitorDisplay.prototype,
+ '_getWorkspacesBoxForState',
+ function (originalFunction, state, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (state === OverviewControls.ControlsState.HIDDEN)
+ return originalFunction.call(this, state, ...args);
+
+ const box = workspaceBoxOriginFixer.call(this, originalFunction, state, ...args);
+ const dock = DockManager.getDefault().getDockByMonitor(this._monitorIndex);
+ if (!dock)
+ return box;
+ if (state === OverviewControls.ControlsState.WINDOW_PICKER &&
+ dock.position === St.Side.BOTTOM) {
+ const [, preferredHeight] = dock.get_preferred_height(box.get_width());
+ box.y2 -= preferredHeight;
+ }
+ return box;
+ /* eslint-enable no-invalid-this */
+ },
+ ], [
+ ControlsManagerLayout.prototype,
+ '_getAppDisplayBoxForState',
+ function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ return workspaceBoxOriginFixer.call(this, originalFunction, ...args);
+ /* eslint-enable no-invalid-this */
+ },
+ ]);
+
+ this._vfuncInjections.addWithLabel(Labels.MAIN_DASH, Workspace.WorkspaceBackground.prototype,
+ 'allocate', function (box) {
+ /* eslint-disable no-invalid-this */
+ this.vfunc_allocate(box);
+
+ // This code has been submitted upstream via GNOME/gnome-shell!1892
+ // so can be removed when that gets merged (or bypassed on newer shell
+ // versions).
+ const monitor = Main.layoutManager.monitors[this._monitorIndex];
+ const [contentWidth, contentHeight] = this._bin.get_content_box().get_size();
+ const [mX1, mX2] = [monitor.x, monitor.x + monitor.width];
+ const [mY1, mY2] = [monitor.y, monitor.y + monitor.height];
+ const [wX1, wX2] = [this._workarea.x, this._workarea.x + this._workarea.width];
+ const [wY1, wY2] = [this._workarea.y, this._workarea.y + this._workarea.height];
+ const xScale = contentWidth / this._workarea.width;
+ const yScale = contentHeight / this._workarea.height;
+ const leftOffset = wX1 - mX1;
+ const topOffset = wY1 - mY1;
+ const rightOffset = mX2 - wX2;
+ const bottomOffset = mY2 - wY2;
+
+ const contentBox = new Clutter.ActorBox();
+ contentBox.set_origin(-leftOffset * xScale, -topOffset * yScale);
+ contentBox.set_size(
+ contentWidth + (leftOffset + rightOffset) * xScale,
+ contentHeight + (topOffset + bottomOffset) * yScale);
+
+ this._backgroundGroup.allocate(contentBox);
+ /* eslint-enable no-invalid-this */
+ });
+
+ // Reduce the space that the workspaces can use in secondary monitors
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH, WorkspacesView.WorkspacesView.prototype,
+ '_getFirstFitAllWorkspaceBox', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ const box = originalFunction.call(this, ...args);
+ if (DockManager.settings.dockFixed ||
+ this._monitorIndex === Main.layoutManager.primaryIndex)
+ return box;
+
+ const dock = DockManager.getDefault().getDockByMonitor(this._monitorIndex);
+ if (!dock)
+ return box;
+
+ if (dock.isHorizontal) {
+ const [, preferredHeight] = dock.get_preferred_height(box.get_width());
+ box.y2 -= preferredHeight;
+ if (dock.position === St.Side.TOP)
+ box.set_origin(box.x1, box.y1 + preferredHeight);
+ } else {
+ const [, preferredWidth] = dock.get_preferred_width(box.get_height());
+ box.x2 -= preferredWidth / 2;
+ if (dock.position === St.Side.LEFT)
+ box.set_origin(box.x1 + preferredWidth, box.y1);
+ }
+ return box;
+ /* eslint-enable no-invalid-this */
+ });
+
+ if (AppDisplay.BaseAppView?.prototype?._pageForCoords) {
+ // Ensure we handle Dnd events happening on the dock when we're
+ // dragging from AppDisplay.
+ // Remove when merged
+ // https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2002
+ this._methodInjections.addWithLabel(Labels.MAIN_DASH,
+ AppDisplay.BaseAppView.prototype,
+ '_pageForCoords', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ if (!this._scrollView.has_pointer)
+ return AppDisplay.SidePages.NONE;
+ return originalFunction.call(this, ...args);
+ /* eslint-enable no-invalid-this */
+ });
+ }
+
+ if (Main.layoutManager._startingUp) {
+ this._prepareStartupAnimation();
+
+ const hadOverview = Main.sessionMode.hasOverview;
+
+ // Convince LayoutManager to use the legacy startup animation:
+ // Reset overview controls state to HIDDEN, as skipping the startup
+ // overview leaves it stuck at WINDOW_PICKER
+ if (this._settings.disableOverviewOnStartup) {
+ Main.sessionMode.hasOverview = false;
+ Main.overview._overview.controls._stateAdjustment.value =
+ OverviewControls.ControlsState.HIDDEN;
+ }
+
+ this._signalsHandler.addWithLabel(Labels.STARTUP_ANIMATION,
+ Main.layoutManager, 'startup-complete', () => {
+ this._signalsHandler.removeWithLabel(Labels.STARTUP_ANIMATION);
+ Main.sessionMode.hasOverview = hadOverview;
+ this._runStartupAnimation();
+ });
+ }
+ }
+
+ _deleteDocks() {
+ if (!this._allDocks.length)
+ return;
+
+ // Remove extra features
+ this._workspaceIsolation.destroy();
+ this._keyboardShortcuts.destroy();
+ this._desktopIconsUsableArea.resetMargins();
+
+ // Delete all docks
+ this._allDocks.forEach(d => d.destroy());
+ this._allDocks = [];
+
+ this.emit('docks-destroyed');
+ }
+
+ _restoreDash() {
+ if (!this._oldDash)
+ return;
+
+ this._signalsHandler.removeWithLabel(Labels.OLD_DASH_CHANGES);
+ [this._methodInjections, this._vfuncInjections, this._propertyInjections].forEach(
+ injections => injections.removeWithLabel(Labels.MAIN_DASH));
+
+ this.overviewControls.layout_manager._dash = this._oldDash;
+ this.overviewControls.dash = this._oldDash;
+ this.searchController._showAppsButton = this._oldDash.showAppsButton;
+ Main.overview.dash.show();
+ Main.overview.dash.set_height(-1); // reset default dash size
+ // This force the recalculation of the icon size
+ Main.overview.dash._maxHeight = -1;
+ }
+
+ get overviewControls() {
+ return Main.overview._overview.controls;
+ }
+
+ get searchController() {
+ return this.overviewControls._searchController;
+ }
+
+ _onShowAppsButtonToggled(button) {
+ const {checked} = button;
+ const {overviewControls} = this;
+
+ if (!Main.overview.visible) {
+ this.mainDock.dash.showAppsButton._fromDesktop = true;
+ Main.overview.show(OverviewControls.ControlsState.APP_GRID);
+ } else if (!checked && this.mainDock.dash.showAppsButton._fromDesktop) {
+ Main.overview.hide();
+ this.mainDock.dash.showAppsButton._fromDesktop = false;
+ } else {
+ // TODO: I'm not sure how reliable this is, we might need to move the
+ // _onShowAppsButtonToggled logic into the extension.
+ if (!checked)
+ this.mainDock.dash.showAppsButton._fromDesktop = false;
+
+
+ // Instead of "syncing" the stock button, let's call its callback directly.
+ overviewControls._onShowAppsButtonToggled();
+ }
+
+ // Because we "disconnected" from the search controller, we have to manage its state.
+ this.searchController._setSearchActive(false);
+ }
+
+ _overrideAppMenus() {
+ this._methodInjections.add(AppMenu.AppMenu.prototype,
+ '_updateFavoriteItem', function (originalFunction, ...args) {
+ /* eslint-disable no-invalid-this */
+ originalFunction.call(this, ...args);
+ if (!this._toggleFavoriteItem.visible)
+ return;
+
+ const {id} = this._app;
+ this._toggleFavoriteItem.label.text = this._appFavorites.isFavorite(id)
+ ? _('Unpin') : __('Pin to Dock');
+ /* eslint-enable no-invalid-this */
+ });
+ }
+
+ destroy() {
+ this.emit('destroy');
+ if (this._toggleLater) {
+ Utils.laterRemove(this._toggleLater);
+ delete this._toggleLater;
+ }
+ this._restoreDash();
+ this._deleteDocks();
+ this._revertPanelCorners();
+ if (this._oldSelectorMargin)
+ this.searchController.margin_bottom = this._oldSelectorMargin;
+ if (this._fm1Client) {
+ this._fm1Client.destroy();
+ this._fm1Client = null;
+ }
+ this._notificationsMonitor.destroy();
+ this._appSpread.destroy();
+ this._trash?.destroy();
+ this._trash = null;
+ Locations.unWrapFileManagerApp();
+ this._removables?.destroy();
+ this._removables = null;
+ this._iconTheme = null;
+ this._remoteModel?.destroy();
+ this._appIconsDecorator?.destroy();
+ this._settings = null;
+ this._appSwitcherSettings = null;
+ this._oldDash = null;
+
+ this._desktopIconsUsableArea?.destroy();
+ this._desktopIconsUsableArea = null;
+ this._extension = null;
+ DockManager._singleton = null;
+ }
+
+ /**
+ * Adjust Panel corners, remove this when 41 won't be supported anymore
+ */
+ _adjustPanelCorners() {
+ if (!this._hasPanelCorners())
+ return;
+
+ const position = Utils.getPosition();
+ const isHorizontal = (position === St.Side.TOP) || (position === St.Side.BOTTOM);
+ const dockOnPrimary = this._settings.multiMonitor ||
+ this._preferredMonitorIndex === Main.layoutManager.primaryIndex;
+
+ if (!isHorizontal && dockOnPrimary && this.settings.dockExtended && this.settings.dockFixed) {
+ Main.panel._rightCorner.hide();
+ Main.panel._leftCorner.hide();
+ } else {
+ this._revertPanelCorners();
+ }
+ }
+
+ _revertPanelCorners() {
+ if (!this._hasPanelCorners())
+ return;
+
+ Main.panel._leftCorner.show();
+ Main.panel._rightCorner.show();
+ }
+
+ _hasPanelCorners() {
+ return !!Main.panel?._rightCorner && !!Main.panel?._leftCorner;
+ }
+}
+Signals.addSignalMethods(DockManager.prototype);
+
+// This class drives long-running icon animations, to keep them running in sync
+// with each other, and to save CPU by pausing them when the dock is hidden.
+export class IconAnimator {
+ constructor(actor) {
+ this._count = 0;
+ this._started = false;
+ this._animations = {
+ wiggle: [],
+ };
+ this._timeline = new Clutter.Timeline({
+ duration: AnimationUtils.adjustAnimationTime(ICON_ANIMATOR_DURATION) || 1,
+ repeat_count: -1,
+ actor,
+ });
+
+ this._updateSettings();
+ this._settingsChangedId = St.Settings.get().connect('notify',
+ () => this._updateSettings());
+
+ this._timeline.connect('new-frame', () => {
+ const progress = this._timeline.get_progress();
+ const wiggleRotation = progress < 1 / 6 ? 15 * Math.sin(progress * 24 * Math.PI) : 0;
+ const wigglers = this._animations.wiggle;
+ for (let i = 0, iMax = wigglers.length; i < iMax; i++)
+ wigglers[i].target.rotation_angle_z = wiggleRotation;
+ });
+ }
+
+ _updateSettings() {
+ this._timeline.set_duration(
+ AnimationUtils.adjustAnimationTime(ICON_ANIMATOR_DURATION) || 1);
+ }
+
+ destroy() {
+ St.Settings.get().disconnect(this._settingsChangedId);
+ this._timeline.stop();
+ this._timeline = null;
+ for (const pairs of Object.values(this._animations)) {
+ for (let i = 0, iMax = pairs.length; i < iMax; i++) {
+ const pair = pairs[i];
+ pair.target.disconnect(pair.targetDestroyId);
+ }
+ }
+ this._animations = null;
+ }
+
+ pause() {
+ if (this._started && this._count > 0)
+ this._timeline.stop();
+
+ this._started = false;
+ }
+
+ start() {
+ if (!this._started && this._count > 0)
+ this._timeline.start();
+
+ this._started = true;
+ }
+
+ addAnimation(target, name) {
+ const targetDestroyId = target.connect('destroy',
+ () => this.removeAnimation(target, name));
+ this._animations[name].push({target, targetDestroyId});
+ if (this._started && this._count === 0)
+ this._timeline.start();
+
+ this._count++;
+ }
+
+ removeAnimation(target, name) {
+ const pairs = this._animations[name];
+ for (let i = 0, iMax = pairs.length; i < iMax; i++) {
+ const pair = pairs[i];
+ if (pair.target === target) {
+ target.disconnect(pair.targetDestroyId);
+ pairs.splice(i, 1);
+ this._count--;
+ if (this._started && this._count === 0)
+ this._timeline.stop();
+
+ return;
+ }
+ }
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js
new file mode 100644
index 0000000..68de2ac
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/extension.js
@@ -0,0 +1,18 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {DockManager} from './docking.js';
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// We export this so it can be accessed by other extensions
+export let dockManager;
+
+export default class DashToDockExtension extends Extension.Extension {
+ enable() {
+ dockManager = new DockManager(this);
+ }
+
+ disable() {
+ dockManager?.destroy();
+ dockManager = null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js
new file mode 100644
index 0000000..192214a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/fileManager1API.js
@@ -0,0 +1,201 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {GLib, Gio} from './dependencies/gi.js';
+const {signals: Signals} = imports;
+
+import {Utils} from './imports.js';
+
+const FileManager1Iface = '<node><interface name="org.freedesktop.FileManager1">\
+ <property name="OpenWindowsWithLocations" type="a{sas}" access="read"/>\
+ </interface></node>';
+
+const FileManager1Proxy = Gio.DBusProxy.makeProxyWrapper(FileManager1Iface);
+
+const Labels = Object.freeze({
+ WINDOWS: Symbol('windows'),
+});
+
+/**
+ * This class implements a client for the org.freedesktop.FileManager1 dbus
+ * interface, and specifically for the OpenWindowsWithLocations property
+ * which is published by Nautilus, but is not an official part of the interface.
+ *
+ * The property is a map from window identifiers to a list of locations open in
+ * the window.
+ */
+export class FileManager1Client {
+ constructor() {
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._cancellable = new Gio.Cancellable();
+
+ this._windowsByPath = new Map();
+ this._windowsByLocation = new Map();
+ this._proxy = new FileManager1Proxy(Gio.DBus.session,
+ 'org.freedesktop.FileManager1',
+ '/org/freedesktop/FileManager1',
+ (initable, error) => {
+ // Use async construction to avoid blocking on errors.
+ if (error) {
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ global.log(error);
+ } else {
+ this._updateWindows();
+ this._updateLocationMap();
+ }
+ }, this._cancellable);
+
+ this._signalsHandler.add([
+ this._proxy,
+ 'g-properties-changed',
+ this._onPropertyChanged.bind(this),
+ ], [
+ // We must additionally listen for Screen events to know when to
+ // rebuild our location map when the set of available windows changes.
+ global.workspaceManager,
+ 'workspace-added',
+ () => this._onWindowsChanged(),
+ ], [
+ global.workspaceManager,
+ 'workspace-removed',
+ () => this._onWindowsChanged(),
+ ], [
+ global.display,
+ 'window-entered-monitor',
+ () => this._onWindowsChanged(),
+ ], [
+ global.display,
+ 'window-left-monitor',
+ () => this._onWindowsChanged(),
+ ]);
+ }
+
+ destroy() {
+ if (this._windowsUpdateIdle) {
+ GLib.source_remove(this._windowsUpdateIdle);
+ delete this._windowsUpdateIdle;
+ }
+ this._cancellable.cancel();
+ this._signalsHandler.destroy();
+ this._windowsByLocation.clear();
+ this._windowsByPath.clear();
+ this._proxy = null;
+ }
+
+ /**
+ * Return an array of windows that are showing a location or
+ * sub-directories of that location.
+ *
+ * @param location
+ */
+ getWindows(location) {
+ if (!location)
+ return [];
+
+ location += location.endsWith('/') ? '' : '/';
+ const windows = [];
+ this._windowsByLocation.forEach((wins, l) => {
+ if (l.startsWith(location))
+ windows.push(...wins);
+ });
+ return [...new Set(windows)];
+ }
+
+ _onPropertyChanged(proxy, changed, _invalidated) {
+ const property = changed.unpack();
+ if (property &&
+ ('OpenWindowsWithLocations' in property))
+ this._updateLocationMap();
+ }
+
+ _updateWindows() {
+ const oldSize = this._windowsByPath.size;
+ const oldPaths = this._windowsByPath.keys();
+ this._windowsByPath = Utils.getWindowsByObjectPath();
+
+ if (oldSize !== this._windowsByPath.size)
+ return true;
+
+ return [...oldPaths].some(path => !this._windowsByPath.has(path));
+ }
+
+ _onWindowsChanged() {
+ if (this._windowsUpdateIdle)
+ return;
+
+ this._windowsUpdateIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ if (this._updateWindows())
+ this._updateLocationMap();
+
+ delete this._windowsUpdateIdle;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ _updateLocationMap() {
+ const properties = this._proxy.get_cached_property_names();
+ if (!properties) {
+ // Nothing to check yet.
+ return;
+ }
+
+ if (properties.includes('OpenWindowsWithLocations'))
+ this._updateFromPaths();
+ }
+
+ _locationMapsEquals(mapA, mapB) {
+ if (mapA.size !== mapB.size)
+ return false;
+
+ const setsEquals = (a, b) => a.size === b.size &&
+ [...a].every(value => b.has(value));
+
+ for (const [key, val] of mapA) {
+ const windowsSet = mapB.get(key);
+ if (!windowsSet || !setsEquals(windowsSet, val))
+ return false;
+ }
+ return true;
+ }
+
+ _updateFromPaths() {
+ const locationsByWindowsPath = this._proxy.OpenWindowsWithLocations;
+
+ const windowsByLocation = new Map();
+ this._signalsHandler.removeWithLabel(Labels.WINDOWS);
+
+ Object.entries(locationsByWindowsPath).forEach(([windowPath, locations]) => {
+ locations.forEach(location => {
+ const win = this._windowsByPath.get(windowPath);
+ const windowGroup = win ? [win] : [];
+
+ win?.foreach_transient(w => windowGroup.push(w) || true);
+
+ windowGroup.forEach(window => {
+ location += location.endsWith('/') ? '' : '/';
+ // Use a set to deduplicate when a window has a
+ // location open in multiple tabs.
+ const windows = windowsByLocation.get(location) || new Set();
+ windows.add(window);
+
+ if (windows.size === 1)
+ windowsByLocation.set(location, windows);
+
+ this._signalsHandler.addWithLabel(Labels.WINDOWS, window,
+ 'unmanaged', () => {
+ const wins = this._windowsByLocation.get(location);
+ wins.delete(window);
+ if (!wins.size)
+ this._windowsByLocation.delete(location);
+ this.emit('windows-changed');
+ });
+ });
+ });
+ });
+
+ if (!this._locationMapsEquals(this._windowsByLocation, windowsByLocation)) {
+ this._windowsByLocation = windowsByLocation;
+ this.emit('windows-changed');
+ }
+ }
+}
+Signals.addSignalMethods(FileManager1Client.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js
new file mode 100644
index 0000000..94bf337
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/imports.js
@@ -0,0 +1,17 @@
+export * as AppIconIndicators from './appIconIndicators.js';
+export * as AppIcons from './appIcons.js';
+export * as AppIconsDecorator from './appIconsDecorator.js';
+export * as AppSpread from './appSpread.js';
+export * as DockDash from './dash.js';
+export * as DBusMenuUtils from './dbusmenuUtils.js';
+export * as DesktopIconsIntegration from './desktopIconsIntegration.js';
+export * as Docking from './docking.js';
+export * as Extension from './extension.js';
+export * as FileManager1API from './fileManager1API.js';
+export * as Intellihide from './intellihide.js';
+export * as LauncherAPI from './launcherAPI.js';
+export * as Locations from './locations.js';
+export * as NotificationsMonitor from './notificationsMonitor.js';
+export * as Theming from './theming.js';
+export * as Utils from './utils.js';
+export * as WindowPreview from './windowPreview.js';
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js
new file mode 100644
index 0000000..9668af2
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/intellihide.js
@@ -0,0 +1,337 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ GLib,
+ Meta,
+ Shell,
+} from './dependencies/gi.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {signals: Signals} = imports;
+
+// A good compromise between reactivity and efficiency; to be tuned.
+const INTELLIHIDE_CHECK_INTERVAL = 100;
+
+const OverlapStatus = Object.freeze({
+ UNDEFINED: -1,
+ FALSE: 0,
+ TRUE: 1,
+});
+
+const IntellihideMode = Object.freeze({
+ ALL_WINDOWS: 0,
+ FOCUS_APPLICATION_WINDOWS: 1,
+ MAXIMIZED_WINDOWS: 2,
+ ALWAYS_ON_TOP: 3,
+});
+
+// List of windows type taken into account. Order is important (keep the original
+// enum order).
+const handledWindowTypes = [
+ Meta.WindowType.NORMAL,
+ Meta.WindowType.DOCK,
+ Meta.WindowType.DIALOG,
+ Meta.WindowType.MODAL_DIALOG,
+ Meta.WindowType.TOOLBAR,
+ Meta.WindowType.MENU,
+ Meta.WindowType.UTILITY,
+ Meta.WindowType.SPLASHSCREEN,
+ Meta.WindowType.DROPDOWN_MENU,
+];
+
+// List of applications, ignore windows of these applications in considering intellihide
+const ignoreApps = ['com.rastersoft.ding', 'com.desktop.ding'];
+
+/**
+ * A rough and ugly implementation of the intellihide behaviour.
+ * Intallihide object: emit 'status-changed' signal when the overlap of windows
+ * with the provided targetBoxClutter.ActorBox changes;
+ */
+export class Intellihide {
+ constructor(monitorIndex) {
+ // Load settings
+ this._monitorIndex = monitorIndex;
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._tracker = Shell.WindowTracker.get_default();
+ this._focusApp = null; // The application whose window is focused.
+ this._topApp = null; // The application whose window is on top on the monitor with the dock.
+
+ this._isEnabled = false;
+ this._status = OverlapStatus.UNDEFINED;
+ this._targetBox = null;
+
+ this._checkOverlapTimeoutContinue = false;
+ this._checkOverlapTimeoutId = 0;
+
+ this._trackedWindows = new Map();
+
+ // Connect global signals
+ this._signalsHandler.add([
+ // Add signals on windows created from now on
+ global.display,
+ 'window-created',
+ this._windowCreated.bind(this),
+ ], [
+ // triggered for instance when the window list order changes,
+ // included when the workspace is switched
+ global.display,
+ 'restacked',
+ this._checkOverlap.bind(this),
+ ], [
+ // when windows are alwasy on top, the focus window can change
+ // without the windows being restacked. Thus monitor window focus change.
+ this._tracker,
+ 'notify::focus-app',
+ this._checkOverlap.bind(this),
+ ], [
+ // update wne monitor changes, for instance in multimonitor when monitor are attached
+ Utils.getMonitorManager(),
+ 'monitors-changed',
+ this._checkOverlap.bind(this),
+ ]);
+ }
+
+ destroy() {
+ // Disconnect global signals
+ this._signalsHandler.destroy();
+
+ // Remove residual windows signals
+ this.disable();
+ }
+
+ enable() {
+ this._isEnabled = true;
+ this._status = OverlapStatus.UNDEFINED;
+ global.get_window_actors().forEach(function (wa) {
+ this._addWindowSignals(wa);
+ }, this);
+ this._doCheckOverlap();
+ }
+
+ disable() {
+ this._isEnabled = false;
+
+ for (const wa of this._trackedWindows.keys())
+ this._removeWindowSignals(wa);
+
+ this._trackedWindows.clear();
+
+ if (this._checkOverlapTimeoutId > 0) {
+ GLib.source_remove(this._checkOverlapTimeoutId);
+ this._checkOverlapTimeoutId = 0;
+ }
+ }
+
+ _windowCreated(display, metaWindow) {
+ this._addWindowSignals(metaWindow.get_compositor_private());
+ this._doCheckOverlap();
+ }
+
+ _addWindowSignals(wa) {
+ if (!this._handledWindow(wa))
+ return;
+ const signalId = wa.connect('notify::allocation', this._checkOverlap.bind(this));
+ this._trackedWindows.set(wa, signalId);
+ wa.connect('destroy', this._removeWindowSignals.bind(this));
+ }
+
+ _removeWindowSignals(wa) {
+ if (this._trackedWindows.get(wa)) {
+ wa.disconnect(this._trackedWindows.get(wa));
+ this._trackedWindows.delete(wa);
+ }
+ }
+
+ updateTargetBox(box) {
+ this._targetBox = box;
+ this._checkOverlap();
+ }
+
+ forceUpdate() {
+ this._status = OverlapStatus.UNDEFINED;
+ this._doCheckOverlap();
+ }
+
+ getOverlapStatus() {
+ return this._status === OverlapStatus.TRUE;
+ }
+
+ _checkOverlap() {
+ if (!this._isEnabled || !this._targetBox)
+ return;
+
+ /* Limit the number of calls to the doCheckOverlap function */
+ if (this._checkOverlapTimeoutId) {
+ this._checkOverlapTimeoutContinue = true;
+ return;
+ }
+
+ this._doCheckOverlap();
+
+ this._checkOverlapTimeoutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, INTELLIHIDE_CHECK_INTERVAL, () => {
+ this._doCheckOverlap();
+ if (this._checkOverlapTimeoutContinue) {
+ this._checkOverlapTimeoutContinue = false;
+ return GLib.SOURCE_CONTINUE;
+ } else {
+ this._checkOverlapTimeoutId = 0;
+ return GLib.SOURCE_REMOVE;
+ }
+ });
+ }
+
+ _doCheckOverlap() {
+ if (!this._isEnabled || !this._targetBox)
+ return;
+
+ let overlaps = OverlapStatus.FALSE;
+ let windows = global.get_window_actors().filter(wa => this._handledWindow(wa));
+
+ if (windows.length > 0) {
+ /*
+ * Get the top window on the monitor where the dock is placed.
+ * The idea is that we dont want to overlap with the windows of the topmost application,
+ * event is it's not the focused app -- for instance because in multimonitor the user
+ * select a window in the secondary monitor.
+ */
+
+ let topWindow = null;
+ for (let i = windows.length - 1; i >= 0; i--) {
+ const metaWin = windows[i].get_meta_window();
+ if (metaWin.get_monitor() === this._monitorIndex) {
+ topWindow = metaWin;
+ break;
+ }
+ }
+
+ if (topWindow) {
+ this._topApp = this._tracker.get_window_app(topWindow);
+ // If there isn't a focused app, use that of the window on top
+ this._focusApp = this._tracker.focus_app || this._topApp;
+
+ windows = windows.filter(this._intellihideFilterInteresting, this);
+
+ for (let i = 0; i < windows.length; i++) {
+ const win = windows[i].get_meta_window();
+
+ if (win) {
+ const rect = win.get_frame_rect();
+
+ const test = (rect.x < this._targetBox.x2) &&
+ (rect.x + rect.width >= this._targetBox.x1) &&
+ (rect.y < this._targetBox.y2) &&
+ (rect.y + rect.height >= this._targetBox.y1);
+
+ if (test) {
+ overlaps = OverlapStatus.TRUE;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (this._status !== overlaps) {
+ this._status = overlaps;
+ this.emit('status-changed', this._status);
+ }
+ }
+
+ // Filter interesting windows to be considered for intellihide.
+ // Consider all windows visible on the current workspace.
+ // Optionally skip windows of other applications
+ _intellihideFilterInteresting(wa) {
+ const metaWin = wa.get_meta_window();
+ const currentWorkspace = global.workspace_manager.get_active_workspace_index();
+ const workspace = metaWin.get_workspace();
+ const workspaceIndex = workspace.index();
+
+ // Depending on the intellihide mode, exclude non-relevent windows
+ switch (Docking.DockManager.settings.intellihideMode) {
+ case IntellihideMode.ALL_WINDOWS:
+ // Do nothing
+ break;
+
+ case IntellihideMode.FOCUS_APPLICATION_WINDOWS:
+ // Skip windows of other apps
+ if (this._focusApp) {
+ // The DropDownTerminal extension is not an application per se
+ // so we match its window by wm class instead
+ if (metaWin.get_wm_class() === 'DropDownTerminalWindow')
+ return true;
+
+ const currentApp = this._tracker.get_window_app(metaWin);
+ const focusWindow = global.display.get_focus_window();
+
+ // Consider half maximized windows side by side
+ // and windows which are alwayson top
+ if (currentApp !== this._focusApp && currentApp !== this._topApp &&
+ !((focusWindow && focusWindow.maximized_vertically &&
+ !focusWindow.maximized_horizontally) &&
+ (metaWin.maximized_vertically && !metaWin.maximized_horizontally) &&
+ metaWin.get_monitor() === focusWindow.get_monitor()) &&
+ !metaWin.is_above())
+ return false;
+ }
+ break;
+
+ case IntellihideMode.MAXIMIZED_WINDOWS:
+ // Skip unmaximized windows
+ if (!metaWin.maximized_vertically && !metaWin.maximized_horizontally && !metaWin.fullscreen)
+ return false;
+ break;
+
+ case IntellihideMode.ALWAYS_ON_TOP:
+ // Always on top, except for fullscreen windows
+ if (this._focusApp) {
+ const {focusWindow} = global.display;
+ if (!focusWindow?.fullscreen)
+ return false;
+ }
+ break;
+ }
+
+ if (workspaceIndex === currentWorkspace && metaWin.showing_on_its_workspace())
+ return true;
+ else
+ return false;
+ }
+
+ // Filter windows by type
+ // inspired by Opacify@gnome-shell.localdomain.pl
+ _handledWindow(wa) {
+ const metaWindow = wa.get_meta_window();
+
+ if (!metaWindow)
+ return false;
+
+ // The DING extension desktop window needs to be excluded
+ // so we match its window by application id and window property.
+ const wmApp = metaWindow.get_gtk_application_id();
+ if (ignoreApps.includes(wmApp) && metaWindow.is_skip_taskbar())
+ return false;
+
+ // The DropDownTerminal extension uses the POPUP_MENU window type hint
+ // so we match its window by wm class instead
+ if (metaWindow.get_wm_class() === 'DropDownTerminalWindow')
+ return true;
+
+ const wtype = metaWindow.get_window_type();
+ for (let i = 0; i < handledWindowTypes.length; i++) {
+ const hwtype = handledWindowTypes[i];
+ if (hwtype === wtype)
+ return true;
+ else if (hwtype > wtype)
+ return false;
+ }
+ return false;
+ }
+}
+
+Signals.addSignalMethods(Intellihide.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js
new file mode 100644
index 0000000..8778d32
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/launcherAPI.js
@@ -0,0 +1,287 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {Gio} from './dependencies/gi.js';
+import {DBusMenuUtils} from './imports.js';
+
+const DBusMenu = await DBusMenuUtils.haveDBusMenu();
+
+export class LauncherEntryRemoteModel {
+ constructor() {
+ this._entrySourceStacks = new Map();
+ this._remoteMaps = new Map();
+
+ this._launcher_entry_dbus_signal_id =
+ Gio.DBus.session.signal_subscribe(null, // sender
+ 'com.canonical.Unity.LauncherEntry', // iface
+ 'Update', // member
+ null, // path
+ null, // arg0
+ Gio.DBusSignalFlags.NONE,
+ (_connection, senderName, _objectPath, _interfaceName, _signalName, parameters) =>
+ this._onUpdate(senderName, ...parameters.deep_unpack()));
+
+ this._dbus_name_owner_changed_signal_id =
+ Gio.DBus.session.signal_subscribe('org.freedesktop.DBus', // sender
+ 'org.freedesktop.DBus', // interface
+ 'NameOwnerChanged', // member
+ '/org/freedesktop/DBus', // path
+ null, // arg0
+ Gio.DBusSignalFlags.NONE,
+ (connection, _senderName, _objectPath, _interfaceName, _signalName, parameters) =>
+ this._onDBusNameChange(...parameters.deep_unpack().slice(1)));
+
+ this._acquireUnityDBus();
+ }
+
+ destroy() {
+ if (this._launcher_entry_dbus_signal_id)
+ Gio.DBus.session.signal_unsubscribe(this._launcher_entry_dbus_signal_id);
+
+
+ if (this._dbus_name_owner_changed_signal_id)
+ Gio.DBus.session.signal_unsubscribe(this._dbus_name_owner_changed_signal_id);
+
+
+ this._releaseUnityDBus();
+ }
+
+ _lookupStackById(appId) {
+ let sourceStack = this._entrySourceStacks.get(appId);
+ if (!sourceStack) {
+ sourceStack = new PropertySourceStack(new LauncherEntry(),
+ launcherEntryDefaults);
+ this._entrySourceStacks.set(appId, sourceStack);
+ }
+
+ return sourceStack;
+ }
+
+ lookupById(appId) {
+ return this._lookupStackById(appId).target;
+ }
+
+ _acquireUnityDBus() {
+ if (!this._unity_bus_id) {
+ this._unity_bus_id = Gio.DBus.session.own_name('com.canonical.Unity',
+ Gio.BusNameOwnerFlags.ALLOW_REPLACEMENT | Gio.BusNameOwnerFlags.REPLACE,
+ null, () => (this._unity_bus_id = 0));
+ }
+ }
+
+ _releaseUnityDBus() {
+ if (this._unity_bus_id) {
+ Gio.DBus.session.unown_name(this._unity_bus_id);
+ this._unity_bus_id = 0;
+ }
+ }
+
+ _onDBusNameChange(before, after) {
+ if (!before || !this._remoteMaps.size)
+ return;
+
+ const remoteMap = this._remoteMaps.get(before);
+ if (!remoteMap)
+ return;
+
+ this._remoteMaps.delete(before);
+ if (after && !this._remoteMaps.has(after)) {
+ this._remoteMaps.set(after, remoteMap);
+ } else {
+ for (const [appId, remote] of remoteMap) {
+ const sourceStack = this._entrySourceStacks.get(appId);
+ const changed = sourceStack.remove(remote);
+ if (changed)
+ sourceStack.target._emitChangedEvents(changed);
+ }
+ }
+ }
+
+ _onUpdate(senderName, appUri, properties) {
+ if (!senderName)
+ return;
+
+
+ const appId = appUri.replace(/(^\w+:|^)\/\//, '');
+ if (!appId)
+ return;
+
+
+ let remoteMap = this._remoteMaps.get(senderName);
+ if (!remoteMap)
+ this._remoteMaps.set(senderName, remoteMap = new Map());
+
+ let remote = remoteMap.get(appId);
+ if (!remote)
+ remoteMap.set(appId, remote = Object.assign({}, launcherEntryDefaults));
+
+ for (const name in properties) {
+ if (name === 'quicklist' && DBusMenu) {
+ const quicklistPath = properties[name].unpack();
+ if (quicklistPath &&
+ (!remote._quicklistMenuClient ||
+ remote._quicklistMenuClient.dbus_object !== quicklistPath)) {
+ remote.quicklist = null;
+ let menuClient = remote._quicklistMenuClient;
+ if (menuClient) {
+ menuClient.dbus_object = quicklistPath;
+ } else {
+ // This property should not be enumerable
+ Object.defineProperty(remote, '_quicklistMenuClient', {
+ writable: true,
+ value: menuClient = new DBusMenu.Client({
+ dbus_name: senderName,
+ dbus_object: quicklistPath,
+ }),
+ });
+ }
+ const handler = () => {
+ const root = menuClient.get_root();
+ if (remote.quicklist !== root) {
+ remote.quicklist = root;
+ if (sourceStack.isTop(remote)) {
+ sourceStack.target.quicklist = root;
+ sourceStack.target._emitChangedEvents(['quicklist']);
+ }
+ }
+ };
+ menuClient.connect(DBusMenu.CLIENT_SIGNAL_ROOT_CHANGED, handler);
+ }
+ } else {
+ remote[name] = properties[name].unpack();
+ }
+ }
+
+ const sourceStack = this._lookupStackById(appId);
+ sourceStack.target._emitChangedEvents(sourceStack.update(remote));
+ }
+}
+
+const launcherEntryDefaults = Object.freeze({
+ count: 0,
+ progress: 0,
+ urgent: false,
+ updating: false,
+ quicklist: null,
+ 'count-visible': false,
+ 'progress-visible': false,
+});
+
+const LauncherEntry = class DashToDockLauncherEntry {
+ constructor() {
+ this._connections = new Map();
+ this._handlers = new Map();
+ this._nextId = 0;
+ }
+
+ connect(eventNames, callback) {
+ if (typeof eventNames === 'string')
+ eventNames = [eventNames];
+
+ callback(this, this);
+ const id = this._nextId++;
+ const handler = {id, callback};
+ eventNames.forEach(name => {
+ let handlerList = this._handlers.get(name);
+ if (!handlerList)
+ this._handlers.set(name, handlerList = []);
+
+ handlerList.push(handler);
+ });
+ this._connections.set(id, eventNames);
+ return id;
+ }
+
+ disconnect(id) {
+ const eventNames = this._connections.get(id);
+ if (!eventNames)
+ return;
+
+ this._connections.delete(id);
+ eventNames.forEach(name => {
+ const handlerList = this._handlers.get(name);
+ if (handlerList) {
+ for (let i = 0, iMax = handlerList.length; i < iMax; i++) {
+ if (handlerList[i].id === id) {
+ handlerList.splice(i, 1);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ _emitChangedEvents(propertyNames) {
+ const handlers = new Set();
+ propertyNames.forEach(name => {
+ const handlerList = this._handlers.get(`${name}-changed`);
+ if (handlerList) {
+ for (let i = 0, iMax = handlerList.length; i < iMax; i++)
+ handlers.add(handlerList[i]);
+ }
+ });
+ Array.from(handlers).sort((x, y) => x.id - y.id).forEach(handler => handler.callback(this, this));
+ }
+};
+
+for (const [name, defaultValue] of Object.entries(launcherEntryDefaults)) {
+ const jsName = name.replaceAll('-', '_');
+ LauncherEntry.prototype[jsName] = defaultValue;
+ if (jsName !== name) {
+ Object.defineProperty(LauncherEntry.prototype, name, {
+ get() {
+ return this[jsName];
+ },
+ set(value) {
+ this[jsName] = value;
+ },
+ });
+ }
+}
+
+const PropertySourceStack = class DashToDockPropertySourceStack {
+ constructor(target, bottom) {
+ this.target = target;
+ this._bottom = bottom;
+ this._stack = [];
+ }
+
+ isTop(source) {
+ return this._stack.length > 0 && this._stack[this._stack.length - 1] === source;
+ }
+
+ update(source) {
+ if (!this.isTop(source)) {
+ this.remove(source);
+ this._stack.push(source);
+ }
+ return this._assignFrom(source);
+ }
+
+ remove(source) {
+ const stack = this._stack;
+ const top = stack[stack.length - 1];
+ if (top === source) {
+ stack.length--;
+ return this._assignFrom(stack.length > 0 ? stack[stack.length - 1] : this._bottom);
+ }
+ for (let i = 0, iMax = stack.length; i < iMax; i++) {
+ if (stack[i] === source) {
+ stack.splice(i, 1);
+ break;
+ }
+ }
+
+ return null;
+ }
+
+ _assignFrom(source) {
+ const changedProperties = [];
+ for (const name in source) {
+ if (this.target[name] !== source[name]) {
+ this.target[name] = source[name];
+ changedProperties.push(name);
+ }
+ }
+ return changedProperties;
+ }
+};
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..ffb5786
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ar/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..ba9a252
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/be/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..87e8892
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/bg/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..7b092bf
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/cs/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..0f7dc82
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/de/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..c64aa83
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/el/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d143515
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/es/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..044406f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/eu/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..9ca1002
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/fr/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..110db82
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/gl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..b8a17f9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/hu/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..3ea32d5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/id/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..56361a5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/it/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..a2ba4ef
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ja/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..47042cf
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ka/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..f5f92c0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/kk/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d9c5308
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ko/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..e6d146a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/lv/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..db5ad46
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nb/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..e926ee0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/nl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..a48230d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..9733e40
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..de2a4e6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/pt_BR/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..a3c2b23
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ro/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..69ed645
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/ru/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..9348b7e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sk/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..2f30b3a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sl/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..b5e6fb6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..8dfc69e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sr@latin/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d6cdf84
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/sv/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..6a12cfb
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/tr/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..d71acaf
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/uk_UA/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..b701af8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_CN/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mo b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mo
new file mode 100644
index 0000000..1bd8e0a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locale/zh_TW/LC_MESSAGES/dashtodock.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js
new file mode 100644
index 0000000..534466c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locations.js
@@ -0,0 +1,1535 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Gio,
+ GioUnix,
+ GLib,
+ GObject,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {ShellMountOperation} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+import {Extension} from './dependencies/shell/extensions/extension.js';
+
+// Use __ () and N__() for the extension gettext domain, and reuse
+// the shell domain with the default _() and N_()
+const {gettext: __} = Extension;
+
+const {signals: Signals} = imports;
+
+const FALLBACK_REMOVABLE_MEDIA_ICON = 'drive-removable-media';
+const FALLBACK_TRASH_ICON = 'user-trash';
+const FILE_MANAGER_DESKTOP_APP_ID = 'org.gnome.Nautilus.desktop';
+const ATTRIBUTE_METADATA_CUSTOM_ICON = 'metadata::custom-icon';
+const TRASH_URI = 'trash://';
+const UPDATE_TRASH_DELAY = 1000;
+const LAUNCH_HANDLER_MAX_WAIT = 200;
+
+const NautilusFileOperations2Interface = '<node>\
+ <interface name="org.gnome.Nautilus.FileOperations2">\
+ <method name="EmptyTrash">\
+ <arg type="b" name="ask_confirmation" direction="in"/>\
+ <arg type="a{sv}" name="platform_data" direction="in"/>\
+ </method>\
+ </interface>\
+</node>';
+
+const NautilusFileOperations2ProxyInterface =
+ Gio.DBusProxy.makeProxyWrapper(NautilusFileOperations2Interface);
+
+const Labels = Object.freeze({
+ LOCATION_WINDOWS: Symbol('location-windows'),
+ WINDOWS_CHANGED: Symbol('windows-changed'),
+});
+
+const GJS_SUPPORTS_FILE_IFACE_PROMISES = imports.system.version >= 17101;
+
+if (GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(Gio.File.prototype, 'query_info_async');
+ Gio._promisify(Gio.File.prototype, 'query_default_handler_async');
+}
+
+
+/**
+ *
+ */
+function makeNautilusFileOperationsProxy() {
+ const proxy = new NautilusFileOperations2ProxyInterface(
+ Gio.DBus.session,
+ 'org.gnome.Nautilus',
+ '/org/gnome/Nautilus/FileOperations2', (_p, error) => {
+ if (error)
+ logError(error, 'Error connecting to Nautilus');
+ }
+ );
+
+ proxy.platformData = params => {
+ const defaultParams = {
+ parentHandle: '',
+ timestamp: global.get_current_time(),
+ windowPosition: 'center',
+ };
+ const {parentHandle, timestamp, windowPosition} = {
+ ...defaultParams,
+ ...params,
+ };
+
+ return {
+ 'parent-handle': new GLib.Variant('s', parentHandle),
+ 'timestamp': new GLib.Variant('u', timestamp),
+ 'window-position': new GLib.Variant('s', windowPosition),
+ };
+ };
+
+ return proxy;
+}
+
+export const LocationAppInfo = GObject.registerClass({
+ Implements: [Gio.AppInfo],
+ Properties: {
+ 'location': GObject.ParamSpec.object(
+ 'location', 'location', 'location',
+ GObject.ParamFlags.READWRITE,
+ Gio.File.$gtype),
+ 'name': GObject.ParamSpec.string(
+ 'name', 'name', 'name',
+ GObject.ParamFlags.READWRITE,
+ null),
+ 'icon': GObject.ParamSpec.object(
+ 'icon', 'icon', 'icon',
+ GObject.ParamFlags.READWRITE,
+ Gio.Icon.$gtype),
+ 'cancellable': GObject.ParamSpec.object(
+ 'cancellable', 'cancellable', 'cancellable',
+ GObject.ParamFlags.READWRITE,
+ Gio.Cancellable.$gtype),
+ },
+}, class LocationAppInfo extends GioUnix.DesktopAppInfo {
+ static get GJS_BINARY_PATH() {
+ if (!this._gjsBinaryPath)
+ this._gjsBinaryPath = GLib.find_program_in_path('gjs');
+
+ return this._gjsBinaryPath;
+ }
+
+ list_actions() {
+ return [];
+ }
+
+ get_action_name() {
+ return null;
+ }
+
+ get_boolean() {
+ return false;
+ }
+
+ vfunc_dup() {
+ return new LocationAppInfo({
+ location: this.location,
+ name: this.name,
+ icon: this.icon,
+ cancellable: this.cancellable,
+ });
+ }
+
+ vfunc_equal(other) {
+ if (this.location)
+ return this.location.equal(other?.location);
+
+ return this.name === other.name &&
+ (this.icon ? this.icon.equal(other?.icon) : !other?.icon);
+ }
+
+ vfunc_get_id() {
+ return 'location:%s'.format(this.location?.get_uri());
+ }
+
+ vfunc_get_name() {
+ return this.name;
+ }
+
+ vfunc_get_description() {
+ return null;
+ }
+
+ vfunc_get_executable() {
+ return null;
+ }
+
+ vfunc_get_icon() {
+ return this.icon;
+ }
+
+ vfunc_launch(files, context) {
+ if (files?.length) {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Launching with files not supported');
+ }
+
+ return this.getHandlerApp().launch_uris([this.location.get_uri()], context);
+ }
+
+ vfunc_supports_uris() {
+ return false;
+ }
+
+ vfunc_supports_files() {
+ return false;
+ }
+
+ vfunc_launch_uris(uris, context) {
+ return this.launch(uris, context);
+ }
+
+ vfunc_should_show() {
+ return true;
+ }
+
+ vfunc_set_as_default_for_type() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_set_as_default_for_extension() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_add_supports_type() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_can_remove_supports_type() {
+ return false;
+ }
+
+ vfunc_remove_supports_type() {
+ return false;
+ }
+
+ vfunc_can_delete() {
+ return false;
+ }
+
+ vfunc_do_delete() {
+ return false;
+ }
+
+ vfunc_get_commandline() {
+ try {
+ return this.getHandlerApp().get_commandline();
+ } catch {
+ return this._getFallbackCommandLine();
+ }
+ }
+
+ vfunc_get_display_name() {
+ return this.name;
+ }
+
+ vfunc_set_as_last_used_for_type() {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_SUPPORTED, 'Not supported');
+ }
+
+ vfunc_get_supported_types() {
+ return [];
+ }
+
+ _getFallbackCommandLine() {
+ return `gio open ${this.location?.get_uri()}`;
+ }
+
+ async _queryLocationIcons(params) {
+ const icons = {standard: null, custom: null};
+ if (!this.location)
+ return icons;
+
+ const cancellable = params.cancellable ?? this.cancellable;
+ const iconsQuery = [];
+ if (params?.standard)
+ iconsQuery.push(Gio.FILE_ATTRIBUTE_STANDARD_ICON);
+
+ if (params?.custom)
+ iconsQuery.push(ATTRIBUTE_METADATA_CUSTOM_ICON);
+
+ if (!iconsQuery.length)
+ throw new Error('Invalid Query Location Icons parameters');
+
+ let info;
+ try {
+ if (!GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(this.location.constructor.prototype,
+ 'query_info_async', 'query_info_finish');
+ }
+ info = await this.location.query_info_async(
+ iconsQuery.join(','),
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_LOW, cancellable);
+ if (info.has_attribute(Gio.FILE_ATTRIBUTE_STANDARD_ICON))
+ icons.standard = info.get_icon();
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND) ||
+ e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return icons;
+ throw e;
+ }
+
+ const customIcon = info.get_attribute_string(ATTRIBUTE_METADATA_CUSTOM_ICON);
+ if (customIcon) {
+ const customIconFile = GLib.uri_parse_scheme(customIcon)
+ ? Gio.File.new_for_uri(customIcon) : Gio.File.new_for_path(customIcon);
+ const iconFileInfo = await customIconFile.query_info_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_TYPE,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_LOW, cancellable);
+
+ if (iconFileInfo.get_file_type() === Gio.FileType.REGULAR)
+ icons.custom = Gio.FileIcon.new(customIconFile);
+ }
+
+ return icons;
+ }
+
+ async _updateLocationIcon(params = {standard: true, custom: true}) {
+ const cancellable = new Utils.CancellableChild(this.cancellable);
+
+ try {
+ this._updateIconCancellable?.cancel();
+ this._updateIconCancellable = cancellable;
+
+ const icons = await this._queryLocationIcons({cancellable, ...params});
+ const icon = icons.custom ?? icons.standard;
+
+ if (icon && !icon.equal(this.icon))
+ this.icon = icon;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'Impossible to update icon for %s'.format(this.get_id()));
+ } finally {
+ cancellable.cancel();
+ if (this._updateIconCancellable === cancellable)
+ delete this._updateIconCancellable;
+ }
+ }
+
+ async _getHandlerAppAsync(cancellable) {
+ if (!this.location)
+ return null;
+
+ try {
+ if (!GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(this.location.constructor.prototype,
+ 'query_default_handler_async',
+ 'query_default_handler_finish');
+ }
+
+ return await this.location.query_default_handler_async(
+ GLib.PRIORITY_DEFAULT, cancellable);
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return getFileManagerApp()?.appInfo;
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e, 'Impossible to find an URI handler for %s'.format(
+ this.get_id()));
+ }
+
+ throw e;
+ }
+ }
+
+ _getHandlerAppFromWorker(cancellable) {
+ const locationsWorker = GLib.build_filenamev([
+ Docking.DockManager.extension.path,
+ 'locationsWorker.js',
+ ]);
+ const locationsWorkerArgs = [LocationAppInfo.GJS_BINARY_PATH, '-m',
+ locationsWorker, 'handler', this.location.get_uri(),
+ '--timeout', `${LAUNCH_HANDLER_MAX_WAIT}`];
+ const subProcess = Gio.Subprocess.new(locationsWorkerArgs,
+ Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE);
+
+ try {
+ const [, stdOut, stdErr] = subProcess.communicate(null, cancellable);
+ subProcess.wait(cancellable);
+ const errorCode = subProcess.get_exit_status();
+ const textDecoder = new TextDecoder();
+
+ if (errorCode) {
+ const errorLines = textDecoder.decode(stdErr.toArray()).split('\n');
+ const error = new GLib.Error(Gio.IOErrorEnum,
+ errorCode === GLib.MAXUINT8 ? 0 : errorCode, errorLines[0]);
+ error.stack = `${errorLines.slice(3).join('\n')}${error.stack}`;
+ throw error;
+ }
+
+ const desktopId = textDecoder.decode(stdOut.toArray()).trim();
+ const handlerApp = Shell.AppSystem.get_default().lookup_app(desktopId)?.appInfo;
+ return handlerApp;
+ } finally {
+ subProcess.force_exit();
+ }
+ }
+
+ getHandlerApp() {
+ if (this._handlerApp)
+ return this._handlerApp;
+
+ if (!this.location)
+ return null;
+
+ const cancellable = new Utils.CancellableChild(this.cancellable);
+
+ try {
+ if (LocationAppInfo.GJS_BINARY_PATH)
+ this._handlerApp = this._getHandlerAppFromWorker(cancellable);
+ else
+ this._handlerApp = this.location.query_default_handler(cancellable);
+
+ if (!this._handlerApp) {
+ throw new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.NOT_FOUND, `Handler for ${this.location} not found`);
+ }
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return getFileManagerApp()?.appInfo;
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e, 'Impossible to find an URI handler for %s'.format(
+ this.get_id()));
+ }
+
+ throw e;
+ }
+
+ return this._handlerApp;
+ }
+
+ destroy() {
+ this.location = null;
+ this.icon = null;
+ this.name = null;
+ this._handlerApp = null;
+ this.cancellable?.cancel();
+ }
+});
+
+const RemovableAction = Object.freeze({
+ MOUNT: 'mount',
+ UNMOUNT: 'unmount',
+ EJECT: 'eject',
+});
+
+const MountableVolumeAppInfo = GObject.registerClass({
+ Implements: [Gio.AppInfo],
+ Properties: {
+ 'volume': GObject.ParamSpec.object(
+ 'volume', 'volume', 'volume',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Gio.Volume.$gtype),
+ 'mount': GObject.ParamSpec.object(
+ 'mount', 'mount', 'mount',
+ GObject.ParamFlags.READWRITE,
+ Gio.Mount.$gtype),
+ 'busy': GObject.ParamSpec.boolean(
+ 'busy', 'busy', 'busy',
+ GObject.ParamFlags.READWRITE,
+ false),
+ },
+},
+class MountableVolumeAppInfo extends LocationAppInfo {
+ _init(volume, cancellable = null) {
+ super._init({
+ volume,
+ cancellable,
+ });
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ const updateAndMonitor = () => {
+ this._update();
+ this._monitorChanges();
+ };
+ updateAndMonitor();
+ this._mountChanged = this.connect('notify::mount', updateAndMonitor);
+
+ if (!this.mount && this.volume.get_identifier('class') === 'network') {
+ // For some devices the mount point isn't advertised promptly
+ // even if it's already existing, and there's no signaling about
+ this._lazyUpdater = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, () => {
+ this._update();
+ delete this._lazyUpdater;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+
+ get busy() {
+ return !!this._currentAction;
+ }
+
+ get currentAction() {
+ return this._currentAction;
+ }
+
+ destroy() {
+ if (this._lazyUpdater) {
+ GLib.source_remove(this._lazyUpdater);
+ delete this._lazyUpdater;
+ }
+ this.disconnect(this._mountChanged);
+ this.mount = null;
+ this._signalsHandler.destroy();
+
+ super.destroy();
+ }
+
+ vfunc_dup() {
+ return new MountableVolumeAppInfo({
+ volume: this.volume,
+ cancellable: this.cancellable,
+ });
+ }
+
+ vfunc_get_id() {
+ const uuid = this.mount?.get_uuid() ?? this.volume.get_uuid();
+ return uuid ? 'mountable-volume:%s'.format(uuid) : super.vfunc_get_id();
+ }
+
+ vfunc_equal(other) {
+ if (this.volume === other?.volume && this.mount === other?.mount)
+ return true;
+
+ return this.get_id() === other?.get_id();
+ }
+
+ list_actions() {
+ const actions = [];
+ const {mount} = this;
+
+ if (mount) {
+ if (this.mount.can_unmount())
+ actions.push(RemovableAction.UNMOUNT);
+ if (this.mount.can_eject())
+ actions.push(RemovableAction.EJECT);
+
+ return actions;
+ }
+
+ if (this.volume.can_mount())
+ actions.push(RemovableAction.MOUNT);
+ if (this.volume.can_eject())
+ actions.push(RemovableAction.EJECT);
+
+ return actions;
+ }
+
+ get_action_name(action) {
+ switch (action) {
+ case RemovableAction.MOUNT:
+ return __('Mount');
+ case RemovableAction.UNMOUNT:
+ return __('Unmount');
+ case RemovableAction.EJECT:
+ return __('Eject');
+ default:
+ return null;
+ }
+ }
+
+ vfunc_launch(files, context) {
+ if (this.mount || files?.length)
+ return super.vfunc_launch(files, context);
+
+ this.mountAndLaunch(files, context);
+ return true;
+ }
+
+ _update() {
+ this.mount = this.volume.get_mount();
+
+ const removable = this.mount ?? this.volume;
+ this.name = removable.get_name();
+ this.icon = removable.get_icon();
+
+ this.location = this.mount?.get_default_location() ??
+ this.volume.get_activation_root();
+
+ this._updateLocationIcon({custom: true});
+ }
+
+ _monitorChanges() {
+ this._signalsHandler.destroy();
+
+ const removable = this.mount ?? this.volume;
+ this._signalsHandler.add(removable, 'changed', () => this._update());
+
+ if (this.mount) {
+ this._signalsHandler.add(this.mount, 'pre-unmount', () => this._update());
+ this._signalsHandler.add(this.mount, 'unmounted', () => this._update());
+ }
+ }
+
+ async mountAndLaunch(files, context) {
+ if (this.mount)
+ return super.vfunc_launch(files, context);
+
+ try {
+ await this.launchAction(RemovableAction.MOUNT);
+ if (!this.mount) {
+ throw new Error('No mounted location to open for %s'.format(
+ this.get_id()));
+ }
+
+ return super.vfunc_launch(files, context);
+ } catch (e) {
+ logError(e, 'Mount and launch %s'.format(this.get_id()));
+ return false;
+ }
+ }
+
+ _notifyActionError(action, message) {
+ switch (action) {
+ case RemovableAction.MOUNT:
+ global.notify_error(__('Failed to mount “%s”'.format(
+ this.get_name())), message);
+ break;
+
+ case RemovableAction.UNMOUNT:
+ global.notify_error(__('Failed to unmount “%s”'.format(
+ this.get_name())), message);
+ break;
+
+ case RemovableAction.EJECT:
+ global.notify_error(__('Failed to eject “%s”'.format(
+ this.get_name())), message);
+ break;
+ }
+ }
+
+ async launchAction(action) {
+ if (!this.list_actions().includes(action))
+ throw new Error('Action %s is not supported by %s', action, this);
+
+ switch (this._currentAction) {
+ case RemovableAction.MOUNT:
+ this._notifyActionError(action,
+ __('Mount operation already in progress'));
+ break;
+
+ case RemovableAction.UNMOUNT:
+ this._notifyActionError(action,
+ __('Unmount operation already in progress'));
+ break;
+
+ case RemovableAction.EJECT:
+ this._notifyActionError(action,
+ __('Eject operation already in progress'));
+ break;
+
+ default:
+ if (this._currentAction) {
+ throw new Error('Another action %s is being performed in %s'.format(
+ this._currentAction, this));
+ }
+ }
+
+ this._currentAction = action;
+ this.notify('busy');
+ const removable = this.mount ?? this.volume;
+ const operation = new ShellMountOperation.ShellMountOperation(removable);
+ try {
+ switch (action) {
+ case RemovableAction.MOUNT:
+ await this.volume.mount(Gio.MountMountFlags.NONE, operation.mountOp,
+ this.cancellable);
+ return true;
+
+ case RemovableAction.UNMOUNT:
+ await this.mount.unmount_with_operation(Gio.MountUnmountFlags.FORCE,
+ operation.mountOp, this.cancellable);
+ return true;
+
+ case RemovableAction.EJECT:
+ await removable.eject_with_operation(Gio.MountUnmountFlags.FORCE,
+ operation.mountOp, this.cancellable);
+ return true;
+
+ default:
+ logError(new Error(), 'No action %s on removable %s'.format(action,
+ removable.get_name()));
+ return false;
+ }
+ } catch (e) {
+ if (action === RemovableAction.MOUNT &&
+ e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.ALREADY_MOUNTED))
+ return true;
+ else if (action === RemovableAction.UNMOUNT &&
+ e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_MOUNTED))
+ return true;
+
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED))
+ this._notifyActionError(action, e.message);
+
+ if (action === RemovableAction.MOUNT && this._isEncryptedMountError(e)) {
+ delete this._currentAction;
+ operation.close();
+ return this.launchAction(action);
+ }
+
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
+ logError(e, 'Impossible to %s removable %s'.format(action,
+ removable.get_name()));
+ }
+
+ return false;
+ } finally {
+ delete this._currentAction;
+ this.notify('busy');
+ this._update();
+ operation.close();
+ }
+ }
+
+ _isEncryptedMountError(error) {
+ // FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
+ // backend, see https://bugs.freedesktop.org/show_bug.cgi?id=51271
+
+ if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED))
+ return false;
+
+ // cryptsetup
+ if (error.message.includes('No key available with this passphrase'))
+ return true;
+
+ // udisks (no password)
+ if (error.message.includes('No key available to unlock device'))
+ return true;
+
+ // libblockdev wrong password opening LUKS device
+ if (error.message.includes('Failed to activate device: Incorrect passphrase'))
+ return true;
+
+ // cryptsetup returns EINVAL in many cases, including wrong TCRYPT password/parameters
+ if (error.message.includes('Failed to load device\'s parameters: Invalid argument') ||
+ error.message.includes(`Failed to load device's parameters: ${GLib.strerror(22 /* EINVAL */)}`))
+ return true;
+
+ // cryptsetup returns EPERM when the TCRYPT header can't be decrypted
+ // with the provided password/parameters.
+ if (error.message.includes('Failed to load device\'s parameters: Operation not permitted') ||
+ error.message.includes(`Failed to load device's parameters: ${GLib.strerror(1 /* EPERM */)}`))
+ return true;
+
+ return false;
+ }
+});
+
+const TrashAppInfo = GObject.registerClass({
+ Implements: [Gio.AppInfo],
+ Properties: {
+ 'empty': GObject.ParamSpec.boolean(
+ 'empty', 'empty', 'empty',
+ GObject.ParamFlags.READWRITE,
+ true),
+ },
+},
+class TrashAppInfo extends LocationAppInfo {
+ static initPromises(file) {
+ if (TrashAppInfo._promisified)
+ return;
+
+ const trashProto = file.constructor.prototype;
+ Gio._promisify(Gio.FileEnumerator.prototype, 'close_async', 'close_finish');
+ Gio._promisify(Gio.FileEnumerator.prototype, 'next_files_async', 'next_files_finish');
+ Gio._promisify(trashProto, 'enumerate_children_async', 'enumerate_children_finish');
+ Gio._promisify(trashProto, 'query_info_async', 'query_info_finish');
+ TrashAppInfo._promisified = true;
+ }
+
+ _init(cancellable = null) {
+ super._init({
+ location: Gio.file_new_for_uri(TRASH_URI),
+ name: __('Trash'),
+ icon: Gio.ThemedIcon.new(FALLBACK_TRASH_ICON),
+ cancellable,
+ });
+ TrashAppInfo.initPromises(this.location);
+
+ try {
+ this._monitor = this.location.monitor_directory(0, this.cancellable);
+ this._schedUpdateId = 0;
+ this._monitorChangedId = this._monitor.connect('changed', () =>
+ this._onTrashChange());
+ } catch (e) {
+ if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ return;
+ logError(e, 'Impossible to monitor trash');
+ }
+ this._updateTrash();
+
+ this.connect('notify::empty', () => this._updateLocationIcon());
+ this.notify('empty');
+ }
+
+ destroy() {
+ if (this._schedUpdateId) {
+ GLib.source_remove(this._schedUpdateId);
+ this._schedUpdateId = 0;
+ }
+ this._updateTrashCancellable?.cancel();
+ this._monitor?.disconnect(this._monitorChangedId);
+ this._monitor = null;
+
+ super.destroy();
+ }
+
+ list_actions() {
+ return this.empty ? [] : ['empty-trash'];
+ }
+
+ get_action_name(action) {
+ switch (action) {
+ case 'empty-trash':
+ return __('Empty Trash');
+ default:
+ return null;
+ }
+ }
+
+ _onTrashChange() {
+ if (this._schedUpdateId) {
+ GLib.source_remove(this._schedUpdateId);
+ this._schedUpdateId = 0;
+ }
+
+ if (this._monitor.is_cancelled())
+ return;
+
+ this._schedUpdateId = GLib.timeout_add(GLib.PRIORITY_LOW,
+ UPDATE_TRASH_DELAY, () => {
+ this._schedUpdateId = 0;
+ this._updateTrash();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ async _updateTrash() {
+ const priority = GLib.PRIORITY_LOW;
+ this._updateTrashCancellable?.cancel();
+ const cancellable = new Utils.CancellableChild(this.cancellable);
+ this._updateTrashCancellable = cancellable;
+
+ try {
+ const trashInfo = await this.location.query_info_async(
+ Gio.FILE_ATTRIBUTE_TRASH_ITEM_COUNT,
+ Gio.FileQueryInfoFlags.NONE,
+ priority, cancellable);
+ this.empty = !trashInfo.get_attribute_uint32(
+ Gio.FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
+ return;
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'Impossible to get trash children from infos');
+ } finally {
+ cancellable.cancel();
+ if (this._updateIconCancellable === cancellable)
+ delete this._updateTrashCancellable;
+ }
+
+ try {
+ const childrenEnumerator = await this.location.enumerate_children_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE,
+ priority, cancellable);
+ const children = await childrenEnumerator.next_files_async(1,
+ priority, cancellable);
+ this.empty = !children.length;
+
+ await childrenEnumerator.close_async(priority, null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
+ logError(e, 'Impossible to enumerate trash children');
+ } finally {
+ cancellable.cancel();
+ if (this._updateIconCancellable === cancellable)
+ delete this._updateTrashCancellable;
+ }
+ }
+
+ launchAction(action, timestamp) {
+ if (!this.list_actions().includes(action))
+ throw new Error('Action %s is not supported by %s', action, this);
+
+ const nautilus = makeNautilusFileOperationsProxy();
+ const askConfirmation = true;
+ nautilus.EmptyTrashRemote(askConfirmation,
+ nautilus.platformData({timestamp}), (_p, error) => {
+ if (error)
+ logError(error, 'Empty trash failed');
+ }, this.cancellable);
+ }
+});
+
+/**
+ * @param shellApp
+ */
+function wrapWindowsBackedApp(shellApp) {
+ if (shellApp._dtdData)
+ throw new Error('%s has been already wrapped'.format(shellApp));
+
+ shellApp._dtdData = {
+ windows: [],
+ state: undefined,
+ startingWorkspace: 0,
+ isFocused: false,
+ proxyProperties: [],
+ sources: new Set(),
+ signalConnections: new Utils.GlobalSignalsHandler(),
+ methodInjections: new Utils.InjectionsHandler(),
+ propertyInjections: new Utils.PropertyInjectionsHandler(),
+ addProxyProperties(parent, proxyProperties) {
+ Object.entries(proxyProperties).forEach(([p, o]) => {
+ const publicProp = o.public ? p : `_${p}`;
+ const get = o.getter && o.value instanceof Function
+ ? () => this[p]() : () => this[p];
+ Object.defineProperty(parent, publicProp, Object.assign({
+ get,
+ set: v => (this[p] = v),
+ configurable: true,
+ enumerable: !!o.enumerable,
+ }, o.readOnly ? {set: undefined} : {}));
+ if (o.value)
+ this[p] = o.value;
+ this.proxyProperties.push(publicProp);
+ });
+ },
+ destroy() {
+ this.windows = [];
+ this.proxyProperties = [];
+ this.sources.forEach(s => GLib.source_remove(s));
+ this.sources.clear();
+ this.signalConnections.destroy();
+ this.methodInjections.destroy();
+ this.propertyInjections.destroy();
+ },
+ };
+
+ shellApp._dtdData.addProxyProperties(shellApp, {
+ windows: {},
+ state: {},
+ startingWorkspace: {},
+ isFocused: {public: true},
+ signalConnections: {readOnly: true},
+ sources: {readOnly: true},
+ checkFocused: {},
+ setDtdData: {},
+ });
+
+ shellApp._setDtdData = function (data, params = {}) {
+ for (const [name, value] of Object.entries(data)) {
+ if (params.readOnly && name in this._dtdData)
+ throw new Error('Property %s is already defined'.format(name));
+ const defaultParams = {public: true, readOnly: true};
+ this._dtdData.addProxyProperties(this, {
+ [name]: {...defaultParams, ...params, value},
+ });
+ }
+ };
+
+ const m = (...args) => shellApp._dtdData.methodInjections.add(shellApp, ...args);
+ const p = (...args) => shellApp._dtdData.propertyInjections.add(shellApp, ...args);
+
+ // mi is Method injector, pi is Property injector
+ shellApp._setDtdData({mi: m, pi: p}, {public: false});
+
+ m('get_state', () => shellApp._state ?? shellApp._getStateByWindows());
+ p('state', {get: () => shellApp.get_state()});
+
+ m('get_windows', () => shellApp._windows);
+ m('get_n_windows', () => shellApp._windows.length);
+ m('get_pids', () => shellApp._windows.reduce((pids, w) => {
+ if (w.get_pid() > 0 && !pids.includes(w.get_pid()))
+ pids.push(w.get_pid());
+ return pids;
+ }, []));
+ m('is_on_workspace', (_om, workspace) => shellApp._windows.some(w =>
+ w.get_workspace() === workspace) ||
+ (shellApp.state === Shell.AppState.STARTING &&
+ [-1, workspace.index()].includes(shellApp._startingWorkspace)));
+ m('request_quit', () => shellApp._windows.filter(w =>
+ w.can_close()).forEach(w => w.delete(global.get_current_time())));
+
+ shellApp._setDtdData({
+ _getStateByWindows() {
+ return this.get_n_windows() ? Shell.AppState.RUNNING : Shell.AppState.STOPPED;
+ },
+
+ _updateWindows() {
+ throw new GObject.NotImplementedError(`_updateWindows in ${this.constructor.name}`);
+ },
+
+ _notifyStateChanged() {
+ Shell.AppSystem.get_default().emit('app-state-changed', this);
+ this.notify('state');
+ },
+
+ _setState(state) {
+ const oldState = this.state;
+ this._state = state;
+
+ if (this.state !== oldState)
+ this._notifyStateChanged();
+ },
+
+ _setWindows(windows) {
+ const oldState = this.state;
+ const oldWindows = this._windows.slice();
+ const result = {windowsChanged: false, stateChanged: false};
+ this._state = undefined;
+
+ if (windows.length !== oldWindows.length ||
+ windows.some((win, index) => win !== oldWindows[index])) {
+ this._windows = windows.filter(w => !w.is_override_redirect());
+ this.emit('windows-changed');
+ result.windowsChanged = true;
+ }
+
+ if (this.state !== oldState) {
+ this._notifyStateChanged();
+ this._checkFocused();
+ result.stateChanged = true;
+ }
+
+ return result;
+ },
+ }, {readOnly: false});
+
+ shellApp._sources.add(GLib.idle_add(GLib.DEFAULT_PRIORITY, () => {
+ shellApp._updateWindows();
+ shellApp._sources.delete(GLib.main_current_source().source_id);
+ return GLib.SOURCE_REMOVE;
+ }));
+
+ const windowTracker = Shell.WindowTracker.get_default();
+ shellApp._checkFocused = function () {
+ if (this._windows.some(w => w.has_focus())) {
+ this.isFocused = true;
+ windowTracker.notify('focus-app');
+ } else if (this.isFocused) {
+ this.isFocused = false;
+ windowTracker.notify('focus-app');
+ }
+ };
+
+ shellApp._checkFocused();
+ shellApp._signalConnections.add(global.display, 'notify::focus-window', () =>
+ shellApp._checkFocused());
+
+ // Re-implements shell_app_activate_window for generic activation and alt-tab support
+ m('activate_window', function (_om, window, timestamp) {
+ /* eslint-disable no-invalid-this */
+ if (!window)
+ [window] = this.get_windows();
+ else if (!this._windows.includes(window))
+ return;
+
+ const currentWorkspace = global.workspace_manager.get_active_workspace();
+ const workspace = window.get_workspace();
+ const sameWorkspaceWindows = this.get_windows().filter(w =>
+ w.get_workspace() === workspace);
+ sameWorkspaceWindows.forEach(w => w.raise());
+
+ if (workspace !== currentWorkspace)
+ workspace.activate_with_focus(window, timestamp);
+ else
+ window.activate(timestamp);
+ /* eslint-enable no-invalid-this */
+ });
+
+ // Re-implements shell_app_activate_full for generic activation and dash support
+ m('activate_full', function (_om, workspace, timestamp) {
+ /* eslint-disable no-invalid-this */
+ if (!timestamp)
+ timestamp = global.get_current_time();
+
+ switch (this.state) {
+ case Shell.AppState.STOPPED:
+ try {
+ this._startingWorkspace = workspace;
+ this._setState(Shell.AppState.STARTING);
+ this.launch(timestamp, workspace, Shell.AppLaunchGpu.APP_PREF);
+ } catch (e) {
+ logError(e);
+ this._setState(Shell.AppState.STOPPED);
+ global.notify_error(_('Failed to launch “%s”'.format(
+ this.get_name())), e.message);
+ }
+ break;
+ case Shell.AppState.RUNNING:
+ this.activate_window(null, timestamp);
+ break;
+ }
+ /* eslint-enable no-invalid-this */
+ });
+
+ m('activate', () => shellApp.activate_full(-1, 0));
+
+ m('compare', (_om, other) => Utils.shellAppCompare(shellApp, other));
+
+ const {destroy: defaultDestroy} = shellApp;
+ shellApp.destroy = function () {
+ /* eslint-disable no-invalid-this */
+ this._dtdData.proxyProperties.forEach(prop => delete this[prop]);
+ this._dtdData.destroy();
+ this._dtdData = undefined;
+ this.appInfo.destroy?.();
+ this.destroy = defaultDestroy;
+ defaultDestroy?.call(this);
+ /* eslint-enable no-invalid-this */
+ };
+
+ return shellApp;
+}
+
+/**
+ * We can't inherit from Shell.App as it's a final type, so let's patch it
+ *
+ * @param params
+ */
+function makeLocationApp(params) {
+ if (!(params?.appInfo instanceof LocationAppInfo))
+ throw new TypeError('Invalid location');
+
+ const {fallbackIconName} = params;
+ delete params.fallbackIconName;
+
+ const shellApp = new Shell.App(params);
+ wrapWindowsBackedApp(shellApp);
+
+ shellApp._setDtdData({
+ location: () => shellApp.appInfo.location,
+ isTrash: shellApp.appInfo instanceof TrashAppInfo,
+ }, {getter: true, enumerable: true});
+
+ shellApp._mi('toString', defaultToString =>
+ '[LocationApp "%s" - %s]'.format(shellApp.get_id(),
+ defaultToString.call(shellApp)));
+
+ shellApp._mi('launch', (_om, timestamp, workspace, _gpuPref) =>
+ shellApp.appInfo.launch([],
+ global.create_app_launch_context(timestamp, workspace)));
+
+ shellApp._mi('launch_action', (_om, actionName, ...args) =>
+ shellApp.appInfo.launchAction(actionName, ...args));
+
+ shellApp._mi('create_icon_texture', (_om, iconSize) => new St.Icon({
+ iconSize,
+ gicon: shellApp.icon,
+ fallbackIconName,
+ }));
+
+ // FIXME: We need to add a new API to Nautilus to open new windows
+ shellApp._mi('can_open_new_window', () => {
+ try {
+ if (!shellApp.get_n_windows())
+ return true;
+
+ const handlerApp = shellApp.appInfo.getHandlerApp();
+
+ if (handlerApp.has_key('SingleMainWindow'))
+ return !handlerApp.get_boolean('SingleMainWindow');
+
+ if (handlerApp.has_key('X-GNOME-SingleWindow'))
+ return !handlerApp.get_boolean('X-GNOME-SingleWindow');
+
+ if (handlerApp.get_commandline()?.split(' ').includes('--new-window'))
+ return true;
+
+ const [window] = shellApp.get_windows();
+ if (window && window.get_gtk_window_object_path())
+ return window.get_gtk_application_id() === null;
+
+ return true;
+ } catch {
+ return false;
+ }
+ });
+
+ shellApp._mi('open_new_window', function (_om, workspace) {
+ /* eslint-disable no-invalid-this */
+ const context = global.create_app_launch_context(0, workspace);
+ if (!this.get_n_windows()) {
+ this.appInfo.launch([], context);
+ return;
+ }
+ const appId = this.appInfo.get_id();
+ Gio.AppInfo.create_from_commandline(this.appInfo.get_commandline(),
+ this.appInfo.get_id(), appId
+ ? Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION
+ : Gio.AppInfoCreateFlags.NONE).launch_uris(
+ [this.appInfo.location.get_uri()], context);
+ /* eslint-enable no-invalid-this */
+ });
+
+ if (shellApp.appInfo instanceof MountableVolumeAppInfo) {
+ shellApp._mi('get_busy', function (parentGetBusy) {
+ /* eslint-disable no-invalid-this */
+ if (this.appInfo.busy)
+ return true;
+ return parentGetBusy.call(this);
+ /* eslint-enable no-invalid-this */
+ });
+ shellApp._pi('busy', {get: () => shellApp.get_busy()});
+ shellApp._signalConnections.add(shellApp.appInfo, 'notify::busy', _ =>
+ shellApp.notify('busy'));
+ }
+
+ shellApp._mi('get_windows', function () {
+ /* eslint-disable no-invalid-this */
+ if (this._needsResort)
+ this._sortWindows();
+ return this._windows;
+ /* eslint-enable no-invalid-this */
+ });
+
+ const {fm1Client} = Docking.DockManager.getDefault();
+ shellApp._setDtdData({
+ _needsResort: true,
+
+ _windowsOrderChanged() {
+ this._needsResort = true;
+ this.emit('windows-changed');
+ },
+
+ _sortWindows() {
+ this._windows.sort(Utils.shellWindowsCompare);
+ this._needsResort = false;
+ },
+
+ _updateWindows() {
+ const windows = fm1Client.getWindows(this.location?.get_uri()).sort(
+ Utils.shellWindowsCompare);
+ const {windowsChanged} = this._setWindows(windows);
+
+ if (!windowsChanged)
+ return;
+
+ this._signalConnections.removeWithLabel(Labels.LOCATION_WINDOWS);
+ windows.forEach(w =>
+ this._signalConnections.addWithLabel(Labels.LOCATION_WINDOWS, w,
+ 'notify::user-time', () => {
+ if (w !== this._windows[0])
+ this._windowsOrderChanged();
+ }));
+ },
+ }, {readOnly: false});
+
+ shellApp._signalConnections.add(fm1Client, 'windows-changed', () =>
+ shellApp._updateWindows());
+ shellApp._signalConnections.add(shellApp.appInfo, 'notify::icon', () =>
+ shellApp.notify('icon'));
+ shellApp._signalConnections.add(global.workspaceManager,
+ 'workspace-switched', () => shellApp._windowsOrderChanged());
+
+ return shellApp;
+}
+
+/**
+ *
+ */
+function getFileManagerApp() {
+ return Shell.AppSystem.get_default().lookup_app(FILE_MANAGER_DESKTOP_APP_ID);
+}
+
+/**
+ *
+ */
+export function wrapFileManagerApp() {
+ const fileManagerApp = getFileManagerApp();
+ if (!fileManagerApp)
+ return null;
+
+ if (fileManagerApp._dtdData)
+ return fileManagerApp;
+
+ const originalGetWindows = fileManagerApp.get_windows;
+ wrapWindowsBackedApp(fileManagerApp);
+
+ const {removables, trash} = Docking.DockManager.getDefault();
+ fileManagerApp._signalConnections.addWithLabel(Labels.WINDOWS_CHANGED,
+ fileManagerApp, 'windows-changed', () => {
+ fileManagerApp.stop_emission_by_name('windows-changed');
+ // Let's wait for the location app to take control before of us
+ const id = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 50, () => {
+ fileManagerApp._sources.delete(id);
+ fileManagerApp._updateWindows();
+ return GLib.SOURCE_REMOVE;
+ });
+ fileManagerApp._sources.add(id);
+ });
+
+ fileManagerApp._signalConnections.add(global.workspaceManager,
+ 'workspace-switched', () => {
+ fileManagerApp._signalConnections.blockWithLabel(Labels.WINDOWS_CHANGED);
+ fileManagerApp.emit('windows-changed');
+ fileManagerApp._signalConnections.unblockWithLabel(Labels.WINDOWS_CHANGED);
+ });
+
+ if (removables) {
+ fileManagerApp._signalConnections.add(removables, 'changed', () =>
+ fileManagerApp._updateWindows());
+ fileManagerApp._signalConnections.add(removables, 'windows-changed', () =>
+ fileManagerApp._updateWindows());
+ }
+
+ if (trash?.getApp()) {
+ fileManagerApp._signalConnections.add(trash.getApp(), 'windows-changed', () =>
+ fileManagerApp._updateWindows());
+ }
+
+ fileManagerApp._updateWindows = function () {
+ const locationWindows = [];
+ getRunningApps().forEach(a => locationWindows.push(...a.get_windows()));
+ const windows = originalGetWindows.call(this).filter(w =>
+ !locationWindows.includes(w));
+
+ this._signalConnections.blockWithLabel(Labels.WINDOWS_CHANGED);
+ this._setWindows(windows);
+ this._signalConnections.unblockWithLabel(Labels.WINDOWS_CHANGED);
+ };
+
+ fileManagerApp._mi('toString', defaultToString =>
+ '[FileManagerApp - %s]'.format(defaultToString.call(fileManagerApp)));
+
+ return fileManagerApp;
+}
+
+/**
+ *
+ */
+export function unWrapFileManagerApp() {
+ const fileManagerApp = getFileManagerApp();
+ if (!fileManagerApp || !fileManagerApp._dtdData)
+ return;
+
+ fileManagerApp.destroy();
+}
+
+/**
+ * This class maintains a Shell.App representing the Trash and keeps it
+ * up-to-date as the trash fills and is emptied over time.
+ */
+export class Trash {
+ destroy() {
+ this._trashApp?.destroy();
+ }
+
+ _ensureApp() {
+ if (this._trashApp)
+ return;
+
+ this._trashApp = makeLocationApp({
+ appInfo: new TrashAppInfo(new Gio.Cancellable()),
+ fallbackIconName: FALLBACK_TRASH_ICON,
+ });
+ }
+
+ getApp() {
+ this._ensureApp();
+ return this._trashApp;
+ }
+}
+
+/**
+ * This class maintains Shell.App representations for removable devices
+ * plugged into the system, and keeps the list of Apps up-to-date as
+ * devices come and go and are mounted and unmounted.
+ */
+export class Removables {
+ static initVolumePromises(object) {
+ // TODO: This can be simplified using actual interface type when we
+ // can depend on gjs 1.72
+ if (!(object instanceof Gio.Volume) || object.constructor.prototype._d2dPromisified)
+ return;
+
+ Gio._promisify(object.constructor.prototype, 'mount', 'mount_finish');
+ Gio._promisify(object.constructor.prototype, 'eject_with_operation',
+ 'eject_with_operation_finish');
+ object.constructor.prototype._d2dPromisified = true;
+ }
+
+ static initMountPromises(object) {
+ // TODO: This can be simplified using actual interface type when we
+ // can depend on gjs 1.72
+ if (!(object instanceof Gio.Mount) || object.constructor.prototype._d2dPromisified)
+ return;
+
+ Gio._promisify(object.constructor.prototype, 'eject_with_operation',
+ 'eject_with_operation_finish');
+ Gio._promisify(object.constructor.prototype, 'unmount_with_operation',
+ 'unmount_with_operation_finish');
+ object.constructor.prototype._d2dPromisified = true;
+ }
+
+ constructor() {
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+
+ this._monitor = Gio.VolumeMonitor.get();
+ this._cancellable = new Gio.Cancellable();
+
+ this._monitor.get_mounts().forEach(m => Removables.initMountPromises(m));
+ this._updateVolumes();
+
+ this._signalsHandler.add([
+ this._monitor,
+ 'volume-added',
+ (_, volume) => this._onVolumeAdded(volume),
+ ], [
+ this._monitor,
+ 'volume-removed',
+ (_, volume) => this._onVolumeRemoved(volume),
+ ], [
+ this._monitor,
+ 'mount-added',
+ (_, mount) => this._onMountAdded(mount),
+ ], [
+ Docking.DockManager.settings,
+ 'changed::show-mounts-only-mounted',
+ () => this._updateVolumes(),
+ ], [
+ Docking.DockManager.settings,
+ 'changed::show-mounts-network',
+ () => this._updateVolumes(),
+ ]);
+ }
+
+ destroy() {
+ this._volumeApps.forEach(a => a.destroy());
+ this._volumeApps = [];
+ this._cancellable.cancel();
+ this._cancellable = null;
+ this._signalsHandler.destroy();
+ this._monitor = null;
+ }
+
+ _updateVolumes() {
+ this._volumeApps?.forEach(a => a.destroy());
+ this._volumeApps = [];
+ this.emit('changed');
+
+ this._monitor.get_volumes().forEach(v => this._onVolumeAdded(v));
+ }
+
+ _onVolumeAdded(volume) {
+ Removables.initVolumePromises(volume);
+
+ if (!Docking.DockManager.settings.showMountsNetwork &&
+ volume.get_identifier('class') === 'network')
+ return;
+
+
+ const mount = volume.get_mount();
+ if (mount) {
+ if (mount.is_shadowed())
+ return;
+ if (!mount.can_eject() && !mount.can_unmount())
+ return;
+ } else {
+ if (Docking.DockManager.settings.showMountsOnlyMounted)
+ return;
+ if (!volume.can_mount() && !volume.can_eject())
+ return;
+ }
+
+ const appInfo = new MountableVolumeAppInfo(volume,
+ new Utils.CancellableChild(this._cancellable));
+ const volumeApp = makeLocationApp({
+ appInfo,
+ fallbackIconName: FALLBACK_REMOVABLE_MEDIA_ICON,
+ });
+
+ volumeApp._signalConnections.add(volumeApp, 'windows-changed',
+ () => this.emit('windows-changed', volumeApp));
+
+ if (Docking.DockManager.settings.showMountsOnlyMounted) {
+ volumeApp._signalConnections.add(appInfo, 'notify::mount',
+ () => !appInfo.mount && this._onVolumeRemoved(appInfo.volume));
+ }
+
+ this._volumeApps.push(volumeApp);
+ this.emit('changed');
+ }
+
+ _onVolumeRemoved(volume) {
+ const volumeIndex = this._volumeApps.findIndex(({appInfo}) =>
+ appInfo.volume === volume);
+ if (volumeIndex !== -1) {
+ const [volumeApp] = this._volumeApps.splice(volumeIndex, 1);
+ // We don't care about cancelling the ongoing operations from now on.
+ volumeApp.appInfo.cancellable = null;
+ volumeApp.destroy();
+ this.emit('changed');
+ }
+ }
+
+ _onMountAdded(mount) {
+ Removables.initMountPromises(mount);
+
+ if (!Docking.DockManager.settings.showMountsOnlyMounted)
+ return;
+
+ if (!this._volumeApps.find(({appInfo}) => appInfo.mount === mount)) {
+ // In some Gio.Mount implementations the volume may be set after
+ // mount is emitted, so we could just ignore it as we'll get it
+ // later via volume-added
+ const volume = mount.get_volume();
+ if (volume)
+ this._onVolumeAdded(volume);
+ }
+ }
+
+ getApps() {
+ return this._volumeApps;
+ }
+}
+Signals.addSignalMethods(Removables.prototype);
+
+/**
+ *
+ */
+function getApps() {
+ const dockManager = Docking.DockManager.getDefault();
+ const locationApps = [];
+
+ if (dockManager.removables)
+ locationApps.push(...dockManager.removables.getApps());
+
+ if (dockManager.trash)
+ locationApps.push(dockManager.trash.getApp());
+
+ return locationApps;
+}
+
+/**
+ *
+ */
+export function getRunningApps() {
+ return getApps().filter(a => a.state === Shell.AppState.RUNNING);
+}
+
+/**
+ *
+ */
+export function getStartingApps() {
+ return getApps().filter(a => a.state === Shell.AppState.STARTING);
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js
new file mode 100644
index 0000000..054c92c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/locationsWorker.js
@@ -0,0 +1,91 @@
+#!/usr/bin/env gjs
+
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+
+const GJS_SUPPORTS_FILE_IFACE_PROMISES = imports.system.version >= 17101;
+
+if (GJS_SUPPORTS_FILE_IFACE_PROMISES)
+ Gio._promisify(Gio.File.prototype, 'query_default_handler_async');
+
+function getHandlerAppAsync(location, cancellable) {
+ if (!location)
+ return null;
+
+ if (!GJS_SUPPORTS_FILE_IFACE_PROMISES) {
+ Gio._promisify(location.constructor.prototype,
+ 'query_default_handler_async',
+ 'query_default_handler_finish');
+ }
+
+ return location.query_default_handler_async(
+ GLib.PRIORITY_DEFAULT, cancellable);
+}
+
+async function mainAsync(argv) {
+ if (argv.length < 1) {
+ const currentBinary = GLib.path_get_basename(new Error().fileName);
+ printerr(`Usage: ${currentBinary} <action> uri [ --timeout <value> ]`);
+ return 1;
+ }
+
+ const [action, uri] = argv;
+ let timeout = 200;
+
+ if (action !== 'handler')
+ throw new TypeError(`Unexpected action ${action}`);
+
+ for (let i = 1; i < argv.length; ++i) {
+ if (argv[i] === '--timeout' && i < argv.length - 1)
+ timeout = argv[++i];
+ }
+
+ const location = Gio.File.new_for_uri(uri);
+ const cancellable = new Gio.Cancellable();
+
+ // GVfs providers could hang when querying the file information, so we
+ // workaround this by using the async API in a sync way, but we need to
+ // use a timeout to avoid this to hang forever, better than hang the
+ // shell.
+ let launchMaxWaitId;
+
+ try {
+ const handler = await Promise.race([
+ getHandlerAppAsync(location, cancellable),
+ new Promise((_resolve, reject) => {
+ launchMaxWaitId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, timeout, () => {
+ launchMaxWaitId = 0;
+ cancellable.cancel();
+ reject(new GLib.Error(Gio.IOErrorEnum,
+ Gio.IOErrorEnum.TIMED_OUT,
+ `Searching for ${location.get_uri()} ` +
+ 'handler took too long'));
+ return GLib.SOURCE_REMOVE;
+ });
+ }),
+ ]);
+
+ print(handler.get_id());
+ } catch (e) {
+ printerr(e.message);
+ logError(e);
+ return e.code ? e.code : GLib.MAXUINT8;
+ } finally {
+ if (launchMaxWaitId)
+ GLib.source_remove(launchMaxWaitId);
+ }
+
+ return 0;
+}
+
+function main(args) {
+ let ret;
+ const loop = new GLib.MainLoop(null, false);
+ mainAsync(args).then(r => (ret = r)).catch(logError).finally(() => loop.quit());
+ loop.run();
+
+ return ret;
+}
+
+imports.system.exit(main(ARGV));
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg
new file mode 100644
index 0000000..55b71ba
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/glossy.svg
@@ -0,0 +1,139 @@
+<?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:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="18.343554mm"
+ height="18.343554mm"
+ viewBox="0 0 14.674843 14.674842"
+ version="1.1"
+ id="svg4941"
+ sodipodi:docname="glossy.svg"
+ inkscape:version="0.92.1 r15371">
+ <defs
+ id="defs4935">
+ <linearGradient
+ id="linearGradient6812"
+ inkscape:collect="always">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="0"
+ id="stop6810" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="1"
+ id="stop6808" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient18962"
+ id="linearGradient35463"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.29132751,0,0,0.15428114,-54.210829,160.22776)"
+ x1="214.71877"
+ y1="404.36081"
+ x2="214.71877"
+ y2="443.54596" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient18962">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop18964" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop18966" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient18806">
+ <stop
+ style="stop-color:#ff0101;stop-opacity:1;"
+ offset="0"
+ id="stop18808" />
+ <stop
+ style="stop-color:#800000;stop-opacity:1;"
+ offset="1"
+ id="stop18810" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient6812"
+ id="radialGradient6798"
+ cx="7.3538475"
+ cy="230.28426"
+ fx="7.3538475"
+ fy="230.28426"
+ r="7.2099228"
+ gradientTransform="matrix(5.9484829,-0.0346444,0.01679088,3.0681664,-40.338609,-476.01412)"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="10.68"
+ inkscape:cx="65.485107"
+ inkscape:cy="29.432163"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2560"
+ inkscape:window-height="1406"
+ inkscape:window-x="1920"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ scale-x="0.8"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <metadata
+ id="metadata4938">
+ <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
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-222.92515)">
+ <rect
+ inkscape:export-ydpi="180"
+ inkscape:export-xdpi="180"
+ inkscape:export-filename="C:\Arbeit\Blog\Tutorials\glossybutton\Glossy_Button_Tutorial.png"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.35100002;fill:url(#linearGradient35463);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.39747861;marker:none;enable-background:accumulate"
+ id="rect19155"
+ width="14.634871"
+ height="3.7392156"
+ x="0.039808333"
+ y="222.98268"
+ rx="1.5496143"
+ ry="0.82064426" />
+ <rect
+ style="opacity:0.427;fill:url(#radialGradient6798);fill-opacity:1;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect6706"
+ width="14.363673"
+ height="14.404656"
+ x="0.090466507"
+ y="223.07919" />
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg
new file mode 100644
index 0000000..19be5a9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg.svg
@@ -0,0 +1,82 @@
+<?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"
+ viewBox="-0.7 0 48 48"
+ version="1.1"
+ id="svg10"
+ sodipodi:docname="highlight_stacked_bg.svg"
+ width="48"
+ height="48"
+ inkscape:version="0.92.1 r15371">
+ <metadata
+ id="metadata16">
+ <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="defs14" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="951"
+ id="namedview12"
+ showgrid="false"
+ viewbox-x="-0.7"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="3.8125"
+ inkscape:cx="-63.872219"
+ inkscape:cy="15.195756"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg10" />
+ <g
+ id="g8"
+ transform="matrix(1,0,0,48,-0.7,0)"
+ style="opacity:0.25;fill:#eeeeee;stroke-width:0.14433756">
+ <rect
+ width="45"
+ height="1"
+ id="rect2"
+ x="0"
+ y="0"
+ style="stroke-width:0.14433756" />
+ <rect
+ x="45"
+ width="1"
+ height="1"
+ id="rect4"
+ y="0"
+ style="opacity:0.2;stroke-width:0.02083333" />
+ <rect
+ x="46"
+ width="2"
+ height="1"
+ id="rect6"
+ y="0"
+ style="opacity:0.6;stroke-width:0.02083333" />
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg
new file mode 100644
index 0000000..eeaa869
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/highlight_stacked_bg_h.svg
@@ -0,0 +1,82 @@
+<?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"
+ viewBox="-0.7 0 48 48"
+ version="1.1"
+ id="svg10"
+ sodipodi:docname="highlight_stacked_bg_h.svg"
+ width="48"
+ height="48"
+ inkscape:version="0.92.1 r15371">
+ <metadata
+ id="metadata16">
+ <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="defs14" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1853"
+ inkscape:window-height="1016"
+ id="namedview12"
+ showgrid="false"
+ viewbox-x="-0.7"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="3.8125"
+ inkscape:cx="-63.872219"
+ inkscape:cy="15.195756"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg10" />
+ <g
+ id="g8"
+ transform="matrix(0,-1,-48,0,47.3,48)"
+ style="opacity:0.25;fill:#eeeeee;stroke-width:0.14433756">
+ <rect
+ width="45"
+ height="1"
+ id="rect2"
+ x="0"
+ y="0"
+ style="stroke-width:0.14433756" />
+ <rect
+ x="45"
+ width="1"
+ height="1"
+ id="rect4"
+ y="0"
+ style="opacity:0.2;stroke-width:0.02083333" />
+ <rect
+ x="46"
+ width="2"
+ height="1"
+ id="rect6"
+ y="0"
+ style="opacity:0.6;stroke-width:0.02083333" />
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg
new file mode 100644
index 0000000..eebd0b1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/media/logo.svg
@@ -0,0 +1,528 @@
+<?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="33.866665mm"
+ height="33.866684mm"
+ viewBox="0 0 33.866665 33.866683"
+ id="svg5179"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="logo.svg">
+ <defs
+ id="defs5181">
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-92-4-9-6-8-0">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-17-7-5-2-0-6"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-8-5-3-2-13-8"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-6-7-9-8-8-1" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-71-5-7-0-6-2"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-74-5-92-0-6-5" />
+ </filter>
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-6-7-5-8-6-01-2">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-1-8-5-2-0-2-7"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-6-1-2-8-2-2-7"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-1-1-3-60-1-4-4" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-4-5-2-24-7-3-3"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-7-0-7-7-9-0-3" />
+ </filter>
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4379-5-6-0-9-8-7-9">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.83189655;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4381-6-8-5-9-9-2-4"
+ width="19.934219"
+ height="33.52573"
+ x="356.02826"
+ y="457.71631" />
+ </clipPath>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4435-63-9-2-4-1-2-6"
+ x="-0.22881356"
+ width="1.4576271"
+ y="-0.22881356"
+ height="1.4576271">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="1.0352993"
+ id="feGaussianBlur4437-0-5-6-8-8-9-9" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:collect="always"
+ id="filter4365-2-4-3-6-3-1-7"
+ x="-0.21864407"
+ width="1.437288"
+ y="-0.21864407"
+ height="1.437288">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.98928601"
+ id="feGaussianBlur4367-1-2-5-3-5-8-3" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ style="color-interpolation-filters:sRGB"
+ id="filter4255"
+ x="-0.20374454"
+ width="1.4074891"
+ y="-0.13779147"
+ height="1.2755829">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.25863247"
+ id="feGaussianBlur4257" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="8"
+ inkscape:cx="60.090739"
+ inkscape:cy="60.108985"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1861"
+ inkscape:window-height="1023"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata5184">
+ <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
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(136.97858,-11.552354)">
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#0055d4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4006-4-6-9-2-0-6"
+ width="33.83363"
+ height="33.859909"
+ x="-136.9473"
+ y="11.552354"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -130.12265,11.559157 c -4.30029,5.691881 -6.67207,12.608761 -6.82289,19.674442 -0.0115,0.54232 -0.0147,1.0766 0,1.62024 0.11433,4.23572 1.04846,8.50668 2.82497,12.565201 l 31.00865,0 0,-33.859883 -27.01073,0 z"
+ id="path6097-2-6-0-89-4"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,18.430158 0,0.7896 0,20.641361 0,0.7896 1.23782,0 2.26288,0 1.60528,0 c 0.68577,0 1.23783,-0.3548 1.23783,-0.7896 l 0,-20.641361 c 0,-0.4398 -0.55206,-0.7896 -1.23783,-0.7896 l -1.60528,0 -2.26288,0 z"
+ id="rect4008-7-9-2-0-3-4"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.15440008;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -119.36792,11.559157 c -10.47023,5.721881 -17.57762,16.847401 -17.57762,29.627402 0,1.43804 0.0897,2.841801 0.26432,4.232481 l 33.5693,0 0,-33.859883 -16.256,0 z"
+ id="path6097-4-5-23-9"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4247-4-4-5-3-8-1"
+ width="33.83363"
+ height="2.1162443"
+ x="-136.9473"
+ y="11.552354"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ d="m -103.11365,13.668597 0,1.05812 c 0,-0.58196 -0.47338,-1.05812 -1.05731,-1.05812 l 1.05731,0 z"
+ id="rect4272-0-7-8-1-1-3-3-1"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-9-2-4-2-5"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="19.488146"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ d="m -136.94728,13.668597 0,1.05812 c 0,-0.58196 0.47337,-1.05812 1.0573,-1.05812 l -1.0573,0 z"
+ id="rect4272-0-2-1-74-41-1-6"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <g
+ id="g4353-9-2-1-5-5-4"
+ transform="matrix(0.10331261,0,0,0.10339285,-173.76079,-27.453246)"
+ clip-path="url(#clipPath4379-92-4-9-6-8-0)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)"
+ id="path3153-1-7-3-5-60-3-6"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-8-5-3-2-13-8);enable-background:accumulate" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)"
+ id="path3153-2-4-1-6-6-9-4-1"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-71-5-7-0-6-2);enable-background:accumulate" />
+ </g>
+ <g
+ id="g4589-4-1-1-3-6-2"
+ transform="matrix(0.49926208,0,0,0.49964988,-318.21072,-206.05794)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099">
+ <g
+ clip-path="url(#clipPath4379-6-7-5-8-6-01-2)"
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,368.5622)"
+ id="g4353-66-1-4-2-6-94-5">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-6-1-2-8-2-2-7);enable-background:accumulate"
+ id="path3153-1-6-4-5-63-7-1-0"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-4-5-2-24-7-3-3);enable-background:accumulate"
+ id="path3153-2-4-7-6-5-8-5-9-5"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" />
+ </g>
+ <g
+ clip-path="url(#clipPath4379-5-6-0-9-8-7-9)"
+ transform="matrix(0.20693061,0,0,0.20693061,289.32686,367.53449)"
+ id="g4353-7-2-2-6-4-5-1">
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.42241378;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4435-63-9-2-4-1-2-6);enable-background:accumulate"
+ id="path3153-1-19-3-1-5-5-7-8"
+ transform="matrix(0.94749688,0,0,0.94749688,96.290796,21.848877)" />
+ <circle
+ r="5.4295697"
+ cy="477.71164"
+ cx="274.13016"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#d7eef4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;filter:url(#filter4365-2-4-3-6-3-1-7);enable-background:accumulate"
+ id="path3153-2-4-5-7-9-9-9-7-6"
+ transform="matrix(0.24231546,0,0,0.24231546,289.60229,358.72226)" />
+ </g>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
+ x="-124.44726"
+ y="13.10139"
+ id="text4824-5-2-0-4-8"
+ sodipodi:linespacing="125%"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099"
+ transform="scale(0.99961185,1.0003883)"><tspan
+ sodipodi:role="line"
+ id="tspan4826-16-3-8-8-1"
+ x="-124.44726"
+ y="13.10139">Dash to Dock</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:1.28805089px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
+ x="-136.50272"
+ y="13.10139"
+ id="text4824-8-8-6-8-7-4"
+ sodipodi:linespacing="125%"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099"
+ transform="scale(0.99961185,1.0003883)"><tspan
+ sodipodi:role="line"
+ id="tspan4826-1-7-7-5-07-5"
+ x="-136.50272"
+ y="13.10139">Michele</tspan></text>
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-8-5-4-0-7-6"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="24.778917"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-7-3-3-6-0-1"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="30.069445"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.04922473;marker:none;enable-background:accumulate"
+ id="rect4031-9-0-6-5-1-3-9-0"
+ width="4.2292037"
+ height="4.2324886"
+ x="-135.89"
+ y="35.359974"
+ rx="1.0583334"
+ ry="1.0583334"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.5;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z"
+ id="rect4008-7-0-0-3-3-3-7-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccsssscccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
+ d="m -136.9473,17.901078 0,0.52908 2.42849,0 2.21372,0 c 0.94338,0 1.7016,0.3372 1.7016,0.77704 l 0,20.649921 c 0,0.43476 -0.75822,0.7936 -1.7016,0.7936 l -2.21372,0 -2.42849,0 0,0.52904 0.90862,0 2.64325,0 1.88332,0 c 0.80005,0 1.43727,-0.3712 1.43727,-0.82664 l 0,-21.625361 c 0,-0.46072 -0.63722,-0.82668 -1.43727,-0.82668 l -1.88332,0 -2.64325,0 z"
+ id="rect4008-7-0-0-3-1-5-0-5-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccsssscccccssssccc"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="44.99099"
+ inkscape:export-ydpi="44.99099" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#f2f2f2;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6777-7-9-6-9-8"
+ width="20.108335"
+ height="18.256252"
+ x="-125.24149"
+ y="19.139757"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-8-7-8-2"
+ width="3.7041669"
+ height="3.7041669"
+ x="-116.71888"
+ y="30.163927"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.15;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -111.94623,19.146638 c -5.49508,1.3884 -10.21465,5.00036 -13.29531,9.92188 l 0,8.334361 20.10833,0 0,-18.256241 -6.81302,0 z"
+ id="path6862-84-2-2-6-7"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <path
+ inkscape:connector-curvature="0"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -125.02657,18.882038 c -0.11728,0 -0.21496,0.0812 -0.21496,0.1984 l 0,0.44648 0,1.2568 0,0.2148 0.21496,0 19.67838,0 0.215,0 0,-0.2148 0,-1.2568 0,-0.44648 c 0,-0.1172 -0.0977,-0.1984 -0.215,-0.1984 l -19.67838,0 z"
+ id="rect6779-5-8-6-4-6"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6779-2-3-9-9-0-8"
+ width="20.108335"
+ height="0.5291667"
+ x="-125.24149"
+ y="20.991808"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ id="rect6779-2-4-8-0-7-1-1"
+ width="15.875001"
+ height="0.5291667"
+ x="21.521105"
+ y="105.13315"
+ transform="rotate(90)"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45" />
+ <g
+ id="g6839-1-5-1-33-0"
+ transform="matrix(0.02002288,0.02002284,-0.02002288,0.02002284,-106.62848,-6.0229242)"
+ style="fill:#1a1a1a"
+ inkscape:export-filename="/home/michele/Dropbox/lavori/gnome-shell-extension/icon/g5218.png"
+ inkscape:export-xdpi="45"
+ inkscape:export-ydpi="45">
+ <rect
+ y="616.07727"
+ x="653.01312"
+ height="41.542522"
+ width="11.313708"
+ id="rect6819-8-9-2-56-9"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" />
+ <rect
+ transform="rotate(90)"
+ y="-679.44122"
+ x="631.19165"
+ height="41.542522"
+ width="11.313708"
+ id="rect6819-3-9-4-3-1-5"
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5;marker:none;filter:url(#filter4365-3);enable-background:accumulate" />
+ </g>
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-6-8-9-1"
+ width="3.7041669"
+ height="3.7041669"
+ x="-123.59805"
+ y="30.163927"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.26458335;stroke-miterlimit:4;stroke-dasharray:none;marker:none;enable-background:accumulate;opacity:0.866;filter:url(#filter4255)"
+ d="m -121.46776,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 0.99716,1.727136 1.33421,-0.770304 -0.99542,-1.724104 0.96903,-0.268366 -1.50959,-0.870995 z"
+ id="path6155-6-0-01-4-5-6-0-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccc" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.13229167;stroke-miterlimit:4;stroke-dasharray:none;marker:none;filter:url(#filter4365-3);enable-background:accumulate"
+ d="m -121.86464,32.043964 -5e-4,1.742839 -4.9e-4,1.742839 0.71518,-0.708051 1.05563,1.8284 1.3342,-0.770304 -1.05388,-1.825368 0.96903,-0.268366 -1.50959,-0.870995 z"
+ id="path6155-6-0-8-0-7-97-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccc" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-4-8-4"
+ width="3.7041669"
+ height="3.7041669"
+ x="-123.59805"
+ y="23.020128"
+ rx="1.0583334"
+ ry="1.0583334" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.13229166;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4923-2-6-7-8"
+ width="3.7041669"
+ height="3.7041669"
+ x="-116.71888"
+ y="23.020128"
+ rx="1.0583334"
+ ry="1.0583334" />
+ </g>
+</svg>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json
new file mode 100644
index 0000000..cbb546f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/metadata.json
@@ -0,0 +1,18 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "A dock for the Gnome Shell. This extension moves the dash out of the overview transforming it in a dock for an easier launching of applications and a faster switching between windows and desktops. Side and bottom placement options are available.",
+ "gettext-domain": "dashtodock",
+ "name": "Dash to Dock",
+ "original-author": "micxgx@gmail.com",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://micheleg.github.io/dash-to-dock/",
+ "uuid": "dash-to-dock@micxgx.gmail.com",
+ "version": 104
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js
new file mode 100644
index 0000000..7dca35d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/notificationsMonitor.js
@@ -0,0 +1,121 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {Gio} from './dependencies/gi.js';
+import {Main} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {signals: Signals} = imports;
+
+const Labels = Object.freeze({
+ SOURCES: Symbol('sources'),
+ NOTIFICATIONS: Symbol('notifications'),
+});
+export class NotificationsMonitor {
+ constructor() {
+ this._settings = new Gio.Settings({
+ schema_id: 'org.gnome.desktop.notifications',
+ });
+
+ this._appNotifications = Object.create(null);
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+
+ const getIsEnabled = () => !this.dndMode &&
+ Docking.DockManager.settings.showIconsNotificationsCounter;
+
+ this._isEnabled = getIsEnabled();
+ const checkIsEnabled = () => {
+ const isEnabled = getIsEnabled();
+ if (isEnabled !== this._isEnabled) {
+ this._isEnabled = isEnabled;
+ this.emit('state-changed');
+
+ this._updateState();
+ }
+ };
+
+ this._dndMode = !this._settings.get_boolean('show-banners');
+ this._signalsHandler.add(this._settings, 'changed::show-banners', () => {
+ this._dndMode = !this._settings.get_boolean('show-banners');
+ checkIsEnabled();
+ });
+ this._signalsHandler.add(Docking.DockManager.settings,
+ 'changed::show-icons-notifications-counter', checkIsEnabled);
+
+ this._updateState();
+ }
+
+ destroy() {
+ this.emit('destroy');
+ this._signalsHandler?.destroy();
+ this._signalsHandler = null;
+ this._appNotifications = null;
+ this._settings = null;
+ }
+
+ get enabled() {
+ return this._isEnabled;
+ }
+
+ get dndMode() {
+ return this._dndMode;
+ }
+
+ getAppNotificationsCount(appId) {
+ return this._appNotifications[appId] ?? 0;
+ }
+
+ _updateState() {
+ if (this.enabled) {
+ this._signalsHandler.addWithLabel(Labels.SOURCES, Main.messageTray,
+ 'source-added', () => this._checkNotifications());
+ this._signalsHandler.addWithLabel(Labels.SOURCES, Main.messageTray,
+ 'source-removed', () => this._checkNotifications());
+ } else {
+ this._signalsHandler.removeWithLabel(Labels.SOURCES);
+ }
+
+ this._checkNotifications();
+ }
+
+ _checkNotifications() {
+ this._appNotifications = Object.create(null);
+ this._signalsHandler.removeWithLabel(Labels.NOTIFICATIONS);
+
+ if (this.enabled) {
+ Main.messageTray.getSources().forEach(source => {
+ this._signalsHandler.addWithLabel(Labels.NOTIFICATIONS, source,
+ 'notification-added', () => this._checkNotifications());
+
+ source.notifications.forEach(notification => {
+ const app = notification.source?.app ?? notification.source?._app;
+ const appId = app?.id ?? app?._appId;
+
+ if (appId) {
+ if (notification.resident) {
+ if (notification.acknowledged)
+ return;
+
+ this._signalsHandler.addWithLabel(Labels.NOTIFICATIONS,
+ notification, 'notify::acknowledged',
+ () => this._checkNotifications());
+ }
+
+ this._signalsHandler.addWithLabel(Labels.NOTIFICATIONS,
+ notification, 'destroy', () => this._checkNotifications());
+
+ this._appNotifications[appId] =
+ (this._appNotifications[appId] ?? 0) + 1;
+ }
+ });
+ });
+ }
+
+ this.emit('changed');
+ }
+}
+
+Signals.addSignalMethods(NotificationsMonitor.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js
new file mode 100644
index 0000000..fae0b99
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/prefs.js
@@ -0,0 +1,1168 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gdk from 'gi://Gdk';
+import Gio from 'gi://Gio';
+import Gtk from 'gi://Gtk';
+
+import {
+ ExtensionPreferences,
+
+ // Use __ () and N__() for the extension gettext domain, and reuse
+ // the shell domain with the default _() and N_()
+ gettext as __,
+} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+const SCALE_UPDATE_TIMEOUT = 500;
+const DEFAULT_ICONS_SIZES = [128, 96, 64, 48, 32, 24, 16];
+
+const TransparencyMode = Object.freeze({
+ DEFAULT: 0,
+ FIXED: 1,
+ DYNAMIC: 3,
+});
+
+const RunningIndicatorStyle = Object.freeze({
+ DEFAULT: 0,
+ DOTS: 1,
+ SQUARES: 2,
+ DASHES: 3,
+ SEGMENTED: 4,
+ SOLID: 5,
+ CILIORA: 6,
+ METRO: 7,
+});
+
+const MonitorsConfig = GObject.registerClass({
+ Signals: {
+ 'updated': {},
+ },
+}, class MonitorsConfig extends GObject.Object {
+ static get XML_INTERFACE() {
+ return '<node>\
+ <interface name="org.gnome.Mutter.DisplayConfig">\
+ <method name="GetCurrentState">\
+ <arg name="serial" direction="out" type="u" />\
+ <arg name="monitors" direction="out" type="a((ssss)a(siiddada{sv})a{sv})" />\
+ <arg name="logical_monitors" direction="out" type="a(iiduba(ssss)a{sv})" />\
+ <arg name="properties" direction="out" type="a{sv}" />\
+ </method>\
+ <signal name="MonitorsChanged" />\
+ </interface>\
+ </node>';
+ }
+
+ static get ProxyWrapper() {
+ return Gio.DBusProxy.makeProxyWrapper(MonitorsConfig.XML_INTERFACE);
+ }
+
+ constructor() {
+ super();
+
+ this._monitorsConfigProxy = new MonitorsConfig.ProxyWrapper(
+ Gio.DBus.session,
+ 'org.gnome.Mutter.DisplayConfig',
+ '/org/gnome/Mutter/DisplayConfig'
+ );
+
+ // Connecting to a D-Bus signal
+ this._monitorsConfigProxy.connectSignal('MonitorsChanged',
+ () => this._updateResources());
+
+ this._primaryMonitor = null;
+ this._monitors = [];
+ this._logicalMonitors = [];
+
+ this._updateResources();
+ }
+
+ _updateResources() {
+ this._monitorsConfigProxy.GetCurrentStateRemote((resources, err) => {
+ if (err) {
+ logError(err);
+ return;
+ }
+
+ const [serial_, monitors, logicalMonitors] = resources;
+ let index = 0;
+ for (const monitor of monitors) {
+ const [monitorSpecs, modes_, props] = monitor;
+ const [connector, vendor, product, serial] = monitorSpecs;
+ this._monitors.push({
+ index: index++,
+ active: false,
+ connector, vendor, product, serial,
+ displayName: props['display-name'].unpack(),
+ });
+ }
+
+ for (const logicalMonitor of logicalMonitors) {
+ const [x_, y_, scale_, transform_, isPrimary, monitorsSpecs] =
+ logicalMonitor;
+
+ // We only care about the first one really
+ for (const monitorSpecs of monitorsSpecs) {
+ const [connector, vendor, product, serial] = monitorSpecs;
+ const monitor = this._monitors.find(m =>
+ m.connector === connector && m.vendor === vendor &&
+ m.product === product && m.serial === serial);
+
+ if (monitor) {
+ monitor.active = true;
+ monitor.isPrimary = isPrimary;
+ if (monitor.isPrimary)
+ this._primaryMonitor = monitor;
+ break;
+ }
+ }
+ }
+
+ const activeMonitors = this._monitors.filter(m => m.active);
+ if (activeMonitors.length > 1 && logicalMonitors.length === 1) {
+ // We're in cloning mode, so let's just activate the primary monitor
+ this._monitors.forEach(m => (m.active = false));
+ this._primaryMonitor.active = true;
+ }
+
+ this._updateMonitorsIndexes();
+ this.emit('updated');
+ });
+ }
+
+ _updateMonitorsIndexes() {
+ // This function ensures that we follow the old Gdk indexing strategy
+ // for monitors, it can be removed when we don't care about breaking
+ // old user configurations or external apps configuring this extension
+ // such as ubuntu's gnome-control-center.
+ const {index: primaryMonitorIndex} = this._primaryMonitor;
+ for (const monitor of this._monitors) {
+ let {index} = monitor;
+ // The The dock uses the Gdk index for monitors, where the primary monitor
+ // always has index 0, so let's follow what dash-to-dock does in docking.js
+ // (as part of _createDocks), but using inverted math
+ index -= primaryMonitorIndex;
+
+ if (index < 0)
+ index += this._monitors.length;
+
+ monitor.index = index;
+ }
+ }
+
+ get primaryMonitor() {
+ return this._primaryMonitor;
+ }
+
+ get monitors() {
+ return this._monitors;
+ }
+});
+
+/**
+ * @param settings
+ */
+function setShortcut(settings) {
+ const shortcutText = settings.get_string('shortcut-text');
+ const [success, key, mods] = Gtk.accelerator_parse(shortcutText);
+
+ if (success && Gtk.accelerator_valid(key, mods)) {
+ const shortcut = Gtk.accelerator_name(key, mods);
+ settings.set_strv('shortcut', [shortcut]);
+ } else {
+ settings.set_strv('shortcut', []);
+ }
+}
+
+const DockSettings = GObject.registerClass({
+ Implements: [Gtk.BuilderScope],
+}, class DashToDockSettings extends GObject.Object {
+ _init(extensionPreferences) {
+ super._init();
+
+ this._extensionPreferences = extensionPreferences;
+ this._settings = extensionPreferences.getSettings(
+ 'org.gnome.shell.extensions.dash-to-dock');
+ this._appSwitcherSettings = new Gio.Settings({schema_id: 'org.gnome.shell.app-switcher'});
+ this._rtl = Gtk.Widget.get_default_direction() === Gtk.TextDirection.RTL;
+
+ this._builder = new Gtk.Builder();
+ this._builder.set_scope(this);
+ this._builder.set_translation_domain(
+ extensionPreferences.metadata['gettext-domain']);
+ this._builder.add_from_file(`${extensionPreferences.path}/Settings.ui`);
+
+ this.widget = this._builder.get_object('settings_notebook');
+
+ // Set a reasonable initial window height
+ this.widget.connect('realize', () => {
+ const rootWindow = this.widget.get_root();
+ rootWindow.set_default_size(-1, 850);
+ rootWindow.connect('close-request', () => this._onWindowsClosed());
+ });
+
+ // Timeout to delay the update of the settings
+ this._dock_size_timeout = 0;
+ this._icon_size_timeout = 0;
+ this._opacity_timeout = 0;
+
+ this._monitorsConfig = new MonitorsConfig();
+ this._bindSettings();
+ }
+
+ _onWindowsClosed() {
+ if (this._dock_size_timeout) {
+ GLib.source_remove(this._dock_size_timeout);
+ delete this._dock_size_timeout;
+ }
+
+ if (this._icon_size_timeout) {
+ GLib.source_remove(this._icon_size_timeout);
+ delete this._icon_size_timeout;
+ }
+
+ if (this._opacity_timeout) {
+ GLib.source_remove(this._opacity_timeout);
+ delete this._opacity_timeout;
+ }
+ }
+
+ vfunc_create_closure(builder, handlerName, flags, connectObject) {
+ if (flags & Gtk.BuilderClosureFlags.SWAPPED)
+ throw new Error('Unsupported template signal flag "swapped"');
+
+ if (typeof this[handlerName] === 'undefined')
+ throw new Error(`${handlerName} is undefined`);
+
+ return this[handlerName].bind(connectObject || this);
+ }
+
+ dock_display_combo_changed_cb(combo) {
+ if (!this._monitors?.length || this._updatingSettings)
+ return;
+
+ const preferredMonitor = this._monitors[combo.get_active()].connector;
+
+ this._updatingSettings = true;
+ this._settings.set_string('preferred-monitor-by-connector', preferredMonitor);
+ this._settings.set_int('preferred-monitor', -2);
+ this._updatingSettings = false;
+ }
+
+ position_top_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 0);
+ }
+
+ position_right_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 1);
+ }
+
+ position_bottom_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 2);
+ }
+
+ position_left_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('dock-position', 3);
+ }
+
+ icon_size_combo_changed_cb(combo) {
+ this._settings.set_int('dash-max-icon-size', this._allIconSizes[combo.get_active()]);
+ }
+
+ dock_size_scale_value_changed_cb(scale) {
+ // Avoid settings the size continuously
+ if (this._dock_size_timeout > 0)
+ GLib.source_remove(this._dock_size_timeout);
+ this._dock_size_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('height-fraction', scale.get_value());
+ this._dock_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ icon_size_scale_value_changed_cb(scale) {
+ // Avoid settings the size consinuosly
+ if (this._icon_size_timeout > 0)
+ GLib.source_remove(this._icon_size_timeout);
+ this._icon_size_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_int('dash-max-icon-size', scale.get_value());
+ this._icon_size_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ preview_size_scale_format_value_cb(scale, value) {
+ return value === 0 ? 'auto' : value;
+ }
+
+ preview_size_scale_value_changed_cb(scale) {
+ this._settings.set_double('preview-size-scale', scale.get_value());
+ }
+
+ custom_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ GLib.source_remove(this._opacity_timeout);
+ this._opacity_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('background-opacity', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ min_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ GLib.source_remove(this._opacity_timeout);
+ this._opacity_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('min-alpha', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ max_opacity_scale_value_changed_cb(scale) {
+ // Avoid settings the opacity consinuosly as it's change is animated
+ if (this._opacity_timeout > 0)
+ GLib.source_remove(this._opacity_timeout);
+ this._opacity_timeout = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT, SCALE_UPDATE_TIMEOUT, () => {
+ this._settings.set_double('max-alpha', scale.get_value());
+ this._opacity_timeout = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+
+ all_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 0);
+ }
+
+ focus_application_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 1);
+ }
+
+ maximized_windows_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 2);
+ }
+
+ always_on_top_radio_button_toggled_cb(button) {
+ if (button.get_active())
+ this._settings.set_enum('intellihide-mode', 3);
+ }
+
+ _updateMonitorsSettings() {
+ // Monitor options
+ const preferredMonitor = this._settings.get_int('preferred-monitor');
+ const preferredMonitorByConnector = this._settings.get_string('preferred-monitor-by-connector');
+ const dockMonitorCombo = this._builder.get_object('dock_monitor_combo');
+
+ this._monitors = [];
+ dockMonitorCombo.remove_all();
+ let primaryIndex = -1;
+
+ // Add connected monitors
+ for (const monitor of this._monitorsConfig.monitors) {
+ if (!monitor.active && monitor.index !== preferredMonitor)
+ continue;
+
+ if (monitor.isPrimary) {
+ dockMonitorCombo.append_text(
+ /* Translators: This will be followed by Display Name - Connector. */
+ `${__('Primary monitor: ') + monitor.displayName} - ${
+ monitor.connector}`);
+ primaryIndex = this._monitors.length;
+ } else {
+ dockMonitorCombo.append_text(
+ /* Translators: Followed by monitor index, Display Name - Connector. */
+ `${__('Secondary monitor ') + (monitor.index + 1)} - ${
+ monitor.displayName} - ${monitor.connector}`);
+ }
+
+ this._monitors.push(monitor);
+
+ if (monitor.index === preferredMonitor ||
+ (preferredMonitor === -2 && preferredMonitorByConnector === monitor.connector))
+ dockMonitorCombo.set_active(this._monitors.length - 1);
+ }
+
+ if (dockMonitorCombo.get_active() < 0 && primaryIndex >= 0)
+ dockMonitorCombo.set_active(primaryIndex);
+ }
+
+ _bindSettings() {
+ // Position and size panel
+
+ this._updateMonitorsSettings();
+ this._monitorsConfig.connect('updated',
+ () => this._updateMonitorsSettings());
+ this._settings.connect('changed::preferred-monitor',
+ () => this._updateMonitorsSettings());
+ this._settings.connect('changed::preferred-monitor-by-connector',
+ () => this._updateMonitorsSettings());
+
+ // Position option
+ const position = this._settings.get_enum('dock-position');
+
+ switch (position) {
+ case 0:
+ this._builder.get_object('position_top_button').set_active(true);
+ break;
+ case 1:
+ this._builder.get_object('position_right_button').set_active(true);
+ break;
+ case 2:
+ this._builder.get_object('position_bottom_button').set_active(true);
+ break;
+ case 3:
+ this._builder.get_object('position_left_button').set_active(true);
+ break;
+ }
+
+ if (this._rtl) {
+ /* Left is Right in rtl as a setting */
+ this._builder.get_object('position_left_button').set_label(__('Right'));
+ this._builder.get_object('position_right_button').set_label(__('Left'));
+ }
+
+ // Intelligent autohide options
+ this._settings.bind('dock-fixed',
+ this._builder.get_object('intelligent_autohide_switch'),
+ 'active',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('dock-fixed',
+ this._builder.get_object('intelligent_autohide_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('autohide',
+ this._builder.get_object('autohide_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('autohide-in-fullscreen',
+ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-dock-urgent-notify',
+ this._builder.get_object('show_dock_urgent_notify_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('require_pressure_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('intellihide',
+ this._builder.get_object('intellihide_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('animation-time',
+ this._builder.get_object('animation_duration_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hide-delay',
+ this._builder.get_object('hide_timeout_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-delay',
+ this._builder.get_object('show_timeout_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('pressure-threshold',
+ this._builder.get_object('pressure_threshold_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ // this._builder.get_object('animation_duration_spinbutton').set_value(
+ // this._settings.get_double('animation-time'));
+
+ // Create dialog for intelligent autohide advanced settings
+ this._builder.get_object('intelligent_autohide_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Intelligent autohide customization'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ const box = this._builder.get_object('intelligent_autohide_advanced_settings_box');
+ dialog.get_content_area().append(box);
+
+ this._settings.bind('intellihide',
+ this._builder.get_object('intellihide_mode_box'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ // intellihide mode
+
+ const intellihideModeRadioButtons = [
+ this._builder.get_object('all_windows_radio_button'),
+ this._builder.get_object('focus_application_windows_radio_button'),
+ this._builder.get_object('maximized_windows_radio_button'),
+ this._builder.get_object('always_on_top_radio_button'),
+ ];
+
+ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('require_pressure_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('autohide_enable_in_fullscreen_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ this._settings.bind('autohide',
+ this._builder.get_object('show_dock_urgent_notify_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('show_timeout_spinbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('show_timeout_label'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('pressure_threshold_spinbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('require-pressure-to-show',
+ this._builder.get_object('pressure_threshold_label'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (_, id) => {
+ if (id === 1) {
+ // restore default settings for the relevant keys
+ const keys = ['intellihide', 'autohide', 'intellihide-mode',
+ 'autohide-in-fullscreen', 'show-dock-urgent-notify',
+ 'require-pressure-to-show', 'animation-time',
+ 'show-delay', 'hide-delay', 'pressure-threshold'];
+ keys.forEach(function (val) {
+ this._settings.set_value(val, this._settings.get_default_value(val));
+ }, this);
+ intellihideModeRadioButtons[this._settings.get_enum('intellihide-mode')].set_active(true);
+ } else {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ }
+ });
+
+ dialog.present();
+ });
+
+ // size options
+ const dockSizeScale = this._builder.get_object('dock_size_scale');
+ dockSizeScale.set_value(this._settings.get_double('height-fraction'));
+ dockSizeScale.add_mark(0.9, Gtk.PositionType.TOP, null);
+ dockSizeScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)} %`;
+ });
+ const iconSizeScale = this._builder.get_object('icon_size_scale');
+ iconSizeScale.set_range(8, DEFAULT_ICONS_SIZES[0]);
+ iconSizeScale.set_value(this._settings.get_int('dash-max-icon-size'));
+ DEFAULT_ICONS_SIZES.forEach(val => {
+ iconSizeScale.add_mark(val, Gtk.PositionType.TOP, val.toString());
+ });
+ iconSizeScale.set_format_value_func((_, value) => {
+ return `${value} px`;
+ });
+ this._builder.get_object('preview_size_scale').set_value(
+ this._settings.get_double('preview-size-scale'));
+
+ // Corrent for rtl languages
+ if (this._rtl) {
+ // Flip value position: this is not done automatically
+ dockSizeScale.set_value_pos(Gtk.PositionType.LEFT);
+ iconSizeScale.set_value_pos(Gtk.PositionType.LEFT);
+ // I suppose due to a bug, having a more than one mark and one above
+ // a value of 100 makes the rendering of the marks wrong in rtl.
+ // This doesn't happen setting the scale as not flippable
+ // and then manually inverting it
+ iconSizeScale.set_flippable(false);
+ iconSizeScale.set_inverted(true);
+ }
+
+ this._settings.bind('icon-size-fixed',
+ this._builder.get_object('icon_size_fixed_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('extend-height',
+ this._builder.get_object('dock_size_extend_checkbutton'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('extend-height',
+ this._builder.get_object('dock_size_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+ this._settings.bind('always-center-icons',
+ this._builder.get_object('dock_center_icons_check'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('extend-height',
+ this._builder.get_object('dock_center_icons_check'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('multi-monitor',
+ this._builder.get_object('dock_monitor_combo'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN);
+
+
+ // Apps panel
+
+ this._settings.bind('show-running',
+ this._builder.get_object('show_running_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ const applicationButtonIsolationButton =
+ this._builder.get_object('application_button_isolation_button');
+ this._settings.bind('isolate-workspaces',
+ applicationButtonIsolationButton,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ applicationButtonIsolationButton.connect(
+ 'notify::sensitive', check => {
+ if (check.sensitive) {
+ [check.label] = check.label.split('\n');
+ } else {
+ check.label += `\n${
+ __('Managed by GNOME Multitasking\'s Application Switching setting.')}`;
+ }
+ });
+ this._appSwitcherSettings.bind('current-workspace-only',
+ applicationButtonIsolationButton,
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN |
+ Gio.SettingsBindFlags.SYNC_CREATE);
+ this._settings.bind('workspace-agnostic-urgent-windows',
+ this._builder.get_object('application_button_urgent_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('isolate-monitors',
+ this._builder.get_object('application_button_monitor_isolation_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-windows-preview',
+ this._builder.get_object('windows_preview_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('multi-monitor',
+ this._builder.get_object('multi_monitor_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-favorites',
+ this._builder.get_object('show_favorite_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-trash',
+ this._builder.get_object('show_trash_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-mounts',
+ this._builder.get_object('show_mounts_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-mounts-only-mounted',
+ this._builder.get_object('show_only_mounted_devices_check'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-mounts-network',
+ this._builder.get_object('show_network_volumes_check'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('isolate-locations',
+ this._builder.get_object('isolate_locations_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ const isolateLocationsBindings = ['show_trash_switch', 'show_mounts_switch'];
+ const updateIsolateLocations = () => {
+ this._builder.get_object('isolate_locations_row').sensitive =
+ isolateLocationsBindings.some(s => this._builder.get_object(s).active);
+ };
+ updateIsolateLocations();
+ isolateLocationsBindings.forEach(s => this._builder.get_object(s).connect(
+ 'notify::active', () => updateIsolateLocations()));
+ this._settings.bind('dance-urgent-applications',
+ this._builder.get_object('wiggle_urgent_applications_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hide-tooltip',
+ this._builder.get_object('hide_tooltip_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-icons-emblems',
+ this._builder.get_object('show_icons_emblems_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ const notificationsCounterCheck = this._builder.get_object(
+ 'notifications_counter_check');
+ this._settings.bind('show-icons-notifications-counter',
+ notificationsCounterCheck,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-icons-emblems',
+ notificationsCounterCheck,
+ 'sensitive',
+ Gio.SettingsBindFlags.GET);
+
+ const applicationsOverrideCounter =
+ this._builder.get_object('applications_override_counter');
+ this._settings.bind('application-counter-overrides-notifications',
+ applicationsOverrideCounter,
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ notificationsCounterCheck.bind_property('active',
+ applicationsOverrideCounter, 'sensitive',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._settings.connect('changed::show-icons-emblems', () => {
+ if (this._settings.get_boolean('show-icons-emblems'))
+ applicationsOverrideCounter.sensitive = notificationsCounterCheck.active;
+ else
+ applicationsOverrideCounter.sensitive = false;
+ });
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('show_applications_button_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-apps-at-top',
+ this._builder.get_object('application_button_first_button'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('application_button_first_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('application_button_animation_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-apps-always-in-the-edge',
+ this._builder.get_object('show_apps_always_in_the_edge'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('show-show-apps-button',
+ this._builder.get_object('show_apps_always_in_the_edge'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('scroll-to-focused-application',
+ this._builder.get_object('scroll_to_icon_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+
+ // Behavior panel
+
+ this._settings.bind('hot-keys',
+ this._builder.get_object('hot_keys_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hot-keys',
+ this._builder.get_object('overlay_button'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._builder.get_object('click_action_combo').set_active(this._settings.get_enum('click-action'));
+ this._builder.get_object('click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('click-action', widget.get_active());
+ });
+
+ this._builder.get_object('scroll_action_combo').set_active(this._settings.get_enum('scroll-action'));
+ this._builder.get_object('scroll_action_combo').connect('changed', widget => {
+ this._settings.set_enum('scroll-action', widget.get_active());
+ });
+
+ this._builder.get_object('shift_click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('shift-click-action', widget.get_active());
+ });
+
+ this._builder.get_object('middle_click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('middle-click-action', widget.get_active());
+ });
+ this._builder.get_object('shift_middle_click_action_combo').connect('changed', widget => {
+ this._settings.set_enum('shift-middle-click-action', widget.get_active());
+ });
+
+ // Create dialog for number overlay options
+ this._builder.get_object('overlay_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Show dock and application numbers'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ const box = this._builder.get_object('box_overlay_shortcut');
+ dialog.get_content_area().append(box);
+
+ this._builder.get_object('overlay_switch').set_active(
+ this._settings.get_boolean('hotkeys-overlay'));
+ this._builder.get_object('show_dock_switch').set_active(
+ this._settings.get_boolean('hotkeys-show-dock'));
+
+ // We need to update the shortcut 'strv' when the text is modified
+ this._settings.connect('changed::shortcut-text', () => setShortcut(this._settings));
+ this._settings.bind('shortcut-text',
+ this._builder.get_object('shortcut_entry'),
+ 'text',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('hotkeys-overlay',
+ this._builder.get_object('overlay_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('hotkeys-show-dock',
+ this._builder.get_object('show_dock_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('shortcut-timeout',
+ this._builder.get_object('timeout_spinbutton'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (_, id) => {
+ if (id === 1) {
+ // restore default settings for the relevant keys
+ const keys = ['shortcut-text', 'hotkeys-overlay',
+ 'hotkeys-show-dock', 'shortcut-timeout'];
+ keys.forEach(function (val) {
+ this._settings.set_value(val, this._settings.get_default_value(val));
+ }, this);
+ } else {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ }
+ });
+
+ dialog.present();
+ });
+
+ // Create dialog for middle-click options
+ this._builder.get_object('middle_click_options_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Customize middle-click behavior'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ // GTK+ leaves positive values for application-defined response ids.
+ // Use +1 for the reset action
+ dialog.add_button(__('Reset to defaults'), 1);
+
+ const box = this._builder.get_object('box_middle_click_options');
+ dialog.get_content_area().append(box);
+
+ this._builder.get_object('shift_click_action_combo').set_active(
+ this._settings.get_enum('shift-click-action'));
+
+ this._builder.get_object('middle_click_action_combo').set_active(
+ this._settings.get_enum('middle-click-action'));
+
+ this._builder.get_object('shift_middle_click_action_combo').set_active(
+ this._settings.get_enum('shift-middle-click-action'));
+
+ this._settings.bind('shift-click-action',
+ this._builder.get_object('shift_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('middle-click-action',
+ this._builder.get_object('middle_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('shift-middle-click-action',
+ this._builder.get_object('shift_middle_click_action_combo'),
+ 'active-id',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ dialog.connect('response', (_, id) => {
+ if (id === 1) {
+ // restore default settings for the relevant keys
+ const keys = ['shift-click-action', 'middle-click-action', 'shift-middle-click-action'];
+ keys.forEach(function (val) {
+ this._settings.set_value(val, this._settings.get_default_value(val));
+ }, this);
+ this._builder.get_object('shift_click_action_combo').set_active(
+ this._settings.get_enum('shift-click-action'));
+ this._builder.get_object('middle_click_action_combo').set_active(
+ this._settings.get_enum('middle-click-action'));
+ this._builder.get_object('shift_middle_click_action_combo').set_active(
+ this._settings.get_enum('shift-middle-click-action'));
+ } else {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ }
+ });
+
+ dialog.present();
+ });
+
+ // Appearance Panel
+
+ this._settings.bind('apply-custom-theme',
+ this._builder.get_object('customize_theme'),
+ 'sensitive',
+ Gio.SettingsBindFlags.INVERT_BOOLEAN | Gio.SettingsBindFlags.GET);
+ this._settings.bind('apply-custom-theme',
+ this._builder.get_object('builtin_theme_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('custom-theme-shrink',
+ this._builder.get_object('shrink_dash_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ // Running indicators
+ this._builder.get_object('running_indicators_combo').set_active(
+ this._settings.get_enum('running-indicator-style')
+ );
+ this._builder.get_object('running_indicators_combo').connect(
+ 'changed',
+ widget => {
+ this._settings.set_enum('running-indicator-style', widget.get_active());
+ }
+ );
+
+ if (this._settings.get_enum('running-indicator-style') === RunningIndicatorStyle.DEFAULT)
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false);
+
+ this._settings.connect('changed::running-indicator-style', () => {
+ if (this._settings.get_enum('running-indicator-style') === RunningIndicatorStyle.DEFAULT)
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(false);
+ else
+ this._builder.get_object('running_indicators_advance_settings_button').set_sensitive(true);
+ });
+
+ // Create dialog for running indicators advanced settings
+ this._builder.get_object('running_indicators_advance_settings_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Customize running indicators'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ const box = this._builder.get_object('running_dots_advance_settings_box');
+ dialog.get_content_area().append(box);
+
+ this._settings.bind('running-indicator-dominant-color',
+ this._builder.get_object('dominant_color_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('custom-theme-customize-running-dots',
+ this._builder.get_object('dot_style_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('custom-theme-customize-running-dots',
+ this._builder.get_object('dot_style_settings_box'),
+ 'sensitive', Gio.SettingsBindFlags.DEFAULT);
+
+ const rgba = new Gdk.RGBA();
+ rgba.parse(this._settings.get_string('custom-theme-running-dots-color'));
+ this._builder.get_object('dot_color_colorbutton').set_rgba(rgba);
+
+ this._builder.get_object('dot_color_colorbutton').connect('notify::rgba', button => {
+ const css = button.rgba.to_string();
+
+ this._settings.set_string('custom-theme-running-dots-color', css);
+ });
+
+ rgba.parse(this._settings.get_string('custom-theme-running-dots-border-color'));
+ this._builder.get_object('dot_border_color_colorbutton').set_rgba(rgba);
+
+ this._builder.get_object('dot_border_color_colorbutton').connect('notify::rgba', button => {
+ const css = button.rgba.to_string();
+
+ this._settings.set_string('custom-theme-running-dots-border-color', css);
+ });
+
+ this._settings.bind('custom-theme-running-dots-border-width',
+ this._builder.get_object('dot_border_width_spin_button'),
+ 'value',
+ Gio.SettingsBindFlags.DEFAULT);
+
+
+ dialog.connect('response', () => {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ });
+
+ dialog.present();
+ });
+
+ this._settings.bind('custom-background-color',
+ this._builder.get_object('custom_background_color_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT);
+ this._settings.bind('custom-background-color',
+ this._builder.get_object('custom_background_color'),
+ 'sensitive', Gio.SettingsBindFlags.DEFAULT);
+
+ const rgba = new Gdk.RGBA();
+ rgba.parse(this._settings.get_string('background-color'));
+ this._builder.get_object('custom_background_color').set_rgba(rgba);
+
+ this._builder.get_object('custom_background_color').connect('notify::rgba', button => {
+ const css = button.rgba.to_string();
+
+ this._settings.set_string('background-color', css);
+ });
+
+ // Opacity
+ this._builder.get_object('customize_opacity_combo').set_active_id(
+ this._settings.get_enum('transparency-mode').toString()
+ );
+ this._builder.get_object('customize_opacity_combo').connect(
+ 'changed',
+ widget => {
+ this._settings.set_enum('transparency-mode', parseInt(widget.get_active_id()));
+ }
+ );
+
+ const customOpacityScale = this._builder.get_object('custom_opacity_scale');
+ customOpacityScale.set_value(this._settings.get_double('background-opacity'));
+ customOpacityScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)}%`;
+ });
+
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED)
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false);
+
+ this._settings.connect('changed::transparency-mode', () => {
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.FIXED)
+ this._builder.get_object('custom_opacity_scale').set_sensitive(false);
+ else
+ this._builder.get_object('custom_opacity_scale').set_sensitive(true);
+ });
+
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC)
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false);
+
+
+ this._settings.connect('changed::transparency-mode', () => {
+ if (this._settings.get_enum('transparency-mode') !== TransparencyMode.DYNAMIC)
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(false);
+
+ else
+ this._builder.get_object('dynamic_opacity_button').set_sensitive(true);
+ });
+
+ // Create dialog for transparency advanced settings
+ this._builder.get_object('dynamic_opacity_button').connect('clicked', () => {
+ const dialog = new Gtk.Dialog({
+ title: __('Customize opacity'),
+ transient_for: this.widget.get_root(),
+ use_header_bar: true,
+ modal: true,
+ });
+
+ const box = this._builder.get_object('advanced_transparency_dialog');
+ dialog.get_content_area().append(box);
+
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('customize_alphas_switch'),
+ 'active',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('min_alpha_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind(
+ 'customize-alphas',
+ this._builder.get_object('max_alpha_scale'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ const minAlphaScale = this._builder.get_object('min_alpha_scale');
+ const maxAlphaScale = this._builder.get_object('max_alpha_scale');
+ minAlphaScale.set_value(
+ this._settings.get_double('min-alpha')
+ );
+ minAlphaScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)} %`;
+ });
+ maxAlphaScale.set_format_value_func((_, value) => {
+ return `${Math.round(value * 100)} %`;
+ });
+
+ maxAlphaScale.set_value(
+ this._settings.get_double('max-alpha')
+ );
+
+ dialog.connect('response', () => {
+ // remove the settings box so it doesn't get destroyed;
+ dialog.get_content_area().remove(box);
+ dialog.destroy();
+ });
+
+ dialog.present();
+ });
+
+
+ this._settings.bind('unity-backlit-items',
+ this._builder.get_object('unity_backlit_items_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind('apply-glossy-effect',
+ this._builder.get_object('apply_gloss_effect_checkbutton'),
+ 'active', Gio.SettingsBindFlags.DEFAULT
+ );
+ this._settings.bind('unity-backlit-items',
+ this._builder.get_object('apply_gloss_effect_checkbutton'),
+ 'sensitive',
+ Gio.SettingsBindFlags.DEFAULT
+ );
+
+ this._settings.bind('force-straight-corner',
+ this._builder.get_object('force_straight_corner_switch'),
+ 'active', Gio.SettingsBindFlags.DEFAULT);
+
+ this._settings.bind('disable-overview-on-startup',
+ this._builder.get_object('show_overview_on_startup_switch'),
+ 'active', Gio.SettingsBindFlags.INVERT_BOOLEAN);
+
+ // About Panel
+
+ this._builder.get_object('extension_version').set_label(
+ `${this._extensionPreferences.metadata.version}`);
+ }
+});
+
+export default class DockPreferences extends ExtensionPreferences {
+ getPreferencesWidget() {
+ const settings = new DockSettings(this);
+ const {widget} = settings;
+ return widget;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiled
new file mode 100644
index 0000000..a67f68f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
new file mode 100644
index 0000000..3a762c3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/schemas/org.gnome.shell.extensions.dash-to-dock.gschema.xml
@@ -0,0 +1,644 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist gettext-domain="gnome-shell-extensions">
+ <enum id='org.gnome.shell.extensions.dash-to-dock.clickAction'>
+ <value value='0' nick='skip'/>
+ <value value='1' nick='minimize'/>
+ <value value='2' nick='launch'/>
+ <value value='3' nick='cycle-windows'/>
+ <value value='4' nick='minimize-or-overview'/>
+ <value value='5' nick='previews'/>
+ <value value='6' nick='minimize-or-previews'/>
+ <value value='7' nick='focus-or-previews'/>
+ <value value='8' nick='focus-or-appspread'/>
+ <value value='9' nick='focus-minimize-or-previews'/>
+ <value value='10' nick='focus-minimize-or-appspread'/>
+ <value value='11' nick='quit'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.scrollAction'>
+ <value value='0' nick='do-nothing'/>
+ <value value='1' nick='cycle-windows'/>
+ <value value='2' nick='switch-workspace'/>
+ </enum>
+ <!-- this is mean to Match StSide. LEFT and RIGHT actual position in reversed in
+ rtl languages -->
+ <enum id='org.gnome.shell.extensions.dash-to-dock.position'>
+ <value value='0' nick='TOP'/>
+ <value value='1' nick='RIGHT'/>
+ <value value='2' nick='BOTTOM'/>
+ <value value='3' nick='LEFT'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.intellihide-mode'>
+ <value value='0' nick='ALL_WINDOWS'/>
+ <value value='1' nick='FOCUS_APPLICATION_WINDOWS'/>
+ <value value='2' nick='MAXIMIZED_WINDOWS'/>
+ <value value='3' nick='ALWAYS_ON_TOP'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.transparency-mode'>
+ <value value='0' nick='DEFAULT'/>
+ <value value='1' nick='FIXED'/>
+ <value value='3' nick='DYNAMIC'/>
+ </enum>
+ <enum id='org.gnome.shell.extensions.dash-to-dock.running-indicator-style'>
+ <value value='0' nick='DEFAULT'/>
+ <value value='1' nick='DOTS'/>
+ <value value='2' nick='SQUARES'/>
+ <value value='3' nick='DASHES'/>
+ <value value='4' nick='SEGMENTED'/>
+ <value value='5' nick='SOLID'/>
+ <value value='6' nick='CILIORA'/>
+ <value value='7' nick='METRO'/>
+ <value value='8' nick='BINARY'/>
+ <value value='9' nick='DOT'/>
+ </enum>
+ <schema path="/org/gnome/shell/extensions/dash-to-dock/" id="org.gnome.shell.extensions.dash-to-dock">
+ <key name="dock-position" enum="org.gnome.shell.extensions.dash-to-dock.position">
+ <default>'BOTTOM'</default>
+ <summary>Dock position</summary>
+ <description>Dock is shown on the Left, Right, Top or Bottom side of the screen.</description>
+ </key>
+ <key type="d" name="animation-time">
+ <default>0.2</default>
+ <summary>Animation time</summary>
+ <description>Sets the time duration of the autohide effect.</description>
+ </key>
+ <key type="d" name="show-delay">
+ <default>0.25</default>
+ <summary>Show delay</summary>
+ <description>Sets the delay after the mouse reaches the screen border before showing the dock.</description>
+ </key>
+ <key type="d" name="hide-delay">
+ <default>0.20</default>
+ <summary>Show delay</summary>
+ <description>Sets the delay after the mouse left the dock before hiding it.</description>
+ </key>
+ <key type="b" name="disable-overview-on-startup">
+ <default>false</default>
+ <summary>Do not show overview on startup</summary>
+ <description>Whether to show or not the overview during startup animation.</description>
+ </key>
+ <key type="b" name="custom-background-color">
+ <default>false</default>
+ <summary>Set a custom dash background background color</summary>
+ <description>Sets the color for the dash background.</description>
+ </key>
+ <key type="s" name="background-color">
+ <default>"#ffffff"</default>
+ <summary>Dash background color.</summary>
+ <description>Customize the background color of the dash.</description>
+ </key>
+ <key name="transparency-mode" enum="org.gnome.shell.extensions.dash-to-dock.transparency-mode">
+ <default>'DEFAULT'</default>
+ <summary>Transparency mode for the dock</summary>
+ <description>FIXED: constant transparency. DYNAMIC: dock takes the opaque style only when windows are close to it.</description>
+ </key>
+ <key name="running-indicator-style" enum="org.gnome.shell.extensions.dash-to-dock.running-indicator-style">
+ <default>'DEFAULT'</default>
+ <summary>...</summary>
+ <description>DEFAULT: .... DOTS: ....</description>
+ </key>
+ <key type="b" name="running-indicator-dominant-color">
+ <default>false</default>
+ <summary>Use application icon dominant color for the indicator color</summary>
+ <description></description>
+ </key>
+ <key type="b" name="customize-alphas">
+ <default>false</default>
+ <summary>Manually set the min and max opacity</summary>
+ <description>For the dynamic mode, the min/max opacity values will be given by 'min-alpha' and 'max-alpha'.</description>
+ </key>
+ <key type="d" name="min-alpha">
+ <default>0.2</default>
+ <summary>Opacity of the dash background when free-floating</summary>
+ <description>Sets the opacity of the dash background when no windows are close.</description>
+ </key>
+ <key type="d" name="max-alpha">
+ <default>0.8</default>
+ <summary>Opacity of the dash background when windows are close.</summary>
+ <description>Sets the opacity of the dash background when windows are close.</description>
+ </key>
+ <key type="d" name="background-opacity">
+ <default>0.8</default>
+ <summary>Opacity of the dash background</summary>
+ <description>Sets the opacity of the dash background when in autohide mode.</description>
+ </key>
+ <key type="b" name="manualhide">
+ <default>false</default>
+ <summary>Dock not shown on desktop</summary>
+ <description>Enable or disable manualhide mode</description>
+ </key>
+ <key type="b" name="intellihide">
+ <default>true</default>
+ <summary>Dock dodges windows</summary>
+ <description>Enable or disable intellihide mode</description>
+ </key>
+ <key name="intellihide-mode" enum="org.gnome.shell.extensions.dash-to-dock.intellihide-mode">
+ <default>'FOCUS_APPLICATION_WINDOWS'</default>
+ <summary>Define which windows are considered for intellihide.</summary>
+ <description></description>
+ </key>
+ <key type="b" name="autohide">
+ <default>true</default>
+ <summary>Dock shown on mouse over</summary>
+ <description>Enable or disable autohide mode</description>
+ </key>
+ <key type="b" name="require-pressure-to-show">
+ <default>true</default>
+ <summary>Require pressure to show dash</summary>
+ <description>Enable or disable requiring pressure to show the dash</description>
+ </key>
+ <key type="d" name="pressure-threshold">
+ <default>100</default>
+ <summary>Pressure threshold</summary>
+ <description>Sets how much pressure is needed to show the dash.</description>
+ </key>
+ <key type="b" name="autohide-in-fullscreen">
+ <default>false</default>
+ <summary>Enable autohide in fullscreen mode.</summary>
+ <description>Enable autohide in fullscreen mode.</description>
+ </key>
+ <key type="b" name="show-dock-urgent-notify">
+ <default>true</default>
+ <summary>Show dock for urgent notifications.</summary>
+ <description>Show dock when urgent notifications are received.</description>
+ </key>
+ <key type="b" name="dock-fixed">
+ <default>false</default>
+ <summary>Dock always visible</summary>
+ <description>Dock is always visible</description>
+ </key>
+ <key type="b" name="scroll-switch-workspace">
+ <default>true</default>
+ <summary>Switch workspace by scrolling over the dock</summary>
+ <description>Add the possibility to switch workspace by mouse scrolling over the dock.</description>
+ </key>
+ <key type="i" name="dash-max-icon-size">
+ <default>48</default>
+ <summary>Maximum dash icon size</summary>
+ <description>Set the allowed maximum dash icon size. Allowed range: 16..64.</description>
+ </key>
+ <key type="d" name="preview-size-scale">
+ <default>0</default>
+ <summary>Preview size scale</summary>
+ <description>Set the allowed maximum dash preview size scale. Allowed range: 0,00..1,00.</description>
+ </key>
+ <key type="b" name="icon-size-fixed">
+ <default>false</default>
+ <summary>Fixed icon size</summary>
+ <description>Keep the icon size fixed by scrolling the dock.</description>
+ </key>
+ <key type="b" name="apply-custom-theme">
+ <default>false</default>
+ <summary>Apply custom theme</summary>
+ <description>Apply customization to the dash appearance</description>
+ </key>
+ <key type="b" name="custom-theme-shrink">
+ <default>false</default>
+ <summary>TODO</summary>
+ <description>TODO</description>
+ </key>
+ <key type="b" name="custom-theme-customize-running-dots">
+ <default>false</default>
+ <summary>Customize the style of the running application indicators.</summary>
+ <description>Customize the style of the running application indicators.</description>
+ </key>
+ <key type="s" name="custom-theme-running-dots-color">
+ <default>"#ffffff"</default>
+ <summary>Running application indicators color</summary>
+ <description>Customize the color of the running application indicators.</description>
+ </key>
+ <key type="s" name="custom-theme-running-dots-border-color">
+ <default>"#ffffff"</default>
+ <summary>Running application indicators border color.</summary>
+ <description>Customize the border color of the running application indicators.</description>
+ </key>
+ <key type="i" name="custom-theme-running-dots-border-width">
+ <default>0</default>
+ <summary>Running application indicators border width.</summary>
+ <description>Customize the border width of the running application indicators.</description>
+ </key>
+ <key type="b" name="show-running">
+ <default>true</default>
+ <summary>Show running apps</summary>
+ <description>Show or hide running applications icons in the dash</description>
+ </key>
+ <key type="b" name="isolate-workspaces">
+ <default>false</default>
+ <summary>Provide workspace isolation</summary>
+ <description>Dash shows only windows from the current workspace, this is applied only if gnome-shell 'current-workspace-only' app-switcher settings is disabled</description>
+ </key>
+ <key type="b" name="workspace-agnostic-urgent-windows">
+ <default>true</default>
+ <summary>Show urgent windows on each workspace</summary>
+ <description>Show or hide urgent windows on each workspace in the dash</description>
+ </key>
+ <key type="b" name="isolate-monitors">
+ <default>false</default>
+ <summary>Provide monitor isolation</summary>
+ <description>Dash shows only windows from the monitor</description>
+ </key>
+ <key type="b" name="scroll-to-focused-application">
+ <default>true</default>
+ <summary>Scroll to focused application</summary>
+ <description>Ensure that the focused application icon is always visible in the dash</description>
+ </key>
+ <key type="b" name="show-windows-preview">
+ <default>true</default>
+ <summary>Show preview of the open windows</summary>
+ <description>Replace open windows list with windows previews</description>
+ </key>
+ <key type="b" name="default-windows-preview-to-open">
+ <default>false</default>
+ <summary>Open windows preview by default</summary>
+ <description>Window previews will show windows by default, rather than hide them behind a drop dowm menu.</description>
+ </key>
+ <key type="b" name="show-favorites">
+ <default>true</default>
+ <summary>Show favorites apps</summary>
+ <description>Show or hide favorite applications icons in the dash</description>
+ </key>
+ <key type="b" name="show-trash">
+ <default>true</default>
+ <summary>Show trash can</summary>
+ <description>Show or hide the trash can icon in the dash</description>
+ </key>
+ <key type="b" name="show-mounts">
+ <default>true</default>
+ <summary>Show mounted volumes and devices</summary>
+ <description>Show or hide mounted volume and device icons in the dash</description>
+ </key>
+ <key type="b" name="show-mounts-only-mounted">
+ <default>true</default>
+ <summary>Only show mounted volumes and devices</summary>
+ <description>Show or hide unmounted volume and device icons in the dash</description>
+ </key>
+ <key type="b" name="show-mounts-network">
+ <default>false</default>
+ <summary>Show network mounted volumes</summary>
+ <description>Show or hide network volumes in the dash</description>
+ </key>
+ <key type="b" name="isolate-locations">
+ <default>true</default>
+ <summary>Isolate volumes, devices and trash windows</summary>
+ <description>Consider volume, devices and trash as different application windows and not part of the file manager</description>
+ </key>
+ <key type="b" name="dance-urgent-applications">
+ <default>true</default>
+ <summary>Wiggle urgent applications</summary>
+ <description>Wiggle urgent application icons in the dash</description>
+ </key>
+ <key type="b" name="show-show-apps-button">
+ <default>true</default>
+ <summary>Show applications button</summary>
+ <description>Show applications button in the dash</description>
+ </key>
+ <key type="b" name="show-apps-at-top">
+ <default>false</default>
+ <summary>Show application button on the left</summary>
+ <description>Show application button on the left of the dash</description>
+ </key>
+ <key type="b" name="show-apps-always-in-the-edge">
+ <default>true</default>
+ <summary>Show application button on the edge when using centered panel mode</summary>
+ </key>
+ <key type="b" name="bolt-support">
+ <default>true</default>
+ <summary>Basic compatibility with bolt extensions</summary>
+ <description>Make the extension work properly when bolt extensions is enabled</description>
+ </key>
+ <key type="d" name="height-fraction">
+ <default>0.90</default>
+ <summary>Dock max height/width (fraction of available space)</summary>
+ </key>
+ <key type="b" name="extend-height">
+ <default>false</default>
+ <summary>Extend the dock container to all the available height/width</summary>
+ </key>
+ <key type="b" name="always-center-icons">
+ <default>false</default>
+ <summary>Center icons when dock container is extended to all the available size</summary>
+ </key>
+ <key type="i" name="preferred-monitor">
+ <default>-2</default>
+ <summary>DEPRECATED: Monitor on which putting the dock (by index)</summary>
+ <description>Set on which monitor to put the dock, use -1 for the primary one. Use -2 refer to the 'preferred-monitor-by-connector' setting instead (this is the preferred value).</description>
+ </key>
+ <key type="s" name="preferred-monitor-by-connector">
+ <default>"primary"</default>
+ <summary>Monitor on which putting the dock (by connector)</summary>
+ <description>Set on which monitor to put the dock, for example 'DisplayPort-0'. Use 'primary' for primary one. This setting is only used if 'preferred-monitor' is -2.</description>
+ </key>
+ <key type="b" name="multi-monitor">
+ <default>false</default>
+ <summary>Enable multi-monitor docks</summary>
+ <description>Show a dock on every monitor</description>
+ </key>
+ <key type="b" name="minimize-shift">
+ <default>true</default>
+ <summary>Minimize on shift+click</summary>
+ </key>
+ <key type="b" name="activate-single-window">
+ <default>true</default>
+ <summary>Activate only one window</summary>
+ </key>
+ <key name="click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'cycle-windows'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when clicking on the icon of a running application</description>
+ </key>
+ <key name="scroll-action" enum="org.gnome.shell.extensions.dash-to-dock.scrollAction">
+ <default>'do-nothing'</default>
+ <summary>Action when scrolling app</summary>
+ <description>Set the action that is executed when scrolling on the application icon</description>
+ </key>
+ <key name="shift-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'minimize'</default>
+ <summary>Action when shift+clicking on a running app</summary>
+ <description>Set the action that is executed when shift+clicking on the icon of a running application</description>
+ </key>
+ <key name="middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'launch'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when middle-clicking on the icon of a running application</description>
+ </key>
+ <key name="shift-middle-click-action" enum="org.gnome.shell.extensions.dash-to-dock.clickAction">
+ <default>'launch'</default>
+ <summary>Action when clicking on a running app</summary>
+ <description>Set the action that is executed when shift+middle-clicking on the icon of a running application</description>
+ </key>
+ <key type="b" name="hot-keys">
+ <default>true</default>
+ <summary>Super Hot-Keys</summary>
+ <description>Launch and switch between dash items using Super+(0-9)</description>
+ </key>
+ <key type="b" name="hotkeys-show-dock">
+ <default>true</default>
+ <summary>Show the dock when using the hotkeys</summary>
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description>
+ </key>
+ <key type="s" name="shortcut-text">
+ <default>"&lt;Super&gt;q"</default>
+ <summary>Keybinding to show the dock and the number overlay.</summary>
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description>
+ </key>
+ <key type="as" name="shortcut">
+ <default><![CDATA[['<Super>q']]]></default>
+ <summary>Keybinding to show the dock and the number overlay.</summary>
+ <description>Behavior depends on hotkeys-show-dock and hotkeys-overlay.</description>
+ </key>
+ <key type="d" name="shortcut-timeout">
+ <default>2</default>
+ <summary>Timeout to hide the dock</summary>
+ <description>Sets the time duration before the dock is hidden again.</description>
+ </key>
+ <key type="b" name="hotkeys-overlay">
+ <default>true</default>
+ <summary>Show the dock when using the hotkeys</summary>
+ <description>The dock will be quickly shown so that the number-overlay is visible and app activation is easier</description>
+ </key>
+ <key name="app-ctrl-hotkey-1" type="as">
+ <default><![CDATA[['<Ctrl><Super>1']]]></default>
+ <summary>Keybinding to launch 1st dash app</summary>
+ <description>
+ Keybinding to launch 1st app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-2" type="as">
+ <default><![CDATA[['<Ctrl><Super>2']]]></default>
+ <summary>Keybinding to launch 2nd dash app</summary>
+ <description>
+ Keybinding to launch 2nd app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-3" type="as">
+ <default><![CDATA[['<Ctrl><Super>3']]]></default>
+ <summary>Keybinding to launch 3rd dash app</summary>
+ <description>
+ Keybinding to launch 3rd app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-4" type="as">
+ <default><![CDATA[['<Ctrl><Super>4']]]></default>
+ <summary>Keybinding to launch 4th dash app</summary>
+ <description>
+ Keybinding to launch 4th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-5" type="as">
+ <default><![CDATA[['<Ctrl><Super>5']]]></default>
+ <summary>Keybinding to launch 5th dash app</summary>
+ <description>
+ Keybinding to launch 5th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-6" type="as">
+ <default><![CDATA[['<Ctrl><Super>6']]]></default>
+ <summary>Keybinding to launch 6th dash app</summary>
+ <description>
+ Keybinding to launch 6th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-7" type="as">
+ <default><![CDATA[['<Ctrl><Super>7']]]></default>
+ <summary>Keybinding to launch 7th dash app</summary>
+ <description>
+ Keybinding to launch 7th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-8" type="as">
+ <default><![CDATA[['<Ctrl><Super>8']]]></default>
+ <summary>Keybinding to launch 8th dash app</summary>
+ <description>
+ Keybinding to launch 8th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-9" type="as">
+ <default><![CDATA[['<Ctrl><Super>9']]]></default>
+ <summary>Keybinding to launch 9th dash app</summary>
+ <description>
+ Keybinding to launch 9th app.
+ </description>
+ </key>
+ <key name="app-ctrl-hotkey-10" type="as">
+ <default><![CDATA[['<Ctrl><Super>0']]]></default>
+ <summary>Keybinding to launch 10th dash app</summary>
+ <description>
+ Keybinding to launch 10th app.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-1" type="as">
+ <default><![CDATA[['<Shift><Super>1']]]></default>
+ <summary>Keybinding to trigger 1st dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 1st app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-2" type="as">
+ <default><![CDATA[['<Shift><Super>2']]]></default>
+ <summary>Keybinding to trigger 2nd dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 2nd app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-3" type="as">
+ <default><![CDATA[['<Shift><Super>3']]]></default>
+ <summary>Keybinding to trigger 3rd dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 3rd app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-4" type="as">
+ <default><![CDATA[['<Shift><Super>4']]]></default>
+ <summary>Keybinding to trigger 4th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 4th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-5" type="as">
+ <default><![CDATA[['<Shift><Super>5']]]></default>
+ <summary>Keybinding to trigger 5th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 5th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-6" type="as">
+ <default><![CDATA[['<Shift><Super>6']]]></default>
+ <summary>Keybinding to trigger 6th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 6th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-7" type="as">
+ <default><![CDATA[['<Shift><Super>7']]]></default>
+ <summary>Keybinding to trigger 7th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 7th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-8" type="as">
+ <default><![CDATA[['<Shift><Super>8']]]></default>
+ <summary>Keybinding to trigger 8th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 8th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-9" type="as">
+ <default><![CDATA[['<Shift><Super>9']]]></default>
+ <summary>Keybinding to trigger 9th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 9th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-shift-hotkey-10" type="as">
+ <default><![CDATA[['<Shift><Super>0']]]></default>
+ <summary>Keybinding to trigger 10th dash app with shift behavior</summary>
+ <description>
+ Keybinding to trigger 10th app with shift behavior.
+ </description>
+ </key>
+ <key name="app-hotkey-1" type="as">
+ <default><![CDATA[['<Super>1']]]></default>
+ <summary>Keybinding to trigger 1st dash app</summary>
+ <description>
+ Keybinding to either show or launch the 1st application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-2" type="as">
+ <default><![CDATA[['<Super>2']]]></default>
+ <summary>Keybinding to trigger 2nd dash app</summary>
+ <description>
+ Keybinding to either show or launch the 2nd application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-3" type="as">
+ <default><![CDATA[['<Super>3']]]></default>
+ <summary>Keybinding to trigger 3rd dash app</summary>
+ <description>
+ Keybinding to either show or launch the 3rd application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-4" type="as">
+ <default><![CDATA[['<Super>4']]]></default>
+ <summary>Keybinding to trigger 4th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 4th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-5" type="as">
+ <default><![CDATA[['<Super>5']]]></default>
+ <summary>Keybinding to trigger 5th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 5th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-6" type="as">
+ <default><![CDATA[['<Super>6']]]></default>
+ <summary>Keybinding to trigger 6th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 6th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-7" type="as">
+ <default><![CDATA[['<Super>7']]]></default>
+ <summary>Keybinding to trigger 7th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 7th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-8" type="as">
+ <default><![CDATA[['<Super>8']]]></default>
+ <summary>Keybinding to trigger 8th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 8th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-9" type="as">
+ <default><![CDATA[['<Super>9']]]></default>
+ <summary>Keybinding to trigger 9th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 9th application in the dash.
+ </description>
+ </key>
+ <key name="app-hotkey-10" type="as">
+ <default><![CDATA[['<Super>0']]]></default>
+ <summary>Keybinding to trigger 10th dash app</summary>
+ <description>
+ Keybinding to either show or launch the 10th application in the dash.
+ </description>
+ </key>
+ <key name="force-straight-corner" type="b">
+ <default>false</default>
+ <summary>Force straight corners in dash</summary>
+ <description>Make the borders in the dash non rounded</description>
+ </key>
+ <key name="unity-backlit-items" type="b">
+ <default>false</default>
+ <summary>Enable unity7 like glossy backlit items</summary>
+ <description>Emulate the unity7 backlit glossy items behaviour</description>
+ </key>
+ <key name="apply-glossy-effect" type="b">
+ <default>true</default>
+ <summary>Enable glossy effect</summary>
+ <description>Toggle the glossy effect of the unity7 backlit feature.</description>
+ </key>
+ <key name="hide-tooltip" type="b">
+ <default>false</default>
+ <summary>Hide application tooltip</summary>
+ <description>Hide tooltip when hovering application in the dash.</description>
+ </key>
+ <key name="show-icons-emblems" type="b">
+ <default>true</default>
+ <summary>Show application icons emblems</summary>
+ <description>Show application icons emblems such as notification counters an progress bars.</description>
+ </key>
+ <key name="show-icons-notifications-counter" type="b">
+ <default>true</default>
+ <summary>Show the number of unread notifications in icons</summary>
+ <description>Show application icons emblems such as notification</description>
+ </key>
+ <key name="application-counter-overrides-notifications" type="b">
+ <default>true</default>
+ <summary>Wether the application-provioded counter via Unity API should override the notifications or be summed to it</summary>
+ <description>If an application provides a counter through the Unity API, then such value will be used. Otherwise the counter will be summed with the unread notification numbers (if enabled)</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css
new file mode 100644
index 0000000..b8d1935
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/stylesheet.css
@@ -0,0 +1,1478 @@
+.dash-item-container .overview-tile .overview-icon,
+.dash-item-container .show-apps .overview-icon {
+ background-color: rgba(255, 255, 255, 0); }
+
+.dash-item-container .overview-tile:hover .overview-icon,
+.dash-item-container .overview-tile.focused .overview-icon,
+.dash-item-container .show-apps:hover .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+
+.dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 8px;
+ offset-y: 2px; }
+
+#dashtodockContainer.bottom #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom #dash .dash-background {
+ margin: 0;
+ margin-bottom: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.bottom #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 4px; }
+ #dashtodockContainer.bottom #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-bottom: 14px;
+ padding-top: 10px; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.fixed #dash .dash-background {
+ margin-top: 4px; }
+
+#dashtodockContainer.bottom.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.fixed #dash .dash-item-container .show-apps {
+ padding-top: 14px; }
+
+.dash-label.bottom {
+ margin-bottom: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.bottom.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-background {
+ margin: 0;
+ margin-bottom: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 4px; }
+ #dashtodockContainer.bottom.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-bottom: 7px;
+ padding-top: 3px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.shrink.fixed #dash .dash-background {
+ margin-top: 1px; }
+
+#dashtodockContainer.bottom.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-top: 4px; }
+
+.dash-label.bottom.shrink {
+ margin-bottom: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.bottom.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom.extended #dash .dash-background {
+ margin: 0;
+ margin-bottom: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.bottom.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 0; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 8px; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 8px; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 8px; }
+ #dashtodockContainer.bottom.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 8px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom.extended #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom.extended #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-bottom: 10px;
+ padding-top: 10px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.extended.fixed #dash .dash-background {
+ margin-top: 0; }
+
+#dashtodockContainer.bottom.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.extended.fixed #dash .dash-item-container .show-apps {
+ padding-top: 10px; }
+
+.dash-label.bottom {
+ margin-bottom: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.bottom.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-bottom: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-bottom: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-bottom: 0;
+ padding-top: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 6px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-bottom: 3px;
+ padding-top: 3px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.bottom.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.bottom.extended.shrink.fixed #dash .dash-background {
+ margin-top: 0; }
+
+#dashtodockContainer.bottom.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.bottom.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-top: 3px; }
+
+.dash-label.bottom.shrink {
+ margin-bottom: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.bottom {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.bottom:ltr:first-child, .dashtodock-app-well-preview-menu-item.bottom:rtl:last-child {
+ padding-left: 1em; }
+ .dashtodock-app-well-preview-menu-item.bottom:ltr:last-child, .dashtodock-app-well-preview-menu-item.bottom:rtl:first-child {
+ padding-right: 1em; }
+
+#dashtodockContainer.top #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top #dash .dash-background {
+ margin: 0;
+ margin-top: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.top #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-top: 4px; }
+ #dashtodockContainer.top #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-top: 14px;
+ padding-bottom: 10px; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.fixed #dash .dash-background {
+ margin-bottom: 4px; }
+
+#dashtodockContainer.top.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 14px; }
+
+.dash-label.top {
+ margin-top: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.top.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top.shrink #dash .dash-background {
+ margin: 0;
+ margin-top: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.top.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-top: 4px; }
+ #dashtodockContainer.top.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-top: 7px;
+ padding-bottom: 3px; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.shrink.fixed #dash .dash-background {
+ margin-bottom: 1px; }
+
+#dashtodockContainer.top.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 4px; }
+
+.dash-label.top.shrink {
+ margin-top: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.top.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top.extended #dash .dash-background {
+ margin: 0;
+ margin-top: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.top.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 4px;
+ width: 1px;
+ height: 0;
+ margin-top: 0; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 8px; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 8px; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 8px; }
+ #dashtodockContainer.top.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 8px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top.extended #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top.extended #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-top: 10px;
+ padding-bottom: 10px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.extended.fixed #dash .dash-background {
+ margin-bottom: 0; }
+
+#dashtodockContainer.top.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.extended.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 10px; }
+
+.dash-label.top {
+ margin-top: 10px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+#dashtodockContainer.top.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-top: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ margin: 0 1px;
+ width: 1px;
+ height: 0;
+ margin-top: 0; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-top: 0;
+ padding-bottom: 0; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-left: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-left: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-right: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-right: 6px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 0 2px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container:ltr:first-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container:rtl:last-child {
+ margin-left: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container:ltr:last-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container:rtl:first-child {
+ margin-right: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-top: 3px;
+ padding-bottom: 3px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:ltr:first-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:rtl:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:ltr:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:rtl:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:ltr:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:rtl:last-child {
+ padding-left: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:ltr:last-child, #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app:rtl:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:ltr:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps:rtl:first-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:ltr:last-child,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile:rtl:first-child {
+ padding-right: 0; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.top.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.top.extended.shrink.fixed #dash .dash-background {
+ margin-bottom: 0; }
+
+#dashtodockContainer.top.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.top.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-bottom: 3px; }
+
+.dash-label.top.shrink {
+ margin-top: 3px;
+ -x-offset: 0;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.top {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.top:ltr:first-child, .dashtodock-app-well-preview-menu-item.top:rtl:last-child {
+ padding-left: 1em; }
+ .dashtodock-app-well-preview-menu-item.top:ltr:last-child, .dashtodock-app-well-preview-menu-item.top:rtl:first-child {
+ padding-right: 1em; }
+
+#dashtodockContainer.left #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left #dash .dash-background {
+ margin: 0;
+ margin-left: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.left #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-left: 4px; }
+ #dashtodockContainer.left #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-left: 14px;
+ padding-right: 10px; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.fixed #dash .dash-background {
+ margin-right: 4px; }
+
+#dashtodockContainer.left.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.fixed #dash .dash-item-container .show-apps {
+ padding-right: 14px; }
+
+.dash-label.left {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.left.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left.shrink #dash .dash-background {
+ margin: 0;
+ margin-left: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.left.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-left: 4px; }
+ #dashtodockContainer.left.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-left: 7px;
+ padding-right: 3px; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.shrink.fixed #dash .dash-background {
+ margin-right: 1px; }
+
+#dashtodockContainer.left.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-right: 4px; }
+
+.dash-label.left.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+#dashtodockContainer.left.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left.extended #dash .dash-background {
+ margin: 0;
+ margin-left: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.left.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-left: 0; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 8px; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 8px; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 8px; }
+ #dashtodockContainer.left.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 8px; }
+ #dashtodockContainer.left.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-left: 10px;
+ padding-right: 10px; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.extended.fixed #dash .dash-background {
+ margin-right: 0; }
+
+#dashtodockContainer.left.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.extended.fixed #dash .dash-item-container .show-apps {
+ padding-right: 10px; }
+
+.dash-label.left {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.left.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-left: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-left: 0; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-left: 0;
+ padding-right: 0; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 6px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-left: 3px;
+ padding-right: 3px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.left.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.left.extended.shrink.fixed #dash .dash-background {
+ margin-right: 0; }
+
+#dashtodockContainer.left.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.left.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-right: 3px; }
+
+.dash-label.left.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.left {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.left:first-child {
+ padding-top: 1em; }
+ .dashtodock-app-well-preview-menu-item.left:last-child {
+ padding-bottom: 1em; }
+
+#dashtodockContainer.right #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right #dash .dash-background {
+ margin: 0;
+ margin-right: 4px;
+ padding: 0;
+ border-radius: 24px;
+ spacing: 6px; }
+ #dashtodockContainer.right #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-right: 4px; }
+ #dashtodockContainer.right #dash #dashtodockDashContainer {
+ padding: 10px;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-right: 14px;
+ padding-left: 10px; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.fixed #dash .dash-background {
+ margin-left: 4px; }
+
+#dashtodockContainer.right.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.fixed #dash .dash-item-container .show-apps {
+ padding-left: 14px; }
+
+.dash-label.right {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.right.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right.shrink #dash .dash-background {
+ margin: 0;
+ margin-right: 4px;
+ padding: 0;
+ border-radius: 18px;
+ spacing: 2px; }
+ #dashtodockContainer.right.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-right: 4px; }
+ #dashtodockContainer.right.shrink #dash #dashtodockDashContainer {
+ padding: 3px;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-right: 7px;
+ padding-left: 3px; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.shrink.fixed #dash .dash-background {
+ margin-left: 1px; }
+
+#dashtodockContainer.right.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-left: 4px; }
+
+.dash-label.right.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+#dashtodockContainer.right.extended #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right.extended #dash .dash-background {
+ margin: 0;
+ margin-right: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 6px; }
+ #dashtodockContainer.right.extended #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 4px 0;
+ margin-right: 0; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 8px; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 8px; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 8px; }
+ #dashtodockContainer.right.extended #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 8px; }
+ #dashtodockContainer.right.extended #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile {
+ padding: 2px 2px;
+ padding-right: 10px;
+ padding-left: 10px; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right.extended #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right.extended #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right.extended #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right.extended #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.extended.fixed #dash .dash-background {
+ margin-left: 0; }
+
+#dashtodockContainer.right.extended.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.extended.fixed #dash .dash-item-container .show-apps {
+ padding-left: 10px; }
+
+.dash-label.right {
+ margin: 0;
+ -x-offset: 10px;
+ -y-offset: 0; }
+
+#dashtodockContainer.right.extended.shrink #dash {
+ margin: 0px;
+ padding: 0px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-background {
+ margin: 0;
+ margin-right: 0;
+ padding: 0;
+ border-radius: 0;
+ spacing: 2px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-separator {
+ background-color: rgba(255, 255, 255, 0.1);
+ width: 0;
+ height: 1px;
+ margin: 1px 0;
+ margin-right: 0; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer {
+ padding: 0;
+ padding-right: 0;
+ padding-left: 0; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer > :first-child .show-apps {
+ padding-top: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:first-child {
+ padding-top: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer > :last-child .show-apps {
+ padding-bottom: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash #dashtodockDashContainer #dashtodockDashScrollview:last-child {
+ padding-bottom: 6px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container {
+ /* Disable all margins defined upstream, we handle them here */
+ margin: 2px 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container > * {
+ margin: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container:first-child {
+ margin-top: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container:last-child {
+ margin-bottom: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile {
+ padding: 1px 1px;
+ padding-right: 3px;
+ padding-left: 3px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app:first-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps:first-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile:first-child {
+ padding-top: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app:last-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps:last-child,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile:last-child {
+ padding-bottom: 0; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app.updating,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .show-apps.updating,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile.updating {
+ opacity: 0.5; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app.running .overview-icon,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile.running .overview-icon {
+ background-image: none; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app.focused .overview-icon,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile.focused .overview-icon {
+ background-color: rgba(238, 238, 238, 0.2); }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app .app-well-app-running-dot,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .app-well-app .app-grid-running-dot,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile .app-well-app-running-dot,
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container .overview-tile .app-grid-running-dot {
+ margin-bottom: 2px; }
+ #dashtodockContainer.right.extended.shrink #dash .dash-item-container > StButton {
+ transition-duration: 250;
+ background-size: contain; }
+
+#dashtodockContainer.right.extended.shrink.fixed #dash .dash-background {
+ margin-left: 0; }
+
+#dashtodockContainer.right.extended.shrink.fixed #dash .dash-item-container .app-well-app,
+#dashtodockContainer.right.extended.shrink.fixed #dash .dash-item-container .show-apps {
+ padding-left: 3px; }
+
+.dash-label.right.shrink {
+ margin: 0;
+ -x-offset: 3px;
+ -y-offset: 0; }
+
+.dashtodock-app-well-preview-menu-item.right {
+ padding: 0.5em 0.5em; }
+ .dashtodock-app-well-preview-menu-item.right:first-child {
+ padding-top: 1em; }
+ .dashtodock-app-well-preview-menu-item.right:last-child {
+ padding-bottom: 1em; }
+
+#dashtodockContainer.top.shrink #dash .dash-background {
+ margin-top: 4px;
+ margin-bottom: 0; }
+
+#dashtodockContainer.straight-corner #dash .dash-background,
+#dashtodockContainer.shrink.straight-corner #dash .dash-background {
+ border-radius: 0px; }
+
+/* Scrollview style */
+.bottom #dashtodockDashScrollview,
+.top #dashtodockDashScrollview {
+ -st-hfade-offset: 24px; }
+
+.left #dashtodockDashScrollview,
+.right #dashtodockDashScrollview {
+ -st-vfade-offset: 24px; }
+
+#dashtodockContainer.dashtodock #dash .dash-background {
+ background: #303030; }
+
+#dashtodockContainer.dashtodock .progress-bar,
+.overview-tile .progress-bar,
+.icon-grid .progress-bar {
+ /* Customization of the progress bar style. The possible elements
+ * are:
+ *
+ * -XXXXXX-YYYYYY: plain color
+ * -XXXXXX-YYYYYY-color-start: start color for gradient
+ * -XXXXXX-YYYYYY-color-end: end color for gradient
+ * -XXXXXX-YYYYYY-offset-start: offset for start color in gradient (0-1)
+ * -XXXXXX-YYYYYY-offset-end: offset for end color in gradient (0-1)
+ * -XXXXXX-line-width: width value for the line used for the border
+ *
+ * XXXXXX can be either
+ *
+ * progress-bar-track: the "track" over which the progress bar is shown
+ * progress-bar: the progress bar itself
+ *
+ * YYYYYY can be either
+ *
+ * background: for the background color
+ * border: for the border color
+ *
+ * plain color entries have priority over gradients, so if both are set for
+ * an element, the plain color will be used.
+ *
+ * Examples:
+ *
+ * -progress-bar-track-background: rgba(128, 128, 0, 0.75);
+ * -progress-bar-track-border: rgba(0, 0, 255, 0.25);
+ *
+ * -progress-bar-background: rgba(0, 204, 204, 1);
+ * -progress-bar-border: rgba(230, 230, 230, 1);
+ *
+ * Default values when these entries aren't set are equivalent to:
+ * -progress-bar-track-background-color-start: 64, 64, 64, 1.0
+ * -progress-bar-track-background-color-end: 89, 89, 89, 1.0
+ * -progress-bar-track-background-start-offset: 0.4
+ * -progress-bar-track-background-end-offset: 0.9
+ * -progress-bar-track-border-color-start: 128, 128, 128, 0.1
+ * -progress-bar-track-border-color-end: 89, 89, 89, 0.4
+ * -progress-bar-track-border-start-offset: 0.5
+ * -progress-bar-track-border-end-offset: 0.9
+ * -progress-bar-track-line-width: 1
+ * -progress-bar-background: 204, 204, 204, 1.0
+ * -progress-bar-border: 230, 230, 230, 1.0
+ * -progress-bar-height-factor: (0.20) 20% of the icon size
+ * -progress-bar-line-width: 1
+ * -progress-bar-top-offset: undefined
+ * -progress-bar-valign: 1.0
+ * -progress-bar-horizontal-padding: 0.05 (5% of icon size)
+ * -progress-bar-vertical-padding: 0.05 (5% of icon size)
+ */
+ -progress-bar-track-background: rgba(0, 0, 0, 0.45);
+ -progress-bar-track-border: rgba(0, 0, 0, 0.7);
+ -progress-bar-background: white;
+ -progress-bar-border: white; }
+
+#overview .progress-bar,
+.apps-scroll-view .progress-bar {
+ -progress-bar-height-factor: 0.15;
+ -progress-bar-top-offset: 0;
+ -progress-bar-valign: 0.95; }
+
+#overview .grid-search-results .overview-tile .progress-bar,
+#overview .app-folder-dialog .progress-bar {
+ -progress-bar-height-factor: 0.13; }
+
+#dashtodockContainer.top #dash .placeholder,
+#dashtodockContainer.bottom #dash .placeholder {
+ width: 32px;
+ height: 1px; }
+
+/*
+ * This is applied to a dummy actor. Only the alpha value for the background and border color
+ * and the transition-duration are used
+ */
+#dashtodockContainer.dummy-opaque {
+ background-color: rgba(0, 0, 0, 0.8);
+ border-color: rgba(0, 0, 0, 0.4);
+ transition-duration: 300ms; }
+
+/*
+ * This is applied to a dummy actor. Only the alpha value for the background and border color
+ * and the transition-duration are used
+ */
+#dashtodockContainer.dummy-transparent {
+ background-color: rgba(0, 0, 0, 0.2);
+ border-color: rgba(0, 0, 0, 0.1);
+ transition-duration: 500ms; }
+
+#dashtodockContainer .number-overlay,
+.overview-tile .number-overlay,
+.icon-grid .number-overlay {
+ color: white;
+ background-color: rgba(0, 0, 0, 0.8);
+ text-align: center; }
+
+#dashtodockContainer .notification-badge,
+.overview-tile .notification-badge,
+.icon-grid .notification-badge {
+ color: white;
+ background-color: red;
+ padding: 0.2em 0.5em;
+ border-radius: 1em;
+ font-weight: bold;
+ text-align: center;
+ margin: 2px; }
+
+#dashtodockPreviewSeparator.popup-separator-menu-item-horizontal {
+ width: 1px;
+ height: auto;
+ border-right-width: 1px;
+ margin: 32px 0px; }
+
+#dashtodockContainer .metro .overview-icon {
+ border-radius: 0px; }
+
+#dashtodockContainer.bottom .metro.running2.focused,
+#dashtodockContainer.top .metro.running2.focused {
+ background-image: url("./media/highlight_stacked_bg.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.left .metro.running2.focused,
+#dashtodockContainer.right .metro.running2.focused {
+ background-image: url("./media/highlight_stacked_bg_h.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.bottom .metro.running3.focused,
+#dashtodockContainer.top .metro.running3.focused {
+ background-image: url("./media/highlight_stacked_bg.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.left .metro.running3.focused,
+#dashtodockContainer.right .metro.running3.focused {
+ background-image: url("./media/highlight_stacked_bg_h.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.bottom .metro.running4.focused,
+#dashtodockContainer.top .metro.running4.focused {
+ background-image: url("./media/highlight_stacked_bg.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
+
+#dashtodockContainer.left .metro.running4.focused,
+#dashtodockContainer.right .metro.running4.focused {
+ background-image: url("./media/highlight_stacked_bg_h.svg");
+ background-position: 0px 0px;
+ background-size: contain; }
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js
new file mode 100644
index 0000000..611eb40
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/theming.js
@@ -0,0 +1,572 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+import {
+ Clutter,
+ Cogl,
+ GObject,
+ Meta,
+ St,
+} from './dependencies/gi.js';
+
+import {Main} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Utils,
+} from './imports.js';
+
+const {signals: Signals} = imports;
+
+/*
+ * DEFAULT: transparency given by theme
+ * FIXED: constant transparency chosen by user
+ * DYNAMIC: apply 'transparent' style when no windows are close to the dock
+ * */
+const TransparencyMode = {
+ DEFAULT: 0,
+ FIXED: 1,
+ DYNAMIC: 3,
+};
+
+const Labels = Object.freeze({
+ TRANSPARENCY: Symbol('transparency'),
+});
+
+export const PositionStyleClass = Object.freeze([
+ 'top',
+ 'right',
+ 'bottom',
+ 'left',
+]);
+
+/**
+ * Manage theme customization and custom theme support
+ */
+export class ThemeManager {
+ constructor(dock) {
+ this._signalsHandler = new Utils.GlobalSignalsHandler(this);
+ this._bindSettingsChanges();
+ this._actor = dock;
+ this._dash = dock.dash;
+
+ // initialize colors with generic values
+ this._customizedBackground = {red: 0, green: 0, blue: 0, alpha: 0};
+ this._customizedBorder = {red: 0, green: 0, blue: 0, alpha: 0};
+ this._transparency = new Transparency(dock);
+
+ this._signalsHandler.add([
+ // When theme changes re-obtain default background color
+ St.ThemeContext.get_for_stage(global.stage),
+ 'changed',
+ this.updateCustomTheme.bind(this),
+ ], [
+ // update :overview pseudoclass
+ Main.overview,
+ 'showing',
+ this._onOverviewShowing.bind(this),
+ ], [
+ Main.overview,
+ 'hiding',
+ this._onOverviewHiding.bind(this),
+ ]);
+
+ this._updateCustomStyleClasses();
+
+ // destroy themeManager when the managed actor is destroyed (e.g. extension unload)
+ // in order to disconnect signals
+ this._signalsHandler.add(this._actor, 'destroy', () => this.destroy());
+ }
+
+ destroy() {
+ this.emit('destroy');
+ this._transparency.destroy();
+ this._destroyed = true;
+ }
+
+ _onOverviewShowing() {
+ this._actor.add_style_pseudo_class('overview');
+ }
+
+ _onOverviewHiding() {
+ this._actor.remove_style_pseudo_class('overview');
+ }
+
+ _updateDashOpacity() {
+ const newAlpha = Docking.DockManager.settings.backgroundOpacity;
+
+ const [backgroundColor, borderColor] = this._getDefaultColors();
+
+ if (!backgroundColor)
+ return;
+
+ // Get the background and border alphas. We check the background alpha
+ // for a minimum of .001 to prevent division by 0 errors
+ const backgroundAlpha = Math.max(Math.round(backgroundColor.alpha / 2.55) / 100, .001);
+ let borderAlpha = Math.round(borderColor.alpha / 2.55) / 100;
+
+ // The border and background alphas should remain in sync
+ // We also limit the borderAlpha to a maximum of 1 (full opacity)
+ borderAlpha = Math.min((borderAlpha / backgroundAlpha) * newAlpha, 1);
+
+ this._customizedBackground = `rgba(${
+ backgroundColor.red},${
+ backgroundColor.green},${
+ backgroundColor.blue},${
+ newAlpha})`;
+
+ this._customizedBorder = `rgba(${
+ borderColor.red},${
+ borderColor.green},${
+ borderColor.blue},${
+ borderAlpha})`;
+ }
+
+ _getDefaultColors() {
+ // Prevent shell crash if the actor is not on the stage.
+ // It happens enabling/disabling repeatedly the extension
+ if (!this._dash._background.get_stage())
+ return [null, null];
+
+ // Remove custom style
+ const oldStyle = this._dash._background.get_style();
+ this._dash._background.set_style(null);
+
+ const themeNode = this._dash._background.get_theme_node();
+ this._dash._background.set_style(oldStyle);
+
+ const backgroundColor = themeNode.get_background_color();
+
+ // Just in case the theme has different border colors ..
+ // We want to find the inside border-color of the dock because it is
+ // the side most visible to the user. We do this by finding the side
+ // opposite the position
+ const position = Utils.getPosition();
+ let side = position + 2;
+ if (side > 3)
+ side = Math.abs(side - 4);
+
+ const borderColor = themeNode.get_border_color(side);
+
+ return [backgroundColor, borderColor];
+ }
+
+ _updateDashColor() {
+ // Retrieve the color. If needed we will adjust it before passing it to
+ // this._transparency.
+ let [backgroundColor] = this._getDefaultColors();
+
+ if (!backgroundColor)
+ return;
+
+ const {settings} = Docking.DockManager;
+
+ if (settings.customBackgroundColor) {
+ // When applying a custom color, we need to check the alpha value,
+ // if not the opacity will always be overridden by the color below.
+ // Note that if using 'dynamic' transparency modes,
+ // the opacity will be set by the opaque/transparent styles anyway.
+ let newAlpha = Math.round(backgroundColor.alpha / 2.55) / 100;
+
+ ({backgroundColor} = settings);
+ // backgroundColor is a string like rgb(0,0,0)
+ const Color = Clutter.Color ?? Cogl.Color;
+ const [ret, color] = Color.from_string(backgroundColor);
+ if (!ret) {
+ logError(new Error(`${backgroundColor} is not a valid color string`));
+ return;
+ }
+
+ if (settings.transparencyMode === TransparencyMode.FIXED) {
+ newAlpha = settings.backgroundOpacity;
+ this._customizedBackground =
+ `rgba(${color.red}, ${color.green}, ${color.blue}, ${newAlpha})`;
+ } else {
+ this._customizedBackground = backgroundColor;
+ }
+
+ this._customizedBorder = this._customizedBackground;
+
+ color.alpha = newAlpha * 255;
+ this._transparency.setColor(color);
+ } else {
+ // backgroundColor is a {Clutter,Cogl}.Color object
+ this._transparency.setColor(backgroundColor);
+ }
+ }
+
+ _updateCustomStyleClasses() {
+ const {settings} = Docking.DockManager;
+
+ if (settings.applyCustomTheme)
+ this._actor.add_style_class_name('dashtodock');
+ else
+ this._actor.remove_style_class_name('dashtodock');
+
+ if (settings.customThemeShrink)
+ this._actor.add_style_class_name('shrink');
+ else
+ this._actor.remove_style_class_name('shrink');
+
+ if (settings.runningIndicatorStyle !== 0)
+ this._actor.add_style_class_name('running-dots');
+ else
+ this._actor.remove_style_class_name('running-dots');
+
+ // If not the built-in theme option is not selected
+ if (!settings.applyCustomTheme) {
+ if (settings.forceStraightCorner)
+ this._actor.add_style_class_name('straight-corner');
+ else
+ this._actor.remove_style_class_name('straight-corner');
+ } else {
+ this._actor.remove_style_class_name('straight-corner');
+ }
+ }
+
+ updateCustomTheme() {
+ if (this._destroyed)
+ throw new Error(`Impossible to update a destroyed ${this.constructor.name}`);
+ this._updateCustomStyleClasses();
+ this._updateDashOpacity();
+ this._updateDashColor();
+ this._adjustTheme();
+ this.emit('updated');
+ }
+
+ /**
+ * Reimported back and adapted from atomdock
+ */
+ _adjustTheme() {
+ // Prevent shell crash if the actor is not on the stage.
+ // It happens enabling/disabling repeatedly the extension
+ if (!this._dash._background.get_stage())
+ return;
+
+ const {settings} = Docking.DockManager;
+
+ // Remove prior style edits
+ this._dash._background.set_style(null);
+ this._transparency.disable();
+
+ // If built-in theme is enabled do nothing else
+ if (settings.applyCustomTheme)
+ return;
+
+ let newStyle = '';
+ const position = Utils.getPosition(settings);
+
+ // obtain theme border settings
+ const themeNode = this._dash._background.get_theme_node();
+ const borderColor = themeNode.get_border_color(St.Side.TOP);
+ const borderWidth = themeNode.get_border_width(St.Side.TOP);
+
+ // We're copying border and corner styles to left border and top-left
+ // corner, also removing bottom border and bottom-right corner styles
+ let borderMissingStyle = '';
+
+ if (this._rtl && (position !== St.Side.RIGHT)) {
+ borderMissingStyle = `border-right: ${borderWidth}px solid ${
+ borderColor.to_string()};`;
+ } else if (!this._rtl && (position !== St.Side.LEFT)) {
+ borderMissingStyle = `border-left: ${borderWidth}px solid ${
+ borderColor.to_string()};`;
+ }
+
+ newStyle = borderMissingStyle;
+
+ if (newStyle) {
+ // I do call set_style possibly twice so that only the background gets the transition.
+ // The transition-property css rules seems to be unsupported
+ this._dash._background.set_style(newStyle);
+ }
+
+ // Customize background
+ const fixedTransparency = settings.transparencyMode === TransparencyMode.FIXED;
+ const defaultTransparency = settings.transparencyMode === TransparencyMode.DEFAULT;
+ if (!defaultTransparency && !fixedTransparency) {
+ this._transparency.enable();
+ } else if (!defaultTransparency || settings.customBackgroundColor) {
+ newStyle = `${newStyle}background-color:${this._customizedBackground}; ` +
+ `border-color:${this._customizedBorder}; ` +
+ 'transition-delay: 0s; transition-duration: 0.250s;';
+ this._dash._background.set_style(newStyle);
+ }
+ }
+
+ _bindSettingsChanges() {
+ const keys = ['transparency-mode',
+ 'customize-alphas',
+ 'min-alpha',
+ 'max-alpha',
+ 'background-opacity',
+ 'custom-background-color',
+ 'background-color',
+ 'apply-custom-theme',
+ 'custom-theme-shrink',
+ 'custom-theme-running-dots',
+ 'extend-height',
+ 'force-straight-corner'];
+
+ this._signalsHandler.add(...keys.map(key => [
+ Docking.DockManager.settings,
+ `changed::${key}`,
+ () => this.updateCustomTheme(),
+ ]));
+ }
+}
+Signals.addSignalMethods(ThemeManager.prototype);
+
+/**
+ * The following class is based on the following upstream commit:
+ * https://git.gnome.org/browse/gnome-shell/commit/?id=447bf55e45b00426ed908b1b1035f472c2466956
+ * Transparency when free-floating
+ */
+class Transparency {
+ constructor(dock) {
+ this._dash = dock.dash;
+ this._actor = this._dash._container;
+ this._backgroundActor = this._dash._background;
+ this._dockActor = dock;
+ this._dock = dock;
+ this._panel = Main.panel;
+ this._position = Utils.getPosition();
+
+ // All these properties are replaced with the ones in the .dummy-opaque
+ // and .dummy-transparent css classes
+ this._backgroundColor = '0,0,0';
+ this._transparentAlpha = '0.2';
+ this._opaqueAlpha = '1';
+ this._transparentAlphaBorder = '0.1';
+ this._opaqueAlphaBorder = '0.5';
+ this._transparentTransition = '0ms';
+ this._opaqueTransition = '0ms';
+ this._base_actor_style = '';
+
+ this._signalsHandler = new Utils.GlobalSignalsHandler();
+ this._trackedWindows = new Map();
+ }
+
+ enable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this.disable();
+
+ this._base_actor_style = this._actor.get_style();
+ if (!this._base_actor_style)
+ this._base_actor_style = '';
+
+
+ let addedSignal = 'child-added';
+ let removedSignal = 'child-removed';
+
+ // for compatibility with Gnome Shell 45
+ if (GObject.signal_lookup('actor-added', global.window_group)) {
+ addedSignal = 'actor-added';
+ removedSignal = 'actor-removed';
+ }
+
+ this._signalsHandler.addWithLabel(Labels.TRANSPARENCY, [
+ global.window_group,
+ addedSignal,
+ this._onWindowActorAdded.bind(this),
+ ], [
+ global.window_group,
+ removedSignal,
+ this._onWindowActorRemoved.bind(this),
+ ], [
+ global.window_manager,
+ 'switch-workspace',
+ this._updateSolidStyle.bind(this),
+ ], [
+ Main.overview,
+ 'hiding',
+ this._updateSolidStyle.bind(this),
+ ], [
+ Main.overview,
+ 'showing',
+ this._updateSolidStyle.bind(this),
+ ]);
+
+ // Window signals
+ global.window_group.get_children().filter(child => {
+ // An irrelevant window actor ('Gnome-shell') produces an error when the signals are
+ // disconnected, therefore do not add signals to it.
+ return child instanceof Meta.WindowActor &&
+ child.get_meta_window().get_wm_class() !== 'Gnome-shell';
+ }).forEach(function (win) {
+ this._onWindowActorAdded(null, win);
+ }, this);
+
+ if (this._actor.get_stage())
+ this._updateSolidStyle();
+
+ this._updateStyles();
+ this._updateSolidStyle();
+
+ this.emit('transparency-enabled');
+ }
+
+ disable() {
+ // ensure I never double-register/inject
+ // although it should never happen
+ this._signalsHandler.removeWithLabel(Labels.TRANSPARENCY);
+
+ for (const key of this._trackedWindows.keys()) {
+ this._trackedWindows.get(key).forEach(id => {
+ key.disconnect(id);
+ });
+ }
+ this._trackedWindows.clear();
+
+ this.emit('transparency-disabled');
+ }
+
+ destroy() {
+ this.disable();
+ this._signalsHandler.destroy();
+ }
+
+ _onWindowActorAdded(container, metaWindowActor) {
+ const signalIds = [];
+ ['notify::allocation', 'notify::visible'].forEach(s => {
+ signalIds.push(metaWindowActor.connect(s, this._updateSolidStyle.bind(this)));
+ });
+ this._trackedWindows.set(metaWindowActor, signalIds);
+ }
+
+ _onWindowActorRemoved(container, metaWindowActor) {
+ if (!this._trackedWindows.get(metaWindowActor))
+ return;
+
+ this._trackedWindows.get(metaWindowActor).forEach(id => {
+ metaWindowActor.disconnect(id);
+ });
+ this._trackedWindows.delete(metaWindowActor);
+ this._updateSolidStyle();
+ }
+
+ _updateSolidStyle() {
+ const isNear = this._dockIsNear();
+ if (isNear) {
+ this._backgroundActor.set_style(this._opaque_style);
+ this._dockActor.remove_style_class_name('transparent');
+ this._dockActor.add_style_class_name('opaque');
+ } else {
+ this._backgroundActor.set_style(this._transparent_style);
+ this._dockActor.remove_style_class_name('opaque');
+ this._dockActor.add_style_class_name('transparent');
+ }
+
+ this.emit('solid-style-updated', isNear);
+ }
+
+ _dockIsNear() {
+ if (this._dockActor.has_style_pseudo_class('overview'))
+ return false;
+ /* Get all the windows in the active workspace that are in the primary monitor and visible */
+ const activeWorkspace = global.workspace_manager.get_active_workspace();
+ const dash = this._dash;
+ const windows = activeWorkspace.list_windows().filter(metaWindow => {
+ return metaWindow.get_monitor() === dash._monitorIndex &&
+ metaWindow.showing_on_its_workspace() &&
+ metaWindow.get_window_type() !== Meta.WindowType.DESKTOP &&
+ !metaWindow.skip_taskbar;
+ });
+
+ /* Check if at least one window is near enough to the panel.
+ * If the dock is hidden, we need to account for the space it would take
+ * up when it slides out. This is avoid an ugly transition.
+ * */
+ let factor = 0;
+ if (!Docking.DockManager.settings.dockFixed &&
+ this._dock.getDockState() === Docking.State.HIDDEN)
+ factor = 1;
+ const [leftCoord, topCoord] = this._actor.get_transformed_position();
+ let threshold;
+ if (this._position === St.Side.LEFT)
+ threshold = leftCoord + this._actor.get_width() * (factor + 1);
+ else if (this._position === St.Side.RIGHT)
+ threshold = leftCoord - this._actor.get_width() * factor;
+ else if (this._position === St.Side.TOP)
+ threshold = topCoord + this._actor.get_height() * (factor + 1);
+ else
+ threshold = topCoord - this._actor.get_height() * factor;
+
+ const scale = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ const isNearEnough = windows.some(metaWindow => {
+ let coord;
+ if (this._position === St.Side.LEFT) {
+ coord = metaWindow.get_frame_rect().x;
+ return coord < threshold + 5 * scale;
+ } else if (this._position === St.Side.RIGHT) {
+ coord = metaWindow.get_frame_rect().x + metaWindow.get_frame_rect().width;
+ return coord > threshold - 5 * scale;
+ } else if (this._position === St.Side.TOP) {
+ coord = metaWindow.get_frame_rect().y;
+ return coord < threshold + 5 * scale;
+ } else {
+ coord = metaWindow.get_frame_rect().y + metaWindow.get_frame_rect().height;
+ return coord > threshold - 5 * scale;
+ }
+ });
+
+ return isNearEnough;
+ }
+
+ _updateStyles() {
+ this._getAlphas();
+
+ this._transparent_style = `${this._base_actor_style
+ }background-color: rgba(${
+ this._backgroundColor}, ${this._transparentAlpha});` +
+ `border-color: rgba(${
+ this._backgroundColor}, ${this._transparentAlphaBorder});` +
+ `transition-duration: ${this._transparentTransition}ms;`;
+
+ this._opaque_style = `${this._base_actor_style
+ }background-color: rgba(${
+ this._backgroundColor}, ${this._opaqueAlpha});` +
+ `border-color: rgba(${
+ this._backgroundColor},${this._opaqueAlphaBorder});` +
+ `transition-duration: ${this._opaqueTransition}ms;`;
+
+ this.emit('styles-updated');
+ }
+
+ setColor(color) {
+ this._backgroundColor = `${color.red},${color.green},${color.blue}`;
+ this._updateStyles();
+ }
+
+ _getAlphas() {
+ // Create dummy object and add to the uiGroup to get it to the stage
+ const dummyObject = new St.Bin({
+ name: 'dashtodockContainer',
+ });
+ Main.uiGroup.add_child(dummyObject);
+
+ dummyObject.add_style_class_name('dummy-opaque');
+ let themeNode = dummyObject.get_theme_node();
+ this._opaqueAlpha = themeNode.get_background_color().alpha / 255;
+ this._opaqueAlphaBorder = themeNode.get_border_color(0).alpha / 255;
+ this._opaqueTransition = themeNode.get_transition_duration();
+
+ dummyObject.add_style_class_name('dummy-transparent');
+ themeNode = dummyObject.get_theme_node();
+ this._transparentAlpha = themeNode.get_background_color().alpha / 255;
+ this._transparentAlphaBorder = themeNode.get_border_color(0).alpha / 255;
+ this._transparentTransition = themeNode.get_transition_duration();
+
+ Main.uiGroup.remove_child(dummyObject);
+
+ const {settings} = Docking.DockManager;
+
+ if (settings.customizeAlphas) {
+ this._opaqueAlpha = settings.maxAlpha;
+ this._opaqueAlphaBorder = this._opaqueAlpha / 2;
+ this._transparentAlpha = settings.minAlpha;
+ this._transparentAlphaBorder = this._transparentAlpha / 2;
+ }
+ }
+}
+Signals.addSignalMethods(Transparency.prototype);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js
new file mode 100644
index 0000000..67b4d58
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/utils.js
@@ -0,0 +1,735 @@
+import {
+ Clutter,
+ GLib,
+ Gio,
+ GObject,
+ Meta,
+ Shell,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ Docking,
+} from './imports.js';
+
+const {_gi: Gi} = imports;
+
+export const SignalsHandlerFlags = Object.freeze({
+ NONE: 0,
+ CONNECT_AFTER: 1,
+});
+
+const GENERIC_KEY = Symbol('generic');
+
+/**
+ * Simplify global signals and function injections handling
+ * abstract class
+ */
+const BasicHandler = class DashToDockBasicHandler {
+ static get genericKey() {
+ return GENERIC_KEY;
+ }
+
+ constructor(parentObject) {
+ this._storage = Object.create(null);
+
+ if (parentObject) {
+ if (!(parentObject.connect instanceof Function))
+ throw new TypeError('Not a valid parent object');
+
+ if (!(parentObject instanceof GObject.Object) ||
+ GObject.signal_lookup('destroy', parentObject.constructor.$gtype)) {
+ this._parentObject = parentObject;
+ this._destroyId = parentObject.connect('destroy', () => this.destroy());
+ }
+ }
+ }
+
+ add(...args) {
+ // Convert arguments object to array, concatenate with generic
+ // Call addWithLabel with ags as if they were passed arguments
+ this.addWithLabel(GENERIC_KEY, ...args);
+ }
+
+ clear() {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ this.removeWithLabel(label));
+ }
+
+ destroy() {
+ this._parentObject?.disconnect(this._destroyId);
+ this._parentObject = null;
+
+ this.clear();
+ }
+
+ block() {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ this.blockWithLabel(label));
+ }
+
+ unblock() {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ this.unblockWithLabel(label));
+ }
+
+ addWithLabel(label, ...args) {
+ if (typeof label !== 'symbol')
+ throw new Error(`Invalid label ${label}, must be a symbol`);
+
+ let argsArray = [...args];
+ if (argsArray.every(arg => !Array.isArray(arg)))
+ argsArray = [argsArray];
+
+ if (this._storage[label] === undefined)
+ this._storage[label] = [];
+
+ // Skip first element of the arguments
+ for (const argArray of argsArray) {
+ if (argArray.length < 3)
+ throw new Error('Unexpected number of arguments');
+ const item = this._storage[label];
+ try {
+ item.push(this._create(...argArray));
+ } catch (e) {
+ logError(e);
+ }
+ }
+ }
+
+ removeWithLabel(label) {
+ this._storage[label]?.reverse().forEach(item => this._remove(item));
+ delete this._storage[label];
+ }
+
+ blockWithLabel(label) {
+ (this._storage[label] || []).forEach(item => this._block(item));
+ }
+
+ unblockWithLabel(label) {
+ (this._storage[label] || []).forEach(item => this._unblock(item));
+ }
+
+ _removeByItem(item) {
+ Object.getOwnPropertySymbols(this._storage).forEach(label =>
+ (this._storage[label] = this._storage[label].filter(it => {
+ if (!this._itemsEqual(it, item))
+ return true;
+ this._remove(item);
+ return false;
+ })));
+ }
+
+ // Virtual methods to be implemented by subclass
+
+ /**
+ * Create single element to be stored in the storage structure
+ *
+ * @param _object
+ * @param _element
+ * @param _callback
+ */
+ _create(_object, _element, _callback) {
+ throw new GObject.NotImplementedError(`_create in ${this.constructor.name}`);
+ }
+
+ /**
+ * Correctly delete single element
+ *
+ * @param _item
+ */
+ _remove(_item) {
+ throw new GObject.NotImplementedError(`_remove in ${this.constructor.name}`);
+ }
+
+ /**
+ * Block single element
+ *
+ * @param _item
+ */
+ _block(_item) {
+ throw new GObject.NotImplementedError(`_block in ${this.constructor.name}`);
+ }
+
+ /**
+ * Unblock single element
+ *
+ * @param _item
+ */
+ _unblock(_item) {
+ throw new GObject.NotImplementedError(`_unblock in ${this.constructor.name}`);
+ }
+
+ _itemsEqual(itemA, itemB) {
+ if (itemA === itemB)
+ return true;
+
+ if (itemA.length !== itemB.length)
+ return false;
+
+ return itemA.every((_, idx) => itemA[idx] === itemB[idx]);
+ }
+};
+
+/**
+ * Manage global signals
+ */
+export class GlobalSignalsHandler extends BasicHandler {
+ _create(object, event, callback, flags = SignalsHandlerFlags.NONE) {
+ if (!object)
+ throw new Error('Impossible to connect to an invalid object');
+
+ const after = flags === SignalsHandlerFlags.CONNECT_AFTER;
+ const connector = after ? object.connect_after : object.connect;
+
+ if (!connector) {
+ throw new Error(`Requested to connect to signal '${event}', ` +
+ `but no implementation for 'connect${after ? '_after' : ''}' ` +
+ `found in ${object.constructor.name}`);
+ }
+
+ const item = [object];
+ const isDestroy = event === 'destroy';
+ const isParentObject = object === this._parentObject;
+
+ if (isDestroy && !isParentObject) {
+ const originalCallback = callback;
+ callback = () => {
+ this._removeByItem(item);
+ originalCallback();
+ };
+ }
+ const id = connector.call(object, event, callback);
+ item.push(id);
+
+ if (isDestroy && isParentObject) {
+ this._parentObject.disconnect(this._destroyId);
+ this._destroyId =
+ this._parentObject.connect('destroy', () => this.destroy());
+ }
+
+ return item;
+ }
+
+ _remove(item) {
+ const [object, id] = item;
+ object.disconnect(id);
+ }
+
+ _block(item) {
+ const [object, id] = item;
+
+ if (object instanceof GObject.Object)
+ GObject.Object.prototype.block_signal_handler.call(object, id);
+ }
+
+ _unblock(item) {
+ const [object, id] = item;
+
+ if (object instanceof GObject.Object)
+ GObject.Object.prototype.unblock_signal_handler.call(object, id);
+ }
+}
+
+/**
+ * Color manipulation utilities
+ */
+export class ColorUtils {
+ // Darken or brigthen color by a fraction dlum
+ // Each rgb value is modified by the same fraction.
+ // Return "#rrggbb" string
+ static ColorLuminance(r, g, b, dlum) {
+ let rgbString = '#';
+
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(r * (1 + dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(g * (1 + dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(b * (1 + dlum), 0), 255)), 2);
+
+ return rgbString;
+ }
+
+ // Convert decimal to an hexadecimal string adding the desired padding
+ static _decimalToHex(d, padding) {
+ let hex = d.toString(16);
+ while (hex.length < padding)
+ hex = `0${hex}`;
+ return hex;
+ }
+
+ // Convert hsv ([0-1, 0-1, 0-1]) to rgb ([0-255, 0-255, 0-255]).
+ // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV
+ // here with h = [0,1] instead of [0, 360]
+ // Accept either (h,s,v) independently or {h:h, s:s, v:v} object.
+ // Return {r:r, g:g, b:b} object.
+ static HSVtoRGB(h, s, v) {
+ if (arguments.length === 1)
+ ({s, v, h} = h);
+
+ let r, g, b;
+ const c = v * s;
+ const h1 = h * 6;
+ const x = c * (1 - Math.abs(h1 % 2 - 1));
+ const m = v - c;
+
+ if (h1 <= 1) {
+ r = c + m;
+ g = x + m;
+ b = m;
+ } else if (h1 <= 2) {
+ r = x + m;
+ g = c + m;
+ b = m;
+ } else if (h1 <= 3) {
+ r = m;
+ g = c + m;
+ b = x + m;
+ } else if (h1 <= 4) {
+ r = m;
+ g = x + m;
+ b = c + m;
+ } else if (h1 <= 5) {
+ r = x + m;
+ g = m;
+ b = c + m;
+ } else {
+ r = c + m;
+ g = m;
+ b = x + m;
+ }
+
+ return {
+ r: Math.round(r * 255),
+ g: Math.round(g * 255),
+ b: Math.round(b * 255),
+ };
+ }
+
+ // Convert rgb ([0-255, 0-255, 0-255]) to hsv ([0-1, 0-1, 0-1]).
+ // Following algorithm in https://en.wikipedia.org/wiki/HSL_and_HSV
+ // here with h = [0,1] instead of [0, 360]
+ // Accept either (r,g,b) independently or {r:r, g:g, b:b} object.
+ // Return {h:h, s:s, v:v} object.
+ static RGBtoHSV(r, g, b) {
+ if (arguments.length === 1)
+ ({r, g, b} = r);
+
+ let h, s;
+
+ const M = Math.max(r, g, b);
+ const m = Math.min(r, g, b);
+ const c = M - m;
+
+ if (c === 0)
+ h = 0;
+ else if (M === r)
+ h = ((g - b) / c) % 6;
+ else if (M === g)
+ h = (b - r) / c + 2;
+ else
+ h = (r - g) / c + 4;
+
+ h /= 6;
+ const v = M / 255;
+ if (M !== 0)
+ s = c / M;
+ else
+ s = 0;
+
+ return {
+ h,
+ s,
+ v,
+ };
+ }
+}
+
+/**
+ * Manage function injection: both instances and prototype can be overridden
+ * and restored
+ */
+export class InjectionsHandler extends BasicHandler {
+ _create(object, name, injectedFunction) {
+ const original = object[name];
+
+ if (!(original instanceof Function))
+ throw new Error(`Function ${name}() is not available for ${object}`);
+
+ object[name] = function (...args) {
+ return injectedFunction.call(this, original, ...args);
+ };
+ return [object, name, original];
+ }
+
+ _remove(item) {
+ const [object, name, original] = item;
+ object[name] = original;
+ }
+}
+
+/**
+ * Manage vfunction injection: both instances and prototype can be overridden
+ * and restored
+ */
+export class VFuncInjectionsHandler extends BasicHandler {
+ _create(prototype, name, injectedFunction) {
+ const original = prototype[`vfunc_${name}`];
+ if (!(original instanceof Function))
+ throw new Error(`Virtual function ${name} is not available for ${prototype}`);
+ this._replaceVFunc(prototype, name, injectedFunction);
+ return [prototype, name];
+ }
+
+ _remove(item) {
+ const [prototype, name] = item;
+ const originalVFunc = prototype[`vfunc_${name}`];
+ try {
+ // This may fail if trying to reset to a never-overridden vfunc
+ // as gjs doesn't consider it a function, even if it's true that
+ // originalVFunc instanceof Function.
+ this._replaceVFunc(prototype, name, originalVFunc);
+ } catch {
+ try {
+ this._replaceVFunc(prototype, name, function (...args) {
+ // eslint-disable-next-line no-invalid-this
+ return originalVFunc.call(this, ...args);
+ });
+ } catch (e) {
+ logError(e, `Removing vfunc_${name}`);
+ }
+ }
+ }
+
+ _replaceVFunc(prototype, name, func) {
+ if (Gi.gobject_prototype_symbol && Gi.gobject_prototype_symbol in prototype)
+ prototype = prototype[Gi.gobject_prototype_symbol];
+
+ return prototype[Gi.hook_up_vfunc_symbol](name, func);
+ }
+}
+
+/**
+ * Manage properties injection: both instances and prototype can be overridden
+ * and restored
+ */
+export class PropertyInjectionsHandler extends BasicHandler {
+ constructor(parentObject, params) {
+ super(parentObject);
+ this._params = params;
+ }
+
+ _create(instance, name, injectedPropertyDescriptor) {
+ if (!this._params?.allowNewProperty && !(name in instance))
+ throw new Error(`Object ${instance} has no '${name}' property`);
+
+ const {prototype} = instance.constructor;
+ const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(prototype, name) ??
+ Object.getOwnPropertyDescriptor(instance, name);
+
+ Object.defineProperty(instance, name, {
+ ...originalPropertyDescriptor,
+ ...injectedPropertyDescriptor,
+ ...{configurable: true},
+ });
+ return [instance, name, originalPropertyDescriptor];
+ }
+
+ _remove(item) {
+ const [instance, name, originalPropertyDescriptor] = item;
+ if (originalPropertyDescriptor)
+ Object.defineProperty(instance, name, originalPropertyDescriptor);
+ else
+ delete instance[name];
+ }
+}
+
+/**
+ * Return the actual position reverseing left and right in rtl
+ */
+export function getPosition() {
+ const position = Docking.DockManager.settings.dockPosition;
+ if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
+ if (position === St.Side.LEFT)
+ return St.Side.RIGHT;
+ else if (position === St.Side.RIGHT)
+ return St.Side.LEFT;
+ }
+ return position;
+}
+
+/**
+ * @param cr
+ * @param x
+ * @param y
+ * @param width
+ * @param height
+ * @param isRoundLeft
+ * @param isRoundRight
+ * @param stroke
+ * @param fill
+ */
+export function drawRoundedLine(cr, x, y, width, height, isRoundLeft, isRoundRight, stroke, fill) {
+ if (height > width) {
+ y += Math.floor((height - width) / 2.0);
+ height = width;
+ }
+
+ height = 2.0 * Math.floor(height / 2.0);
+
+ const leftRadius = isRoundLeft ? height / 2.0 : 0.0;
+ const rightRadius = isRoundRight ? height / 2.0 : 0.0;
+
+ cr.moveTo(x + width - rightRadius, y);
+ cr.lineTo(x + leftRadius, y);
+ if (isRoundLeft)
+ cr.arcNegative(x + leftRadius, y + leftRadius, leftRadius, -Math.PI / 2, Math.PI / 2);
+ else
+ cr.lineTo(x, y + height);
+ cr.lineTo(x + width - rightRadius, y + height);
+ if (isRoundRight)
+ cr.arcNegative(x + width - rightRadius, y + rightRadius, rightRadius, Math.PI / 2, -Math.PI / 2);
+ else
+ cr.lineTo(x + width, y);
+ cr.closePath();
+
+ if (fill) {
+ cr.setSource(fill);
+ cr.fillPreserve();
+ }
+ if (stroke)
+ cr.setSource(stroke);
+ cr.stroke();
+}
+
+/**
+ * Convert a signal handler with n value parameters (that is, excluding the
+ * signal source parameter) to an array of n handlers that are each responsible
+ * for receiving one of the n values and calling the original handler with the
+ * most up-to-date arguments.
+ *
+ * @param handler
+ */
+export function splitHandler(handler) {
+ if (handler.length > 30)
+ throw new Error('too many parameters');
+
+ const count = handler.length - 1;
+ let missingValueBits = (1 << count) - 1;
+ const values = Array.from({length: count});
+ return values.map((_ignored, i) => {
+ const mask = ~(1 << i);
+ return (obj, value) => {
+ values[i] = value;
+ missingValueBits &= mask;
+ if (missingValueBits === 0)
+ handler(obj, ...values);
+ };
+ });
+}
+
+/**
+ * Construct a map of gtk application window object paths to MetaWindows.
+ */
+export function getWindowsByObjectPath() {
+ const windowsByObjectPath = new Map();
+ const {workspaceManager} = global;
+ const workspaces = [...new Array(workspaceManager.nWorkspaces)].map(
+ (_c, i) => workspaceManager.get_workspace_by_index(i));
+
+ workspaces.forEach(ws => {
+ ws.list_windows().forEach(w => {
+ const path = w.get_gtk_window_object_path();
+ if (path)
+ windowsByObjectPath.set(path, w);
+ });
+ });
+
+ return windowsByObjectPath;
+}
+
+/**
+ * Re-implements shell_app_compare so that can be used to resort running apps
+ *
+ * @param appA
+ * @param appB
+ */
+export function shellAppCompare(appA, appB) {
+ if (appA.state !== appB.state) {
+ if (appA.state === Shell.AppState.RUNNING)
+ return -1;
+ return 1;
+ }
+
+ const windowsA = appA.get_windows();
+ const windowsB = appB.get_windows();
+
+ const isMinimized = windows => !windows.some(w => w.showing_on_its_workspace());
+ const minimizedB = isMinimized(windowsB);
+ if (isMinimized(windowsA) !== minimizedB) {
+ if (minimizedB)
+ return -1;
+ return 1;
+ }
+
+ if (appA.state === Shell.AppState.RUNNING) {
+ if (windowsA.length && !windowsB.length)
+ return -1;
+ else if (!windowsA.length && windowsB.length)
+ return 1;
+
+ const lastUserTime = windows =>
+ Math.max(...windows.map(w => w.get_user_time()));
+ return lastUserTime(windowsB) - lastUserTime(windowsA);
+ }
+
+ return 0;
+}
+
+/**
+ * Re-implements shell_app_compare_windows
+ *
+ * @param winA
+ * @param winB
+ */
+export function shellWindowsCompare(winA, winB) {
+ const activeWorkspace = global.workspaceManager.get_active_workspace();
+ const wsA = winA.get_workspace() === activeWorkspace;
+ const wsB = winB.get_workspace() === activeWorkspace;
+
+ if (wsA && !wsB)
+ return -1;
+ else if (!wsA && wsB)
+ return 1;
+
+ const visA = winA.showing_on_its_workspace();
+ const visB = winB.showing_on_its_workspace();
+
+ if (visA && !visB)
+ return -1;
+ else if (!visA && visB)
+ return 1;
+
+ return winB.get_user_time() - winA.get_user_time();
+}
+
+export const CancellableChild = GObject.registerClass({
+ Properties: {
+ 'parent': GObject.ParamSpec.object(
+ 'parent', 'parent', 'parent',
+ GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY,
+ Gio.Cancellable.$gtype),
+ },
+},
+class CancellableChild extends Gio.Cancellable {
+ _init(parent) {
+ if (parent && !(parent instanceof Gio.Cancellable))
+ throw TypeError('Not a valid cancellable');
+
+ super._init({parent});
+
+ if (parent?.is_cancelled()) {
+ this.cancel();
+ return;
+ }
+
+ this._connectToParent();
+ }
+
+ _connectToParent() {
+ this._connectId = this.parent?.connect(() => {
+ this._realCancel();
+
+ if (this._disconnectIdle)
+ return;
+
+ this._disconnectIdle = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ delete this._disconnectIdle;
+ this._disconnectFromParent();
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+ }
+
+ _disconnectFromParent() {
+ if (this._connectId && !this._disconnectIdle) {
+ this.parent.disconnect(this._connectId);
+ delete this._connectId;
+ }
+ }
+
+ _realCancel() {
+ Gio.Cancellable.prototype.cancel.call(this);
+ }
+
+ cancel() {
+ this._disconnectFromParent();
+ this._realCancel();
+ }
+});
+
+/**
+ *
+ */
+export function getMonitorManager() {
+ return global.backend.get_monitor_manager?.() ?? Meta.MonitorManager.get();
+}
+
+/**
+ * @param laterType
+ * @param callback
+ */
+export function laterAdd(laterType, callback) {
+ return global.compositor?.get_laters?.().add(laterType, callback) ??
+ Meta.later_add(laterType, callback);
+}
+
+/**
+ * @param id
+ */
+export function laterRemove(id) {
+ if (global.compositor?.get_laters)
+ global.compositor?.get_laters().remove(id);
+ else
+ Meta.later_remove(id);
+}
+
+/**
+ * Up to Gnome Shell 45, the Cairo Context object didn't export the
+ * `setSourceColor()` method, so Clutter included a function call for
+ * that, written in C. In Gnome Shell 46, the method was finally exported,
+ * so that function was removed.
+ *
+ * This function is, thus, required for Gnome Shell 45 compatibility.
+ *
+ * @param {*} cr A cairo context
+ * @param {*} sourceColor The new color for source
+ */
+export function cairoSetSourceColor(cr, sourceColor) {
+ if (Clutter.cairo_set_source_color)
+ Clutter.cairo_set_source_color(cr, sourceColor);
+ else
+ cr.setSourceColor(sourceColor);
+}
+
+/**
+ * Specifies if the system supports extended barriers. This function
+ * is required for Gnome Shell 45 compatibility, which used
+ * `global.display.supports_extended_barriers`. Gnome Shell 46 moved
+ * that into global.backend.capabilities.
+ *
+ * @returns True if the system supports extended barriers.
+ */
+export function supportsExtendedBarriers() {
+ if (global.display.supports_extended_barriers)
+ return global.display.supports_extended_barriers();
+ return !!(global.backend.capabilities & Meta.BackendCapabilities.BARRIERS);
+}
+
+export function addActor(element, actor) {
+ if (element.add_actor)
+ element.add_actor(actor);
+ else
+ element.add_child(actor);
+}
+
+export const clamp = (v, m, M) => Math.min(Math.max(v, m), M);
+export const clampDouble = v => clamp(v, 0, 1);
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js
new file mode 100644
index 0000000..337d5f1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/dash-to-dock@micxgx.gmail.com/windowPreview.js
@@ -0,0 +1,678 @@
+/*
+ * Credits:
+ * This file is based on code from the Dash to Panel extension by Jason DeRose
+ * and code from the Taskbar extension by Zorin OS
+ * Some code was also adapted from the upstream Gnome Shell source code.
+ */
+
+import {
+ Clutter,
+ GLib,
+ GObject,
+ Meta,
+ St,
+} from './dependencies/gi.js';
+
+import {
+ BoxPointer,
+ Main,
+ PopupMenu,
+ Workspace,
+} from './dependencies/shell/ui.js';
+
+import {
+ Docking,
+ Theming,
+ Utils,
+} from './imports.js';
+
+const PREVIEW_MAX_WIDTH = 250;
+const PREVIEW_MAX_HEIGHT = 150;
+
+const PREVIEW_ANIMATION_DURATION = 250;
+const MAX_PREVIEW_GENERATION_ATTEMPTS = 15;
+
+const MENU_MARGINS = 10;
+
+export class WindowPreviewMenu extends PopupMenu.PopupMenu {
+ constructor(source) {
+ super(source, 0.5, Utils.getPosition());
+
+ // We want to keep the item hovered while the menu is up
+ this.blockSourceEvents = true;
+
+ this._source = source;
+ this._app = this._source.app;
+ const workArea = Main.layoutManager.getWorkAreaForMonitor(
+ this._source.monitorIndex);
+ const {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
+
+ this.actor.add_style_class_name('app-menu');
+ this.actor.set_style(
+ `max-width: ${Math.round(workArea.width / scaleFactor) - MENU_MARGINS}px; ` +
+ `max-height: ${Math.round(workArea.height / scaleFactor) - MENU_MARGINS}px;`);
+ this.actor.hide();
+
+ // Chain our visibility and lifecycle to that of the source
+ this._mappedId = this._source.connect('notify::mapped', () => {
+ if (!this._source.mapped)
+ this.close();
+ });
+ this._destroyId = this._source.connect('destroy', this.destroy.bind(this));
+
+ Utils.addActor(Main.uiGroup, this.actor);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _redisplay() {
+ if (this._previewBox)
+ this._previewBox.destroy();
+ this._previewBox = new WindowPreviewList(this._source);
+ this.addMenuItem(this._previewBox);
+ this._previewBox._redisplay();
+ }
+
+ popup() {
+ const windows = this._source.getInterestingWindows();
+ if (windows.length > 0) {
+ this._redisplay();
+ this.open(BoxPointer.PopupAnimation.FULL);
+ this.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ this._source.emit('sync-tooltip');
+ }
+ }
+
+ _onDestroy() {
+ if (this._mappedId)
+ this._source.disconnect(this._mappedId);
+
+ if (this._destroyId)
+ this._source.disconnect(this._destroyId);
+ }
+}
+
+class WindowPreviewList extends PopupMenu.PopupMenuSection {
+ constructor(source) {
+ super();
+ this.actor = new St.ScrollView({
+ name: 'dashtodockWindowScrollview',
+ hscrollbar_policy: St.PolicyType.NEVER,
+ vscrollbar_policy: St.PolicyType.NEVER,
+ overlay_scrollbars: true,
+ enable_mouse_scrolling: true,
+ });
+
+ this.actor.connect('scroll-event', this._onScrollEvent.bind(this));
+
+ const position = Utils.getPosition();
+ this.isHorizontal = position === St.Side.BOTTOM || position === St.Side.TOP;
+ this.box.set_vertical(!this.isHorizontal);
+ this.box.set_name('dashtodockWindowList');
+ Utils.addActor(this.actor, this.box);
+ this.actor._delegate = this;
+
+ this._shownInitially = false;
+
+ this._source = source;
+ this.app = source.app;
+
+ this._redisplayId = Main.initializeDeferredWork(this.actor, this._redisplay.bind(this));
+
+ this.actor.connect('destroy', this._onDestroy.bind(this));
+ this._stateChangedId = this.app.connect('windows-changed',
+ this._queueRedisplay.bind(this));
+ }
+
+ _queueRedisplay() {
+ Main.queueDeferredWork(this._redisplayId);
+ }
+
+ _onScrollEvent(actor, event) {
+ // Event coordinates are relative to the stage but can be transformed
+ // as the actor will only receive events within his bounds.
+ const [stageX, stageY] = event.get_coords();
+ const [,, eventY] = actor.transform_stage_point(stageX, stageY);
+ const [, actorH] = actor.get_size();
+
+ // If the scroll event is within a 1px margin from
+ // the relevant edge of the actor, let the event propagate.
+ if (eventY >= actorH - 2)
+ return Clutter.EVENT_PROPAGATE;
+
+ // Skip to avoid double events mouse
+ if (event.is_pointer_emulated())
+ return Clutter.EVENT_STOP;
+
+ let adjustment, delta;
+
+ if (this.isHorizontal)
+ adjustment = this.actor.get_hscroll_bar().get_adjustment();
+ else
+ adjustment = this.actor.get_vscroll_bar().get_adjustment();
+
+ const increment = adjustment.step_increment;
+
+ switch (event.get_scroll_direction()) {
+ case Clutter.ScrollDirection.UP:
+ delta = -increment;
+ break;
+ case Clutter.ScrollDirection.DOWN:
+ delta = Number(increment);
+ break;
+ case Clutter.ScrollDirection.SMOOTH: {
+ const [dx, dy] = event.get_scroll_delta();
+ delta = dy * increment;
+ delta += dx * increment;
+ break;
+ }
+ }
+
+ adjustment.set_value(adjustment.get_value() + delta);
+
+ return Clutter.EVENT_STOP;
+ }
+
+ _onDestroy() {
+ this.app.disconnect(this._stateChangedId);
+ this._stateChangedId = 0;
+ }
+
+ _createPreviewItem(window) {
+ const preview = new WindowPreviewMenuItem(window, Utils.getPosition());
+ return preview;
+ }
+
+ _redisplay() {
+ const children = this._getMenuItems().filter(actor => {
+ return actor._window;
+ });
+
+ // Windows currently on the menu
+ const oldWin = children.map(actor => {
+ return actor._window;
+ });
+
+ // All app windows with a static order
+ const newWin = this._source.getInterestingWindows().sort((a, b) =>
+ a.get_stable_sequence() > b.get_stable_sequence());
+
+ const addedItems = [];
+ const removedActors = [];
+
+ let newIndex = 0;
+ let oldIndex = 0;
+
+ while (newIndex < newWin.length || oldIndex < oldWin.length) {
+ const currentOldWin = oldWin[oldIndex];
+ const currentNewWin = newWin[newIndex];
+
+ // No change at oldIndex/newIndex
+ if (currentOldWin === currentNewWin) {
+ oldIndex++;
+ newIndex++;
+ continue;
+ }
+
+ // Window removed at oldIndex
+ if (currentOldWin && !newWin.includes(currentOldWin)) {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ continue;
+ }
+
+ // Window added at newIndex
+ if (currentNewWin && !oldWin.includes(currentNewWin)) {
+ addedItems.push({
+ item: this._createPreviewItem(currentNewWin),
+ pos: newIndex,
+ });
+ newIndex++;
+ continue;
+ }
+
+ // Window moved
+ const insertHere = newWin[newIndex + 1] &&
+ newWin[newIndex + 1] === currentOldWin;
+ const alreadyRemoved = removedActors.reduce((result, actor) =>
+ result || actor._window === currentNewWin, false);
+
+ if (insertHere || alreadyRemoved) {
+ addedItems.push({
+ item: this._createPreviewItem(currentNewWin),
+ pos: newIndex + removedActors.length,
+ });
+ newIndex++;
+ } else {
+ removedActors.push(children[oldIndex]);
+ oldIndex++;
+ }
+ }
+
+ for (let i = 0; i < addedItems.length; i++) {
+ this.addMenuItem(addedItems[i].item,
+ addedItems[i].pos);
+ }
+
+ for (let i = 0; i < removedActors.length; i++) {
+ const item = removedActors[i];
+ if (this._shownInitially)
+ item._animateOutAndDestroy();
+ else
+ item.actor.destroy();
+ }
+
+ // Skip animations on first run when adding the initial set
+ // of items, to avoid all items zooming in at once
+ const animate = this._shownInitially;
+
+ if (!this._shownInitially)
+ this._shownInitially = true;
+
+ for (let i = 0; i < addedItems.length; i++)
+ addedItems[i].item.show(animate);
+
+ // Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
+ // Without it, StBoxLayout may use a stale size cache
+ this.box.queue_relayout();
+
+ if (newWin.length < 1)
+ this._getTopMenu().close(~0);
+
+ // As for upstream:
+ // St.ScrollView always requests space horizontally for a possible vertical
+ // scrollbar if in AUTOMATIC mode. Doing better would require implementation
+ // of width-for-height in St.BoxLayout and St.ScrollView. This looks bad
+ // when we *don't* need it, so turn off the scrollbar when that's true.
+ // Dynamic changes in whether we need it aren't handled properly.
+ const needsScrollbar = this._needsScrollbar();
+ const scrollbarPolicy = needsScrollbar
+ ? St.PolicyType.AUTOMATIC : St.PolicyType.NEVER;
+ if (this.isHorizontal)
+ this.actor.hscrollbarPolicy = scrollbarPolicy;
+ else
+ this.actor.vscrollbarPolicy = scrollbarPolicy;
+
+ if (needsScrollbar)
+ this.actor.add_style_pseudo_class('scrolled');
+ else
+ this.actor.remove_style_pseudo_class('scrolled');
+ }
+
+ _needsScrollbar() {
+ const topMenu = this._getTopMenu();
+ const topThemeNode = topMenu.actor.get_theme_node();
+ if (this.isHorizontal) {
+ const [topMinWidth_, topNaturalWidth] =
+ topMenu.actor.get_preferred_width(-1);
+ const topMaxWidth = topThemeNode.get_max_width();
+ return topMaxWidth >= 0 && topNaturalWidth >= topMaxWidth;
+ } else {
+ const [topMinHeight_, topNaturalHeight] =
+ topMenu.actor.get_preferred_height(-1);
+ const topMaxHeight = topThemeNode.get_max_height();
+ return topMaxHeight >= 0 && topNaturalHeight >= topMaxHeight;
+ }
+ }
+
+ isAnimatingOut() {
+ return this.actor.get_children().reduce((result, actor) => {
+ return result || actor.animatingOut;
+ }, false);
+ }
+}
+
+export const WindowPreviewMenuItem = GObject.registerClass(
+class WindowPreviewMenuItem extends PopupMenu.PopupBaseMenuItem {
+ _init(window, position, params) {
+ super._init(params);
+
+ this._window = window;
+ this._destroyId = 0;
+ this._windowAddedId = 0;
+
+ // We don't want this: it adds spacing on the left of the item.
+ this.remove_child(this._ornamentIcon);
+ this.add_style_class_name('dashtodock-app-well-preview-menu-item');
+ this.add_style_class_name(Theming.PositionStyleClass[position]);
+ if (Docking.DockManager.settings.customThemeShrink)
+ this.add_style_class_name('shrink');
+
+ // Now we don't have to set PREVIEW_MAX_WIDTH and PREVIEW_MAX_HEIGHT as
+ // preview size - that made all kinds of windows either stretched or
+ // squished (aspect ratio problem)
+ this._cloneBin = new St.Bin();
+
+ this._updateWindowPreviewSize();
+
+ // TODO: improve the way the closebutton is layout. Just use some padding
+ // for the moment.
+ this._cloneBin.set_style('padding-bottom: 0.5em');
+
+ const buttonLayout = Meta.prefs_get_button_layout();
+ this.closeButton = new St.Button({
+ style_class: 'window-close',
+ opacity: 0,
+ x_expand: true,
+ y_expand: true,
+ x_align: buttonLayout.left_buttons.includes(Meta.ButtonFunction.CLOSE)
+ ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
+ y_align: Clutter.ActorAlign.START,
+ });
+ Utils.addActor(this.closeButton, new St.Icon({icon_name: 'window-close-symbolic'}));
+ this.closeButton.connect('clicked', () => this._closeWindow());
+
+ const overlayGroup = new Clutter.Actor({
+ layout_manager: new Clutter.BinLayout(),
+ y_expand: true,
+ });
+
+ overlayGroup.add_child(this._cloneBin);
+ overlayGroup.add_child(this.closeButton);
+
+ const label = new St.Label({text: window.get_title()});
+ label.set_style(`max-width: ${PREVIEW_MAX_WIDTH}px`);
+ const labelBin = new St.Bin({
+ child: label,
+ x_align: Clutter.ActorAlign.CENTER,
+ });
+
+ this._windowTitleId = this._window.connect('notify::title', () => {
+ label.set_text(this._window.get_title());
+ });
+
+ const box = new St.BoxLayout({
+ vertical: true,
+ reactive: true,
+ x_expand: true,
+ });
+
+ if (box.add) {
+ box.add(overlayGroup);
+ box.add(labelBin);
+ } else {
+ box.add_child(overlayGroup);
+ box.add_child(labelBin);
+ }
+ this._box = box;
+ this.add_child(box);
+
+ this._cloneTexture(window);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ vfunc_style_changed() {
+ super.vfunc_style_changed();
+
+ // For some crazy clutter / St reason we can't just have this handled
+ // automatically or here via vfunc_allocate + vfunc_get_preferred_*
+ // because if we do so, the St paddings on first / last child are lost
+ const themeNode = this.get_theme_node();
+ let [minWidth, naturalWidth] = this._box.get_preferred_width(-1);
+ let [minHeight, naturalHeight] = this._box.get_preferred_height(naturalWidth);
+ [minWidth, naturalWidth] = themeNode.adjust_preferred_width(minWidth, naturalWidth);
+ [minHeight, naturalHeight] = themeNode.adjust_preferred_height(minHeight, naturalHeight);
+ this.set({minWidth, naturalWidth, minHeight, naturalHeight});
+ }
+
+ _getWindowPreviewSize() {
+ const emptySize = [0, 0, 0];
+
+ const mutterWindow = this._window.get_compositor_private();
+ if (!mutterWindow?.get_texture())
+ return emptySize;
+
+ const [width, height] = mutterWindow.get_size();
+ if (!width || !height)
+ return emptySize;
+
+ let {previewSizeScale: scale} = Docking.DockManager.settings;
+ if (!scale) {
+ // a simple example with 1680x1050:
+ // * 250/1680 = 0,1488
+ // * 150/1050 = 0,1429
+ // => scale is 0,1429
+ scale = Math.min(1.0, PREVIEW_MAX_WIDTH / width, PREVIEW_MAX_HEIGHT / height);
+ }
+
+ scale *= St.ThemeContext.get_for_stage(global.stage).scaleFactor;
+
+ // width and height that we wanna multiply by scale
+ return [width, height, scale];
+ }
+
+ _updateWindowPreviewSize() {
+ // This gets the actual windows size for the preview
+ [this._width, this._height, this._scale] = this._getWindowPreviewSize();
+ this._cloneBin.set_size(this._width * this._scale, this._height * this._scale);
+ }
+
+ _cloneTexture(metaWin) {
+ // Newly-created windows are added to a workspace before
+ // the compositor finds out about them...
+ if (!this._width || !this._height) {
+ this._cloneTextureLater = Utils.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => {
+ // Check if there's still a point in getting the texture,
+ // otherwise this could go on indefinitely
+ this._updateWindowPreviewSize();
+
+ if (this._width && this._height) {
+ this._cloneTexture(metaWin);
+ } else {
+ this._cloneAttempt = (this._cloneAttempt || 0) + 1;
+ if (this._cloneAttempt < MAX_PREVIEW_GENERATION_ATTEMPTS)
+ return GLib.SOURCE_CONTINUE;
+ }
+ delete this._cloneTextureLater;
+ return GLib.SOURCE_REMOVE;
+ });
+ return;
+ }
+
+ const mutterWindow = metaWin.get_compositor_private();
+ const clone = new Clutter.Clone({
+ source: mutterWindow,
+ reactive: true,
+ width: this._width * this._scale,
+ height: this._height * this._scale,
+ });
+
+ // when the source actor is destroyed, i.e. the window closed, first destroy the clone
+ // and then destroy the menu item (do this animating out)
+ this._destroyId = mutterWindow.connect('destroy', () => {
+ clone.destroy();
+ this._destroyId = 0; // avoid to try to disconnect this signal from mutterWindow in _onDestroy(),
+ // as the object was just destroyed
+ this._animateOutAndDestroy();
+ });
+
+ this._clone = clone;
+ this._mutterWindow = mutterWindow;
+ this._cloneBin.set_child(this._clone);
+
+ this._clone.connect('destroy', () => {
+ if (this._destroyId) {
+ mutterWindow.disconnect(this._destroyId);
+ this._destroyId = 0;
+ }
+ this._clone = null;
+ });
+ }
+
+ _windowCanClose() {
+ return this._window.can_close() &&
+ !this._hasAttachedDialogs();
+ }
+
+ _closeWindow() {
+ this._workspace = this._window.get_workspace();
+
+ // This mechanism is copied from the workspace.js upstream code
+ // It forces window activation if the windows don't get closed,
+ // for instance because asking user confirmation, by monitoring the opening of
+ // such additional confirmation window
+ this._windowAddedId = this._workspace.connect('window-added',
+ this._onWindowAdded.bind(this));
+
+ this.deleteAllWindows();
+ }
+
+ deleteAllWindows() {
+ // Delete all windows, starting from the bottom-most (most-modal) one
+ // let windows = this._window.get_compositor_private().get_children();
+ const windows = this._clone.get_children();
+ for (let i = windows.length - 1; i >= 1; i--) {
+ const realWindow = windows[i].source;
+ const metaWindow = realWindow.meta_window;
+
+ metaWindow.delete(global.get_current_time());
+ }
+
+ this._window.delete(global.get_current_time());
+ }
+
+ _onWindowAdded(workspace, win) {
+ const metaWindow = this._window;
+
+ if (win.get_transient_for() === metaWindow) {
+ workspace.disconnect(this._windowAddedId);
+ this._windowAddedId = 0;
+
+ // use an idle handler to avoid mapping problems -
+ // see comment in Workspace._windowAdded
+ const activationEvent = Clutter.get_current_event();
+ this._windowAddedLater = Utils.laterAdd(Meta.LaterType.BEFORE_REDRAW, () => {
+ delete this._windowAddedLater;
+ this.emit('activate', activationEvent);
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ }
+
+ _hasAttachedDialogs() {
+ // count transient windows
+ let n = 0;
+ this._window.foreach_transient(() => {
+ n++;
+ });
+ return n > 0;
+ }
+
+ vfunc_key_focus_in() {
+ super.vfunc_key_focus_in();
+ this._showCloseButton();
+ }
+
+ vfunc_key_focus_out() {
+ super.vfunc_key_focus_out();
+ this._hideCloseButton();
+ }
+
+ vfunc_enter_event(crossingEvent) {
+ this._showCloseButton();
+ return super.vfunc_enter_event(crossingEvent);
+ }
+
+ vfunc_leave_event(crossingEvent) {
+ this._hideCloseButton();
+ return super.vfunc_leave_event(crossingEvent);
+ }
+
+ _idleToggleCloseButton() {
+ this._idleToggleCloseId = 0;
+
+ this._hideCloseButton();
+
+ return GLib.SOURCE_REMOVE;
+ }
+
+ _showCloseButton() {
+ if (this._windowCanClose()) {
+ this.closeButton.show();
+ this.closeButton.remove_all_transitions();
+ this.closeButton.ease({
+ opacity: 255,
+ duration: Workspace.WINDOW_OVERLAY_FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_OUT_QUAD,
+ });
+ }
+ }
+
+ _hideCloseButton() {
+ if (this.closeButton.has_pointer ||
+ this.get_children().some(a => a.has_pointer))
+ return;
+
+ this.closeButton.remove_all_transitions();
+ this.closeButton.ease({
+ opacity: 0,
+ duration: Workspace.WINDOW_OVERLAY_FADE_TIME,
+ mode: Clutter.AnimationMode.EASE_IN_QUAD,
+ });
+ }
+
+ show(animate) {
+ const fullWidth = this.get_width();
+
+ this.opacity = 0;
+ this.set_width(0);
+
+ const time = animate ? PREVIEW_ANIMATION_DURATION : 0;
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 255,
+ width: fullWidth,
+ duration: time,
+ mode: Clutter.AnimationMode.EASE_IN_OUT_QUAD,
+ });
+ }
+
+ _animateOutAndDestroy() {
+ this.remove_all_transitions();
+ this.ease({
+ opacity: 0,
+ duration: PREVIEW_ANIMATION_DURATION,
+ });
+
+ this.ease({
+ width: 0,
+ height: 0,
+ duration: PREVIEW_ANIMATION_DURATION,
+ delay: PREVIEW_ANIMATION_DURATION,
+ onComplete: () => this.destroy(),
+ });
+ }
+
+ activate() {
+ Main.activateWindow(this._window);
+ this._getTopMenu().close();
+ }
+
+ _onDestroy() {
+ if (this._cloneTextureLater) {
+ Utils.laterRemove(this._cloneTextureLater);
+ delete this._cloneTextureLater;
+ }
+
+ if (this._windowAddedLater) {
+ Utils.laterRemove(this._windowAddedLater);
+ delete this._windowAddedLater;
+ }
+
+ if (this._windowAddedId > 0) {
+ this._workspace.disconnect(this._windowAddedId);
+ this._windowAddedId = 0;
+ }
+
+ if (this._destroyId > 0) {
+ this._mutterWindow.disconnect(this._destroyId);
+ this._destroyId = 0;
+ }
+
+ if (this._windowTitleId > 0) {
+ this._window.disconnect(this._windowTitleId);
+ this._windowTitleId = 0;
+ }
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/COPYING b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/COPYING
new file mode 100644
index 0000000..e55e5b8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/NEWS b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/NEWS
new file mode 100644
index 0000000..267e905
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/NEWS
@@ -0,0 +1,1652 @@
+50.0
+====
+* Bump version
+
+
+50.rc
+=====
+* workspace-indicator: Make workspace menu section scrollable [Florian; !445]
+* Misc. bug fixes and cleanups [Florian; !444, !446]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Lenny Angst [de], Changwoo Ryu [ko]
+
+50.alpha
+========
+* classic: Drop xorg sessions [Jordan; !283]
+* classic: Deifne a gnome-session session [Adrian; !3960]
+* Misc. bug fixes and cleanups [Florian, Jordan; !425, !429, !430, !432, !431,
+ !433, !434, !435, !439]
+
+Contributors:
+ Florian Müllner, Jordan Petridis, Adrian Vovk
+
+Translators:
+ BoF ButterflyOfFire [kab], Giannis Antypas [el],
+ Asier Saratsua Garmendia [eu], Hugo Carvalho [pt], twlvnn kraftwerk [bg],
+ Baurzhan Muftakhidinov [kk], Ibai Oihanguren Sala [eu]
+
+49.0
+====
+* window-list: Adjust to gnome-shell changes [Florian; !421]
+* system-monitor: Unbreak on BSD [Antoine; !356]
+* Misc. bug fixes and cleanups [Florian; !422, !423]
+
+Contributors:
+ Antoine Jacoutot, Florian Müllner
+
+Translators:
+ Jiri Grönroos [fi], Aurimas Aurimas Černius [lt]
+
+49.rc
+=====
+
+Translators:
+ Emilio Sepúlveda [ia], Dušan Kazik [sk], Daniel Rusek [cs],
+ Nathan Follens [nl], Piotr Drąg [pl], Balázs Úr [hu], Yosef Or Boczko [he]
+
+49.beta
+=======
+* Misc. bug fixes and cleanups [Florian; !408]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Anders Jonsson [sv], Makoto Sakaguchi [ja], Danial Behzadi [fa],
+ Jordi Mas i Hernandez [ca]
+
+49.alpha.1
+==========
+* workspaces-indicator, window-list: Better expose workspace names
+ [Florian; !405]
+* window-list: Animate transition to/from overview [Florian; !412]
+* Misc. bug fixes and cleanups [Florian; !409, !411, !413]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Martin [sl], Emin Tufan Çetin [tr], Yuri Chornoivan [uk], Luming Zh [zh_CN],
+ Yago Raña [gl], Ekaterine Papava [ka], Vasil Pupkin [be],
+ Álvaro Burns [pt_BR], Sergej A. [ru]
+
+49.alpha.0
+==========
+* windowsNavigator: Fix handling keyboard shortcuts [Daniel; !395]
+* build: Allow disabling the X11 session [Neal; !396, !400]
+* Disable X11 session by default [Jordan; !399]
+* Misc. bug fixes and cleanups [Florian; !398, !406]
+
+Contributors:
+ Daniel Buch Hansen, Neal Gompa, Florian Müllner, Jordan Petridis
+
+Translators:
+ Emilio Sepúlveda [ia]
+
+48.1
+====
+
+Translators:
+ Hugo Carvalho [pt], Danial Behzadi [fa]
+
+48.0
+====
+* apps-menu: Fix scrolling items into view on keynav [Victor; !391]
+* Misc. bug fixes and cleanups [Florian, Stuart; !390, !392]
+
+Contributors:
+ Stuart Hayhurst, Victor Kareh, Florian Müllner
+
+48.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !385, !388]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Emilio Sepúlveda [ia], Mathews M [ml], Daniel Rusek [cs], Piotr Drąg [pl],
+ Anders Jonsson [sv], Ekaterine Papava [ka], Yuri Chornoivan [uk],
+ Aurimas Černius [lt], Luming Zh [zh_CN], Jiri Grönroos [fi]
+
+48.beta
+=======
+* window-list: Fix regression in chrome tracking [Florian; !379]
+* Misc. bug fixes and cleanups [Florian; !380]
+
+Contributors:
+ Florian Müllner, Emilio Sepúlveda
+
+Translators:
+ Rafael Fontenelle [pt_BR], Emilio Sepúlveda [ia]
+
+48.alpha
+========
+* classic: Add missing top-bar indicators [Florian; !339]
+* window-list: Fix window state styling [Florian; !342]
+* window-list: Fix "ignore-workspace" setting getting reset [Florian; !341]
+* window-list: Allow rearranging window buttons [Florian, Jakub; !338]
+* window-list: Add workspaces page to preference dialog [Florian; !344]
+* places-menu: Sync list of places with nautilus [Florian; !340]
+* places-menu: Fix a11y labelling [Florian; #542]
+* places-menu: Fix opening drives with mount operations [Florian; !361]
+* window-list: Fix hiding when entering overview with gestures [Florian; !364]
+* workspace-indicator: Only show previews of regular windows [Florian; !363]
+* window-list: Add attention indicator [Florian; !366]
+* Misc. bug fixes and cleanups [Florian, Bartłomiej; !337, !343, !345, !347,
+ !348, !349, !351, !352, !353, !354, !358, !362, !365, !367, !368, !370, !375]
+
+Contributors:
+ Florian Müllner, Bartłomiej Piotrowski, Jakub Steiner
+
+Translators:
+ Fabio Tomat [fur], Martin [sl], Jordi Mas i Hernandez [ca], Vasil Pupkin [be],
+ Nathan Follens [nl], Artur S0 [ru], Марко Костић [sr],
+ Yaron Shahrabani [he], Sabri Ünal [tr], Yi-Jyun Pan [zh_TW]
+
+47.0
+====
+
+Translators:
+ twlvnn kraftwerk [bg], Alexander Shopov [bg], Fran Dieguez [gl],
+ Aurimas Černius [lt], Daniel [es], Andika Triwidada [id],
+ Andi Chandler [en_GB], Ask Hjorth Larsen [da], Aefgh Threenine [th],
+ Dušan Kazik [sk], Rūdolfs Mazurs [lv], Irénée THIRION [fr]
+
+47.rc
+=====
+* Misc. bug fixes and cleanups [Sophie; !333]
+
+Contributors:
+ Sophie Herold
+
+Translators:
+ Daniel Șerbănescu [ro], Giannis Antypas [el], Ekaterine Papava [ka],
+ Jordi Mas i Hernandez [ca], Yuri Chornoivan [uk], Jiri Grönroos [fi],
+ Daniel Rusek [cs], Vasil Pupkin [be], Luming Zh [zh_CN], Brage Fuglseth [nb],
+ Asier Sarasua Garmendia [eu], Danial Behzadi [fa], Rafael Fontenelle [pt_BR],
+ Quentin PAGÈS [oc], Anders Jonsson [sv], Jürgen Benvenuti [de],
+ Changwoo Ryu [ko], Hugo Carvalho [pt], Piotr Drąg [pl], Sabri Ünal [tr]
+
+47.beta
+=======
+* window-list: Modernize styling [Jakub; !330]
+* Include "status-icons" extension [Florian; !194]
+* Misc. bug fixes and cleanups [Florian; !328, !331, !327]
+
+Contributors:
+ Florian Müllner, Jakub Steiner
+
+Translators:
+ Sabri Ünal [tr], Chao-Hsiung Liao [zh_TW]
+
+47.alpha
+========
+* Improve workspace previews in window-list and workspace-indicator
+ [Florian; !307, !316]
+* apps-menu: Fix a11y of category labels [Florian; !319]
+* window-list: Fix long-press support [Florian; !320]
+* window-list: Animate transitions [Florian; !325]
+* Misc. bug fixes and cleanups [Florian; !315, !321, !324]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jordi Mas i Hernandez [ca], Martin [sl], Hugo Carvalho [pt], Jose Riha [sk],
+ Scrambled 777 [hi], Artur S0 [ru], Милош Поповић [sr], Yosef Or Boczko [he],
+ Balázs Úr [hu]
+
+46.1
+====
+* screenshot-window-sizer: Add flathub-recommended size [Florian; !317]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Rachida SACI [kab], Matheus Polkorny [pt_BR], Fabio Tomat [fur]
+
+46.0
+====
+* system-monitor: Fix net speed [Florian; !313]
+* Misc. bug fixes and cleanups [Aral; !311]
+
+Contributors:
+ Aral Balkan, Florian Müllner
+
+Translators:
+ Anders Jonsson [sv], Piotr Drąg [pl], Balázs Úr [hu], Milo Casagrande [it],
+ Quentin PAGÈS [oc], Athmane MOKRAOUI [kab], Changwoo Ryu [ko],
+ Ask Hjorth Larsen [da]
+
+46.rc
+=====
+* Fix window previews in workspace indicator [Florian; !304]
+* Fix menu ornament in workspace indicator [Florian; !305]
+* Misc. bug fixes and cleanups [Florian; !306, !309]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Danial Behzadi [fa], Ekaterine Papava [ka], Sabri Ünal [tr], Artur S0 [ru],
+ Yuri Chornoivan [uk], Vasil Pupkin [be], Asier Sarasua Garmendia [eu],
+ Yaron Shahrabani [he], Brage Fuglseth [nb], Nathan Follens [nl],
+ Aurimas Černius [lt], Matej Urbančič [sl], Boyuan Yang [zh_CN],
+ Kukuh Syafaat [id], Fran Dieguez [gl], Andi Chandler [en_GB],
+ Baurzhan Muftakhidinov [kk], Rūdolfs Mazurs [lv], Guillaume Bernard [fr],
+ Daniel Mustieles [es], Jiri Grönroos [fi]
+
+46.beta
+=======
+* apps-menu: Rename Applications to Apps [Allan; !299]
+* Misc. bug fixes and cleanups [Florian; !296, !297, !300, !301, !302]
+
+Contributors:
+ Allan Day, Florian Müllner
+
+Translators:
+ Gabriel Brand [de], Daniel Rusek [cs], Fran Dieguez [gl],
+ Aefgh Threenine [th], Vasil Pupkin [be], Artur S0 [ru], Yosef Or Boczko [he],
+ Sabri Ünal [tr]
+
+46.alpha
+========
+* workspace-indicator: Fix initial preview visibility [Florian; !280, !292]
+* screenshot-window-sizer: Fix cycling between sizes backwards [Florian; !284]
+* Add back overview in Classic session [Florian; !287]
+* Allow running Classic session headless [Jonas; !289]
+* window-list: Fix buttons not being clickable at the screen edge
+ [Florian; !291]
+* Add system-monitor extension [Florian; !277]
+* Fixed crash [Florian; !290]
+* Misc. bug fixes and cleanups [Florian; !276, !275, !278, !281, !286, !288]
+
+Contributors:
+ Jonas Ådahl, Florian Müllner
+
+Translators:
+ Kristjan SCHMIDT [eo], Brage Fuglseth [nb]
+
+45.0
+====
+
+Contributors:
+ Andre Klapper
+
+Translators:
+ Bruce Cowan [en_GB]
+
+45.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !267, !224, !272]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Sabri Ünal [tr], Florentina Musat [ro], A S Alam [pa]
+
+45.beta
+=======
+* Port extensions to ESM [Florian; !259, !266, !268, !269]
+* Misc. bug fixes and cleanups [Florian; !260, !261, !262, !263, !264]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Efstathios Iosifidis [el]
+
+45.alpha
+========
+* window-list: Modernize default styling [Alexander; !253]
+* Replace classic styling with built-in light style [Florian; !254]
+* window-list: Add tooltip for long window titles [Arik; !251]
+* light-style: New extension [Florian; !256]
+* Misc. bug fixes and cleanups [Florian; !255, !257]
+
+Contributors:
+ Florian Müllner, Arik W, Alexander Weichart
+
+44.0
+====
+* Bump version
+
+44.rc
+=====
+* Bump version
+
+44.beta
+=======
+* Tweak menu alignment [robxnano; !246]
+
+Contributors:
+ Florian Müllner, robxnano
+
+Translators:
+ Vasil Pupkin [be]
+
+43.1
+====
+* Fixed crash [Florian; !243]
+* Misc. bug fixes and cleanups [mowemcfc; !244]
+
+Contributors:
+ Florian Müllner, mowemcfc
+
+Translators:
+ Sabri Ünal [tr]
+
+43.0
+====
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Pawan Chitrakar [ne], Zurab Kargareteli [ka], Aleksandr Melman [ru]
+
+43.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !240]
+
+Contributors:
+ Florian Müllner
+
+43.beta
+=======
+* Misc. bug fixes and cleanups [Florian; !237, !238]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Nart Tlisha [ab]
+
+43.alpha
+========
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Marco Ciampa [it]
+
+42.3
+====
+* screenshot-window-sizer: Fix reported sizes on wayland [Florian; !232]
+* window-list: Improve touch support [Florian; !233]
+
+Contributors:
+ Florian Müllner
+
+42.2
+====
+* native-window-placement: Adjust to gnome-shell 42 changes [Florian; !229]
+* window-list: Fix visibility on non-primary monitors [Jason; !230]
+
+Contributors:
+ Jason Lynch, Florian Müllner
+
+Translators:
+ Cheng-Chia Tseng [zh_TW]
+
+42.1
+====
+* Misc. bug fixes and cleanups [Florian; !223, !222, !225]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Milo Casagrande [it], Rūdolfs Mazurs [lv], Nathan Follens [nl],
+ Ngọc Quân Trần [vi], Zurab Kargareteli [ka]
+
+42.0
+====
+
+Translators:
+ Philipp Kiemle [de], Balázs Úr [hu], Марко Костић [sr], sicklylife [ja],
+ Baurzhan Muftakhidinov [kk]
+
+42.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !215, !218]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Marek Černocký [cs], Dušan Kazik [sk], Piotr Drąg [pl], Jiri Grönroos [fi],
+ Luna Jernberg [sv], Alan Mortensen [da], Charles Monzat [fr],
+ Changwoo Ryu [ko]
+
+42.beta
+=======
+* workspace-indicator: Fix cancelling editing with Esc [Florian; !208]
+* window-list: Update window tracking to avoid missing icons [Florian; !207]
+* Use libadwaita for preferences [Florian; !209, !213]
+* Adapt to Clutter grab API changes [Florian; !212]
+* Misc. bug fixes and cleanups [Jan, Florian; !210, !214]
+
+Contributors:
+ Jan Beich, Florian Müllner, Naala Nanba
+
+Translators:
+ Boyuan Yang [zh_CN], Matej Urbančič [sl], Naala Nanba [ab],
+ Alexander Shopov [bg], Emin Tufan Çetin [tr]
+
+42.alpha
+========
+* native-window-placement: Fix distorted layout in app grid [Sebastian; !189]
+* window-list: Fix on-screen keyboard [Florian; !199]
+* Misc. bug fixes and cleanups [Neal; Just; !195, !197]
+
+Contributors:
+ Piotr Drąg, Neal Gompa, Sebastian Keller, Florian Müllner, Just Perfection
+
+Translators:
+ Goran Vidović [hr], Sveinn í Felli [is], Yuri Chornoivan [uk],
+ Fabio Tomat [fur], Quentin PAGÈS [oc], Hugo Carvalho [pt],
+ Yaron Shahrabani [he], Jordi Mas i Hernandez [ca], MohammadSaleh Kamyab [fa],
+ Fran Dieguez [gl], Daniel Mustieles [es], Aleksandr Melman [ru],
+ Aurimas Černius [lt], Asier Sarasua Garmendia [eu], Kukuh Syafaat [id],
+ Rafael Fontenelle [pt_BR]
+
+41.0
+====
+* Bump version
+
+41.rc.1
+=======
+* Fix pre-generating stylesheets in tarball [Florian; !190]
+
+Contributors:
+ Florian Müllner
+
+41.rc
+=====
+* window-list: Adapt to overview-on-startup [Florian; !185]
+* apps-menu: Use a custom 'toggle-menu' shortcut [Florian; !173]
+* Misc. bug fixes and cleanups [Florian; !186]
+
+Contributors:
+ Florian Müllner
+
+41.beta
+=======
+* window-list: Extend reactive area of minimap to screen edges [Adam; !171]
+* drive-menu: Improve detection of network mounts [Florian; !27, !176]
+* Use distinct gettext domain for e.g.o uploads [Florian; #335]
+* Misc. bug fixes and cleanups [Florian; !172, !174, !177, !167, !178, !180,
+ !181, !182, !183]
+
+Contributors:
+ Marco Trevisan (Treviño), Adam Goode, Florian Müllner
+
+Translators:
+ Hugo Carvalho [pt], Juliano de Souza Camargo [pt], Alexander Shopov [bg]
+
+40.1
+====
+* Disable welcome dialog in classic session [Florian; !169]
+* windowsNavigator: Adjust to a late gnome-shell change [Florian; !170]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Ngọc Quân Trần [vi], Anders Jonsson [sv], Carmen Bianca BAKKER [eo],
+ Pawan Chitrakar [ne], Quentin PAGÈS [oc]
+
+40.0
+====
+
+Translators:
+ Jiri Grönroos [fi]
+
+40.rc
+=====
+* native-window-placement: Adjust to gnome-shell changes [Florian; !164]
+* windows-navigator: Adjust to gnome-shell changes [Florian; !163]
+* window-list, workspace-indicator: Only show previews for up to six workspaces
+ [Florian; !165]
+* window-list, workspace-indicator: Improve workspace preview appearance
+ [Florian; !166]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Fran Dieguez [gl]
+
+40.beta
+=======
+* Add tooltips to workspace thumbnails [Florian; !155]
+* Drop arrows from top bar menus [Florian; !156]
+* drive-menu: Mark mounts that can be unmounted as removable [Michael; !152]
+* Remove horizontal-workspaces extension [Florian; !158]
+* Adjust to shell overview changes [Florian; !159, !160]
+* Fix crashes [Daniel; !157]
+* Misc. bug fixes and cleanups [Florian; !154, !161]
+
+Contributors:
+ Michael Lawton, Florian Müllner, Daniel van Vugt
+
+Translators:
+ Аляксей [be], A S Alam [pa]
+
+40.alpha.1
+==========
+* Don't depend on sassc when building from tarball [Florian; !150]
+* Port extensions preferences to GTK4 [Florian; !148]
+* Misc. bug fixes and cleanups [Florian, Jonas; !149, !151, !153]
+
+Contributors:
+ Jonas Dreßler, Florian Müllner
+
+40.alpha
+========
+* window-list: Honor changes in skip-taskbar property [Sergio; !130]
+* window-list, workspace-indicator: Adjust to 3.38 changes [Florian; !133]
+* window-list, workspace-indicator: Improve previews in workspace thumbs
+ [Florian; #260, !142]
+* auto-move: Improve behavior on multi-monitor setups [Florian; !135]
+* windowNavigator: Adjust to 3.38 changes [Thun; #259]
+* Misc. bug fixes and cleanups [Florian, Jonas Å, Jordan, Ray; !131, !136,
+ !137, !140, !141, !144, !146, !145]
+
+Contributors:
+ Sergio Costas, Florian Müllner, Jordan Petridis, Thun Pin, Ray Strode,
+ Jonas Ådahl
+
+Translators:
+ Fabio Tomat [fur], Jordi Mas [ca]
+
+3.38.1
+======
+
+Contributors:
+ Yacine Bouklif, Florian Müllner
+
+Translators:
+ Yacine Bouklif [kab], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
+ Yosef Or Boczko [he]
+
+3.38.0
+======
+
+Translators:
+ Balázs Meskó [hu], Alan Mortensen [da], Juliano Camargo [pt], Tim Sabsch [de],
+ Milo Casagrande [it], Rūdolfs Mazurs [lv]
+
+3.37.92
+=======
+
+Translators:
+ Nathan Follens [nl], Zander Brown [en_GB], Aurimas Černius [lt],
+ Marek Černocký [cs], Changwoo Ryu [ko], Dušan Kazik [sk]
+
+3.37.91
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Fran Dieguez [gl], Akarshan Biswas [bn_IN], Kukuh Syafaat [id],
+ Piotr Drąg [pl], Rafael Fontenelle [pt_BR], Jiri Grönroos [fi],
+ Марко Костић [sr], Goran Vidović [hr]
+
+3.37.90
+=======
+* Misc. bug fixes and cleanups [Florian, Piotr; !126, !128]
+
+Contributors:
+ Piotr Drąg, Florian Müllner
+
+Translators:
+ Fabio Tomat [fur], Efstathios Iosifidis [el], Anders Jonsson [sv],
+ Asier Sarasua Garmendia [eu], Alexandre Franke [fr]
+
+3.37.3
+======
+* window-list, native-window-placement: Adjust to shell changes [Florian; !124]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jordi Mas [ca], sicklylife [ja], Boyuan Yang [zh_CN],
+ Baurzhan Muftakhidinov [kk]
+
+3.37.2
+======
+* window-list, auto-move: Modernize preference dialogs [Florian; !121]
+* Adjust to gnome-shell changes [Florian; !122]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Cheng-Chia Tseng [zh_TW], Yuri Chornoivan [uk], Daniel Mustieles [es],
+ Emin Tufan Çetin [tr], Danial Behzadi [fa], Daniel Șerbănescu [ro],
+ Matej Urbančič [sl]
+
+3.37.1
+======
+* drive-menu: Emphasize eject buttons [Florian; #223]
+* user-theme: Add preference dialog [Florian; !117]
+* window-list: Fix inconsistent state in preference dialog [Milan; !119]
+* workspace-indicator: Overhaul preference dialog [Florian; !120]
+* user-theme: Support session mode styles [Florian; !118]
+* Misc. bug fixes and cleanups [Florian, Xiaoguang; !113, !106, !114, !116]
+
+Contributors:
+ Milan Crha, Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Daniel Korostil [uk], Yosef Or Boczko [he], Kristjan SCHMIDT [eo],
+ Dz Chen [zh_CN], Danial Behzadi [fa], Yuri Chornoivan [uk],
+ Anders Jonsson [sv], Daniel Mustieles [es]
+
+3.36.0
+======
+
+Contributors:
+ Florian Müllner
+
+3.35.91
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Zander Brown [en_GB]
+
+3.35.90
+=======
+* Adjust to gnome-shell changes [Florian; !100, !101, !102]
+* Force single-line window titles in window list [Florian; #202]
+* Misc. bug fixes and cleanup [Florian; !104, !105]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ sicklylife [ja], Umarzuki Bin Mochlis Moktar [ms]
+
+3.35.3
+======
+
+Translators:
+ Fran Dieguez [gl]
+
+3.35.2
+======
+* Adjust to gnome-shell changes [Marco, Florian; !89, !95, !96]
+* window-list, workspace-indicator: Exclude DESKTOP windows from previews
+ [Florian; !93]
+* screenshot-window-sizer: Fix cycling through all valid sizes [Willy; !97]
+
+Contributors:
+ Marco Trevisan (Treviño), Florian Müllner, Willy Stadnick
+
+3.34.1
+======
+* Adjust to gnome-settings-daemon plugin removals [Xiaoguang; !94]
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Nathan Follens [nl], Dušan Kazik [sk], Ask Hjorth Larsen [da],
+ Yi-Jyun Pan [zh_TW]
+
+3.34.0
+======
+
+Translators:
+ Rafael Fontenelle [pt_BR], Efstathios Iosifidis [el], Milo Casagrande [it],
+ Sabri Ünal [tr]
+
+3.33.92
+=======
+
+Translators:
+ Марко Костић [sr], Tim Sabsch [de], Rūdolfs Mazurs [lv], Matej Urbančič [sl],
+ Balázs Úr [hu], Claude Paroz [fr], Fran Dieguez [gl], Changwoo Ryu [ko],
+ Ryuta Fujii [ja], Fabio Tomat [fur], Goran Vidović [hr]
+
+3.33.91
+=======
+* Misc. bug fixes and cleanups [Florian; !88, !90, !91, !92]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Asier Sarasua Garmendia [eu], Anders Jonsson [sv], Marek Černocký [cs],
+ Kukuh Syafaat [id], Jiri Grönroos [fi], Florentina Mușat [ro],
+ Aurimas Černius [lt], Daniel Mustieles [es], Piotr Drąg [pl], Jordi Mas [ca],
+ Danial Behzadi [fa]
+3.33.90
+=======
+* window-list: Support showing windows from all workspaces [Florian; #154]
+* Misc. bug fixes and cleanups [Florian; !86, !87]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jor Teron [mjw]
+
+3.33.4
+======
+* Make GNOME Classic more classic:
+ - Disable GNOME 3 overview [Florian; !69]
+ - Add window picker button to window list [Florian; !73, !80]
+ - Style improvements and fixes [Jakub; #169, !82]
+ - Support horizontal workspace layout in window list [Florian; !70]
+ - Add draggable previews to window list workspace switcher [Florian; !74]
+ - Arrange workspaces horizontally [Florian; !72]
+* workspace-indicator: Support horizontal workspace layout [Florian; !71]
+* workspace-indicator: Add draggable previews [Florian; !77]
+* Misc. bug fixes and cleanups [Florian; !75, !76, !79, !78, #168, !84]
+
+Contributors:
+ Florian Müllner, Jakub Steiner, Jor Teron
+
+Translators:
+ Jor Teron [mjw]
+
+3.33.3
+======
+* Misc. bug fixes [Florian, Marco; !67, !68]
+
+Contributors:
+ Florian Müllner, Marco Trevisan (Treviño)
+
+3.33.2
+======
+* Misc. bug fixes and cleanups [Florian; !66]
+
+Contributors:
+ Florian Müllner
+
+3.33.1
+======
+* Misc. bug fixes [Florian; !64]
+
+Contributors:
+ Florian Müllner
+
+3.32.1
+======
+* Fix windowsNavigator extension after ES6 port [Florian; #143]
+* screenshot-window-sizer: Add phone screenshot sizes [Adrien; !65]
+* Misc. bug fixes and cleanups [Fabian; !62]
+
+Contributors:
+ Florian Müllner, Adrien Plazas, Fabian P. Schmidt
+
+3.32.0
+======
+
+Contributors:
+ Florian Müllner
+
+Translations:
+ Victor Ibragimov [tg], Kristjan SCHMIDT [eo], Mart Raudsepp [et]
+
+3.31.92
+=======
+* Misc. bug fixes and cleanups [Florian; !57, !58, !59, !60]
+
+Contributors:
+ Florian Müllner
+
+3.31.91
+=======
+* apps-menu: Remove outdated legacy-tray handling [Florian; !53]
+* user-theme: Allow using XDG user data dir [Tomasz; !55]
+* Misc. bug fixes and cleanups [Florian; !52, !54, !56]
+
+Contributors:
+ Tomasz Gąsior, Florian Müllner
+
+Translators:
+ Matej Urbančič [sl], Gun Chleoc [gd]
+
+3.31.90
+=======
+* Misc. bug fixes and cleanups [Florian; !49, !50, !51]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Ryuta Fujii [ja], Charles Monzat [fr], Pieter Schalk Schoeman [af]
+
+3.31.2
+======
+* Remove obsolete alternate-tab extension [Florian; #786496]
+* Adjust to gnome-shell changes [Florian; #113]
+
+Contributors:
+ Florian Müllner
+
+3.30.1
+======
+* apps-menu: Fix height on HiDPI systems [Florian; #102]
+* window-list: Only switch between windows on active workspace when scrolling
+ [Florian; #78]
+
+Contributors:
+ Florian Müllner
+
+3.30.0
+======
+* Bump version
+
+3.29.91
+=======
+* Misc. bug fixes [Florian; #90]
+
+Contributors:
+ Florian Müllner
+
+3.29.90
+=======
+* Misc. bug fixes [Florian; #786496]
+
+Contributors:
+ Florian Müllner
+
+3.29.3
+======
+* Adjust to global.screen removal [Jonas; #759538]
+
+Contributors:
+ Jonas Ådahl, Florian Müllner
+
+3.29.2
+======
+* Misc. bug fixes [Florian; #69]
+
+Contributors:
+ Florian Müllner
+
+3.28.1
+======
+* Misc. bug fixes [Xiaoguang, Florian; #59, #62]
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Dz Chen [zh_CN]
+
+3.28.0
+======
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Aman Alam [pa], Bruce Cowan [en_GB]
+
+3.27.92
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Piotr Drąg [es], GNOME Translation Robot [gd], Daniel Șerbănescu [ro]
+
+3.27.91
+=======
+* places-menu: Support unmounting ejectable places [Rémy; #17]
+* apps-menu: Support separators and custom sort order [Florian; #27]
+* Port to meson [Florian; #31, #45]
+* window-list: Fix missing icons on wayland [Florian; #10]
+* places-menu: Fix terminating gnome-shell with recent gjs [Florian; #44]
+* auto-move: Make it work with wayland windows [Florian; #33]
+* Classic theme fixes [Florian, Jonas; #26, #41, #39, #40]
+* Require sassc for classic styling [Florian; !28]
+* Misc. bug fixes [Piotr, Florian; #772211, #32, #30]
+
+Contributors:
+ Jeremy Bicha, Piotr Drąg, Jonas Kümmerlin, Rémy Lefevre, Iñigo Martínez,
+ Florian Müllner
+
+Translators:
+ Matej Urbančič [sl], Kjartan Maraas [nb]
+
+3.27.1
+======
+* updated translations (ca@valencia)
+
+3.26.1
+======
+* native-window-placement: Adjust to gnome-shell changes
+* updated translations: el, fa, ru, sv
+
+3.26.0
+======
+* updated translations (be, bg, ca, da, eu, fi, is, it, ko, lv, ml,
+ nl, pt_BR, vi, zh_TW)
+
+3.25.91
+=======
+* updated translations (ca, fr, it, pl, pt_BR, sr, sr@latin, tr)
+
+3.25.90
+=======
+* updated translations (es, gl, hr, hu, kk, sl, sv, sv)
+
+3.25.4
+======
+* screenshot-window-sizer: Fix backward cycling
+* updated translations (ar, be, ca, cs, de, fur, id, lt, pl, sk)
+
+3.25.3
+======
+* places-menu: Use mount operation if necessary
+* window-list: Respect MWM hints
+* updated translations (es, fur, kk)
+
+3.25.2
+======
+* places-menu: Make URI launching asynchronous
+* updated translations (de, fur, hr, hu, id, sl)
+
+3.25.1
+======
+* apps-menu: Mark copied launchers as trusted
+* places-menu: Make icon lookup asynchronous
+* updated translations (hr)
+
+3.24.1
+======
+* apps-menu: Allow creating desktop launchers via DND
+* updated translations (el, vi)
+
+3.24.0
+======
+* updated translations (lv, tr)
+
+3.23.92
+=======
+* update classic theme
+* updated translations (be, ko, ca, da, cs, ru, lt)
+
+3.23.91
+=======
+* updated translations (de, es, eu, fi, fr, fur, gl, hu, id, it, kk, nb, pl, pt_BR,
+ sk, sr, sr@latin, sv, uk, zh_TW)
+
+3.23.90
+=======
+* window-list: Improve styling
+* window-list: Hide workspace indicator when there's a single (static) workspace
+* new translation (be)
+
+3.23.2
+======
+* alternateTab: Don't take over 'switch-group' shortcut
+* updated translations (zh_CN)
+
+3.22.1
+======
+* window-list: Update icon on app changes
+
+3.22.0
+======
+* updated translations (en_GB)
+
+3.21.92
+=======
+* update style
+* updated translations (pl, vi)
+
+3.21.91
+=======
+* updated translations (pl)
+
+3.21.90
+=======
+* updated translations (es, gu)
+
+3.21.4
+======
+* apps-menu: Fix entries from non-standard AppDir directories
+
+3.21.3
+======
+* adjust to gnome-shell changes
+* updated translations (oc)
+
+3.21.2
+======
+* version bump, nothing to see here
+
+3.20.1
+======
+* update classic style
+* updated translations (gd, oc)
+
+3.20.0
+======
+* version bump, nothing to see here
+
+3.19.92
+=======
+* version bump, nothing to see here
+
+3.19.91
+=======
+* updated translations (oc)
+
+3.19.90
+=======
+* version bump, nothing to see here
+
+3.19.4
+======
+* screenshot-window-sizer: HiDPI support
+* Fix gnome-shell component in classic session
+* updated translations (lt)
+
+3.19.3
+======
+* native-window-placement: Don't let border overlap title
+* apps-menu: Fix handling of .desktop files in subdirectories
+* updated translations (is)
+
+3.19.2
+======
+* updated translations (gd)
+
+3.19.1
+======
+* Fix some theme issues
+
+3.18.1
+======
+* window-list: Fix accessibility of window buttons
+* apps-menu: Fix unreliable highlight
+* updated translations (ar)
+
+3.18.0
+======
+* Bump version
+
+3.17.92
+=======
+* places: Include DESKTOP when desktop icons are enabled
+* updated translations (fa)
+
+3.17.91
+=======
+* updated translations (nl, pl, zh_TW)
+
+3.17.90
+=======
+* window-list: Improve application ordering
+* workspace-indicator: Use consistent workspace numbering
+
+3.17.4
+======
+* updated translations (fur)
+
+3.17.3
+======
+* window-list: Adjust with text-scaling-factor
+* classic style updates
+* updated translations (pt, ro)
+
+3.17.2
+======
+* updated translations (oc, pt, zh_CN)
+
+3.17.1
+======
+* style updates
+* updated translations (oc)
+
+3.16.1
+======
+* window-list: Fix workspace indicators popup menu position
+* apps-menu: Fix taking over panel-main-menu shortcut
+* updated translations (et, ja, lv)
+
+3.16.0
+======
+* updated translations (ca)
+
+3.15.92
+=======
+* classic: Update theme
+* update for mutter API changes
+* updated translations (bg, bs, da, fi, pa, ru, sr, sr@latin, tg)
+
+3.15.91
+=======
+* classic: Update theme
+* systemMonitor extension was removed, as the message tray where it
+ put its indicator no longer exists
+* window-list: Adjust for gnome-shell changes
+* updated translations (gl, it, kk, ko, lt, pl, sk, uk, zh_TW)
+
+3.15.90
+=======
+* classic: Visual refresh based on new shell theme
+* window-list: Adjust for gnome-shell changes
+* updated translations (an, el, eo, eu, fr, he, is, sv, tr)
+
+3.15.4
+======
+* window-list: Improve interaction with system modal dialogs
+* updated translations (cs, de, es, eu, fur, hu, id, nb, pt_BR, ru, sl, vi)
+
+3.15.3.1
+========
+* adjust to gnome-shell change
+
+3.15.3
+======
+* classic-mode: Add high-contrast theme variant, drop .desktop file
+* places-menu: Fix error when XDG user directories are not set up
+* window-list: Add option to show on all monitors
+* updated translations (eu, hu, kk, ro, tr)
+
+3.15.2
+======
+* removable-drive, user-theme, window-list: Update for gnome-shell changes
+* apps-menu: Fix some visual glitches
+* Fix classic mode style
+* updated translations (an, cs, he, vi)
+
+3.15.1
+======
+ * updated translations (es, nb)
+
+3.14.1
+======
+ * alternateTab: Fix dismissing popup with Escape
+ * some improvements to the window-list
+ (spacing in app buttons, no flash when closing windows with auto-grouping)
+ * updated translations (lv, it, pt, bg)
+
+3.14.0
+======
+ * updated translations (bn_IN, hi, kn, sr, sr@latin, uk)
+
+3.13.92
+=======
+* new extension: screenshot-window-sizer
+* window-list: Don't add sticky windows more than once
+* updated translations (da, de, fi, ko, mr, ms, ne, pa, pl, sk, sv, ta, te, tr)
+
+3.13.91
+=======
+* window-list: restore fitts'ability of workspace button
+* updated for gnome-shell changes
+* updated translations (cs, kk, fr, or, fa, ja, gu, id)
+
+3.13.90
+=======
+* updated translations (as, ca, eu, nl, zh_CN, zh_HK, zh_TW)
+
+3.13.4
+======
+* Updated for gnome-shell changes
+* updated translations (el, gl, ru)
+
+3.13.3
+======
+* Tweak preference UIs some more
+* Fix classic mode schema overrides
+* updated translations (es, he, hu, lt, nb, pt_BR, sl, tr)
+
+3.13.2
+======
+* Fix sorting of grouped buttons in window list
+* Tweak preference UIs
+* updated translations (en_GB)
+
+3.13.1
+======
+* add DesktopNames key to the classic session file
+* classic theme: remove rounded corners from tile previews
+* window-list: don't shift message tray on other monitors
+* auto-move-windows: several fixes and updates for api changes
+* launch-new-instances: updates for api changes
+* updated translations (ja, km)
+
+3.12.0
+======
+* updated translations (zh_HK, zh_TW)
+
+3.11.92
+=======
+* nothing to see here, move on
+
+3.11.91
+=======
+* updated translations (ko, fur)
+
+3.11.90
+=======
+* several fixes and improvements to the window-list
+ (can be scrolled, works correctly with the OSD
+ keyboard, filters skip-taskbar windows, does not
+ force all notifications to bold)
+* drive-menu fixed not to show shadowed mounts
+* updates for gnome-shell changes (launch-new-instance,
+ auto-move-windows, places-menu)
+* build system fixes for systems without /bin/bash
+* updated translations (or, tr, uk)
+
+3.11.5
+======
+* updates for gnome-shell changes
+* updated translations (kn)
+
+3.11.4
+======
+* classic mode now supports session saving
+* updates for gnome-shell changes
+* updated translations (ar, kn)
+
+3.11.3
+======
+* workspace-indicator is vertically aligned now
+* updated translations (ar, eo, ta, te)
+
+3.11.2
+======
+* updated translations (zh_CN)
+
+3.11.1
+======
+* ignore shadowed mounts in drive-menu extension
+* updates for gnome-shell/gjs changes
+* updated translations (el, th)
+
+3.10.1
+======
+* updated translations (af, ca, ca@valencia, de, et, eu, fa,
+ hu, lt, lv, nb, nl, pa, pt, sk, sr, sr@latin, tr)
+
+3.10.0
+======
+* updated translations (as, cs, da, es, et, fi, fr, gl,
+ he, id, it, kk, lt, lv, pa, pl, pt, pt_BR, ru, sl, tg,
+ uk, zh_HK, zh_TW)
+
+3.9.92
+======
+* more updates and fixes for gnome-shell master changes
+ and regressions (systemMonitor, window-list, apps-menu)
+* lots of updated translations (ar, as, cs, da, de, el, es,
+ eu, fi, gl, he, hu, it, ja, kk, ko, lv, nb, nl, pa, pl,
+ pt_BR, ru, sk, sr, sr@latin, ta, tg)
+
+3.9.91
+======
+* update the classic mode session and theme to work with the
+ new system menu
+* the usual round of updates and fixes for gnome-shell
+ API changes
+* updated translations (de, it, lt, nl, pl, pt_BR, sk,
+ zh_HK, zh_TW)
+
+3.9.90
+======
+* xrandr-indicator was removed, as the implementation
+ was incompatible with the new DisplayConfig mutter API
+* various extensions were updated for the 3.9.90 gnome-shell API
+* updated translations (cs, es, fur, gl, he, hu, id, ja, sl, tg,
+ zh_CN, zh_HK, zh_TW)
+
+3.9.5
+=====
+* alternative-status-menu was removed entirely, as
+ it does not fit in the designs of the new unified
+ status menu
+* updated translations (as, gu, it, ru)
+
+3.9.4
+=====
+* apps-menu: fixed handling of hot corner in case
+ of screen reconfiguration
+* alternative-status-menu now correctly honors polkit
+ for hibernation
+* user-menu now loads themes from $XDG_DATA_HOME too
+* translation updates (de, id, pt_BR, vi, zh_CN, ml)
+
+3.9.3
+=====
+* classic mode mini extensions were replaced with a
+ GSettings override specified in the .json file
+* styling of classic mode improved
+* native-window-placement is back working on 3.9
+* misc bug fixes
+* traslation updates (an, cs, el, es, gl, nb, pl, sk, tg)
+
+3.9.2
+=====
+* apps-menu: appearance of the scrollbars was improved
+* window-list is a little taller in classic mode (to account
+ for the workspace switcher)
+* alternative-status-menu honors again the dconf configuration
+* translation updates (sr)
+
+3.9.1
+=====
+* updates to window-list, xrandr-indicator,
+ workspace-indicator, windowsNavigator for gnome-shell
+ changes
+* translation updates (cs, es, lt, pl, pt_BR, sl)
+
+3.8.1
+=====
+* many improvements to window-list:
+ - windows are activated by DND over them
+ - window buttons now have the right size,
+ even if the text is smaller or larger
+ than the ideal
+ - window buttons can be grouped automatically
+ when the panel becomes crowded
+ - added a workspace switcher menu
+* added keyboard navigation to apps-menu
+* small tweaks to classic-mode theme, in particular
+ for menus
+* translation updates (gl, ko, sr)
+
+3.8.0
+=====
+* translation updates (hu, ja, fi, it)
+
+3.7.92
+======
+* misc bug fixes to app-menu and window-list
+* translation updates (de, sl, pt_BR, ru)
+
+3.7.91
+======
+* various updates for shell changes
+* update window-list to always use application icons
+* update apps-menu to not load subdirectories as
+ separate categories
+* translation updates (lt, zh_CN)
+
+3.7.90
+======
+* various fixes to make places-menu behave more
+ like Nautilus, including showing the machine
+ name in place of File System
+* various updates for shell changes
+* alternative-status-menu no longer supports
+ ConsoleKit systems, you need to install logind
+ to have suspend or hibernate
+* translation updates (es, cz, pl, sr)
+
+3.7.5.1
+=======
+* new extension forgotten in previous NEWS entry:
+ windows-list
+* also forgotten previously: classic mode got a new
+ GNOME2 style
+* build fixes
+
+3.7.5
+=====
+* places-menu is back in the classic extensions, with
+ a new old GNOME-2 look
+* classic mode moved the date menu to right, where we
+ all know it rightly belongs
+* apps-menu received a face-lift, with the inclusion
+ of a reduced form of AxeMenu
+* new extension in the classic set: launch-new-instance,
+ which modifies the behavior of clicking in the dash
+ and app launcher
+* alternate-tab, native-window-placement and windowsNavigator
+ updated for gnome-shell changes
+* translation updates (es, cz, pl)
+
+3.7.4
+=====
+* a separate configure switch has been added to enable
+ classic mode session definitions
+* places-menu is no longer part of the classic-mode
+ extension set
+* updated translations (ar, gl, hu, lt, pt_BR, sr)
+
+3.7.3
+=====
+* new extensions: default-min-max, static-workspaces
+* alternate-tab now uses the built-in window switcher and just
+ takes over the switch-application keybinding
+* workspace-indicator: is no longer part of classic-mode
+* we now install classic-mode data files for gdm, gnome-session
+ and gnome-shell, so if you enable classic-mode you get a new
+ session option in GDM
+* updated translations (ar, es, pl, ru, sl, zh_HK, zh_TW)
+
+3.7.2
+=====
+* fixed crashes with places-menu, windowsNavigator, alternate-tab
+ and native-window-placement
+* alternate-tab now hides attached modal dialogs
+* places-menu has restored support for Nautilus 3.4
+* the default for hibernate is now to show in alternative-status-menu
+* some extensions are now tagged as "classic", and can be chosen with
+ --enable-extensions=classic-mode
+* dock and gajim were removed at the beginning of the 3.7.1 cycle,
+ as they were buggy and unmaintained
+* updated translations (ar, cs, de, el, es, gl, id, lt, lv, pa, pl
+ ru, sk, sl, sr, sr@latin)
+
+3.6.1
+=====
+* fixed alternative-status-menu for the new lock screen
+* squashed some alternate-tab warnings
+* drive-menu now works with 3.6 again
+* updated translations (ar, cs, el, es, gl, id, lv, pl, sl)
+
+3.6.0
+=====
+* major rework in places menu, to make it work without
+ removed supporting code in the shell and to make it look like
+ the nautilus sidebar
+ (similar work would be needed for drive-menu, not done yet)
+* updated translations (ca, cs, de, el, en_GB, es, fi, hu, id, lt,
+ pl, pt_BR, ru, sl, sr)
+
+3.5.91
+======
+* various crashers were fixed in alternative-tab
+* auto-move-windows now can be made to work with static workspaces
+* place-menu is now on the left and uses symbolic icons like Files
+* StIconType usage was removed from all extensions, after it was
+ removed in core
+* systemMonitor, xrandr-indicator, apps-menu, places-menu,
+ alternative-status-menu were updated for the newer shell
+* updated translations (es, gl, it, pl, sl)
+
+3.5.90
+======
+* alternate-tab has been reworked again, the old mode switch
+ was removed and the all&thumbnails code extended to handle
+ icons and filtering to the workspace
+* alternate-tab thumbnails now reflect the aspect ratio of the windows
+* systemMonitor now shows a tooltip above the indicator
+* native-window-placement, systemMonitor and windowsNavigator have been updated
+ for the newer shell
+* updated translations (es, pa)
+
+3.5.5
+=====
+* convenience module has been relicensed to BSD,
+ for compatibility with GPLv3 extensions
+* alternate-tab has been refactored and seen various
+ improvements to all&thumbnails mode, including a new
+ overlaid application icon
+* updated translations (lt, id, sr)
+
+3.5.4
+=====
+* updated translations (de, es, ar, sl, lv, zh_CN)
+
+3.5.2
+=====
+* removable-drive-menu is now a11y friendly
+* the dock can now be placed on any monitor, not just the primary
+* dock is now clipped to its monitor
+* alternative-status-menu now exposes GSettings for Suspend
+ and Hibernate visibility - no UI yet
+* more gnome-shell API changes (places-menu, removable-drive-menu,
+ alternative-status-menu)
+* miscellaneous bug fixes (native-window-placement, gajim,
+ auto-move-windows)
+* updated translations
+
+3.4.0
+=====
+* build system improvements
+* updated translations (ar, cs, fr)
+
+3.3.92
+======
+* various updates for gnome-shell API changes (dock,
+ native-window-placement)
+* local-install is now a make rule, not a shell script
+* updated translations (zh, es, sw, ga, hu, it, no, pt_BR, de, sl,
+ pl, la, fi, sr)
+
+3.3.90
+======
+* system wide installation via "make install" is possible
+ again
+* alternate-tab can now pre-activate the selected window
+* auto-move-windows, workspace-indicator and example gained
+ new preference dialogs
+* workspace-indicator: fixed a bug wrt focus stealing prevention
+* updated translations (es, pt_BR, it, sl, gl, sr)
+
+3.3.5
+=====
+* improvements to the build system and convenience module,
+ making it easier for other extensions to use, and bringing
+ it up to date with gnome-shell changes
+* all extensions were ported to the Lang.Class framework
+ (except xrandr-indicator, which is pending GDBus merge)
+* alternate-tab and dock were slightly refactored to clean up
+ some old code
+
+3.3.4
+=====
+* improved styling of windowsNavigator tooltips
+* fixed windowsNavigator when used with the numeric keypad
+* fixed native-window-placement with custom button layout
+* updated translations (pt_BR, cz)
+
+3.3.3
+=====
+* windowsNavigator was fixed to work with azerty keyboards
+* drive-menu was changed to use media-eject icon instead of media-optical
+* dock: the default value of hide-effect is now move
+* dock: if autohide is disabled, now it pushes maximized windows aside
+* dock was updated to match current core shell styling
+* native-window-placement: position stategy setting was removed
+* alternative-status-menu no longer conflicts with other extensions
+ in the user menu
+* various other minor bug fixes
+* updated translations (zh, uk, es, it, cz, sl, sk, fi)
+
+3.3.2
+=====
+* all extensions are now self-contained, including l10n and settings
+* introduce a convenience module that can be shared among all extensions
+* you can know build an installable zip file with make zip-file
+* apps-menu no longer shows NoDisplay apps
+* alternative-status-menu, alternate-tab: fix for master shell
+
+3.2.1
+=====
+* dock: added "move" hide effect
+* systemMonitor: now it enables/disables properly
+* systemMonitor: improved styling
+* alternate-tab: both modes now work with gnome-shell 3.2
+* various other bug fixes
+* updated translations
+
+3.2.0
+=====
+
+* various: update for gnome-shell API changes
+
+3.1.91
+======
+
+* gajim: update for gnome-shell API changes
+
+3.1.90
+======
+
+* All extensions have been ported to the new extension
+ system (including live enable/disable)
+* Updated translations
+* xrandr-indicator no longer requires a specific gjs version
+* windowsNavigator fixed for more than 2 workspaces
+
+3.1.4
+=====
+
+* New extension: a menu for changing workspace (workspace-indicator)
+* systemMonitor: lower the requirement on libgtop
+* auto-move-windows: open overview when last window on
+ last workspace is closed
+* dock: implement autohiding, with various configurable
+ effects
+* alternate-tab: more configurable implementations available
+* native-window-placement: don't rearrange the windows when
+ the workspace switcher is shown/hidden
+* update for gnome-shell 3.1.4 API changes
+
+3.1.3
+=====
+
+* New extension: a menu for removable drives (drive-menu
+* New extensions: GNOME 2 like menus for apps and places
+ (apps-menu, places-menu)
+* New extension: additional configurability for the window
+ layout in the overview, including a mechanism similar to
+ KDE4 (native-window-placement)
+* New extension: a message tray indicator for CPU and memory
+ usage (uses libgtop) (systemMonitor)
+* user-theme: fixed resetting theme
+* user-theme: support themes installed in /usr/share/themes
+* alternative-status-menu: ported to gnome-shell master
+* dock: ported to gnome-shell master
+* dock: make position configurable (can be left or right)
+* Updated translations
+
+3.0.2
+=====
+
+* Updated translations.
+* Fixed bug #647386 (reverting of user-theme to default)
+* Fixed bug #647599 (support globally installed themes)
+* Added license and README
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/extension.js
new file mode 100644
index 0000000..bb18b0f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/extension.js
@@ -0,0 +1,211 @@
+// SPDX-FileCopyrightText: 2011 Giovanni Campagna <gcampagna@src.gnome.org>
+// SPDX-FileCopyrightText: 2018 Florian Müllner <fmuellner@gnome.org>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// Drive menu extension
+import Clutter from 'gi://Clutter';
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import Shell from 'gi://Shell';
+import St from 'gi://St';
+
+import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as ShellMountOperation from 'resource:///org/gnome/shell/ui/shellMountOperation.js';
+
+Gio._promisify(Gio.File.prototype, 'query_filesystem_info_async');
+
+class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
+ static {
+ GObject.registerClass(this);
+ }
+
+ constructor(mount) {
+ super({
+ style_class: 'drive-menu-item',
+ });
+
+ this.label = new St.Label({
+ text: mount.get_name(),
+ x_expand: true,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this.add_child(this.label);
+ this.label_actor = this.label;
+
+ this.mount = mount;
+
+ const ejectIcon = new St.Icon({
+ icon_name: 'media-eject-symbolic',
+ style_class: 'popup-menu-icon',
+ });
+ const ejectButton = new St.Button({
+ child: ejectIcon,
+ style_class: 'button',
+ });
+ ejectButton.connect('clicked', this._eject.bind(this));
+ this.add_child(ejectButton);
+
+ this.hide();
+
+ mount.connectObject('changed',
+ () => this._syncVisibility(), this);
+ this._syncVisibility();
+ }
+
+ async _isInteresting() {
+ if (!this.mount.can_eject() && !this.mount.can_unmount())
+ return false;
+ if (this.mount.is_shadowed())
+ return false;
+
+ const volume = this.mount.get_volume();
+
+ if (volume)
+ return volume.get_identifier('class') !== 'network';
+
+ const root = this.mount.get_root();
+
+ try {
+ const attr = Gio.FILE_ATTRIBUTE_FILESYSTEM_REMOTE;
+ const info = await root.query_filesystem_info_async(attr, null);
+ return !info.get_attribute_boolean(attr);
+ } catch (e) {
+ log(`Failed to query filesystem: ${e.message}`);
+ }
+
+ // Hack, fall back to looking at GType
+ return Gio._LocalFilePrototype.isPrototypeOf(root);
+ }
+
+ async _syncVisibility() {
+ this.visible = await this._isInteresting();
+ }
+
+ _eject() {
+ const unmountArgs = [
+ Gio.MountUnmountFlags.NONE,
+ new ShellMountOperation.ShellMountOperation(this.mount).mountOp,
+ null, // Gio.Cancellable
+ ];
+
+ if (this.mount.can_eject()) {
+ this.mount.eject_with_operation(...unmountArgs,
+ this._ejectFinish.bind(this));
+ } else {
+ this.mount.unmount_with_operation(...unmountArgs,
+ this._unmountFinish.bind(this));
+ }
+ }
+
+ _unmountFinish(mount, result) {
+ try {
+ mount.unmount_with_operation_finish(result);
+ } catch (e) {
+ this._reportFailure(e);
+ }
+ }
+
+ _ejectFinish(mount, result) {
+ try {
+ mount.eject_with_operation_finish(result);
+ } catch (e) {
+ this._reportFailure(e);
+ }
+ }
+
+ _reportFailure(exception) {
+ // TRANSLATORS: %s is the filesystem name
+ const msg = _('Ejecting drive “%s” failed:').format(this.mount.get_name());
+ Main.notifyError(msg, exception.message);
+ }
+
+ activate(event) {
+ const uri = this.mount.get_root().get_uri();
+ const context = global.create_app_launch_context(event.get_time(), -1);
+ Gio.AppInfo.launch_default_for_uri(uri, context);
+
+ super.activate(event);
+ }
+}
+
+class DriveMenu extends PanelMenu.Button {
+ static {
+ GObject.registerClass(this);
+ }
+
+ constructor() {
+ super(0.5, _('Removable devices'));
+
+ const icon = new St.Icon({
+ icon_name: 'media-eject-symbolic',
+ style_class: 'system-status-icon',
+ });
+
+ this.add_child(icon);
+
+ this._monitor = Gio.VolumeMonitor.get();
+ this._monitor.connectObject(
+ 'mount-added', (monitor, mount) => this._addMount(mount),
+ 'mount-removed', (monitor, mount) => {
+ this._removeMount(mount);
+ this._updateMenuVisibility();
+ }, this);
+
+ this._mounts = [];
+
+ this._monitor.get_mounts().forEach(this._addMount.bind(this));
+
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.menu.addAction(_('Open Files'), event => {
+ const appSystem = Shell.AppSystem.get_default();
+ const app = appSystem.lookup_app('org.gnome.Nautilus.desktop');
+ app.activate_full(-1, event.get_time());
+ });
+
+ this._updateMenuVisibility();
+ }
+
+ _updateMenuVisibility() {
+ if (this._mounts.filter(i => i.visible).length > 0)
+ this.show();
+ else
+ this.hide();
+ }
+
+ _addMount(mount) {
+ const item = new MountMenuItem(mount);
+ this._mounts.unshift(item);
+ this.menu.addMenuItem(item, 0);
+
+ item.connect('notify::visible', () => this._updateMenuVisibility());
+ }
+
+ _removeMount(mount) {
+ for (let i = 0; i < this._mounts.length; i++) {
+ const item = this._mounts[i];
+ if (item.mount === mount) {
+ item.destroy();
+ this._mounts.splice(i, 1);
+ return;
+ }
+ }
+ log('Removing a mount that was never added to the menu');
+ }
+}
+
+export default class PlaceMenuExtension extends Extension {
+ enable() {
+ this._indicator = new DriveMenu();
+ Main.panel.addToStatusArea('drive-menu', this._indicator);
+ }
+
+ disable() {
+ this._indicator.destroy();
+ delete this._indicator;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ab/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ab/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..6c9b622
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ab/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/af/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/af/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..376fa25
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/af/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/an/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/an/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..40be536
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/an/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ar/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ar/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..5151e30
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ar/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/as/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/as/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..7b7172c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/as/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/be/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/be/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..5c86e6a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/be/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bg/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bg/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..5f62348
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bg/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bn_IN/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bn_IN/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..513a8df
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bn_IN/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bs/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bs/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..a104985
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/bs/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..2a6f63a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca@valencia/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca@valencia/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..7c9b6bd
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ca@valencia/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/cs/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/cs/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..4f30742
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/cs/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/da/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/da/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..362880c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/da/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/de/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/de/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..4d2f485
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/de/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/el/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/el/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..33f41d5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/el/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/en_GB/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/en_GB/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..cddf68c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/en_GB/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eo/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eo/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..3f39b5a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eo/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/es/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/es/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..4b1b333
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/es/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/et/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/et/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..7e8e3dc
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/et/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..6c0c45f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/eu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fa/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fa/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..6812a0c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fa/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..208f965
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..3a37691
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fur/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fur/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..23bb827
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/fur/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gd/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gd/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..8e5f8f1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gd/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..1098936
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..e437420
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/gu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/he/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/he/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..e033ced
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/he/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..e437090
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..e7f238a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..88f9fc4
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/hu/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ia/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ia/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..33bbeae
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ia/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/id/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/id/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..5875f01
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/id/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/is/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/is/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..dc1d9f3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/is/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/it/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/it/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..60183f3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/it/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ja/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ja/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..60ecaa0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ja/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ka/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ka/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..c551c33
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ka/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kab/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kab/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..d4351eb
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kab/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..035ed04
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/km/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/km/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..00ac490
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/km/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kn/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kn/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..5826698
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/kn/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ko/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ko/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..60431f6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ko/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lt/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lt/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..92f6ad7
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lt/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lv/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lv/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..07e7c6e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/lv/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mjw/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mjw/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..41ea452
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mjw/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ml/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ml/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..3222735
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ml/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..c76b576
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/mr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ms/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ms/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..c36d52c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ms/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nb/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nb/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..17797d8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nb/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ne/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ne/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..2c340e8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ne/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..6ec3908
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/nl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/oc/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/oc/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..104d236
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/oc/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/or/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/or/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..4d3d3d3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/or/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pa/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pa/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..91056a8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pa/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..db0defe
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..cc582be
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt_BR/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt_BR/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..8fc0768
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/pt_BR/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ro/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ro/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..252442f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ro/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ru/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ru/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..d82d090
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ru/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..8301e67
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..a43e2b9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sl/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..1e0b627
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr@latin/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr@latin/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..50d14f0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sr@latin/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sv/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sv/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..3c6dd21
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/sv/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ta/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ta/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..72a603a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/ta/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/te/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/te/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..0e07528
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/te/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tg/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tg/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..987df82
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tg/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/th/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/th/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..dbf2ca9
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/th/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..0a68ea6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/tr/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/uk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/uk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..3ef84fc
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/uk/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/vi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/vi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..a25aa0d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/vi/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_CN/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_CN/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..2582a66
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_CN/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_HK/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_HK/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..b380ac7
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_HK/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_TW/LC_MESSAGES/gnome-shell-extension-drive-menu.mo b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_TW/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
new file mode 100644
index 0000000..125d98d
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/locale/zh_TW/LC_MESSAGES/gnome-shell-extension-drive-menu.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/metadata.json
new file mode 100644
index 0000000..c659081
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/metadata.json
@@ -0,0 +1,14 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "A status menu for accessing and unmounting removable devices.",
+ "extension-id": "drive-menu",
+ "gettext-domain": "gnome-shell-extension-drive-menu",
+ "name": "Removable Drive Menu",
+ "settings-schema": "org.gnome.shell.extensions.drive-menu",
+ "shell-version": [
+ "50"
+ ],
+ "url": "https://gitlab.gnome.org/GNOME/gnome-shell-extensions",
+ "uuid": "drive-menu@gnome-shell-extensions.gcampax.github.com",
+ "version": 75
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/stylesheet.css b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/stylesheet.css
new file mode 100644
index 0000000..022697a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/drive-menu@gnome-shell-extensions.gcampax.github.com/stylesheet.css
@@ -0,0 +1,13 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Florian Müllner <fmuellner@gnome.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+.drive-menu-item { spacing: 12px; }
+
+.drive-menu-item .button {
+ border-radius: 99px;
+ padding: 3px;
+ min-height: auto;
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/LICENSE b/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/extension.js
new file mode 100644
index 0000000..1230777
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/extension.js
@@ -0,0 +1,39 @@
+/*
+ * GNOME Shell extension to hide Universal Access icon from the status bar
+ *
+ * Copyright (C) 2019 Akatsuki Rui
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
+import { panel } from "resource:///org/gnome/shell/ui/main.js";
+
+export default class HideUniversalAccess extends Extension {
+ enable() {
+ let a11y = panel.statusArea["a11y"];
+ if (a11y != null) {
+ a11y.container.hide();
+ }
+ }
+
+ disable() {
+ let a11y = panel.statusArea["a11y"];
+ if (a11y != null) {
+ a11y.container.show();
+ }
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/metadata.json
new file mode 100644
index 0000000..b03eab6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/hide-universal-access@akiirui.github.io/metadata.json
@@ -0,0 +1,12 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Hide Universal Access icon from the status bar",
+ "name": "Hide Universal Access",
+ "shell-version": [
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/akiirui/hide-universal-access",
+ "uuid": "hide-universal-access@akiirui.github.io",
+ "version": 20
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/extension.js
new file mode 100644
index 0000000..7d7a148
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/extension.js
@@ -0,0 +1,18 @@
+/*
+ No overview at start-up
+ GNOME Shell 46+ extension
+ Contributors: @fthx
+ License: GPL v3
+*/
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+export default class NoOverviewExtension {
+ enable() {
+ Main.layoutManager.connectObject('startup-complete', () => Main.overview.hide(), this);
+ }
+
+ disable() {
+ Main.layoutManager.disconnectObject(this);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/metadata.json
new file mode 100644
index 0000000..46eb936
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/no-overview@fthx/metadata.json
@@ -0,0 +1,19 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "No overview at start-up. Nothing more.",
+ "donations": {
+ "paypal": "fthx75"
+ },
+ "name": "No overview at start-up",
+ "original-authors": [
+ "fthx"
+ ],
+ "shell-version": [
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/fthx/no-overview",
+ "uuid": "no-overview@fthx",
+ "version": 22
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/CHANGELOG.md b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/CHANGELOG.md
new file mode 100644
index 0000000..7ec9db3
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/CHANGELOG.md
@@ -0,0 +1,29 @@
+## Version 14
+ * add Gnome 49 support
+## Version 13
+ * add Gnome 48 support
+## Version 12
+ * add Gnome 47 support
+## Version 11
+ * Gnome 46 support
+## Version 10
+ * null 'this._settings' in disable
+## Version 9
+ * porting to Gnome 45
+ * add 'do not timeout' option
+## Version 8
+ * add possibility to change urgency to normal
+## Version 7
+ * Gnome 44 support
+## Version 6
+ * Gnome 43 support
+## Version 5
+ * remove non-debug logging
+## Version 4
+ * Gnome 41 support
+## Version 3
+ * Gnome 40 support
+## Version 2
+ * address e.g.o comment
+## Version 1
+ * initial versions
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/LICENSE b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/LICENSE
new file mode 100644
index 0000000..dfb190e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2023 Václav Chlumský
+
+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. \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/README.md b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/README.md
new file mode 100644
index 0000000..777b168
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/README.md
@@ -0,0 +1,24 @@
+# notification-timeout
+
+## Gnome Shell extension
+notification-timeout is a Gnome Shell extension that allows configuring the same timeout for all notifications. It also allows ignoring the idle state, which means all the notification can timeout even if the system is idle.
+
+## Supported Gnome Shell version
+This extension supports Gnome Shell verison 45 and above.
+
+## Installation from e.g.o
+https://extensions.gnome.org/extension/3795/notification-timeout/
+
+## Manual installation
+
+ 1. `git clone https://github.com/vchlum/notification-timeout.git`
+ 1. `cd notification-timeout`
+ 1. `make build`
+ 1. `make install`
+ 1. Log out & Log in
+ 1. `gnome-extensions enable notification-timeout@chlumskyvaclav.gmail.com`
+
+## Install dependencies
+ - These are only required to install from source
+ - `make`
+ - `gnome-shell` (`gnome-extensions` command)
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/extension.js
new file mode 100644
index 0000000..d441143
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/extension.js
@@ -0,0 +1,133 @@
+'use strict';
+
+/**
+ * extension notification-timeout
+ * JavaScript Gnome extension for setting same timeout for all notifications.
+ *
+ * @author Václav Chlumský
+ * @copyright Copyright 2023, Václav Chlumský.
+ */
+
+ /**
+ * @license
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Václav Chlumský
+ *
+ * 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.
+ */
+
+import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
+import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
+
+let newTimeout = 1000;
+let alwaysNormal = true;
+let ignoreIdle = true;
+
+export default class NotificationTimeoutExtension extends Extension {
+
+ readSettings() {
+ ignoreIdle = this._settings.get_boolean("ignore-idle");
+ alwaysNormal = this._settings.get_boolean("always-normal");
+ newTimeout = this._settings.get_int("timeout");
+
+ }
+
+ _modifiedUpdateNotificationTimeout(timeout) {
+ if (timeout > 0) {
+ timeout = newTimeout;
+ }
+
+ /* call the original _updateNotificationTimeout with new timeout */
+ this._updateNotificationTimeoutOrig(timeout);
+ }
+
+ _modifiedUpdateStatus() {
+ if (ignoreIdle) {
+ this._userActiveWhileNotificationShown = true;
+ }
+
+ /* call the original _updateState anyway */
+ this._updateStateOrig();
+ }
+
+ _modifiedSetUrgency(urgency) {
+ /* call the original setUrgency */
+ if (newTimeout === 0) {
+ this._setUrgencyOrig(MessageTray.Urgency.CRITICAL);
+ } else if (alwaysNormal) {
+ this._setUrgencyOrig(MessageTray.Urgency.NORMAL);
+ } else {
+ this._setUrgencyOrig(urgency);
+ }
+ }
+
+ enable() {
+ this._settings = this.getSettings();
+
+ this.readSettings();
+ this._settingsConnectId = this._settings.connect(
+ "changed",
+ () => {
+ this.readSettings();
+ }
+ );
+
+ /**
+ * Change _updateNotificationTimeout()
+ */
+ MessageTray.MessageTray.prototype._updateNotificationTimeoutOrig = MessageTray.MessageTray.prototype._updateNotificationTimeout;
+ MessageTray.MessageTray.prototype._updateNotificationTimeout = this._modifiedUpdateNotificationTimeout;
+
+ /**
+ * Change _updateState()
+ */
+ MessageTray.MessageTray.prototype._updateStateOrig = MessageTray.MessageTray.prototype._updateState;
+ MessageTray.MessageTray.prototype._updateState = this._modifiedUpdateStatus;
+
+ /**
+ * Change setUrgency()
+ */
+ MessageTray.Notification.prototype._setUrgencyOrig = MessageTray.Notification.prototype.setUrgency;
+ MessageTray.Notification.prototype.setUrgency = this._modifiedSetUrgency;
+ }
+
+ disable() {
+ this._settings.disconnect(this._settingsConnectId);
+ this._settings = null;
+
+ /**
+ * Reveret change _updateNotificationTimeout()
+ */
+ MessageTray.MessageTray.prototype._updateNotificationTimeout = MessageTray.MessageTray.prototype._updateNotificationTimeoutOrig;
+ delete MessageTray.MessageTray.prototype._updateNotificationTimeoutOrig;
+
+ /**
+ * Reveret change _updateState()
+ */
+ MessageTray.MessageTray.prototype._updateState = MessageTray.MessageTray.prototype._updateStateOrig;
+ delete MessageTray.MessageTray.prototype._updateStateOrig;
+
+ /**
+ * Reveret change setUrgency()
+ */
+ MessageTray.Notification.prototype.setUrgency = MessageTray.Notification.prototype._setUrgencyOrig;
+ delete MessageTray.Notification.prototype._setUrgencyOrig;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/metadata.json
new file mode 100644
index 0000000..22d4875
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/metadata.json
@@ -0,0 +1,19 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "This extension allows configuring the same timeout for all notifications. It also allows ignoring the idle state.",
+ "gettext-domain": "notification-timeout",
+ "name": "Notification Timeout",
+ "original-author": "chlumskyvaclav@gmail.com",
+ "settings-schema": "org.gnome.shell.extensions.notification-timeout",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/vchlum/notification-timeout",
+ "uuid": "notification-timeout@chlumskyvaclav.gmail.com",
+ "version": 14
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefs.js
new file mode 100644
index 0000000..435bd4e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefs.js
@@ -0,0 +1,47 @@
+'use strict';
+
+/**
+ * extension notification-timeout
+ * JavaScript Gnome extension for setting same timeout for all notifications.
+ *
+ * @author Václav Chlumský
+ * @copyright Copyright 2023, Václav Chlumský.
+ */
+
+ /**
+ * @license
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Václav Chlumský
+ *
+ * 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.
+ */
+
+ import {ExtensionPreferences} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+ import * as PrefsPage from './prefspage.js';
+
+ export default class HueLightsPreferences extends ExtensionPreferences {
+ fillPreferencesWindow(window) {
+
+ window.set_default_size(860, 580);
+
+ const prefspage = new PrefsPage.PreferencesPage(this.metadata, this.getSettings());
+ window.add(prefspage.buildPrefsWidget());
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefspage.js b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefspage.js
new file mode 100644
index 0000000..ed2859a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/prefspage.js
@@ -0,0 +1,275 @@
+'use strict';
+
+/**
+ * extension notification-timeout
+ * JavaScript Gnome extension for setting same timeout for all notifications.
+ *
+ * @author Václav Chlumský
+ * @copyright Copyright 2023, Václav Chlumský.
+ */
+
+ /**
+ * @license
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2023 Václav Chlumský
+ *
+ * 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.
+ */
+
+import Gtk from 'gi://Gtk';
+import Adw from 'gi://Adw';
+import GObject from 'gi://GObject';
+import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+export const PreferencesPage = GObject.registerClass({
+ GTypeName: 'NotificationTimeoutPrefsWidget'
+}, class PreferencesPage extends GObject.Object {
+ _init(metadata, settings) {
+ super._init();
+
+ this._metadata = metadata;
+ this._settings = settings;
+ this._ignoreIdle = settings.get_boolean("ignore-idle");
+ this._alwaysNormal = settings.get_boolean("always-normal");
+ this._newTimeout = settings.get_int("timeout");
+ }
+
+ /**
+ * Build Settings page with general settings
+ *
+ * @method buildGeneralPage
+ * @return {Object} widget for page in notebook
+ */
+ _buildGeneralPage() {
+
+ let top = 1;
+ let labelWidget = null;
+
+ let generalPage = new Gtk.Grid(
+ {
+ hexpand: true,
+ vexpand: true,
+ halign:Gtk.Align.CENTER,
+ valign:Gtk.Align.CENTER
+ }
+ );
+
+ /**
+ * Ignore idle
+ */
+ labelWidget = new Gtk.Label(
+ {label: _("Ignore idle (always use timeout):")}
+ );
+ generalPage.attach(labelWidget, 1, top, 1, 1);
+
+
+ let ignoreIdleSwitch = new Gtk.Switch(
+ {
+ active: this._ignoreIdle,
+ hexpand: false,
+ vexpand: false,
+ halign:Gtk.Align.CENTER,
+ valign:Gtk.Align.CENTER
+ }
+ );
+
+ ignoreIdleSwitch.connect(
+ "notify::active",
+ () => {
+ this._ignoreIdle = ignoreIdleSwitch.get_active();
+ this._settings.set_boolean("ignore-idle", this._ignoreIdle);
+ }
+ )
+ generalPage.attach_next_to(
+ ignoreIdleSwitch,
+ labelWidget,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1
+ );
+
+ top++;
+
+ /**
+ * Notification' urgency is always normal
+ */
+ labelWidget = new Gtk.Label(
+ {label: _("Timeouted notifications are always 'normal' ('critical' don't timeout):")}
+ );
+ generalPage.attach(labelWidget, 1, top, 1, 1);
+
+
+ let alwaysNormalSwitch = new Gtk.Switch(
+ {
+ active: this._alwaysNormal,
+ hexpand: false,
+ vexpand: false,
+ halign:Gtk.Align.CENTER,
+ valign:Gtk.Align.CENTER
+ }
+ );
+
+ alwaysNormalSwitch.connect(
+ "notify::active",
+ () => {
+ this._alwaysNormal = alwaysNormalSwitch.get_active();
+ this._settings.set_boolean("always-normal", this._alwaysNormal);
+ }
+ )
+ generalPage.attach_next_to(
+ alwaysNormalSwitch,
+ labelWidget,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1
+ );
+
+ top++;
+
+ /**
+ * Notification timeout
+ */
+ labelWidget = new Gtk.Label(
+ {label: _("Notification timeout:")}
+ );
+ generalPage.attach(labelWidget, 1, top, 1, 1);
+
+ let timeoutComboBox = new Gtk.ComboBoxText();
+
+ timeoutComboBox.append("0", _("Don't timeout"));
+ timeoutComboBox.append("500", _("500 ms"));
+ timeoutComboBox.append("1000", _("1 s"));
+ timeoutComboBox.append("2000", _("2 s"));
+ timeoutComboBox.append("3000", _("3 s"));
+ timeoutComboBox.append("4000", _("4 s"));
+ timeoutComboBox.append("5000", _("5 s"));
+ timeoutComboBox.append("6000", _("6 s"));
+ timeoutComboBox.append("7000", _("7 s"));
+ timeoutComboBox.append("8000", _("8 s"));
+ timeoutComboBox.append("9000", _("9 s"));
+ timeoutComboBox.append("10000", _("10 s"));
+ timeoutComboBox.append("15000", _("15 s"));
+ timeoutComboBox.append("20000", _("20 s"));
+ timeoutComboBox.append("30000", _("30 s"));
+ timeoutComboBox.append("45000", _("45 s"));
+ timeoutComboBox.append("60000", _("1 min"));
+ timeoutComboBox.append("120000", _("2 min"));
+ timeoutComboBox.append("180000", _("3 min"));
+ timeoutComboBox.append("240000", _("4 min"));
+ timeoutComboBox.append("300000", _("5 min"));
+ timeoutComboBox.append("600000", _("10 min"));
+ timeoutComboBox.append("900000", _("15 min"));
+ timeoutComboBox.append("1800000", _("30 min"));
+
+ timeoutComboBox.set_active_id(this._newTimeout.toString());
+
+ timeoutComboBox.connect(
+ "changed",
+ () => {
+ this._newTimeout = parseInt(timeoutComboBox.get_active_id());
+ this._settings.set_int("timeout", this._newTimeout);
+ }
+ );
+
+ generalPage.attach_next_to(
+ timeoutComboBox,
+ labelWidget,
+ Gtk.PositionType.RIGHT,
+ 1,
+ 1
+ );
+
+ top++;
+
+ return generalPage;
+ }
+
+ /**
+ * Build About page
+ *
+ * @method buildGeneralPage
+ * @return {Object} widget for page in notebook
+ */
+ _buildAboutPage() {
+
+ let aboutWidget = new Gtk.Box(
+ {
+ hexpand: true,
+ vexpand: true,
+ halign:Gtk.Align.CENTER,
+ valign:Gtk.Align.CENTER
+ }
+ );
+
+ let labelWidget = new Gtk.Label(
+ {label: `${this._metadata.name}, version: ${this._metadata.version}, Copyright (c) 2023 Václav Chlumský`}
+ );
+
+ aboutWidget.append(labelWidget);
+
+ return aboutWidget;
+ }
+
+ /**
+ * This function is called when the preferences window is first created to build
+ * and return a Gtk widget.
+ *
+ * @method buildPrefsWidget
+ * @return {Object} returns the prefsWidget
+ */
+ buildPrefsWidget() {
+
+ let prefsPage = new Adw.PreferencesPage();
+ let prefsGroup = new Adw.PreferencesGroup();
+
+ let prefsMainBox = new Gtk.Box(
+ {
+ hexpand: true,
+ vexpand: true,
+ vexpand_set:true,
+ hexpand_set: true,
+ halign:Gtk.Align.FILL,
+ valign:Gtk.Align.FILL
+ }
+ );
+
+ let notebook = new Gtk.Notebook();
+
+ let pageGeneral = this._buildGeneralPage();
+ pageGeneral.border_width = 10;
+ notebook.append_page(
+ pageGeneral,
+ new Gtk.Label({label: _("General settings")})
+ );
+
+ let pageAbout = this._buildAboutPage()
+ pageAbout.border_width = 10;
+ notebook.append_page(
+ pageAbout,
+ Gtk.Image.new_from_icon_name("help-about", Gtk.IconSize.MENU)
+ );
+
+ prefsMainBox.append(notebook);
+ prefsGroup.add(prefsMainBox);
+ prefsPage.add(prefsGroup);
+
+ return prefsPage;
+ }
+});
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/gschemas.compiled
new file mode 100644
index 0000000..285234b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/org.gnome.shell.extensions.notification-timeout.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/org.gnome.shell.extensions.notification-timeout.gschema.xml
new file mode 100644
index 0000000..1d23477
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/notification-timeout@chlumskyvaclav.gmail.com/schemas/org.gnome.shell.extensions.notification-timeout.gschema.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+
+ <schema id="org.gnome.shell.extensions.notification-timeout" path="/org/gnome/shell/extensions/notification-timeout/">
+
+ <!-- See also: https://developer.gnome.org/glib/stable/gvariant-format-strings.html -->
+
+ <key name="ignore-idle" type="b">
+ <default>true</default>
+ <summary>Ignores idle user - always timeout</summary>
+ </key>
+ <key name="always-normal" type="b">
+ <default>true</default>
+ <summary>Always as normal.</summary>
+ </key>
+ <key name="timeout" type="i">
+ <default>3000</default>
+ <summary>Notification timeout</summary>
+ </key>
+ </schema>
+</schemalist> \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/extension.js
new file mode 100644
index 0000000..cdbdc6a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/extension.js
@@ -0,0 +1,180 @@
+/* extension.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+/* exported init */
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import Clutter from 'gi://Clutter';
+import St from 'gi://St';
+import Pango from 'gi://Pango';
+import { panel } from 'resource:///org/gnome/shell/ui/main.js';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js';
+import { Extension, gettext as _, } from 'resource:///org/gnome/shell/extensions/extension.js';
+const GRUB_CONFIG_PATH = "/boot/grub/grub.cfg";
+export default class RebootToWindowsExtension extends Extension {
+ menu;
+ proxy;
+ rebootToWindowsItem;
+ counter;
+ seconds;
+ counterIntervalId;
+ messageIntervalId;
+ sourceId;
+ manager;
+ constructor(metadata) {
+ super(metadata);
+ }
+ modifySystemItem() {
+ this.menu =
+ panel.statusArea.quickSettings._system?.quickSettingsItems[0].menu;
+ // if(this.manager == null){
+ // return
+ // }
+ this.proxy = this.manager(Gio.DBus.system, 'org.freedesktop.login1', '/org/freedesktop/login1');
+ this.rebootToWindowsItem = new PopupMenu.PopupMenuItem(`${_('Restart to Windows')}...`);
+ this.rebootToWindowsItem.connect('activate', () => {
+ this.counter = 60;
+ this.seconds = this.counter;
+ const dialog = this.buildDialog();
+ dialog.open();
+ this.counterIntervalId = setInterval(() => {
+ if (this.counter > 0) {
+ this.counter--;
+ if (this.counter % 10 === 0) {
+ this.seconds = this.counter;
+ }
+ }
+ else {
+ this.clearIntervals();
+ this.reboot();
+ }
+ }, 1000);
+ });
+ this.menu.addMenuItem(this.rebootToWindowsItem, 2);
+ }
+ queueModifySystemItem() {
+ this.sourceId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ if (!panel.statusArea.quickSettings._system)
+ return GLib.SOURCE_CONTINUE;
+ this.modifySystemItem();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ enable() {
+ const ManagerInterface = `<node>
+ <interface name="org.freedesktop.login1.Manager">
+ <method name="Reboot">
+ <arg type="b" direction="in"/>
+ </method>
+ </interface>
+ </node>`;
+ this.manager = Gio.DBusProxy.makeProxyWrapper(ManagerInterface);
+ if (!panel.statusArea.quickSettings._system) {
+ this.queueModifySystemItem();
+ }
+ else {
+ this.modifySystemItem();
+ }
+ }
+ disable() {
+ this.manager = null;
+ this.clearIntervals();
+ this.rebootToWindowsItem?.destroy();
+ this.rebootToWindowsItem = null;
+ this.proxy = null;
+ if (this.sourceId) {
+ GLib.Source.remove(this.sourceId);
+ this.sourceId = null;
+ }
+ }
+ get_windows_grub_entry(file_path) {
+ const file = Gio.File.new_for_path(file_path);
+ let boot_entry = "";
+ let content = file.load_contents(null)[1];
+ const contentsText = new TextDecoder('utf-8').decode(content);
+ const arr = contentsText.split(/\r?\n/);
+ let menu_pattern = new RegExp("^\\s*menuentry ['\"]([^'\"]*)['\"]");
+ arr.forEach((line) => {
+ let matches = menu_pattern.exec(line);
+ if (matches != null && !matches[1].toLowerCase().search("windows")) {
+ boot_entry = matches[1];
+ }
+ });
+ return boot_entry;
+ }
+ async reboot() {
+ let windows_grub_entry = this.get_windows_grub_entry(GRUB_CONFIG_PATH);
+ const [, argv] = GLib.shell_parse_argv(`pkexec grub-reboot "${windows_grub_entry}"`);
+ const proc = Gio.Subprocess.new(argv, Gio.SubprocessFlags.NONE);
+ await proc.wait_check_async(null);
+ this.proxy?.RebootRemote(true);
+ }
+ buildDialog() {
+ const dialog = new ModalDialog.ModalDialog({ styleClass: 'modal-dialog' });
+ dialog.setButtons([
+ {
+ label: _('Cancel'),
+ action: () => {
+ this.clearIntervals();
+ dialog.close();
+ },
+ key: Clutter.KEY_Escape,
+ default: false,
+ },
+ {
+ label: _('Restart'),
+ action: () => {
+ this.clearIntervals();
+ this.reboot();
+ },
+ default: false,
+ },
+ ]);
+ const dialogTitle = new St.Label({
+ text: _('Restart into Windows'),
+ // style_class: 'dialog-title' // TODO investigate why css classes are not working
+ style: 'font-weight: bold;font-size:18px',
+ });
+ let dialogMessage = new St.Label({
+ text: this.getDialogMessageText(),
+ });
+ dialogMessage.clutterText.ellipsize = Pango.EllipsizeMode.NONE;
+ dialogMessage.clutterText.lineWrap = true;
+ const titleBox = new St.BoxLayout({
+ xAlign: Clutter.ActorAlign.CENTER,
+ });
+ titleBox.add_child(new St.Label({ text: ' ' }));
+ titleBox.add_child(dialogTitle);
+ let box = new St.BoxLayout({ yExpand: true, vertical: true });
+ box.add_child(titleBox);
+ box.add_child(new St.Label({ text: ' ' }));
+ box.add_child(dialogMessage);
+ this.messageIntervalId = setInterval(() => {
+ dialogMessage?.set_text(this.getDialogMessageText());
+ }, 500);
+ dialog.contentLayout.add_child(box);
+ return dialog;
+ }
+ getDialogMessageText() {
+ return _(`The system will restart automatically in %d seconds.`).replace('%d', String(this.seconds));
+ }
+ clearIntervals() {
+ clearInterval(this.counterIntervalId);
+ clearInterval(this.messageIntervalId);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/locale/de/LC_MESSAGES/rebootintoWindows@nimz.cool.mo b/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/locale/de/LC_MESSAGES/rebootintoWindows@nimz.cool.mo
new file mode 100644
index 0000000..cfb77be
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/locale/de/LC_MESSAGES/rebootintoWindows@nimz.cool.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/metadata.json
new file mode 100644
index 0000000..b11a18e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/rebootintowindows@nimz.cool/metadata.json
@@ -0,0 +1,18 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Reboot system into Windows\n\nIt is a fork of RebootToUEFI.",
+ "gettext-domain": "rebootintowindows@nimz.cool",
+ "license": "GPLv3",
+ "name": "RebootIntoWindows",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/jusnim/rebootintowindows",
+ "uuid": "rebootintowindows@nimz.cool",
+ "version": 4
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/extension.js
new file mode 100644
index 0000000..ecaa1f5
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/extension.js
@@ -0,0 +1,159 @@
+/* extension.js
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+/* exported init */
+import GLib from 'gi://GLib';
+import Gio from 'gi://Gio';
+import Clutter from 'gi://Clutter';
+import St from 'gi://St';
+import Pango from 'gi://Pango';
+import { panel } from 'resource:///org/gnome/shell/ui/main.js';
+import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
+import * as ModalDialog from 'resource:///org/gnome/shell/ui/modalDialog.js';
+import { Extension, gettext as _, } from 'resource:///org/gnome/shell/extensions/extension.js';
+const ManagerInterface = `<node>
+ <interface name="org.freedesktop.login1.Manager">
+ <method name="SetRebootToFirmwareSetup">
+ <arg type="b" direction="in"/>
+ </method>
+ <method name="Reboot">
+ <arg type="b" direction="in"/>
+ </method>
+ </interface>
+</node>`;
+const Manager = Gio.DBusProxy.makeProxyWrapper(ManagerInterface);
+export default class RebootToUefiExtension extends Extension {
+ menu;
+ proxy;
+ rebootToUefiItem;
+ counter;
+ seconds;
+ counterIntervalId;
+ messageIntervalId;
+ sourceId;
+ constructor(metadata) {
+ super(metadata);
+ }
+ modifySystemItem() {
+ this.menu =
+ panel.statusArea.quickSettings._system?.quickSettingsItems[0].menu;
+ this.proxy = Manager(Gio.DBus.system, 'org.freedesktop.login1', '/org/freedesktop/login1');
+ this.rebootToUefiItem = new PopupMenu.PopupMenuItem(`${_('Restart to UEFI')}...`);
+ this.rebootToUefiItem.connect('activate', () => {
+ this.counter = 60;
+ this.seconds = this.counter;
+ const dialog = this.buildDialog();
+ dialog.open();
+ this.counterIntervalId = setInterval(() => {
+ if (this.counter > 0) {
+ this.counter--;
+ if (this.counter % 10 === 0) {
+ this.seconds = this.counter;
+ }
+ }
+ else {
+ this.clearIntervals();
+ this.reboot();
+ }
+ }, 1000);
+ });
+ this.menu.addMenuItem(this.rebootToUefiItem, 2);
+ }
+ queueModifySystemItem() {
+ this.sourceId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
+ if (!panel.statusArea.quickSettings._system)
+ return GLib.SOURCE_CONTINUE;
+ this.modifySystemItem();
+ return GLib.SOURCE_REMOVE;
+ });
+ }
+ enable() {
+ if (!panel.statusArea.quickSettings._system) {
+ this.queueModifySystemItem();
+ }
+ else {
+ this.modifySystemItem();
+ }
+ }
+ disable() {
+ this.clearIntervals();
+ this.rebootToUefiItem?.destroy();
+ this.rebootToUefiItem = null;
+ this.proxy = null;
+ if (this.sourceId) {
+ GLib.Source.remove(this.sourceId);
+ this.sourceId = null;
+ }
+ }
+ reboot() {
+ this.proxy?.SetRebootToFirmwareSetupRemote(true);
+ this.proxy?.RebootRemote(false);
+ }
+ buildDialog() {
+ const dialog = new ModalDialog.ModalDialog({ styleClass: 'modal-dialog' });
+ dialog.setButtons([
+ {
+ label: _('Cancel'),
+ action: () => {
+ this.clearIntervals();
+ dialog.close();
+ },
+ key: Clutter.KEY_Escape,
+ default: false,
+ },
+ {
+ label: _('Restart'),
+ action: () => {
+ this.clearIntervals();
+ this.reboot();
+ },
+ default: false,
+ },
+ ]);
+ const dialogTitle = new St.Label({
+ text: _('Restart to UEFI'),
+ // style_class: 'dialog-title' // TODO investigate why css classes are not working
+ style: 'font-weight: bold;font-size:18px',
+ });
+ let dialogMessage = new St.Label({
+ text: this.getDialogMessageText(),
+ });
+ dialogMessage.clutterText.ellipsize = Pango.EllipsizeMode.NONE;
+ dialogMessage.clutterText.lineWrap = true;
+ const titleBox = new St.BoxLayout({
+ xAlign: Clutter.ActorAlign.CENTER,
+ });
+ titleBox.add_child(new St.Label({ text: ' ' }));
+ titleBox.add_child(dialogTitle);
+ let box = new St.BoxLayout({ yExpand: true, vertical: true });
+ box.add_child(titleBox);
+ box.add_child(new St.Label({ text: ' ' }));
+ box.add_child(dialogMessage);
+ this.messageIntervalId = setInterval(() => {
+ dialogMessage?.set_text(this.getDialogMessageText());
+ }, 500);
+ dialog.contentLayout.add_child(box);
+ return dialog;
+ }
+ getDialogMessageText() {
+ return _(`The system will restart automatically in %d seconds.`).replace('%d', String(this.seconds));
+ }
+ clearIntervals() {
+ clearInterval(this.counterIntervalId);
+ clearInterval(this.messageIntervalId);
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/de/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/de/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..bf69be6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/de/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/es/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/es/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..0bf7143
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/es/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fa/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fa/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..a57b55f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fa/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fr/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fr/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..2c0190e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/fr/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/hu/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/hu/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..148ced1
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/hu/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/it/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/it/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..9a11583
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/it/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/nl/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/nl/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..93ae66b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/nl/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pl/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pl/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..88d48b0
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pl/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pt_BR/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pt_BR/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..6526459
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/pt_BR/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/ru/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/ru/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..7228b00
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/ru/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/tr/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/tr/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..d991f86
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/tr/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/zh_TW/LC_MESSAGES/reboottouefi@ubaygd.com.mo b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/zh_TW/LC_MESSAGES/reboottouefi@ubaygd.com.mo
new file mode 100644
index 0000000..6be1f6a
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/locale/zh_TW/LC_MESSAGES/reboottouefi@ubaygd.com.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/metadata.json
new file mode 100644
index 0000000..329b993
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/reboottouefi@ubaygd.com/metadata.json
@@ -0,0 +1,18 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Reboot system into UEFI",
+ "gettext-domain": "reboottouefi@ubaygd.com",
+ "license": "GPLv3",
+ "name": "RebootToUEFI",
+ "shell-version": [
+ "45",
+ "46",
+ "47",
+ "48",
+ "49",
+ "50"
+ ],
+ "url": "https://github.com/UbayGD/reboottouefi",
+ "uuid": "reboottouefi@ubaygd.com",
+ "version": 26
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/COPYING b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/COPYING
new file mode 100644
index 0000000..e55e5b8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/NEWS b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/NEWS
new file mode 100644
index 0000000..267e905
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/NEWS
@@ -0,0 +1,1652 @@
+50.0
+====
+* Bump version
+
+
+50.rc
+=====
+* workspace-indicator: Make workspace menu section scrollable [Florian; !445]
+* Misc. bug fixes and cleanups [Florian; !444, !446]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Lenny Angst [de], Changwoo Ryu [ko]
+
+50.alpha
+========
+* classic: Drop xorg sessions [Jordan; !283]
+* classic: Deifne a gnome-session session [Adrian; !3960]
+* Misc. bug fixes and cleanups [Florian, Jordan; !425, !429, !430, !432, !431,
+ !433, !434, !435, !439]
+
+Contributors:
+ Florian Müllner, Jordan Petridis, Adrian Vovk
+
+Translators:
+ BoF ButterflyOfFire [kab], Giannis Antypas [el],
+ Asier Saratsua Garmendia [eu], Hugo Carvalho [pt], twlvnn kraftwerk [bg],
+ Baurzhan Muftakhidinov [kk], Ibai Oihanguren Sala [eu]
+
+49.0
+====
+* window-list: Adjust to gnome-shell changes [Florian; !421]
+* system-monitor: Unbreak on BSD [Antoine; !356]
+* Misc. bug fixes and cleanups [Florian; !422, !423]
+
+Contributors:
+ Antoine Jacoutot, Florian Müllner
+
+Translators:
+ Jiri Grönroos [fi], Aurimas Aurimas Černius [lt]
+
+49.rc
+=====
+
+Translators:
+ Emilio Sepúlveda [ia], Dušan Kazik [sk], Daniel Rusek [cs],
+ Nathan Follens [nl], Piotr Drąg [pl], Balázs Úr [hu], Yosef Or Boczko [he]
+
+49.beta
+=======
+* Misc. bug fixes and cleanups [Florian; !408]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Anders Jonsson [sv], Makoto Sakaguchi [ja], Danial Behzadi [fa],
+ Jordi Mas i Hernandez [ca]
+
+49.alpha.1
+==========
+* workspaces-indicator, window-list: Better expose workspace names
+ [Florian; !405]
+* window-list: Animate transition to/from overview [Florian; !412]
+* Misc. bug fixes and cleanups [Florian; !409, !411, !413]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Martin [sl], Emin Tufan Çetin [tr], Yuri Chornoivan [uk], Luming Zh [zh_CN],
+ Yago Raña [gl], Ekaterine Papava [ka], Vasil Pupkin [be],
+ Álvaro Burns [pt_BR], Sergej A. [ru]
+
+49.alpha.0
+==========
+* windowsNavigator: Fix handling keyboard shortcuts [Daniel; !395]
+* build: Allow disabling the X11 session [Neal; !396, !400]
+* Disable X11 session by default [Jordan; !399]
+* Misc. bug fixes and cleanups [Florian; !398, !406]
+
+Contributors:
+ Daniel Buch Hansen, Neal Gompa, Florian Müllner, Jordan Petridis
+
+Translators:
+ Emilio Sepúlveda [ia]
+
+48.1
+====
+
+Translators:
+ Hugo Carvalho [pt], Danial Behzadi [fa]
+
+48.0
+====
+* apps-menu: Fix scrolling items into view on keynav [Victor; !391]
+* Misc. bug fixes and cleanups [Florian, Stuart; !390, !392]
+
+Contributors:
+ Stuart Hayhurst, Victor Kareh, Florian Müllner
+
+48.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !385, !388]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Emilio Sepúlveda [ia], Mathews M [ml], Daniel Rusek [cs], Piotr Drąg [pl],
+ Anders Jonsson [sv], Ekaterine Papava [ka], Yuri Chornoivan [uk],
+ Aurimas Černius [lt], Luming Zh [zh_CN], Jiri Grönroos [fi]
+
+48.beta
+=======
+* window-list: Fix regression in chrome tracking [Florian; !379]
+* Misc. bug fixes and cleanups [Florian; !380]
+
+Contributors:
+ Florian Müllner, Emilio Sepúlveda
+
+Translators:
+ Rafael Fontenelle [pt_BR], Emilio Sepúlveda [ia]
+
+48.alpha
+========
+* classic: Add missing top-bar indicators [Florian; !339]
+* window-list: Fix window state styling [Florian; !342]
+* window-list: Fix "ignore-workspace" setting getting reset [Florian; !341]
+* window-list: Allow rearranging window buttons [Florian, Jakub; !338]
+* window-list: Add workspaces page to preference dialog [Florian; !344]
+* places-menu: Sync list of places with nautilus [Florian; !340]
+* places-menu: Fix a11y labelling [Florian; #542]
+* places-menu: Fix opening drives with mount operations [Florian; !361]
+* window-list: Fix hiding when entering overview with gestures [Florian; !364]
+* workspace-indicator: Only show previews of regular windows [Florian; !363]
+* window-list: Add attention indicator [Florian; !366]
+* Misc. bug fixes and cleanups [Florian, Bartłomiej; !337, !343, !345, !347,
+ !348, !349, !351, !352, !353, !354, !358, !362, !365, !367, !368, !370, !375]
+
+Contributors:
+ Florian Müllner, Bartłomiej Piotrowski, Jakub Steiner
+
+Translators:
+ Fabio Tomat [fur], Martin [sl], Jordi Mas i Hernandez [ca], Vasil Pupkin [be],
+ Nathan Follens [nl], Artur S0 [ru], Марко Костић [sr],
+ Yaron Shahrabani [he], Sabri Ünal [tr], Yi-Jyun Pan [zh_TW]
+
+47.0
+====
+
+Translators:
+ twlvnn kraftwerk [bg], Alexander Shopov [bg], Fran Dieguez [gl],
+ Aurimas Černius [lt], Daniel [es], Andika Triwidada [id],
+ Andi Chandler [en_GB], Ask Hjorth Larsen [da], Aefgh Threenine [th],
+ Dušan Kazik [sk], Rūdolfs Mazurs [lv], Irénée THIRION [fr]
+
+47.rc
+=====
+* Misc. bug fixes and cleanups [Sophie; !333]
+
+Contributors:
+ Sophie Herold
+
+Translators:
+ Daniel Șerbănescu [ro], Giannis Antypas [el], Ekaterine Papava [ka],
+ Jordi Mas i Hernandez [ca], Yuri Chornoivan [uk], Jiri Grönroos [fi],
+ Daniel Rusek [cs], Vasil Pupkin [be], Luming Zh [zh_CN], Brage Fuglseth [nb],
+ Asier Sarasua Garmendia [eu], Danial Behzadi [fa], Rafael Fontenelle [pt_BR],
+ Quentin PAGÈS [oc], Anders Jonsson [sv], Jürgen Benvenuti [de],
+ Changwoo Ryu [ko], Hugo Carvalho [pt], Piotr Drąg [pl], Sabri Ünal [tr]
+
+47.beta
+=======
+* window-list: Modernize styling [Jakub; !330]
+* Include "status-icons" extension [Florian; !194]
+* Misc. bug fixes and cleanups [Florian; !328, !331, !327]
+
+Contributors:
+ Florian Müllner, Jakub Steiner
+
+Translators:
+ Sabri Ünal [tr], Chao-Hsiung Liao [zh_TW]
+
+47.alpha
+========
+* Improve workspace previews in window-list and workspace-indicator
+ [Florian; !307, !316]
+* apps-menu: Fix a11y of category labels [Florian; !319]
+* window-list: Fix long-press support [Florian; !320]
+* window-list: Animate transitions [Florian; !325]
+* Misc. bug fixes and cleanups [Florian; !315, !321, !324]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jordi Mas i Hernandez [ca], Martin [sl], Hugo Carvalho [pt], Jose Riha [sk],
+ Scrambled 777 [hi], Artur S0 [ru], Милош Поповић [sr], Yosef Or Boczko [he],
+ Balázs Úr [hu]
+
+46.1
+====
+* screenshot-window-sizer: Add flathub-recommended size [Florian; !317]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Rachida SACI [kab], Matheus Polkorny [pt_BR], Fabio Tomat [fur]
+
+46.0
+====
+* system-monitor: Fix net speed [Florian; !313]
+* Misc. bug fixes and cleanups [Aral; !311]
+
+Contributors:
+ Aral Balkan, Florian Müllner
+
+Translators:
+ Anders Jonsson [sv], Piotr Drąg [pl], Balázs Úr [hu], Milo Casagrande [it],
+ Quentin PAGÈS [oc], Athmane MOKRAOUI [kab], Changwoo Ryu [ko],
+ Ask Hjorth Larsen [da]
+
+46.rc
+=====
+* Fix window previews in workspace indicator [Florian; !304]
+* Fix menu ornament in workspace indicator [Florian; !305]
+* Misc. bug fixes and cleanups [Florian; !306, !309]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Danial Behzadi [fa], Ekaterine Papava [ka], Sabri Ünal [tr], Artur S0 [ru],
+ Yuri Chornoivan [uk], Vasil Pupkin [be], Asier Sarasua Garmendia [eu],
+ Yaron Shahrabani [he], Brage Fuglseth [nb], Nathan Follens [nl],
+ Aurimas Černius [lt], Matej Urbančič [sl], Boyuan Yang [zh_CN],
+ Kukuh Syafaat [id], Fran Dieguez [gl], Andi Chandler [en_GB],
+ Baurzhan Muftakhidinov [kk], Rūdolfs Mazurs [lv], Guillaume Bernard [fr],
+ Daniel Mustieles [es], Jiri Grönroos [fi]
+
+46.beta
+=======
+* apps-menu: Rename Applications to Apps [Allan; !299]
+* Misc. bug fixes and cleanups [Florian; !296, !297, !300, !301, !302]
+
+Contributors:
+ Allan Day, Florian Müllner
+
+Translators:
+ Gabriel Brand [de], Daniel Rusek [cs], Fran Dieguez [gl],
+ Aefgh Threenine [th], Vasil Pupkin [be], Artur S0 [ru], Yosef Or Boczko [he],
+ Sabri Ünal [tr]
+
+46.alpha
+========
+* workspace-indicator: Fix initial preview visibility [Florian; !280, !292]
+* screenshot-window-sizer: Fix cycling between sizes backwards [Florian; !284]
+* Add back overview in Classic session [Florian; !287]
+* Allow running Classic session headless [Jonas; !289]
+* window-list: Fix buttons not being clickable at the screen edge
+ [Florian; !291]
+* Add system-monitor extension [Florian; !277]
+* Fixed crash [Florian; !290]
+* Misc. bug fixes and cleanups [Florian; !276, !275, !278, !281, !286, !288]
+
+Contributors:
+ Jonas Ådahl, Florian Müllner
+
+Translators:
+ Kristjan SCHMIDT [eo], Brage Fuglseth [nb]
+
+45.0
+====
+
+Contributors:
+ Andre Klapper
+
+Translators:
+ Bruce Cowan [en_GB]
+
+45.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !267, !224, !272]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Sabri Ünal [tr], Florentina Musat [ro], A S Alam [pa]
+
+45.beta
+=======
+* Port extensions to ESM [Florian; !259, !266, !268, !269]
+* Misc. bug fixes and cleanups [Florian; !260, !261, !262, !263, !264]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Efstathios Iosifidis [el]
+
+45.alpha
+========
+* window-list: Modernize default styling [Alexander; !253]
+* Replace classic styling with built-in light style [Florian; !254]
+* window-list: Add tooltip for long window titles [Arik; !251]
+* light-style: New extension [Florian; !256]
+* Misc. bug fixes and cleanups [Florian; !255, !257]
+
+Contributors:
+ Florian Müllner, Arik W, Alexander Weichart
+
+44.0
+====
+* Bump version
+
+44.rc
+=====
+* Bump version
+
+44.beta
+=======
+* Tweak menu alignment [robxnano; !246]
+
+Contributors:
+ Florian Müllner, robxnano
+
+Translators:
+ Vasil Pupkin [be]
+
+43.1
+====
+* Fixed crash [Florian; !243]
+* Misc. bug fixes and cleanups [mowemcfc; !244]
+
+Contributors:
+ Florian Müllner, mowemcfc
+
+Translators:
+ Sabri Ünal [tr]
+
+43.0
+====
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Pawan Chitrakar [ne], Zurab Kargareteli [ka], Aleksandr Melman [ru]
+
+43.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !240]
+
+Contributors:
+ Florian Müllner
+
+43.beta
+=======
+* Misc. bug fixes and cleanups [Florian; !237, !238]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Nart Tlisha [ab]
+
+43.alpha
+========
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Marco Ciampa [it]
+
+42.3
+====
+* screenshot-window-sizer: Fix reported sizes on wayland [Florian; !232]
+* window-list: Improve touch support [Florian; !233]
+
+Contributors:
+ Florian Müllner
+
+42.2
+====
+* native-window-placement: Adjust to gnome-shell 42 changes [Florian; !229]
+* window-list: Fix visibility on non-primary monitors [Jason; !230]
+
+Contributors:
+ Jason Lynch, Florian Müllner
+
+Translators:
+ Cheng-Chia Tseng [zh_TW]
+
+42.1
+====
+* Misc. bug fixes and cleanups [Florian; !223, !222, !225]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Milo Casagrande [it], Rūdolfs Mazurs [lv], Nathan Follens [nl],
+ Ngọc Quân Trần [vi], Zurab Kargareteli [ka]
+
+42.0
+====
+
+Translators:
+ Philipp Kiemle [de], Balázs Úr [hu], Марко Костић [sr], sicklylife [ja],
+ Baurzhan Muftakhidinov [kk]
+
+42.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !215, !218]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Marek Černocký [cs], Dušan Kazik [sk], Piotr Drąg [pl], Jiri Grönroos [fi],
+ Luna Jernberg [sv], Alan Mortensen [da], Charles Monzat [fr],
+ Changwoo Ryu [ko]
+
+42.beta
+=======
+* workspace-indicator: Fix cancelling editing with Esc [Florian; !208]
+* window-list: Update window tracking to avoid missing icons [Florian; !207]
+* Use libadwaita for preferences [Florian; !209, !213]
+* Adapt to Clutter grab API changes [Florian; !212]
+* Misc. bug fixes and cleanups [Jan, Florian; !210, !214]
+
+Contributors:
+ Jan Beich, Florian Müllner, Naala Nanba
+
+Translators:
+ Boyuan Yang [zh_CN], Matej Urbančič [sl], Naala Nanba [ab],
+ Alexander Shopov [bg], Emin Tufan Çetin [tr]
+
+42.alpha
+========
+* native-window-placement: Fix distorted layout in app grid [Sebastian; !189]
+* window-list: Fix on-screen keyboard [Florian; !199]
+* Misc. bug fixes and cleanups [Neal; Just; !195, !197]
+
+Contributors:
+ Piotr Drąg, Neal Gompa, Sebastian Keller, Florian Müllner, Just Perfection
+
+Translators:
+ Goran Vidović [hr], Sveinn í Felli [is], Yuri Chornoivan [uk],
+ Fabio Tomat [fur], Quentin PAGÈS [oc], Hugo Carvalho [pt],
+ Yaron Shahrabani [he], Jordi Mas i Hernandez [ca], MohammadSaleh Kamyab [fa],
+ Fran Dieguez [gl], Daniel Mustieles [es], Aleksandr Melman [ru],
+ Aurimas Černius [lt], Asier Sarasua Garmendia [eu], Kukuh Syafaat [id],
+ Rafael Fontenelle [pt_BR]
+
+41.0
+====
+* Bump version
+
+41.rc.1
+=======
+* Fix pre-generating stylesheets in tarball [Florian; !190]
+
+Contributors:
+ Florian Müllner
+
+41.rc
+=====
+* window-list: Adapt to overview-on-startup [Florian; !185]
+* apps-menu: Use a custom 'toggle-menu' shortcut [Florian; !173]
+* Misc. bug fixes and cleanups [Florian; !186]
+
+Contributors:
+ Florian Müllner
+
+41.beta
+=======
+* window-list: Extend reactive area of minimap to screen edges [Adam; !171]
+* drive-menu: Improve detection of network mounts [Florian; !27, !176]
+* Use distinct gettext domain for e.g.o uploads [Florian; #335]
+* Misc. bug fixes and cleanups [Florian; !172, !174, !177, !167, !178, !180,
+ !181, !182, !183]
+
+Contributors:
+ Marco Trevisan (Treviño), Adam Goode, Florian Müllner
+
+Translators:
+ Hugo Carvalho [pt], Juliano de Souza Camargo [pt], Alexander Shopov [bg]
+
+40.1
+====
+* Disable welcome dialog in classic session [Florian; !169]
+* windowsNavigator: Adjust to a late gnome-shell change [Florian; !170]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Ngọc Quân Trần [vi], Anders Jonsson [sv], Carmen Bianca BAKKER [eo],
+ Pawan Chitrakar [ne], Quentin PAGÈS [oc]
+
+40.0
+====
+
+Translators:
+ Jiri Grönroos [fi]
+
+40.rc
+=====
+* native-window-placement: Adjust to gnome-shell changes [Florian; !164]
+* windows-navigator: Adjust to gnome-shell changes [Florian; !163]
+* window-list, workspace-indicator: Only show previews for up to six workspaces
+ [Florian; !165]
+* window-list, workspace-indicator: Improve workspace preview appearance
+ [Florian; !166]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Fran Dieguez [gl]
+
+40.beta
+=======
+* Add tooltips to workspace thumbnails [Florian; !155]
+* Drop arrows from top bar menus [Florian; !156]
+* drive-menu: Mark mounts that can be unmounted as removable [Michael; !152]
+* Remove horizontal-workspaces extension [Florian; !158]
+* Adjust to shell overview changes [Florian; !159, !160]
+* Fix crashes [Daniel; !157]
+* Misc. bug fixes and cleanups [Florian; !154, !161]
+
+Contributors:
+ Michael Lawton, Florian Müllner, Daniel van Vugt
+
+Translators:
+ Аляксей [be], A S Alam [pa]
+
+40.alpha.1
+==========
+* Don't depend on sassc when building from tarball [Florian; !150]
+* Port extensions preferences to GTK4 [Florian; !148]
+* Misc. bug fixes and cleanups [Florian, Jonas; !149, !151, !153]
+
+Contributors:
+ Jonas Dreßler, Florian Müllner
+
+40.alpha
+========
+* window-list: Honor changes in skip-taskbar property [Sergio; !130]
+* window-list, workspace-indicator: Adjust to 3.38 changes [Florian; !133]
+* window-list, workspace-indicator: Improve previews in workspace thumbs
+ [Florian; #260, !142]
+* auto-move: Improve behavior on multi-monitor setups [Florian; !135]
+* windowNavigator: Adjust to 3.38 changes [Thun; #259]
+* Misc. bug fixes and cleanups [Florian, Jonas Å, Jordan, Ray; !131, !136,
+ !137, !140, !141, !144, !146, !145]
+
+Contributors:
+ Sergio Costas, Florian Müllner, Jordan Petridis, Thun Pin, Ray Strode,
+ Jonas Ådahl
+
+Translators:
+ Fabio Tomat [fur], Jordi Mas [ca]
+
+3.38.1
+======
+
+Contributors:
+ Yacine Bouklif, Florian Müllner
+
+Translators:
+ Yacine Bouklif [kab], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
+ Yosef Or Boczko [he]
+
+3.38.0
+======
+
+Translators:
+ Balázs Meskó [hu], Alan Mortensen [da], Juliano Camargo [pt], Tim Sabsch [de],
+ Milo Casagrande [it], Rūdolfs Mazurs [lv]
+
+3.37.92
+=======
+
+Translators:
+ Nathan Follens [nl], Zander Brown [en_GB], Aurimas Černius [lt],
+ Marek Černocký [cs], Changwoo Ryu [ko], Dušan Kazik [sk]
+
+3.37.91
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Fran Dieguez [gl], Akarshan Biswas [bn_IN], Kukuh Syafaat [id],
+ Piotr Drąg [pl], Rafael Fontenelle [pt_BR], Jiri Grönroos [fi],
+ Марко Костић [sr], Goran Vidović [hr]
+
+3.37.90
+=======
+* Misc. bug fixes and cleanups [Florian, Piotr; !126, !128]
+
+Contributors:
+ Piotr Drąg, Florian Müllner
+
+Translators:
+ Fabio Tomat [fur], Efstathios Iosifidis [el], Anders Jonsson [sv],
+ Asier Sarasua Garmendia [eu], Alexandre Franke [fr]
+
+3.37.3
+======
+* window-list, native-window-placement: Adjust to shell changes [Florian; !124]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jordi Mas [ca], sicklylife [ja], Boyuan Yang [zh_CN],
+ Baurzhan Muftakhidinov [kk]
+
+3.37.2
+======
+* window-list, auto-move: Modernize preference dialogs [Florian; !121]
+* Adjust to gnome-shell changes [Florian; !122]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Cheng-Chia Tseng [zh_TW], Yuri Chornoivan [uk], Daniel Mustieles [es],
+ Emin Tufan Çetin [tr], Danial Behzadi [fa], Daniel Șerbănescu [ro],
+ Matej Urbančič [sl]
+
+3.37.1
+======
+* drive-menu: Emphasize eject buttons [Florian; #223]
+* user-theme: Add preference dialog [Florian; !117]
+* window-list: Fix inconsistent state in preference dialog [Milan; !119]
+* workspace-indicator: Overhaul preference dialog [Florian; !120]
+* user-theme: Support session mode styles [Florian; !118]
+* Misc. bug fixes and cleanups [Florian, Xiaoguang; !113, !106, !114, !116]
+
+Contributors:
+ Milan Crha, Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Daniel Korostil [uk], Yosef Or Boczko [he], Kristjan SCHMIDT [eo],
+ Dz Chen [zh_CN], Danial Behzadi [fa], Yuri Chornoivan [uk],
+ Anders Jonsson [sv], Daniel Mustieles [es]
+
+3.36.0
+======
+
+Contributors:
+ Florian Müllner
+
+3.35.91
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Zander Brown [en_GB]
+
+3.35.90
+=======
+* Adjust to gnome-shell changes [Florian; !100, !101, !102]
+* Force single-line window titles in window list [Florian; #202]
+* Misc. bug fixes and cleanup [Florian; !104, !105]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ sicklylife [ja], Umarzuki Bin Mochlis Moktar [ms]
+
+3.35.3
+======
+
+Translators:
+ Fran Dieguez [gl]
+
+3.35.2
+======
+* Adjust to gnome-shell changes [Marco, Florian; !89, !95, !96]
+* window-list, workspace-indicator: Exclude DESKTOP windows from previews
+ [Florian; !93]
+* screenshot-window-sizer: Fix cycling through all valid sizes [Willy; !97]
+
+Contributors:
+ Marco Trevisan (Treviño), Florian Müllner, Willy Stadnick
+
+3.34.1
+======
+* Adjust to gnome-settings-daemon plugin removals [Xiaoguang; !94]
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Nathan Follens [nl], Dušan Kazik [sk], Ask Hjorth Larsen [da],
+ Yi-Jyun Pan [zh_TW]
+
+3.34.0
+======
+
+Translators:
+ Rafael Fontenelle [pt_BR], Efstathios Iosifidis [el], Milo Casagrande [it],
+ Sabri Ünal [tr]
+
+3.33.92
+=======
+
+Translators:
+ Марко Костић [sr], Tim Sabsch [de], Rūdolfs Mazurs [lv], Matej Urbančič [sl],
+ Balázs Úr [hu], Claude Paroz [fr], Fran Dieguez [gl], Changwoo Ryu [ko],
+ Ryuta Fujii [ja], Fabio Tomat [fur], Goran Vidović [hr]
+
+3.33.91
+=======
+* Misc. bug fixes and cleanups [Florian; !88, !90, !91, !92]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Asier Sarasua Garmendia [eu], Anders Jonsson [sv], Marek Černocký [cs],
+ Kukuh Syafaat [id], Jiri Grönroos [fi], Florentina Mușat [ro],
+ Aurimas Černius [lt], Daniel Mustieles [es], Piotr Drąg [pl], Jordi Mas [ca],
+ Danial Behzadi [fa]
+3.33.90
+=======
+* window-list: Support showing windows from all workspaces [Florian; #154]
+* Misc. bug fixes and cleanups [Florian; !86, !87]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jor Teron [mjw]
+
+3.33.4
+======
+* Make GNOME Classic more classic:
+ - Disable GNOME 3 overview [Florian; !69]
+ - Add window picker button to window list [Florian; !73, !80]
+ - Style improvements and fixes [Jakub; #169, !82]
+ - Support horizontal workspace layout in window list [Florian; !70]
+ - Add draggable previews to window list workspace switcher [Florian; !74]
+ - Arrange workspaces horizontally [Florian; !72]
+* workspace-indicator: Support horizontal workspace layout [Florian; !71]
+* workspace-indicator: Add draggable previews [Florian; !77]
+* Misc. bug fixes and cleanups [Florian; !75, !76, !79, !78, #168, !84]
+
+Contributors:
+ Florian Müllner, Jakub Steiner, Jor Teron
+
+Translators:
+ Jor Teron [mjw]
+
+3.33.3
+======
+* Misc. bug fixes [Florian, Marco; !67, !68]
+
+Contributors:
+ Florian Müllner, Marco Trevisan (Treviño)
+
+3.33.2
+======
+* Misc. bug fixes and cleanups [Florian; !66]
+
+Contributors:
+ Florian Müllner
+
+3.33.1
+======
+* Misc. bug fixes [Florian; !64]
+
+Contributors:
+ Florian Müllner
+
+3.32.1
+======
+* Fix windowsNavigator extension after ES6 port [Florian; #143]
+* screenshot-window-sizer: Add phone screenshot sizes [Adrien; !65]
+* Misc. bug fixes and cleanups [Fabian; !62]
+
+Contributors:
+ Florian Müllner, Adrien Plazas, Fabian P. Schmidt
+
+3.32.0
+======
+
+Contributors:
+ Florian Müllner
+
+Translations:
+ Victor Ibragimov [tg], Kristjan SCHMIDT [eo], Mart Raudsepp [et]
+
+3.31.92
+=======
+* Misc. bug fixes and cleanups [Florian; !57, !58, !59, !60]
+
+Contributors:
+ Florian Müllner
+
+3.31.91
+=======
+* apps-menu: Remove outdated legacy-tray handling [Florian; !53]
+* user-theme: Allow using XDG user data dir [Tomasz; !55]
+* Misc. bug fixes and cleanups [Florian; !52, !54, !56]
+
+Contributors:
+ Tomasz Gąsior, Florian Müllner
+
+Translators:
+ Matej Urbančič [sl], Gun Chleoc [gd]
+
+3.31.90
+=======
+* Misc. bug fixes and cleanups [Florian; !49, !50, !51]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Ryuta Fujii [ja], Charles Monzat [fr], Pieter Schalk Schoeman [af]
+
+3.31.2
+======
+* Remove obsolete alternate-tab extension [Florian; #786496]
+* Adjust to gnome-shell changes [Florian; #113]
+
+Contributors:
+ Florian Müllner
+
+3.30.1
+======
+* apps-menu: Fix height on HiDPI systems [Florian; #102]
+* window-list: Only switch between windows on active workspace when scrolling
+ [Florian; #78]
+
+Contributors:
+ Florian Müllner
+
+3.30.0
+======
+* Bump version
+
+3.29.91
+=======
+* Misc. bug fixes [Florian; #90]
+
+Contributors:
+ Florian Müllner
+
+3.29.90
+=======
+* Misc. bug fixes [Florian; #786496]
+
+Contributors:
+ Florian Müllner
+
+3.29.3
+======
+* Adjust to global.screen removal [Jonas; #759538]
+
+Contributors:
+ Jonas Ådahl, Florian Müllner
+
+3.29.2
+======
+* Misc. bug fixes [Florian; #69]
+
+Contributors:
+ Florian Müllner
+
+3.28.1
+======
+* Misc. bug fixes [Xiaoguang, Florian; #59, #62]
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Dz Chen [zh_CN]
+
+3.28.0
+======
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Aman Alam [pa], Bruce Cowan [en_GB]
+
+3.27.92
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Piotr Drąg [es], GNOME Translation Robot [gd], Daniel Șerbănescu [ro]
+
+3.27.91
+=======
+* places-menu: Support unmounting ejectable places [Rémy; #17]
+* apps-menu: Support separators and custom sort order [Florian; #27]
+* Port to meson [Florian; #31, #45]
+* window-list: Fix missing icons on wayland [Florian; #10]
+* places-menu: Fix terminating gnome-shell with recent gjs [Florian; #44]
+* auto-move: Make it work with wayland windows [Florian; #33]
+* Classic theme fixes [Florian, Jonas; #26, #41, #39, #40]
+* Require sassc for classic styling [Florian; !28]
+* Misc. bug fixes [Piotr, Florian; #772211, #32, #30]
+
+Contributors:
+ Jeremy Bicha, Piotr Drąg, Jonas Kümmerlin, Rémy Lefevre, Iñigo Martínez,
+ Florian Müllner
+
+Translators:
+ Matej Urbančič [sl], Kjartan Maraas [nb]
+
+3.27.1
+======
+* updated translations (ca@valencia)
+
+3.26.1
+======
+* native-window-placement: Adjust to gnome-shell changes
+* updated translations: el, fa, ru, sv
+
+3.26.0
+======
+* updated translations (be, bg, ca, da, eu, fi, is, it, ko, lv, ml,
+ nl, pt_BR, vi, zh_TW)
+
+3.25.91
+=======
+* updated translations (ca, fr, it, pl, pt_BR, sr, sr@latin, tr)
+
+3.25.90
+=======
+* updated translations (es, gl, hr, hu, kk, sl, sv, sv)
+
+3.25.4
+======
+* screenshot-window-sizer: Fix backward cycling
+* updated translations (ar, be, ca, cs, de, fur, id, lt, pl, sk)
+
+3.25.3
+======
+* places-menu: Use mount operation if necessary
+* window-list: Respect MWM hints
+* updated translations (es, fur, kk)
+
+3.25.2
+======
+* places-menu: Make URI launching asynchronous
+* updated translations (de, fur, hr, hu, id, sl)
+
+3.25.1
+======
+* apps-menu: Mark copied launchers as trusted
+* places-menu: Make icon lookup asynchronous
+* updated translations (hr)
+
+3.24.1
+======
+* apps-menu: Allow creating desktop launchers via DND
+* updated translations (el, vi)
+
+3.24.0
+======
+* updated translations (lv, tr)
+
+3.23.92
+=======
+* update classic theme
+* updated translations (be, ko, ca, da, cs, ru, lt)
+
+3.23.91
+=======
+* updated translations (de, es, eu, fi, fr, fur, gl, hu, id, it, kk, nb, pl, pt_BR,
+ sk, sr, sr@latin, sv, uk, zh_TW)
+
+3.23.90
+=======
+* window-list: Improve styling
+* window-list: Hide workspace indicator when there's a single (static) workspace
+* new translation (be)
+
+3.23.2
+======
+* alternateTab: Don't take over 'switch-group' shortcut
+* updated translations (zh_CN)
+
+3.22.1
+======
+* window-list: Update icon on app changes
+
+3.22.0
+======
+* updated translations (en_GB)
+
+3.21.92
+=======
+* update style
+* updated translations (pl, vi)
+
+3.21.91
+=======
+* updated translations (pl)
+
+3.21.90
+=======
+* updated translations (es, gu)
+
+3.21.4
+======
+* apps-menu: Fix entries from non-standard AppDir directories
+
+3.21.3
+======
+* adjust to gnome-shell changes
+* updated translations (oc)
+
+3.21.2
+======
+* version bump, nothing to see here
+
+3.20.1
+======
+* update classic style
+* updated translations (gd, oc)
+
+3.20.0
+======
+* version bump, nothing to see here
+
+3.19.92
+=======
+* version bump, nothing to see here
+
+3.19.91
+=======
+* updated translations (oc)
+
+3.19.90
+=======
+* version bump, nothing to see here
+
+3.19.4
+======
+* screenshot-window-sizer: HiDPI support
+* Fix gnome-shell component in classic session
+* updated translations (lt)
+
+3.19.3
+======
+* native-window-placement: Don't let border overlap title
+* apps-menu: Fix handling of .desktop files in subdirectories
+* updated translations (is)
+
+3.19.2
+======
+* updated translations (gd)
+
+3.19.1
+======
+* Fix some theme issues
+
+3.18.1
+======
+* window-list: Fix accessibility of window buttons
+* apps-menu: Fix unreliable highlight
+* updated translations (ar)
+
+3.18.0
+======
+* Bump version
+
+3.17.92
+=======
+* places: Include DESKTOP when desktop icons are enabled
+* updated translations (fa)
+
+3.17.91
+=======
+* updated translations (nl, pl, zh_TW)
+
+3.17.90
+=======
+* window-list: Improve application ordering
+* workspace-indicator: Use consistent workspace numbering
+
+3.17.4
+======
+* updated translations (fur)
+
+3.17.3
+======
+* window-list: Adjust with text-scaling-factor
+* classic style updates
+* updated translations (pt, ro)
+
+3.17.2
+======
+* updated translations (oc, pt, zh_CN)
+
+3.17.1
+======
+* style updates
+* updated translations (oc)
+
+3.16.1
+======
+* window-list: Fix workspace indicators popup menu position
+* apps-menu: Fix taking over panel-main-menu shortcut
+* updated translations (et, ja, lv)
+
+3.16.0
+======
+* updated translations (ca)
+
+3.15.92
+=======
+* classic: Update theme
+* update for mutter API changes
+* updated translations (bg, bs, da, fi, pa, ru, sr, sr@latin, tg)
+
+3.15.91
+=======
+* classic: Update theme
+* systemMonitor extension was removed, as the message tray where it
+ put its indicator no longer exists
+* window-list: Adjust for gnome-shell changes
+* updated translations (gl, it, kk, ko, lt, pl, sk, uk, zh_TW)
+
+3.15.90
+=======
+* classic: Visual refresh based on new shell theme
+* window-list: Adjust for gnome-shell changes
+* updated translations (an, el, eo, eu, fr, he, is, sv, tr)
+
+3.15.4
+======
+* window-list: Improve interaction with system modal dialogs
+* updated translations (cs, de, es, eu, fur, hu, id, nb, pt_BR, ru, sl, vi)
+
+3.15.3.1
+========
+* adjust to gnome-shell change
+
+3.15.3
+======
+* classic-mode: Add high-contrast theme variant, drop .desktop file
+* places-menu: Fix error when XDG user directories are not set up
+* window-list: Add option to show on all monitors
+* updated translations (eu, hu, kk, ro, tr)
+
+3.15.2
+======
+* removable-drive, user-theme, window-list: Update for gnome-shell changes
+* apps-menu: Fix some visual glitches
+* Fix classic mode style
+* updated translations (an, cs, he, vi)
+
+3.15.1
+======
+ * updated translations (es, nb)
+
+3.14.1
+======
+ * alternateTab: Fix dismissing popup with Escape
+ * some improvements to the window-list
+ (spacing in app buttons, no flash when closing windows with auto-grouping)
+ * updated translations (lv, it, pt, bg)
+
+3.14.0
+======
+ * updated translations (bn_IN, hi, kn, sr, sr@latin, uk)
+
+3.13.92
+=======
+* new extension: screenshot-window-sizer
+* window-list: Don't add sticky windows more than once
+* updated translations (da, de, fi, ko, mr, ms, ne, pa, pl, sk, sv, ta, te, tr)
+
+3.13.91
+=======
+* window-list: restore fitts'ability of workspace button
+* updated for gnome-shell changes
+* updated translations (cs, kk, fr, or, fa, ja, gu, id)
+
+3.13.90
+=======
+* updated translations (as, ca, eu, nl, zh_CN, zh_HK, zh_TW)
+
+3.13.4
+======
+* Updated for gnome-shell changes
+* updated translations (el, gl, ru)
+
+3.13.3
+======
+* Tweak preference UIs some more
+* Fix classic mode schema overrides
+* updated translations (es, he, hu, lt, nb, pt_BR, sl, tr)
+
+3.13.2
+======
+* Fix sorting of grouped buttons in window list
+* Tweak preference UIs
+* updated translations (en_GB)
+
+3.13.1
+======
+* add DesktopNames key to the classic session file
+* classic theme: remove rounded corners from tile previews
+* window-list: don't shift message tray on other monitors
+* auto-move-windows: several fixes and updates for api changes
+* launch-new-instances: updates for api changes
+* updated translations (ja, km)
+
+3.12.0
+======
+* updated translations (zh_HK, zh_TW)
+
+3.11.92
+=======
+* nothing to see here, move on
+
+3.11.91
+=======
+* updated translations (ko, fur)
+
+3.11.90
+=======
+* several fixes and improvements to the window-list
+ (can be scrolled, works correctly with the OSD
+ keyboard, filters skip-taskbar windows, does not
+ force all notifications to bold)
+* drive-menu fixed not to show shadowed mounts
+* updates for gnome-shell changes (launch-new-instance,
+ auto-move-windows, places-menu)
+* build system fixes for systems without /bin/bash
+* updated translations (or, tr, uk)
+
+3.11.5
+======
+* updates for gnome-shell changes
+* updated translations (kn)
+
+3.11.4
+======
+* classic mode now supports session saving
+* updates for gnome-shell changes
+* updated translations (ar, kn)
+
+3.11.3
+======
+* workspace-indicator is vertically aligned now
+* updated translations (ar, eo, ta, te)
+
+3.11.2
+======
+* updated translations (zh_CN)
+
+3.11.1
+======
+* ignore shadowed mounts in drive-menu extension
+* updates for gnome-shell/gjs changes
+* updated translations (el, th)
+
+3.10.1
+======
+* updated translations (af, ca, ca@valencia, de, et, eu, fa,
+ hu, lt, lv, nb, nl, pa, pt, sk, sr, sr@latin, tr)
+
+3.10.0
+======
+* updated translations (as, cs, da, es, et, fi, fr, gl,
+ he, id, it, kk, lt, lv, pa, pl, pt, pt_BR, ru, sl, tg,
+ uk, zh_HK, zh_TW)
+
+3.9.92
+======
+* more updates and fixes for gnome-shell master changes
+ and regressions (systemMonitor, window-list, apps-menu)
+* lots of updated translations (ar, as, cs, da, de, el, es,
+ eu, fi, gl, he, hu, it, ja, kk, ko, lv, nb, nl, pa, pl,
+ pt_BR, ru, sk, sr, sr@latin, ta, tg)
+
+3.9.91
+======
+* update the classic mode session and theme to work with the
+ new system menu
+* the usual round of updates and fixes for gnome-shell
+ API changes
+* updated translations (de, it, lt, nl, pl, pt_BR, sk,
+ zh_HK, zh_TW)
+
+3.9.90
+======
+* xrandr-indicator was removed, as the implementation
+ was incompatible with the new DisplayConfig mutter API
+* various extensions were updated for the 3.9.90 gnome-shell API
+* updated translations (cs, es, fur, gl, he, hu, id, ja, sl, tg,
+ zh_CN, zh_HK, zh_TW)
+
+3.9.5
+=====
+* alternative-status-menu was removed entirely, as
+ it does not fit in the designs of the new unified
+ status menu
+* updated translations (as, gu, it, ru)
+
+3.9.4
+=====
+* apps-menu: fixed handling of hot corner in case
+ of screen reconfiguration
+* alternative-status-menu now correctly honors polkit
+ for hibernation
+* user-menu now loads themes from $XDG_DATA_HOME too
+* translation updates (de, id, pt_BR, vi, zh_CN, ml)
+
+3.9.3
+=====
+* classic mode mini extensions were replaced with a
+ GSettings override specified in the .json file
+* styling of classic mode improved
+* native-window-placement is back working on 3.9
+* misc bug fixes
+* traslation updates (an, cs, el, es, gl, nb, pl, sk, tg)
+
+3.9.2
+=====
+* apps-menu: appearance of the scrollbars was improved
+* window-list is a little taller in classic mode (to account
+ for the workspace switcher)
+* alternative-status-menu honors again the dconf configuration
+* translation updates (sr)
+
+3.9.1
+=====
+* updates to window-list, xrandr-indicator,
+ workspace-indicator, windowsNavigator for gnome-shell
+ changes
+* translation updates (cs, es, lt, pl, pt_BR, sl)
+
+3.8.1
+=====
+* many improvements to window-list:
+ - windows are activated by DND over them
+ - window buttons now have the right size,
+ even if the text is smaller or larger
+ than the ideal
+ - window buttons can be grouped automatically
+ when the panel becomes crowded
+ - added a workspace switcher menu
+* added keyboard navigation to apps-menu
+* small tweaks to classic-mode theme, in particular
+ for menus
+* translation updates (gl, ko, sr)
+
+3.8.0
+=====
+* translation updates (hu, ja, fi, it)
+
+3.7.92
+======
+* misc bug fixes to app-menu and window-list
+* translation updates (de, sl, pt_BR, ru)
+
+3.7.91
+======
+* various updates for shell changes
+* update window-list to always use application icons
+* update apps-menu to not load subdirectories as
+ separate categories
+* translation updates (lt, zh_CN)
+
+3.7.90
+======
+* various fixes to make places-menu behave more
+ like Nautilus, including showing the machine
+ name in place of File System
+* various updates for shell changes
+* alternative-status-menu no longer supports
+ ConsoleKit systems, you need to install logind
+ to have suspend or hibernate
+* translation updates (es, cz, pl, sr)
+
+3.7.5.1
+=======
+* new extension forgotten in previous NEWS entry:
+ windows-list
+* also forgotten previously: classic mode got a new
+ GNOME2 style
+* build fixes
+
+3.7.5
+=====
+* places-menu is back in the classic extensions, with
+ a new old GNOME-2 look
+* classic mode moved the date menu to right, where we
+ all know it rightly belongs
+* apps-menu received a face-lift, with the inclusion
+ of a reduced form of AxeMenu
+* new extension in the classic set: launch-new-instance,
+ which modifies the behavior of clicking in the dash
+ and app launcher
+* alternate-tab, native-window-placement and windowsNavigator
+ updated for gnome-shell changes
+* translation updates (es, cz, pl)
+
+3.7.4
+=====
+* a separate configure switch has been added to enable
+ classic mode session definitions
+* places-menu is no longer part of the classic-mode
+ extension set
+* updated translations (ar, gl, hu, lt, pt_BR, sr)
+
+3.7.3
+=====
+* new extensions: default-min-max, static-workspaces
+* alternate-tab now uses the built-in window switcher and just
+ takes over the switch-application keybinding
+* workspace-indicator: is no longer part of classic-mode
+* we now install classic-mode data files for gdm, gnome-session
+ and gnome-shell, so if you enable classic-mode you get a new
+ session option in GDM
+* updated translations (ar, es, pl, ru, sl, zh_HK, zh_TW)
+
+3.7.2
+=====
+* fixed crashes with places-menu, windowsNavigator, alternate-tab
+ and native-window-placement
+* alternate-tab now hides attached modal dialogs
+* places-menu has restored support for Nautilus 3.4
+* the default for hibernate is now to show in alternative-status-menu
+* some extensions are now tagged as "classic", and can be chosen with
+ --enable-extensions=classic-mode
+* dock and gajim were removed at the beginning of the 3.7.1 cycle,
+ as they were buggy and unmaintained
+* updated translations (ar, cs, de, el, es, gl, id, lt, lv, pa, pl
+ ru, sk, sl, sr, sr@latin)
+
+3.6.1
+=====
+* fixed alternative-status-menu for the new lock screen
+* squashed some alternate-tab warnings
+* drive-menu now works with 3.6 again
+* updated translations (ar, cs, el, es, gl, id, lv, pl, sl)
+
+3.6.0
+=====
+* major rework in places menu, to make it work without
+ removed supporting code in the shell and to make it look like
+ the nautilus sidebar
+ (similar work would be needed for drive-menu, not done yet)
+* updated translations (ca, cs, de, el, en_GB, es, fi, hu, id, lt,
+ pl, pt_BR, ru, sl, sr)
+
+3.5.91
+======
+* various crashers were fixed in alternative-tab
+* auto-move-windows now can be made to work with static workspaces
+* place-menu is now on the left and uses symbolic icons like Files
+* StIconType usage was removed from all extensions, after it was
+ removed in core
+* systemMonitor, xrandr-indicator, apps-menu, places-menu,
+ alternative-status-menu were updated for the newer shell
+* updated translations (es, gl, it, pl, sl)
+
+3.5.90
+======
+* alternate-tab has been reworked again, the old mode switch
+ was removed and the all&thumbnails code extended to handle
+ icons and filtering to the workspace
+* alternate-tab thumbnails now reflect the aspect ratio of the windows
+* systemMonitor now shows a tooltip above the indicator
+* native-window-placement, systemMonitor and windowsNavigator have been updated
+ for the newer shell
+* updated translations (es, pa)
+
+3.5.5
+=====
+* convenience module has been relicensed to BSD,
+ for compatibility with GPLv3 extensions
+* alternate-tab has been refactored and seen various
+ improvements to all&thumbnails mode, including a new
+ overlaid application icon
+* updated translations (lt, id, sr)
+
+3.5.4
+=====
+* updated translations (de, es, ar, sl, lv, zh_CN)
+
+3.5.2
+=====
+* removable-drive-menu is now a11y friendly
+* the dock can now be placed on any monitor, not just the primary
+* dock is now clipped to its monitor
+* alternative-status-menu now exposes GSettings for Suspend
+ and Hibernate visibility - no UI yet
+* more gnome-shell API changes (places-menu, removable-drive-menu,
+ alternative-status-menu)
+* miscellaneous bug fixes (native-window-placement, gajim,
+ auto-move-windows)
+* updated translations
+
+3.4.0
+=====
+* build system improvements
+* updated translations (ar, cs, fr)
+
+3.3.92
+======
+* various updates for gnome-shell API changes (dock,
+ native-window-placement)
+* local-install is now a make rule, not a shell script
+* updated translations (zh, es, sw, ga, hu, it, no, pt_BR, de, sl,
+ pl, la, fi, sr)
+
+3.3.90
+======
+* system wide installation via "make install" is possible
+ again
+* alternate-tab can now pre-activate the selected window
+* auto-move-windows, workspace-indicator and example gained
+ new preference dialogs
+* workspace-indicator: fixed a bug wrt focus stealing prevention
+* updated translations (es, pt_BR, it, sl, gl, sr)
+
+3.3.5
+=====
+* improvements to the build system and convenience module,
+ making it easier for other extensions to use, and bringing
+ it up to date with gnome-shell changes
+* all extensions were ported to the Lang.Class framework
+ (except xrandr-indicator, which is pending GDBus merge)
+* alternate-tab and dock were slightly refactored to clean up
+ some old code
+
+3.3.4
+=====
+* improved styling of windowsNavigator tooltips
+* fixed windowsNavigator when used with the numeric keypad
+* fixed native-window-placement with custom button layout
+* updated translations (pt_BR, cz)
+
+3.3.3
+=====
+* windowsNavigator was fixed to work with azerty keyboards
+* drive-menu was changed to use media-eject icon instead of media-optical
+* dock: the default value of hide-effect is now move
+* dock: if autohide is disabled, now it pushes maximized windows aside
+* dock was updated to match current core shell styling
+* native-window-placement: position stategy setting was removed
+* alternative-status-menu no longer conflicts with other extensions
+ in the user menu
+* various other minor bug fixes
+* updated translations (zh, uk, es, it, cz, sl, sk, fi)
+
+3.3.2
+=====
+* all extensions are now self-contained, including l10n and settings
+* introduce a convenience module that can be shared among all extensions
+* you can know build an installable zip file with make zip-file
+* apps-menu no longer shows NoDisplay apps
+* alternative-status-menu, alternate-tab: fix for master shell
+
+3.2.1
+=====
+* dock: added "move" hide effect
+* systemMonitor: now it enables/disables properly
+* systemMonitor: improved styling
+* alternate-tab: both modes now work with gnome-shell 3.2
+* various other bug fixes
+* updated translations
+
+3.2.0
+=====
+
+* various: update for gnome-shell API changes
+
+3.1.91
+======
+
+* gajim: update for gnome-shell API changes
+
+3.1.90
+======
+
+* All extensions have been ported to the new extension
+ system (including live enable/disable)
+* Updated translations
+* xrandr-indicator no longer requires a specific gjs version
+* windowsNavigator fixed for more than 2 workspaces
+
+3.1.4
+=====
+
+* New extension: a menu for changing workspace (workspace-indicator)
+* systemMonitor: lower the requirement on libgtop
+* auto-move-windows: open overview when last window on
+ last workspace is closed
+* dock: implement autohiding, with various configurable
+ effects
+* alternate-tab: more configurable implementations available
+* native-window-placement: don't rearrange the windows when
+ the workspace switcher is shown/hidden
+* update for gnome-shell 3.1.4 API changes
+
+3.1.3
+=====
+
+* New extension: a menu for removable drives (drive-menu
+* New extensions: GNOME 2 like menus for apps and places
+ (apps-menu, places-menu)
+* New extension: additional configurability for the window
+ layout in the overview, including a mechanism similar to
+ KDE4 (native-window-placement)
+* New extension: a message tray indicator for CPU and memory
+ usage (uses libgtop) (systemMonitor)
+* user-theme: fixed resetting theme
+* user-theme: support themes installed in /usr/share/themes
+* alternative-status-menu: ported to gnome-shell master
+* dock: ported to gnome-shell master
+* dock: make position configurable (can be left or right)
+* Updated translations
+
+3.0.2
+=====
+
+* Updated translations.
+* Fixed bug #647386 (reverting of user-theme to default)
+* Fixed bug #647599 (support globally installed themes)
+* Added license and README
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/extension.js
new file mode 100644
index 0000000..f881694
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/extension.js
@@ -0,0 +1,91 @@
+// SPDX-FileCopyrightText: 2018 Adel Gadllah <adel.gadllah@gmail.com>
+// SPDX-FileCopyrightText: 2018 Florian Müllner <fmuellner@gnome.org>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+import Clutter from 'gi://Clutter';
+import Shell from 'gi://Shell';
+import St from 'gi://St';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import {Button as PanelButton} from 'resource:///org/gnome/shell/ui/panelMenu.js';
+
+const PANEL_ICON_SIZE = 16;
+
+const STANDARD_TRAY_ICON_IMPLEMENTATIONS = [
+ 'bluetooth-applet',
+ 'gnome-sound-applet',
+ 'nm-applet',
+ 'gnome-power-manager',
+ 'keyboard',
+ 'a11y-keyboard',
+ 'kbd-scrolllock',
+ 'kbd-numlock',
+ 'kbd-capslock',
+ 'ibus-ui-gtk',
+];
+
+export default class SysTray {
+ constructor() {
+ this._icons = new Map();
+ this._tray = null;
+ }
+
+ _onTrayIconAdded(o, icon) {
+ const wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
+ if (STANDARD_TRAY_ICON_IMPLEMENTATIONS.includes(wmClass))
+ return;
+
+ const button = new PanelButton(0.5, null, true);
+
+ const scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ const iconSize = PANEL_ICON_SIZE * scaleFactor;
+
+ icon.set({
+ width: iconSize,
+ height: iconSize,
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+
+ const iconBin = new St.Widget({
+ layout_manager: new Clutter.BinLayout(),
+ style_class: 'system-status-icon',
+ });
+ iconBin.add_child(icon);
+ button.add_child(iconBin);
+
+ this._icons.set(icon, button);
+
+ button.connect('button-release-event',
+ (actor, event) => icon.click(event));
+ button.connect('key-press-event',
+ (actor, event) => icon.click(event));
+
+ const role = `${icon}`;
+ Main.panel.addToStatusArea(role, button);
+ }
+
+ _onTrayIconRemoved(o, icon) {
+ const button = this._icons.get(icon);
+ button?.destroy();
+ this._icons.delete(icon);
+ }
+
+ enable() {
+ this._tray = new Shell.TrayManager();
+ this._tray.connect('tray-icon-added',
+ this._onTrayIconAdded.bind(this));
+ this._tray.connect('tray-icon-removed',
+ this._onTrayIconRemoved.bind(this));
+ this._tray.manage_screen(Main.panel);
+ }
+
+ disable() {
+ this._icons.forEach(button => button.destroy());
+ this._icons.clear();
+
+ this._tray.unmanage_screen();
+ this._tray = null;
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/metadata.json
new file mode 100644
index 0000000..c373d21
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/status-icons@gnome-shell-extensions.gcampax.github.com/metadata.json
@@ -0,0 +1,14 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Show status icons in the top bar",
+ "extension-id": "status-icons",
+ "gettext-domain": "gnome-shell-extension-status-icons",
+ "name": "Status Icons",
+ "settings-schema": "org.gnome.shell.extensions.status-icons",
+ "shell-version": [
+ "50"
+ ],
+ "url": "https://gitlab.gnome.org/GNOME/gnome-shell-extensions",
+ "uuid": "status-icons@gnome-shell-extensions.gcampax.github.com",
+ "version": 17
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/extension.js
new file mode 100644
index 0000000..b188440
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/extension.js
@@ -0,0 +1,580 @@
+import Atk from "gi://Atk";
+import Clutter from "gi://Clutter";
+import Meta from "gi://Meta";
+import St from "gi://St";
+
+import Gio from 'gi://Gio';
+import GObject from 'gi://GObject';
+import GLib from 'gi://GLib';
+import Shell from 'gi://Shell';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+import * as AltTab from 'resource:///org/gnome/shell/ui/altTab.js';
+import * as SwitcherPopup from 'resource:///org/gnome/shell/ui/switcherPopup.js';
+
+import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import * as Utils from './utils.js';
+
+
+const baseIconSizes = [96, 64, 48, 32, 22];
+
+
+let injections = {};
+let extensionInstance = null;
+
+function getWindows(workspace) {
+ let windows = global.display.get_tab_list(Meta.TabList.NORMAL_ALL, workspace);
+ return windows.map(w => {
+ return w.is_attached_dialog() ? w.get_transient_for() : w;
+ }).filter((w, i, a) => w && !w.skip_taskbar && a.indexOf(w) === i);
+}
+
+function _finish(timestamp) {
+ this._currentWindow = this._currentWindow < 0 ? 0 : this._currentWindow;
+ return injections._finish.call(this, timestamp);
+}
+
+function _initialSelection(backward, binding) {
+ if (backward || binding != 'switch-applications'
+ || this._items.length == 0 || this._items[0].cachedWindows.length < 2) {
+ injections._initialSelection.call(this, backward, binding);
+ return;
+ }
+
+ let ws = global.workspace_manager.get_active_workspace();
+ let wt = Shell.WindowTracker.get_default();
+ let tab_list = global.display.get_tab_list(Meta.TabList.NORMAL, ws);
+
+ if (!tab_list || tab_list.length < 2) {
+ injections._initialSelection.call(this, backward, binding);
+ return;
+ }
+
+ let currentApp = wt.get_window_app(tab_list[0]);
+ let secondApp = wt.get_window_app(tab_list[1]);
+
+ if (currentApp == secondApp) {
+ this._select(0, 1);
+ } else {
+ injections._initialSelection.call(this, backward, binding);
+ }
+}
+
+function highlight2(index, justOutline) {
+ if (this._items[this._highlighted]) {
+ this._items[this._highlighted].remove_style_pseudo_class('outlined');
+ this._items[this._highlighted].remove_style_pseudo_class('selected');
+ }
+
+ if (this._items[index]) {
+ if (justOutline)
+ this._items[index].add_style_pseudo_class('outlined');
+ else
+ this._items[index].add_style_pseudo_class('selected');
+ }
+
+ this._highlighted = index;
+
+ // GNOME 45+ compatibility: use hadjustment instead of hscroll.adjustment
+ let adjustment = this._scrollView.hadjustment || (this._scrollView.hscroll ? this._scrollView.hscroll.adjustment : null);
+ if (adjustment) {
+ this._scroll(index);
+ }
+}
+
+function _scroll(index) {
+ // GNOME 45+ compatibility: use hadjustment instead of hscroll.adjustment
+ let adjustment = this._scrollView.hadjustment || (this._scrollView.hscroll ? this._scrollView.hscroll.adjustment : null);
+ if (!adjustment) return;
+
+ let { upper, page_size: pageSize } = adjustment;
+
+ let n = this._items.length;
+ let fakeSize = 2;
+
+ this._scrollableRight = index !== n - 1;
+ this._scrollableLeft = index !== 0;
+ if (upper === pageSize)
+ return;
+
+ let item = this._items[index];
+ if (!item) return;
+
+ let sizeItem = (item.allocation.x2 - item.allocation.x1);
+ let value = (upper - pageSize + sizeItem) * (index / n);
+ let maxScrollingAmount = (upper - pageSize);
+ let percentaje = (index - fakeSize) / (n - 1 - 2 * fakeSize);
+ value = percentaje * maxScrollingAmount;
+
+ if (index < fakeSize || percentaje <= 0) {
+ this._scrollableLeft = false;
+ value = 0;
+ } else if (index >= n - fakeSize || percentaje >= 1) {
+ this._scrollableRight = false;
+ value = maxScrollingAmount;
+ }
+
+ adjustment.ease(value, {
+ progress_mode: Clutter.AnimationMode.EASE_OUT_EXPO,
+ duration: 250,
+ onComplete: () => {
+ this.queue_relayout();
+ },
+ });
+}
+
+function addColours(settings) {
+ injections.WINDOW_PREVIEW_SIZE = AltTab.WINDOW_PREVIEW_SIZE;
+
+ if (AltTab.AppSwitcherPopup) {
+ injections._init = AltTab.AppSwitcherPopup.prototype._init;
+ AltTab.AppSwitcherPopup.prototype._init = function (items, mask, action, timeout) {
+ // Call original
+ injections._init.call(this, items, mask, action, timeout);
+
+ // Custom additions
+ this._thumbnails = null;
+ this._thumbnailTimeoutId = 0;
+ this._currentWindow = -1;
+ this.thumbnailsVisible = false;
+
+ let apps = Shell.AppSystem.get_default().get_running();
+
+ // Remove the original switcher list if it exists
+ if (this._switcherList && typeof this._switcherList.destroy === 'function') {
+ try {
+ this._switcherList.destroy();
+ } catch (e) {
+ // Already destroyed or other error
+ }
+ }
+
+ this._switcherList = new AppSwitcher(apps, this, settings);
+ this._items = this._switcherList.icons;
+
+ this.add_child(this._switcherList);
+ };
+ }
+
+ injections.highlight2 = SwitcherPopup.SwitcherList.prototype.highlight;
+ SwitcherPopup.SwitcherList.prototype.highlight = highlight2;
+
+ injections._scroll = SwitcherPopup.SwitcherList.prototype._scroll;
+ SwitcherPopup.SwitcherList.prototype._scroll = _scroll;
+}
+
+function removeColours() {
+ if (AltTab.AppSwitcherPopup && injections._init) {
+ AltTab.AppSwitcherPopup.prototype._init = injections._init;
+ }
+
+ if (injections.highlight2) {
+ SwitcherPopup.SwitcherList.prototype.highlight = injections.highlight2;
+ injections.highlight2 = undefined;
+ }
+
+ if (injections._scroll) {
+ SwitcherPopup.SwitcherList.prototype._scroll = injections._scroll;
+ injections._scroll = undefined;
+ }
+}
+
+function setInitialSelection() {
+ if (!injections._finish && AltTab.AppSwitcherPopup) {
+ injections._finish = AltTab.AppSwitcherPopup.prototype._finish;
+ AltTab.AppSwitcherPopup.prototype._finish = _finish;
+ }
+
+ if (!injections._initialSelection && AltTab.AppSwitcherPopup) {
+ injections._initialSelection = AltTab.AppSwitcherPopup.prototype._initialSelection;
+ AltTab.AppSwitcherPopup.prototype._initialSelection = _initialSelection;
+ }
+}
+
+function resetInitialSelection() {
+ if (injections._finish && AltTab.AppSwitcherPopup) {
+ AltTab.AppSwitcherPopup.prototype._finish = injections._finish;
+ injections._finish = undefined;
+ }
+
+ if (injections._initialSelection && AltTab.AppSwitcherPopup) {
+ AltTab.AppSwitcherPopup.prototype._initialSelection = injections._initialSelection;
+ injections._initialSelection = undefined;
+ }
+}
+
+class MyExtension {
+ constructor(settings) {
+ this._settings = settings;
+ this._connectSettings();
+ this._firstChangeWindowChanged();
+ this._updateHoverSettings();
+ }
+
+ _connectSettings() {
+ this._settingsHandlerFirstSwitch = this._settings.connect(
+ 'changed::first-change-window',
+ this._firstChangeWindowChanged.bind(this)
+ );
+
+ this._settingsHandlers = [];
+ [
+ 'hover-shade-level', 'hover-border-width', 'hover-glow-size',
+ 'hover-opacity', 'icon-spacing',
+ 'container-padding', 'background-color', 'border-size', 'border-color',
+ 'background-border-radius', 'icon-border-radius'
+ ].forEach(key => {
+ this._settingsHandlers.push(
+ this._settings.connect(`changed::${key}`, this._updateHoverSettings.bind(this))
+ );
+ });
+ }
+
+ _updateHoverSettings() {
+ const keys = this._settings.list_keys();
+ this.hoverShadeLevel = keys.includes('hover-shade-level') ? this._settings.get_double('hover-shade-level') : 0.2;
+ this.hoverBorderWidth = keys.includes('hover-border-width') ? this._settings.get_int('hover-border-width') : 2;
+ this.hoverGlowSize = keys.includes('hover-glow-size') ? this._settings.get_int('hover-glow-size') : 15;
+ this.hoverOpacity = keys.includes('hover-opacity') ? this._settings.get_double('hover-opacity') : 0.8;
+
+ this.iconSpacing = keys.includes('icon-spacing') ? this._settings.get_int('icon-spacing') : 10;
+ this.containerPadding = keys.includes('container-padding') ? this._settings.get_int('container-padding') : 24;
+ this.backgroundColor = keys.includes('background-color') ? this._settings.get_string('background-color') : 'rgba(0, 0, 0, 0.5)';
+ this.borderSize = keys.includes('border-size') ? this._settings.get_int('border-size') : 2;
+ this.borderColor = keys.includes('border-color') ? this._settings.get_string('border-color') : 'rgba(255, 255, 255, 0.2)';
+ this.backgroundBorderRadius = keys.includes('background-border-radius') ? this._settings.get_int('background-border-radius') : 24;
+ this.iconBorderRadius = keys.includes('icon-border-radius') ? this._settings.get_int('icon-border-radius') : 16;
+ }
+
+ _firstChangeWindowChanged() {
+ this._firstChangeWindow = this._settings.get_boolean('first-change-window');
+ if (this._firstChangeWindow) {
+ setInitialSelection();
+ } else {
+ resetInitialSelection();
+ }
+ }
+
+ destroy() {
+ this._disconnectSettings();
+ resetInitialSelection();
+ }
+
+ _disconnectSettings() {
+ if (this._settingsHandlerFirstSwitch) {
+ this._settings.disconnect(this._settingsHandlerFirstSwitch);
+ this._settingsHandlerFirstSwitch = 0;
+ }
+ if (this._settingsHandlers) {
+ this._settingsHandlers.forEach(h => this._settings.disconnect(h));
+ this._settingsHandlers = [];
+ }
+ }
+}
+
+export default class UnityLikeAppSwitcherExtension extends Extension {
+ enable() {
+ this.initTranslations("unity-window-switcher");
+ const settings = this.getSettings();
+ extensionInstance = new MyExtension(settings);
+
+ addColours(settings);
+ }
+
+ disable() {
+ removeColours();
+
+ if (extensionInstance) {
+ extensionInstance.destroy();
+ extensionInstance = null;
+ }
+ }
+}
+
+const AppSwitcher = GObject.registerClass(
+ class AppSwitcher extends SwitcherPopup.SwitcherList {
+ _init(apps, altTabPopup, extensionSettings) {
+ super._init(true);
+
+ this.icons = [];
+ this._arrows = [];
+ this._extensionSettings = extensionSettings;
+
+ let windowTracker = Shell.WindowTracker.get_default();
+ let settings = new Gio.Settings({ schema_id: 'org.gnome.shell.app-switcher' });
+
+ let workspace = null;
+ if (settings.get_boolean('current-workspace-only')) {
+ let workspaceManager = global.workspace_manager;
+ workspace = workspaceManager.get_active_workspace();
+ }
+
+ let allWindows = getWindows(workspace);
+
+ for (let i = 0; i < apps.length; i++) {
+ let appIcon = new AltTab.AppIcon(apps[i]);
+ appIcon.cachedWindows = allWindows.filter(
+ w => windowTracker.get_window_app(w) === appIcon.app);
+ if (appIcon.cachedWindows.length > 0)
+ this._addIcon(appIcon);
+ }
+
+ this._altTabPopup = altTabPopup;
+ this._delayedHighlighted = -1;
+ this._mouseTimeOutId = 0;
+
+ // Apply container styles
+ const keys = this._extensionSettings.list_keys();
+ const padding = keys.includes('container-padding') ? this._extensionSettings.get_int('container-padding') : 24;
+ const bgColor = keys.includes('background-color') ? this._extensionSettings.get_string('background-color') : 'rgba(0, 0, 0, 0.5)';
+ const borderWidth = keys.includes('border-size') ? this._extensionSettings.get_int('border-size') : 2;
+ const borderColor = keys.includes('border-color') ? this._extensionSettings.get_string('border-color') : 'rgba(255, 255, 255, 0.2)';
+ const spacing = keys.includes('icon-spacing') ? this._extensionSettings.get_int('icon-spacing') : 10;
+ const borderRadius = keys.includes('background-border-radius') ? this._extensionSettings.get_int('background-border-radius') : 24;
+ this._iconBorderRadius = keys.includes('icon-border-radius') ? this._extensionSettings.get_int('icon-border-radius') : 16;
+
+ this.set_style(`
+ padding: ${padding}px;
+ background-color: ${bgColor};
+ border: ${borderWidth}px solid ${borderColor};
+ border-radius: ${borderRadius}px;
+ `);
+ this._list.set_style(`spacing: ${spacing}px;`);
+
+ this.connect('destroy', this._onDestroy.bind(this));
+ }
+
+ _onDestroy() {
+ if (this._mouseTimeOutId !== 0)
+ GLib.source_remove(this._mouseTimeOutId);
+
+ this.icons.forEach(
+ icon => icon.app.disconnectObject(this));
+ }
+
+ _setIconSize() {
+ let j = 0;
+ while (j < this._items.length && this._items[j].style_class !== 'item-box')
+ j++;
+
+ if (j >= this._items.length) return;
+
+ let themeNode = this._items[j].get_theme_node();
+ this._list.ensure_style();
+
+ let iconPadding = themeNode.get_horizontal_padding();
+ let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
+ let labelNaturalHeight = 0;
+ if (this.icons[j].label) {
+ [, labelNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
+ }
+ let iconSpacing = labelNaturalHeight + iconPadding + iconBorder;
+
+ const keys = this._extensionSettings.list_keys();
+ const spacing = keys.includes('icon-spacing') ? this._extensionSettings.get_int('icon-spacing') : 10;
+ let totalSpacing = spacing * (this._items.length - 1);
+
+ let primary = Main.layoutManager.primaryMonitor;
+ let parent = this.get_parent();
+ let parentPadding = parent ? parent.get_theme_node().get_horizontal_padding() : 0;
+ let availWidth = primary.width - parentPadding - this.get_theme_node().get_horizontal_padding();
+
+ let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
+ let iconSizes = baseIconSizes.map(s => s * scaleFactor);
+ let iconSize = baseIconSizes[0];
+
+ if (this._items.length > 1) {
+ for (let i = 0; i < baseIconSizes.length; i++) {
+ iconSize = baseIconSizes[i];
+ let height = iconSizes[i] + iconSpacing;
+ let w = height * this._items.length + totalSpacing;
+ if (w <= availWidth)
+ break;
+ }
+ }
+
+ this._iconSize = iconSize;
+
+ for (let i = 0; i < this.icons.length; i++) {
+ if (this.icons[i] && this.icons[i].icon == null) {
+ this.icons[i].set_size(iconSize);
+ }
+ }
+ }
+
+ vfunc_get_preferred_height(forWidth) {
+ if (!this._iconSize)
+ this._setIconSize();
+
+ return super.vfunc_get_preferred_height(forWidth);
+ }
+
+ vfunc_allocate(box) {
+ super.vfunc_allocate(box);
+
+ let contentBox = this.get_theme_node().get_content_box(box);
+ let arrowHeight = Math.floor(this.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
+ let arrowWidth = arrowHeight * 2;
+
+ let childBox = new Clutter.ActorBox();
+ for (let i = 0; i < this._items.length; i++) {
+ let itemBox = this._items[i].allocation;
+ childBox.x1 = contentBox.x1 + Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
+ childBox.x2 = childBox.x1 + arrowWidth;
+ childBox.y1 = contentBox.y1 + itemBox.y2 + arrowHeight;
+ childBox.y2 = childBox.y1 + arrowHeight;
+ if (this._arrows[i])
+ this._arrows[i].allocate(childBox);
+ }
+ }
+
+ _onItemMotion(item) {
+ if (item === this._items[this._highlighted] ||
+ item === this._items[this._delayedHighlighted])
+ return Clutter.EVENT_PROPAGATE;
+
+ const index = this._items.indexOf(item);
+
+ if (this._mouseTimeOutId !== 0) {
+ GLib.source_remove(this._mouseTimeOutId);
+ this._delayedHighlighted = -1;
+ this._mouseTimeOutId = 0;
+ }
+
+ if (this._altTabPopup && this._altTabPopup.thumbnailsVisible) {
+ this._delayedHighlighted = index;
+ this._mouseTimeOutId = GLib.timeout_add(
+ GLib.PRIORITY_DEFAULT,
+ AltTab.APP_ICON_HOVER_TIMEOUT,
+ () => {
+ this._enterItem(index);
+ this._delayedHighlighted = -1;
+ this._mouseTimeOutId = 0;
+ return GLib.SOURCE_REMOVE;
+ });
+ } else {
+ this._itemEntered(index);
+ }
+
+ return Clutter.EVENT_PROPAGATE;
+ }
+
+ _enterItem(index) {
+ let [x, y] = global.get_pointer();
+ let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
+ if (this._items[index] && this._items[index].contains(pickedActor))
+ this._itemEntered(index);
+ }
+
+ highlight(n, justOutline) {
+ if (this.icons[this._highlighted]) {
+ if (this._arrows[this._highlighted]) {
+ if (this.icons[this._highlighted].cachedWindows.length === 1)
+ this._arrows[this._highlighted].hide();
+ else
+ this._arrows[this._highlighted].remove_style_pseudo_class('highlighted');
+ }
+ }
+
+ let previous = this._items[this._highlighted];
+ if (previous && previous.originalStyle) {
+ previous.set_style(previous.originalStyle);
+ }
+
+ let item = this._items[n];
+ if (item && item.colorPalette && item.colorPalette.baseRgb) {
+ let style = item.originalStyle || '';
+
+ // Read directly from the settings object passed to AppSwitcher
+ const keys = this._extensionSettings.list_keys();
+ let shadeLevel = keys.includes('hover-shade-level') ? this._extensionSettings.get_double('hover-shade-level') : 0.2;
+ let borderWidth = keys.includes('hover-border-width') ? this._extensionSettings.get_int('hover-border-width') : 2;
+ let glowSize = keys.includes('hover-glow-size') ? this._extensionSettings.get_int('hover-glow-size') : 15;
+ let opacity = keys.includes('hover-opacity') ? this._extensionSettings.get_double('hover-opacity') : 0.8;
+ let iconBorderRadius = this._iconBorderRadius;
+
+ let r = Math.round(Math.min(Math.max(item.colorPalette.baseRgb.r * (1 + shadeLevel), 0), 255));
+ let g = Math.round(Math.min(Math.max(item.colorPalette.baseRgb.g * (1 + shadeLevel), 0), 255));
+ let b = Math.round(Math.min(Math.max(item.colorPalette.baseRgb.b * (1 + shadeLevel), 0), 255));
+ let hoverColor = `rgba(${r}, ${g}, ${b}, ${opacity})`;
+
+ item.set_style(style +
+ ' box-shadow: inset 0 0 ' + glowSize + 'px ' + hoverColor + ', 0 0 0 ' + borderWidth + 'px ' + hoverColor + ' !important; ' +
+ ' border: 3px solid transparent !important; ' +
+ ' border-radius: ' + iconBorderRadius + 'px !important;');
+ }
+
+ highlight2.call(this, n, justOutline);
+ this._curApp = n;
+
+ if (this._highlighted !== -1 && this.icons[this._highlighted] && this._arrows[this._highlighted]) {
+ if (justOutline && this.icons[this._highlighted].cachedWindows.length === 1)
+ this._arrows[this._highlighted].show();
+ else
+ this._arrows[this._highlighted].add_style_pseudo_class('highlighted');
+ }
+ }
+
+ _addIcon(appIcon) {
+ this.icons.push(appIcon);
+ let item = this.addItem(appIcon, appIcon.label);
+
+ if (appIcon.label) {
+ appIcon.label.set_style('text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.7); font-weight: bold;');
+ }
+
+ try {
+ item.colorPalette = new Utils.DominantColorExtractor(appIcon.app)._getColorPalette();
+ } catch (e) {
+ item.colorPalette = null;
+ }
+
+ const iconBorderRadius = this._iconBorderRadius;
+
+ if (item.colorPalette == null) {
+ item.colorPalette = {
+ original: '#000000',
+ lighter: '#666666',
+ darker: '#000000',
+ baseRgb: { r: 0, g: 0, b: 0 }
+ }
+ }
+ let hex = item.colorPalette.original;
+ let rgb = Utils.ColorUtils._hexToRgb(hex);
+ // Force background-color and remove background-image (gradients) using !important
+ item.originalStyle = 'background-color: rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ', 0.6) !important; background-image: none !important; border: 3px solid transparent; border-radius: ' + iconBorderRadius + 'px !important;';
+ item.set_style(item.originalStyle);
+
+ appIcon.app.connectObject('notify::state', app => {
+ if (app.state !== Shell.AppState.RUNNING)
+ this._removeIcon(app);
+ }, this);
+
+ let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
+ arrow.connect('repaint', () => SwitcherPopup.drawArrow(arrow, St.Side.BOTTOM));
+ this.add_child(arrow);
+ this._arrows.push(arrow);
+
+ if (appIcon.cachedWindows.length === 1)
+ arrow.hide();
+ else
+ item.add_accessible_state(Atk.StateType.EXPANDABLE);
+ }
+
+ _removeIcon(app) {
+ let index = this.icons.findIndex(icon => {
+ return icon.app === app;
+ });
+ if (index === -1)
+ return;
+
+ if (this._arrows[index]) {
+ this._arrows[index].destroy();
+ this._arrows.splice(index, 1);
+ }
+
+ this.icons.splice(index, 1);
+ this.removeItem(index);
+ }
+ });
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/en/LC_MESSAGES/unity-window-switcher.mo b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/en/LC_MESSAGES/unity-window-switcher.mo
new file mode 100644
index 0000000..5f0d96e
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/en/LC_MESSAGES/unity-window-switcher.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/hu/LC_MESSAGES/unity-window-switcher.mo b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/hu/LC_MESSAGES/unity-window-switcher.mo
new file mode 100644
index 0000000..9e8b840
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/locale/hu/LC_MESSAGES/unity-window-switcher.mo
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/metadata.json
new file mode 100644
index 0000000..1d1638c
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/metadata.json
@@ -0,0 +1,14 @@
+{
+ "name": "Unity-like App Switcher (Reforged)",
+ "description": "A modern, AI-reforged version of the Unity-like AppSwitcher for GNOME 48+. Bigger, more colorful, and highly customizable.",
+ "original-authors": "gonza",
+ "uuid": "unity-like-appswitcher-reforged@gabeszm",
+ "gettext-domain": "unity-window-switcher",
+ "url": "https://github.com/gonzaarcr/unity-like-switcher-gnome-ext",
+ "settings-schema": "org.gnome.shell.extensions.unity-window-switcher-reforged",
+ "shell-version": [
+ "48",
+ "49",
+ "50"
+ ]
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/prefs.js
new file mode 100644
index 0000000..92949e6
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/prefs.js
@@ -0,0 +1,256 @@
+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 { ExtensionPreferences, gettext as _ } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+
+class PrefsWidget {
+
+ constructor(schema) {
+ this._updating = false;
+ this._buildable = new Gtk.Builder();
+ this._buildable.add_from_file(
+ Gio.File.new_for_uri(import.meta.url).get_parent().get_path() + '/settings.ui'
+ );
+
+ let prefsWidget = this._getWidget('prefs_widget');
+ if (!prefsWidget) {
+ console.error('UnityLikeAppSwitcher: Main widget not found');
+ }
+
+ this._settings = schema;
+ this._bindBooleans();
+ this._bindNumbers();
+ this._bindStrings();
+
+ let resetButton = this._getWidget('reset_button');
+ if (resetButton) {
+ resetButton.connect('clicked', () => {
+ this._resetAll();
+ });
+ }
+
+ this._settings.connect(
+ 'changed::first-change-window',
+ this._firstChangeWindowChanged.bind(this)
+ );
+ this._firstChangeWindowChanged();
+ }
+
+ _resetAll() {
+ this._updating = true;
+ try {
+ this._getNumbers().forEach(setting => {
+ this._settings.reset(setting);
+ // Trigger UI update manually for numbers since they use manual binding
+ this._updateNumberWidget(setting);
+ });
+
+ this._getBooleans().forEach(setting => {
+ this._settings.reset(setting);
+ });
+
+ this._getStrings().forEach(setting => {
+ this._settings.reset(setting);
+ this._updateStringWidget(setting);
+ });
+
+ Gio.Settings.sync();
+ } finally {
+ this._updating = false;
+ }
+ }
+
+ _updateNumberWidget(setting) {
+ if (this._updating) return;
+ let widget = this._getWidget(setting);
+ if (!widget || !widget.set_value) {
+ return;
+ }
+ if (!this._settings.list_keys().includes(setting)) {
+ // Fallback values if key is missing
+ let defaultValues = {
+ 'hover-shade-level': 0.2,
+ 'hover-opacity': 0.8,
+ 'hover-border-width': 2,
+ 'hover-glow-size': 15,
+ 'icon-spacing': 10,
+ 'container-padding': 24,
+ 'border-size': 2,
+ 'background-border-radius': 24,
+ 'icon-border-radius': 16
+ };
+ widget.set_value(defaultValues[setting] || 0);
+ return;
+ }
+ let isDouble = (setting === 'hover-shade-level' || setting === 'hover-opacity');
+ if (isDouble) {
+ widget.set_value(this._settings.get_double(setting));
+ } else {
+ widget.set_value(this._settings.get_int(setting));
+ }
+ }
+
+ _updateStringWidget(setting) {
+ if (this._updating) return;
+ let widget = this._getWidget(setting);
+ if (!widget) return;
+ let colorStr = '';
+ if (!this._settings.list_keys().includes(setting)) {
+ let defaultValues = {
+ 'background-color': 'rgba(0, 0, 0, 0.5)',
+ 'border-color': 'rgba(255, 255, 255, 0.2)'
+ };
+ colorStr = defaultValues[setting] || '';
+ } else {
+ colorStr = this._settings.get_string(setting);
+ }
+
+ if (widget instanceof Gtk.ColorDialogButton) {
+ let rgba = new Gdk.RGBA();
+ if (rgba.parse(colorStr)) {
+ this._updating = true;
+ widget.set_rgba(rgba);
+ this._updating = false;
+ }
+ } else if (widget.set_text) {
+ this._updating = true;
+ widget.set_text(colorStr);
+ this._updating = false;
+ }
+ }
+
+ _getWidget(name) {
+ let wname = name.replace(/-/g, '_');
+ return this._buildable.get_object(wname);
+ }
+
+ _getBooleans() {
+ return [
+ 'first-change-window'
+ ];
+ }
+
+ _getNumbers() {
+ return [
+ 'hover-shade-level',
+ 'hover-border-width',
+ 'hover-glow-size',
+ 'hover-opacity',
+ 'icon-spacing',
+ 'container-padding',
+ 'border-size',
+ 'background-border-radius',
+ 'icon-border-radius'
+ ];
+ }
+
+ _getStrings() {
+ return [
+ 'background-color',
+ 'border-color'
+ ];
+ }
+
+ _bindBoolean(setting) {
+ let widget = this._getWidget(setting);
+ if (!widget) return;
+ this._settings.bind(setting, widget, 'active', Gio.SettingsBindFlags.DEFAULT);
+ }
+
+ _bindBooleans() {
+ this._getBooleans().forEach(this._bindBoolean, this);
+ }
+
+ _bindString(setting) {
+ let widget = this._getWidget(setting);
+ if (!widget) return;
+ this._updateStringWidget(setting);
+
+ if (widget instanceof Gtk.ColorDialogButton) {
+ widget.connect('notify::rgba', (w) => {
+ if (this._updating) return;
+ if (!this._settings.list_keys().includes(setting)) return;
+ let rgba = w.get_rgba();
+ let val = rgba.to_string(); // CSS format
+ let current = this._settings.get_string(setting);
+ if (current !== val) {
+ this._settings.set_string(setting, val);
+ }
+ });
+ } else {
+ widget.connect('changed', (w) => {
+ if (this._updating) return;
+ if (!this._settings.list_keys().includes(setting)) return;
+ let val = w.get_text();
+ let current = this._settings.get_string(setting);
+ if (current !== val) {
+ this._settings.set_string(setting, val);
+ }
+ });
+ }
+
+ this._settings.connect(`changed::${setting}`, () => {
+ if (this._updating) return;
+ this._updateStringWidget(setting);
+ });
+ }
+
+ _bindStrings() {
+ this._getStrings().forEach(this._bindString, this);
+ }
+
+ _bindNumber(setting) {
+ let widget = this._getWidget(setting);
+ if (!widget) return;
+ let isDouble = (setting === 'hover-shade-level' || setting === 'hover-opacity');
+
+ // Érték betöltése indításkor
+ this._updateNumberWidget(setting);
+
+ // Automatikus mentés változáskor
+ widget.connect('notify::value', (w) => {
+ if (this._updating) return;
+ if (!this._settings.list_keys().includes(setting)) return;
+ let val = w.get_value();
+ if (isDouble) {
+ // Only update if significantly different to avoid feedback loops
+ let current = this._settings.get_double(setting);
+ if (Math.abs(current - val) > 0.001) {
+ this._settings.set_double(setting, val);
+ }
+ } else {
+ let current = this._settings.get_int(setting);
+ if (current !== Math.round(val)) {
+ this._settings.set_int(setting, Math.round(val));
+ }
+ }
+ });
+
+ // Update widget if setting changes externally (e.g. reset)
+ this._settings.connect(`changed::${setting}`, () => {
+ if (this._updating) return;
+ this._updateNumberWidget(setting);
+ });
+ }
+
+ _bindNumbers() {
+ this._getNumbers().forEach(this._bindNumber, this);
+ }
+
+ _firstChangeWindowChanged() {
+ this._settings.get_boolean('first-change-window');
+ }
+}
+
+export default class UnityLikeAppSwitcherPreferences extends ExtensionPreferences {
+ fillPreferencesWindow (window) {
+ this.initTranslations("unity-window-switcher");
+ window._settings = this.getSettings();
+ const widget = new PrefsWidget(window._settings);
+ window.add(widget._getWidget('prefs_widget'));
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/gschemas.compiled
new file mode 100644
index 0000000..a6544ab
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/org.gnome.shell.extensions.unity-window-switcher-reforged.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/org.gnome.shell.extensions.unity-window-switcher-reforged.gschema.xml
new file mode 100644
index 0000000..e616c9f
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/schemas/org.gnome.shell.extensions.unity-window-switcher-reforged.gschema.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist gettext-domain="unity-window-switcher">
+ <schema id="org.gnome.shell.extensions.unity-window-switcher-reforged" path="/org/gnome/shell/extensions/unity-window-switcher-reforged/">
+ <key name="first-change-window" type="b">
+ <default>true</default>
+ <summary>Focus the second window when switching</summary>
+ <description>When the switcher is opened, the second window is focused by default, similar to the original Unity behavior.</description>
+ </key>
+ <key name="hover-shade-level" type="d">
+ <default>1.0</default>
+ <summary>Hover Shade Level</summary>
+ <description>The brightness multiplier for the hover glow (e.g., 0.1 means 10% brighter).</description>
+ </key>
+ <key name="hover-border-width" type="i">
+ <default>2</default>
+ <summary>Hover Border Width</summary>
+ <description>The thickness of the border around the hovered item in pixels.</description>
+ </key>
+ <key name="hover-glow-size" type="i">
+ <range min="0" max="30"/>
+ <default>8</default>
+ <summary>Hover Glow Size</summary>
+ <description>The size of the inner glow (box-shadow) for the hovered item.</description>
+ </key>
+ <key name="hover-opacity" type="d">
+ <default>0.9</default>
+ <summary>Hover Opacity</summary>
+ <description>The opacity of the hover highlight (0.0 to 1.0).</description>
+ </key>
+ <key name="icon-spacing" type="i">
+ <default>10</default>
+ <summary>Icon Spacing</summary>
+ <description>The distance between icons in pixels.</description>
+ </key>
+ <key name="container-padding" type="i">
+ <default>10</default>
+ <summary>Container Padding</summary>
+ <description>The padding around the icons inside the switcher.</description>
+ </key>
+ <key name="background-color" type="s">
+ <default>'rgb(24,24,26)'</default>
+ <summary>Background Color</summary>
+ <description>The background color of the switcher (CSS format).</description>
+ </key>
+ <key name="border-size" type="i">
+ <default>0</default>
+ <summary>Border Size</summary>
+ <description>The thickness of the switcher border.</description>
+ </key>
+ <key name="border-color" type="s">
+ <default>'rgba(255, 255, 255, 0.2)'</default>
+ <summary>Border Color</summary>
+ <description>The color of the switcher border (CSS format).</description>
+ </key>
+ <key name="background-border-radius" type="i">
+ <default>24</default>
+ <summary>Background Border Radius</summary>
+ <description>The border radius of the main switcher container in pixels.</description>
+ </key>
+ <key name="icon-border-radius" type="i">
+ <default>16</default>
+ <summary>Icon Border Radius</summary>
+ <description>The border radius of the application icons in pixels.</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/settings.ui b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/settings.ui
new file mode 100644
index 0000000..ac10116
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/settings.ui
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="unity-window-switcher">
+ <requires lib="gtk" version="4.0"/>
+ <requires lib="adw" version="1.0"/>
+ <object class="AdwPreferencesPage" id="prefs_widget">
+ <property name="width_request">500</property>
+ <property name="visible">True</property>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Behavior</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">First tab changes windows (or apps)</property>
+ <child>
+ <object class="GtkSwitch" id="first_change_window">
+ <property name="valign">center</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Panel Appearance</property>
+ <child>
+ <object class="AdwActionRow" id="background_color_row">
+ <property name="title" translatable="yes">Background Color</property>
+ <child type="suffix">
+ <object class="GtkColorDialogButton" id="background_color">
+ <property name="valign">center</property>
+ <property name="dialog">
+ <object class="GtkColorDialog">
+ <property name="with_alpha">True</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwSpinRow" id="background_border_radius">
+ <property name="title" translatable="yes">Background Border Radius</property>
+ <property name="subtitle" translatable="yes">The corner radius of the main switcher window</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="value">24</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwSpinRow" id="container_padding">
+ <property name="title" translatable="yes">Container Padding</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="value">24</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwSpinRow" id="border_size">
+ <property name="title" translatable="yes">Border Size</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">20</property>
+ <property name="step_increment">1</property>
+ <property name="value">2</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow" id="border_color_row">
+ <property name="title" translatable="yes">Border Color</property>
+ <child type="suffix">
+ <object class="GtkColorDialogButton" id="border_color">
+ <property name="valign">center</property>
+ <property name="dialog">
+ <object class="GtkColorDialog">
+ <property name="with_alpha">True</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Icons</property>
+ <child>
+ <object class="AdwSpinRow" id="icon_spacing">
+ <property name="title" translatable="yes">Icon Spacing</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="value">10</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwSpinRow" id="icon_border_radius">
+ <property name="title" translatable="yes">Icon Border Radius</property>
+ <property name="subtitle" translatable="yes">The corner radius of the application icons</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="value">16</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title" translatable="yes">Hover Effect</property>
+ <child>
+ <object class="AdwSpinRow" id="hover_shade_level">
+ <property name="title" translatable="yes">Brightness level</property>
+ <property name="subtitle" translatable="yes">Darker (&lt; 0) or lighter (&gt; 0) hover effect (-1.0 to 1.0)</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">-1</property>
+ <property name="upper">1</property>
+ <property name="step_increment">0.05</property>
+ <property name="page_increment">0.1</property>
+ <property name="value">0.5</property>
+ </object>
+ </property>
+ <property name="digits">2</property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwSpinRow" id="hover_opacity">
+ <property name="title" translatable="yes">Opacity</property>
+ <property name="subtitle" translatable="yes">Transparency of the hover effect (0.0 to 1.0)</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">1</property>
+ <property name="step_increment">0.05</property>
+ <property name="page_increment">0.1</property>
+ <property name="value">0.8</property>
+ </object>
+ </property>
+ <property name="digits">2</property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwSpinRow" id="hover_border_width">
+ <property name="title" translatable="yes">Border width</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">20</property>
+ <property name="step_increment">1</property>
+ <property name="value">3</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwSpinRow" id="hover_glow_size">
+ <property name="title" translatable="yes">Glow size</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="upper">30</property>
+ <property name="step_increment">1</property>
+ <property name="value">20</property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="GtkButton" id="reset_button">
+ <property name="label" translatable="yes">Reset</property>
+ <property name="halign">center</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <style>
+ <class name="destructive-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js
new file mode 100644
index 0000000..898b047
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/unity-like-appswitcher-reforged@gabeszm/utils.js
@@ -0,0 +1,346 @@
+import Gdk from "gi://Gdk";
+import GdkPixbuf from "gi://GdkPixbuf";
+import Gio from "gi://Gio";
+import Gtk from "gi://Gtk";
+import St from "gi://St";
+import GLib from "gi://GLib";
+
+let iconCacheMap = new Map();
+const MAX_CACHED_ITEMS = 1000;
+const BATCH_SIZE_TO_DELETE = 50;
+const DOMINANT_COLOR_ICON_SIZE = 64;
+
+function debugLog(msg) {
+ try {
+ let f = Gio.File.new_for_path('/tmp/color_log.txt');
+ let out = f.append_to(Gio.FileCreateFlags.NONE, null);
+ out.write_all(new Date().toISOString() + " - " + msg + "\n", null);
+ out.close(null);
+ } catch (e) { }
+}
+
+function findFallbackIconPath(iconName) {
+ if (!iconName) return null;
+ if (GLib.file_test(iconName, GLib.FileTest.EXISTS)) return iconName;
+
+ const sizes = ['128x128', '256x256', '512x512', '64x64', 'scalable', '48x48'];
+
+ let currentTheme = 'Adwaita';
+ try {
+ const settings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' });
+ if (settings.get_string('icon-theme')) {
+ currentTheme = settings.get_string('icon-theme');
+ }
+ } catch (e) {
+ debugLog("Failed to get active icon theme setting: " + e);
+ }
+
+ // Tegyük a hicolort az első helyre, a jelenleg aktív témát a másodikra, aztán a többi népszerű.
+ // A Set-tel kiszűrjük az esetleges ismétlődéseket.
+ const themes = [...new Set(['hicolor', currentTheme, 'locolor', 'Yaru', 'Pop', 'Adwaita', 'MoreWaita', 'Papirus', 'Tela'])];
+ const bases = [
+ GLib.get_home_dir() + '/.local/share/icons',
+ GLib.get_home_dir() + '/.local/share/flatpak/exports/share/icons',
+ '/var/lib/flatpak/exports/share/icons',
+ '/var/lib/snapd/desktop/icons',
+ '/usr/share/icons',
+ '/usr/local/share/icons'
+ ];
+ const formats = ['.svg', '.png', '.xpm'];
+
+ for (const base of bases) {
+ for (const theme of themes) {
+ for (const size of sizes) {
+ for (const format of formats) {
+ let path = `${base}/${theme}/${size}/apps/${iconName}${format}`;
+ if (GLib.file_test(path, GLib.FileTest.EXISTS)) return path;
+ }
+ }
+ }
+ }
+
+ // Check pixmaps
+ for (const format of formats) {
+ let path = `/usr/share/pixmaps/${iconName}${format}`;
+ if (GLib.file_test(path, GLib.FileTest.EXISTS)) return path;
+ }
+
+ return null;
+}
+
+export class DominantColorExtractor {
+ constructor(app) {
+ this._app = app;
+ }
+
+ _getIconPixBuf() {
+ debugLog("--- START Extracting for app: " + (this._app ? this._app.get_id() : "undefined") + " ---");
+ if (!this._app) {
+ debugLog("Error: _app is null or undefined");
+ return null;
+ }
+
+ const gicon = this._app.get_icon();
+ if (!gicon) {
+ debugLog("Fail: get_icon() returned null");
+ return null;
+ }
+
+ debugLog("Got gicon of type: " + gicon.constructor.name);
+
+ // Try to load via Gio.FileIcon directly
+ if (gicon instanceof Gio.FileIcon) {
+ try {
+ debugLog("gicon is Gio.FileIcon. Getting file path...");
+ const path = gicon.get_file().get_path();
+ debugLog("File path: " + path);
+ if (path) {
+ return GdkPixbuf.Pixbuf.new_from_file(path);
+ }
+ } catch (e) {
+ debugLog("Exception in Gio.FileIcon lookup: " + e);
+ }
+ }
+
+ // Try fallback manual lookup
+ if (gicon instanceof Gio.ThemedIcon) {
+ try {
+ debugLog("Attempting manual fallback search for ThemedIcon...");
+ const names = gicon.get_names();
+ for (const name of names) {
+ let path = findFallbackIconPath(name);
+ if (path) {
+ debugLog("Manual fallback found file: " + path);
+ return GdkPixbuf.Pixbuf.new_from_file(path);
+ }
+ }
+ } catch (e) {
+ debugLog("Exception in manual fallback: " + e);
+ }
+ }
+
+ // Try St.TextureCache as a last resort
+ try {
+ debugLog("Attempting St.TextureCache lookup...");
+ let textureCache = St.TextureCache.get_default();
+ let themeContext = St.ThemeContext.get_for_stage(global.stage);
+ let iconSize = 64 * themeContext.scale_factor;
+
+ let iconTexture = textureCache.load_gicon(null, gicon, iconSize);
+ if (iconTexture) {
+ debugLog("Found texture in cache, but cannot easily convert to Pixbuf in this context. Fallback to default.");
+ }
+ } catch (e) {
+ debugLog("St.TextureCache lookup failed: " + e);
+ }
+
+ debugLog("--- FAIL: Exhausted all methods to get icon pixbuf ---");
+ return null;
+ }
+
+ _getColorPalette() {
+ debugLog("--- START _getColorPalette ---");
+ const appId = this._app ? this._app.get_id() : null;
+ debugLog("App ID: " + appId);
+
+ if (appId && iconCacheMap.has(appId)) {
+ debugLog("Returning cached color for: " + appId);
+ return iconCacheMap.get(appId);
+ }
+
+ debugLog("Cache miss, calling _getIconPixBuf()");
+ const pixBuf = this._getIconPixBuf();
+ if (!pixBuf) {
+ debugLog("FAIL: pixBuf is null, cannot extract color.");
+ return null;
+ }
+
+ try {
+ const width = pixBuf.get_width();
+ const height = pixBuf.get_height();
+ const rowstride = pixBuf.get_rowstride();
+ const nChannels = pixBuf.get_n_channels();
+ const hasAlpha = pixBuf.get_has_alpha();
+
+ debugLog(`PixBuf info: ${width}x${height}, rowstride: ${rowstride}, channels: ${nChannels}, alpha: ${hasAlpha}`);
+
+ const pixels = pixBuf.get_pixels();
+ if (!pixels) {
+ debugLog("FAIL: pixBuf.get_pixels() returned null or undefined");
+ return null;
+ }
+
+ let total = 0, rTotal = 0, gTotal = 0, bTotal = 0;
+ const step = Math.max(1, Math.floor(width / 32));
+ debugLog("Calculated step for sampling: " + step);
+
+ let sampledCount = 0;
+ for (let y = 0; y < height; y += step) {
+ for (let x = 0; x < width; x += step) {
+ const offset = y * rowstride + x * nChannels;
+ const r = pixels[offset];
+ const g = pixels[offset + 1];
+ const b = pixels[offset + 2];
+ const a = hasAlpha ? pixels[offset + 3] : 255;
+
+ if (a < 128) continue;
+
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+ const saturation = (max - min) / (max || 1);
+ const relevance = a * (saturation * saturation + 0.1);
+
+ rTotal += r * relevance;
+ gTotal += g * relevance;
+ bTotal += b * relevance;
+ total += relevance;
+ sampledCount++;
+ }
+ }
+
+ debugLog(`Sampling complete. Sampled pixels: ${sampledCount}. Total accumulator: ${total}`);
+
+ if (total === 0) {
+ debugLog("FAIL: Valid pixel total is 0. Returning null.");
+ return null;
+ }
+
+ let r = rTotal / total;
+ let g = gTotal / total;
+ let b = bTotal / total;
+ debugLog(`Averaged RGB: Math.round(${r}), Math.round(${g}), Math.round(${b})`);
+
+ let hsv = ColorUtils.RGBtoHSV(r, g, b);
+ debugLog(`HSV before normalize: h:${hsv.h.toFixed(2)}, s:${hsv.s.toFixed(2)}, v:${hsv.v.toFixed(2)}`);
+
+ if (hsv.s < 0.2) hsv.s = 0.35;
+ hsv.v = Math.max(0.6, Math.min(hsv.v, 0.9));
+
+ debugLog(`HSV after normalize: h:${hsv.h.toFixed(2)}, s:${hsv.s.toFixed(2)}, v:${hsv.v.toFixed(2)}`);
+
+ const rgb = ColorUtils.HSVtoRGB(hsv.h, hsv.s, hsv.v);
+
+ const backgroundColor = {
+ lighter: ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0.5),
+ original: ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, 0),
+ darker: ColorUtils.ColorLuminance(rgb.r, rgb.g, rgb.b, -0.5),
+ baseRgb: rgb
+ };
+
+ debugLog(`Generated Palette: original=${backgroundColor.original}, lighter=${backgroundColor.lighter}, darker=${backgroundColor.darker}`);
+
+ if (iconCacheMap.size >= MAX_CACHED_ITEMS) {
+ debugLog("Cache full, clearing batch.");
+ const keys = Array.from(iconCacheMap.keys());
+ for (let i = 0; i < BATCH_SIZE_TO_DELETE; i++) {
+ iconCacheMap.delete(keys[i]);
+ }
+ }
+
+ if (appId) {
+ iconCacheMap.set(appId, backgroundColor);
+ debugLog("Added to cache for appId: " + appId);
+ }
+
+ debugLog("--- SUCCESS: Color extraction finished ---");
+ return backgroundColor;
+
+ } catch (e) {
+ debugLog("Exception during pixel processing: " + e);
+ return null;
+ }
+ }
+}
+
+export class ColorUtils {
+ static ColorLuminance(r, g, b, dlum) {
+ let rgbString = '#';
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(r * (1 + dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(g * (1 + dlum), 0), 255)), 2);
+ rgbString += ColorUtils._decimalToHex(Math.round(Math.min(Math.max(b * (1 + dlum), 0), 255)), 2);
+ return rgbString;
+ }
+
+ static _decimalToHex(d, padding) {
+ let hex = d.toString(16);
+ while (hex.length < padding)
+ hex = '0' + hex;
+ return hex;
+ }
+
+ static _hexToRgb(h) {
+ return {
+ r: parseInt(h.substr(1, 2), 16),
+ g: parseInt(h.substr(3, 2), 16),
+ b: parseInt(h.substr(5, 2), 16)
+ }
+ }
+
+ static HSVtoRGB(h, s, v) {
+ if (arguments.length === 1) {
+ s = h.s;
+ v = h.v;
+ h = h.h;
+ }
+
+ let r, g, b;
+ let c = v * s;
+ let h1 = h * 6;
+ let x = c * (1 - Math.abs(h1 % 2 - 1));
+ let m = v - c;
+
+ if (h1 <= 1)
+ r = c + m, g = x + m, b = m;
+ else if (h1 <= 2)
+ r = x + m, g = c + m, b = m;
+ else if (h1 <= 3)
+ r = m, g = c + m, b = x + m;
+ else if (h1 <= 4)
+ r = m, g = x + m, b = c + m;
+ else if (h1 <= 5)
+ r = x + m, g = m, b = c + m;
+ else
+ r = c + m, g = m, b = x + m;
+
+ return {
+ r: Math.round(r * 255),
+ g: Math.round(g * 255),
+ b: Math.round(b * 255)
+ };
+ }
+
+ static RGBtoHSV(r, g, b) {
+ if (arguments.length === 1) {
+ r = r.r;
+ g = r.g;
+ b = r.b;
+ }
+
+ let h, s, v;
+ let M = Math.max(r, g, b);
+ let m = Math.min(r, g, b);
+ let c = M - m;
+
+ if (c == 0)
+ h = 0;
+ else if (M == r)
+ h = ((g - b) / c) % 6;
+ else if (M == g)
+ h = (b - r) / c + 2;
+ else
+ h = (r - g) / c + 4;
+
+ h = h / 6;
+ v = M / 255;
+ if (M !== 0)
+ s = c / M;
+ else
+ s = 0;
+
+ return {
+ h: h,
+ s: s,
+ v: v
+ };
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/COPYING b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/COPYING
new file mode 100644
index 0000000..e55e5b8
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/NEWS b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/NEWS
new file mode 100644
index 0000000..267e905
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/NEWS
@@ -0,0 +1,1652 @@
+50.0
+====
+* Bump version
+
+
+50.rc
+=====
+* workspace-indicator: Make workspace menu section scrollable [Florian; !445]
+* Misc. bug fixes and cleanups [Florian; !444, !446]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Lenny Angst [de], Changwoo Ryu [ko]
+
+50.alpha
+========
+* classic: Drop xorg sessions [Jordan; !283]
+* classic: Deifne a gnome-session session [Adrian; !3960]
+* Misc. bug fixes and cleanups [Florian, Jordan; !425, !429, !430, !432, !431,
+ !433, !434, !435, !439]
+
+Contributors:
+ Florian Müllner, Jordan Petridis, Adrian Vovk
+
+Translators:
+ BoF ButterflyOfFire [kab], Giannis Antypas [el],
+ Asier Saratsua Garmendia [eu], Hugo Carvalho [pt], twlvnn kraftwerk [bg],
+ Baurzhan Muftakhidinov [kk], Ibai Oihanguren Sala [eu]
+
+49.0
+====
+* window-list: Adjust to gnome-shell changes [Florian; !421]
+* system-monitor: Unbreak on BSD [Antoine; !356]
+* Misc. bug fixes and cleanups [Florian; !422, !423]
+
+Contributors:
+ Antoine Jacoutot, Florian Müllner
+
+Translators:
+ Jiri Grönroos [fi], Aurimas Aurimas Černius [lt]
+
+49.rc
+=====
+
+Translators:
+ Emilio Sepúlveda [ia], Dušan Kazik [sk], Daniel Rusek [cs],
+ Nathan Follens [nl], Piotr Drąg [pl], Balázs Úr [hu], Yosef Or Boczko [he]
+
+49.beta
+=======
+* Misc. bug fixes and cleanups [Florian; !408]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Anders Jonsson [sv], Makoto Sakaguchi [ja], Danial Behzadi [fa],
+ Jordi Mas i Hernandez [ca]
+
+49.alpha.1
+==========
+* workspaces-indicator, window-list: Better expose workspace names
+ [Florian; !405]
+* window-list: Animate transition to/from overview [Florian; !412]
+* Misc. bug fixes and cleanups [Florian; !409, !411, !413]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Martin [sl], Emin Tufan Çetin [tr], Yuri Chornoivan [uk], Luming Zh [zh_CN],
+ Yago Raña [gl], Ekaterine Papava [ka], Vasil Pupkin [be],
+ Álvaro Burns [pt_BR], Sergej A. [ru]
+
+49.alpha.0
+==========
+* windowsNavigator: Fix handling keyboard shortcuts [Daniel; !395]
+* build: Allow disabling the X11 session [Neal; !396, !400]
+* Disable X11 session by default [Jordan; !399]
+* Misc. bug fixes and cleanups [Florian; !398, !406]
+
+Contributors:
+ Daniel Buch Hansen, Neal Gompa, Florian Müllner, Jordan Petridis
+
+Translators:
+ Emilio Sepúlveda [ia]
+
+48.1
+====
+
+Translators:
+ Hugo Carvalho [pt], Danial Behzadi [fa]
+
+48.0
+====
+* apps-menu: Fix scrolling items into view on keynav [Victor; !391]
+* Misc. bug fixes and cleanups [Florian, Stuart; !390, !392]
+
+Contributors:
+ Stuart Hayhurst, Victor Kareh, Florian Müllner
+
+48.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !385, !388]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Emilio Sepúlveda [ia], Mathews M [ml], Daniel Rusek [cs], Piotr Drąg [pl],
+ Anders Jonsson [sv], Ekaterine Papava [ka], Yuri Chornoivan [uk],
+ Aurimas Černius [lt], Luming Zh [zh_CN], Jiri Grönroos [fi]
+
+48.beta
+=======
+* window-list: Fix regression in chrome tracking [Florian; !379]
+* Misc. bug fixes and cleanups [Florian; !380]
+
+Contributors:
+ Florian Müllner, Emilio Sepúlveda
+
+Translators:
+ Rafael Fontenelle [pt_BR], Emilio Sepúlveda [ia]
+
+48.alpha
+========
+* classic: Add missing top-bar indicators [Florian; !339]
+* window-list: Fix window state styling [Florian; !342]
+* window-list: Fix "ignore-workspace" setting getting reset [Florian; !341]
+* window-list: Allow rearranging window buttons [Florian, Jakub; !338]
+* window-list: Add workspaces page to preference dialog [Florian; !344]
+* places-menu: Sync list of places with nautilus [Florian; !340]
+* places-menu: Fix a11y labelling [Florian; #542]
+* places-menu: Fix opening drives with mount operations [Florian; !361]
+* window-list: Fix hiding when entering overview with gestures [Florian; !364]
+* workspace-indicator: Only show previews of regular windows [Florian; !363]
+* window-list: Add attention indicator [Florian; !366]
+* Misc. bug fixes and cleanups [Florian, Bartłomiej; !337, !343, !345, !347,
+ !348, !349, !351, !352, !353, !354, !358, !362, !365, !367, !368, !370, !375]
+
+Contributors:
+ Florian Müllner, Bartłomiej Piotrowski, Jakub Steiner
+
+Translators:
+ Fabio Tomat [fur], Martin [sl], Jordi Mas i Hernandez [ca], Vasil Pupkin [be],
+ Nathan Follens [nl], Artur S0 [ru], Марко Костић [sr],
+ Yaron Shahrabani [he], Sabri Ünal [tr], Yi-Jyun Pan [zh_TW]
+
+47.0
+====
+
+Translators:
+ twlvnn kraftwerk [bg], Alexander Shopov [bg], Fran Dieguez [gl],
+ Aurimas Černius [lt], Daniel [es], Andika Triwidada [id],
+ Andi Chandler [en_GB], Ask Hjorth Larsen [da], Aefgh Threenine [th],
+ Dušan Kazik [sk], Rūdolfs Mazurs [lv], Irénée THIRION [fr]
+
+47.rc
+=====
+* Misc. bug fixes and cleanups [Sophie; !333]
+
+Contributors:
+ Sophie Herold
+
+Translators:
+ Daniel Șerbănescu [ro], Giannis Antypas [el], Ekaterine Papava [ka],
+ Jordi Mas i Hernandez [ca], Yuri Chornoivan [uk], Jiri Grönroos [fi],
+ Daniel Rusek [cs], Vasil Pupkin [be], Luming Zh [zh_CN], Brage Fuglseth [nb],
+ Asier Sarasua Garmendia [eu], Danial Behzadi [fa], Rafael Fontenelle [pt_BR],
+ Quentin PAGÈS [oc], Anders Jonsson [sv], Jürgen Benvenuti [de],
+ Changwoo Ryu [ko], Hugo Carvalho [pt], Piotr Drąg [pl], Sabri Ünal [tr]
+
+47.beta
+=======
+* window-list: Modernize styling [Jakub; !330]
+* Include "status-icons" extension [Florian; !194]
+* Misc. bug fixes and cleanups [Florian; !328, !331, !327]
+
+Contributors:
+ Florian Müllner, Jakub Steiner
+
+Translators:
+ Sabri Ünal [tr], Chao-Hsiung Liao [zh_TW]
+
+47.alpha
+========
+* Improve workspace previews in window-list and workspace-indicator
+ [Florian; !307, !316]
+* apps-menu: Fix a11y of category labels [Florian; !319]
+* window-list: Fix long-press support [Florian; !320]
+* window-list: Animate transitions [Florian; !325]
+* Misc. bug fixes and cleanups [Florian; !315, !321, !324]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jordi Mas i Hernandez [ca], Martin [sl], Hugo Carvalho [pt], Jose Riha [sk],
+ Scrambled 777 [hi], Artur S0 [ru], Милош Поповић [sr], Yosef Or Boczko [he],
+ Balázs Úr [hu]
+
+46.1
+====
+* screenshot-window-sizer: Add flathub-recommended size [Florian; !317]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Rachida SACI [kab], Matheus Polkorny [pt_BR], Fabio Tomat [fur]
+
+46.0
+====
+* system-monitor: Fix net speed [Florian; !313]
+* Misc. bug fixes and cleanups [Aral; !311]
+
+Contributors:
+ Aral Balkan, Florian Müllner
+
+Translators:
+ Anders Jonsson [sv], Piotr Drąg [pl], Balázs Úr [hu], Milo Casagrande [it],
+ Quentin PAGÈS [oc], Athmane MOKRAOUI [kab], Changwoo Ryu [ko],
+ Ask Hjorth Larsen [da]
+
+46.rc
+=====
+* Fix window previews in workspace indicator [Florian; !304]
+* Fix menu ornament in workspace indicator [Florian; !305]
+* Misc. bug fixes and cleanups [Florian; !306, !309]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Danial Behzadi [fa], Ekaterine Papava [ka], Sabri Ünal [tr], Artur S0 [ru],
+ Yuri Chornoivan [uk], Vasil Pupkin [be], Asier Sarasua Garmendia [eu],
+ Yaron Shahrabani [he], Brage Fuglseth [nb], Nathan Follens [nl],
+ Aurimas Černius [lt], Matej Urbančič [sl], Boyuan Yang [zh_CN],
+ Kukuh Syafaat [id], Fran Dieguez [gl], Andi Chandler [en_GB],
+ Baurzhan Muftakhidinov [kk], Rūdolfs Mazurs [lv], Guillaume Bernard [fr],
+ Daniel Mustieles [es], Jiri Grönroos [fi]
+
+46.beta
+=======
+* apps-menu: Rename Applications to Apps [Allan; !299]
+* Misc. bug fixes and cleanups [Florian; !296, !297, !300, !301, !302]
+
+Contributors:
+ Allan Day, Florian Müllner
+
+Translators:
+ Gabriel Brand [de], Daniel Rusek [cs], Fran Dieguez [gl],
+ Aefgh Threenine [th], Vasil Pupkin [be], Artur S0 [ru], Yosef Or Boczko [he],
+ Sabri Ünal [tr]
+
+46.alpha
+========
+* workspace-indicator: Fix initial preview visibility [Florian; !280, !292]
+* screenshot-window-sizer: Fix cycling between sizes backwards [Florian; !284]
+* Add back overview in Classic session [Florian; !287]
+* Allow running Classic session headless [Jonas; !289]
+* window-list: Fix buttons not being clickable at the screen edge
+ [Florian; !291]
+* Add system-monitor extension [Florian; !277]
+* Fixed crash [Florian; !290]
+* Misc. bug fixes and cleanups [Florian; !276, !275, !278, !281, !286, !288]
+
+Contributors:
+ Jonas Ådahl, Florian Müllner
+
+Translators:
+ Kristjan SCHMIDT [eo], Brage Fuglseth [nb]
+
+45.0
+====
+
+Contributors:
+ Andre Klapper
+
+Translators:
+ Bruce Cowan [en_GB]
+
+45.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !267, !224, !272]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Sabri Ünal [tr], Florentina Musat [ro], A S Alam [pa]
+
+45.beta
+=======
+* Port extensions to ESM [Florian; !259, !266, !268, !269]
+* Misc. bug fixes and cleanups [Florian; !260, !261, !262, !263, !264]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Efstathios Iosifidis [el]
+
+45.alpha
+========
+* window-list: Modernize default styling [Alexander; !253]
+* Replace classic styling with built-in light style [Florian; !254]
+* window-list: Add tooltip for long window titles [Arik; !251]
+* light-style: New extension [Florian; !256]
+* Misc. bug fixes and cleanups [Florian; !255, !257]
+
+Contributors:
+ Florian Müllner, Arik W, Alexander Weichart
+
+44.0
+====
+* Bump version
+
+44.rc
+=====
+* Bump version
+
+44.beta
+=======
+* Tweak menu alignment [robxnano; !246]
+
+Contributors:
+ Florian Müllner, robxnano
+
+Translators:
+ Vasil Pupkin [be]
+
+43.1
+====
+* Fixed crash [Florian; !243]
+* Misc. bug fixes and cleanups [mowemcfc; !244]
+
+Contributors:
+ Florian Müllner, mowemcfc
+
+Translators:
+ Sabri Ünal [tr]
+
+43.0
+====
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Pawan Chitrakar [ne], Zurab Kargareteli [ka], Aleksandr Melman [ru]
+
+43.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !240]
+
+Contributors:
+ Florian Müllner
+
+43.beta
+=======
+* Misc. bug fixes and cleanups [Florian; !237, !238]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Nart Tlisha [ab]
+
+43.alpha
+========
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Marco Ciampa [it]
+
+42.3
+====
+* screenshot-window-sizer: Fix reported sizes on wayland [Florian; !232]
+* window-list: Improve touch support [Florian; !233]
+
+Contributors:
+ Florian Müllner
+
+42.2
+====
+* native-window-placement: Adjust to gnome-shell 42 changes [Florian; !229]
+* window-list: Fix visibility on non-primary monitors [Jason; !230]
+
+Contributors:
+ Jason Lynch, Florian Müllner
+
+Translators:
+ Cheng-Chia Tseng [zh_TW]
+
+42.1
+====
+* Misc. bug fixes and cleanups [Florian; !223, !222, !225]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Milo Casagrande [it], Rūdolfs Mazurs [lv], Nathan Follens [nl],
+ Ngọc Quân Trần [vi], Zurab Kargareteli [ka]
+
+42.0
+====
+
+Translators:
+ Philipp Kiemle [de], Balázs Úr [hu], Марко Костић [sr], sicklylife [ja],
+ Baurzhan Muftakhidinov [kk]
+
+42.rc
+=====
+* Misc. bug fixes and cleanups [Florian; !215, !218]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Marek Černocký [cs], Dušan Kazik [sk], Piotr Drąg [pl], Jiri Grönroos [fi],
+ Luna Jernberg [sv], Alan Mortensen [da], Charles Monzat [fr],
+ Changwoo Ryu [ko]
+
+42.beta
+=======
+* workspace-indicator: Fix cancelling editing with Esc [Florian; !208]
+* window-list: Update window tracking to avoid missing icons [Florian; !207]
+* Use libadwaita for preferences [Florian; !209, !213]
+* Adapt to Clutter grab API changes [Florian; !212]
+* Misc. bug fixes and cleanups [Jan, Florian; !210, !214]
+
+Contributors:
+ Jan Beich, Florian Müllner, Naala Nanba
+
+Translators:
+ Boyuan Yang [zh_CN], Matej Urbančič [sl], Naala Nanba [ab],
+ Alexander Shopov [bg], Emin Tufan Çetin [tr]
+
+42.alpha
+========
+* native-window-placement: Fix distorted layout in app grid [Sebastian; !189]
+* window-list: Fix on-screen keyboard [Florian; !199]
+* Misc. bug fixes and cleanups [Neal; Just; !195, !197]
+
+Contributors:
+ Piotr Drąg, Neal Gompa, Sebastian Keller, Florian Müllner, Just Perfection
+
+Translators:
+ Goran Vidović [hr], Sveinn í Felli [is], Yuri Chornoivan [uk],
+ Fabio Tomat [fur], Quentin PAGÈS [oc], Hugo Carvalho [pt],
+ Yaron Shahrabani [he], Jordi Mas i Hernandez [ca], MohammadSaleh Kamyab [fa],
+ Fran Dieguez [gl], Daniel Mustieles [es], Aleksandr Melman [ru],
+ Aurimas Černius [lt], Asier Sarasua Garmendia [eu], Kukuh Syafaat [id],
+ Rafael Fontenelle [pt_BR]
+
+41.0
+====
+* Bump version
+
+41.rc.1
+=======
+* Fix pre-generating stylesheets in tarball [Florian; !190]
+
+Contributors:
+ Florian Müllner
+
+41.rc
+=====
+* window-list: Adapt to overview-on-startup [Florian; !185]
+* apps-menu: Use a custom 'toggle-menu' shortcut [Florian; !173]
+* Misc. bug fixes and cleanups [Florian; !186]
+
+Contributors:
+ Florian Müllner
+
+41.beta
+=======
+* window-list: Extend reactive area of minimap to screen edges [Adam; !171]
+* drive-menu: Improve detection of network mounts [Florian; !27, !176]
+* Use distinct gettext domain for e.g.o uploads [Florian; #335]
+* Misc. bug fixes and cleanups [Florian; !172, !174, !177, !167, !178, !180,
+ !181, !182, !183]
+
+Contributors:
+ Marco Trevisan (Treviño), Adam Goode, Florian Müllner
+
+Translators:
+ Hugo Carvalho [pt], Juliano de Souza Camargo [pt], Alexander Shopov [bg]
+
+40.1
+====
+* Disable welcome dialog in classic session [Florian; !169]
+* windowsNavigator: Adjust to a late gnome-shell change [Florian; !170]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Ngọc Quân Trần [vi], Anders Jonsson [sv], Carmen Bianca BAKKER [eo],
+ Pawan Chitrakar [ne], Quentin PAGÈS [oc]
+
+40.0
+====
+
+Translators:
+ Jiri Grönroos [fi]
+
+40.rc
+=====
+* native-window-placement: Adjust to gnome-shell changes [Florian; !164]
+* windows-navigator: Adjust to gnome-shell changes [Florian; !163]
+* window-list, workspace-indicator: Only show previews for up to six workspaces
+ [Florian; !165]
+* window-list, workspace-indicator: Improve workspace preview appearance
+ [Florian; !166]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Fran Dieguez [gl]
+
+40.beta
+=======
+* Add tooltips to workspace thumbnails [Florian; !155]
+* Drop arrows from top bar menus [Florian; !156]
+* drive-menu: Mark mounts that can be unmounted as removable [Michael; !152]
+* Remove horizontal-workspaces extension [Florian; !158]
+* Adjust to shell overview changes [Florian; !159, !160]
+* Fix crashes [Daniel; !157]
+* Misc. bug fixes and cleanups [Florian; !154, !161]
+
+Contributors:
+ Michael Lawton, Florian Müllner, Daniel van Vugt
+
+Translators:
+ Аляксей [be], A S Alam [pa]
+
+40.alpha.1
+==========
+* Don't depend on sassc when building from tarball [Florian; !150]
+* Port extensions preferences to GTK4 [Florian; !148]
+* Misc. bug fixes and cleanups [Florian, Jonas; !149, !151, !153]
+
+Contributors:
+ Jonas Dreßler, Florian Müllner
+
+40.alpha
+========
+* window-list: Honor changes in skip-taskbar property [Sergio; !130]
+* window-list, workspace-indicator: Adjust to 3.38 changes [Florian; !133]
+* window-list, workspace-indicator: Improve previews in workspace thumbs
+ [Florian; #260, !142]
+* auto-move: Improve behavior on multi-monitor setups [Florian; !135]
+* windowNavigator: Adjust to 3.38 changes [Thun; #259]
+* Misc. bug fixes and cleanups [Florian, Jonas Å, Jordan, Ray; !131, !136,
+ !137, !140, !141, !144, !146, !145]
+
+Contributors:
+ Sergio Costas, Florian Müllner, Jordan Petridis, Thun Pin, Ray Strode,
+ Jonas Ådahl
+
+Translators:
+ Fabio Tomat [fur], Jordi Mas [ca]
+
+3.38.1
+======
+
+Contributors:
+ Yacine Bouklif, Florian Müllner
+
+Translators:
+ Yacine Bouklif [kab], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
+ Yosef Or Boczko [he]
+
+3.38.0
+======
+
+Translators:
+ Balázs Meskó [hu], Alan Mortensen [da], Juliano Camargo [pt], Tim Sabsch [de],
+ Milo Casagrande [it], Rūdolfs Mazurs [lv]
+
+3.37.92
+=======
+
+Translators:
+ Nathan Follens [nl], Zander Brown [en_GB], Aurimas Černius [lt],
+ Marek Černocký [cs], Changwoo Ryu [ko], Dušan Kazik [sk]
+
+3.37.91
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Fran Dieguez [gl], Akarshan Biswas [bn_IN], Kukuh Syafaat [id],
+ Piotr Drąg [pl], Rafael Fontenelle [pt_BR], Jiri Grönroos [fi],
+ Марко Костић [sr], Goran Vidović [hr]
+
+3.37.90
+=======
+* Misc. bug fixes and cleanups [Florian, Piotr; !126, !128]
+
+Contributors:
+ Piotr Drąg, Florian Müllner
+
+Translators:
+ Fabio Tomat [fur], Efstathios Iosifidis [el], Anders Jonsson [sv],
+ Asier Sarasua Garmendia [eu], Alexandre Franke [fr]
+
+3.37.3
+======
+* window-list, native-window-placement: Adjust to shell changes [Florian; !124]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jordi Mas [ca], sicklylife [ja], Boyuan Yang [zh_CN],
+ Baurzhan Muftakhidinov [kk]
+
+3.37.2
+======
+* window-list, auto-move: Modernize preference dialogs [Florian; !121]
+* Adjust to gnome-shell changes [Florian; !122]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Cheng-Chia Tseng [zh_TW], Yuri Chornoivan [uk], Daniel Mustieles [es],
+ Emin Tufan Çetin [tr], Danial Behzadi [fa], Daniel Șerbănescu [ro],
+ Matej Urbančič [sl]
+
+3.37.1
+======
+* drive-menu: Emphasize eject buttons [Florian; #223]
+* user-theme: Add preference dialog [Florian; !117]
+* window-list: Fix inconsistent state in preference dialog [Milan; !119]
+* workspace-indicator: Overhaul preference dialog [Florian; !120]
+* user-theme: Support session mode styles [Florian; !118]
+* Misc. bug fixes and cleanups [Florian, Xiaoguang; !113, !106, !114, !116]
+
+Contributors:
+ Milan Crha, Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Daniel Korostil [uk], Yosef Or Boczko [he], Kristjan SCHMIDT [eo],
+ Dz Chen [zh_CN], Danial Behzadi [fa], Yuri Chornoivan [uk],
+ Anders Jonsson [sv], Daniel Mustieles [es]
+
+3.36.0
+======
+
+Contributors:
+ Florian Müllner
+
+3.35.91
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Zander Brown [en_GB]
+
+3.35.90
+=======
+* Adjust to gnome-shell changes [Florian; !100, !101, !102]
+* Force single-line window titles in window list [Florian; #202]
+* Misc. bug fixes and cleanup [Florian; !104, !105]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ sicklylife [ja], Umarzuki Bin Mochlis Moktar [ms]
+
+3.35.3
+======
+
+Translators:
+ Fran Dieguez [gl]
+
+3.35.2
+======
+* Adjust to gnome-shell changes [Marco, Florian; !89, !95, !96]
+* window-list, workspace-indicator: Exclude DESKTOP windows from previews
+ [Florian; !93]
+* screenshot-window-sizer: Fix cycling through all valid sizes [Willy; !97]
+
+Contributors:
+ Marco Trevisan (Treviño), Florian Müllner, Willy Stadnick
+
+3.34.1
+======
+* Adjust to gnome-settings-daemon plugin removals [Xiaoguang; !94]
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Nathan Follens [nl], Dušan Kazik [sk], Ask Hjorth Larsen [da],
+ Yi-Jyun Pan [zh_TW]
+
+3.34.0
+======
+
+Translators:
+ Rafael Fontenelle [pt_BR], Efstathios Iosifidis [el], Milo Casagrande [it],
+ Sabri Ünal [tr]
+
+3.33.92
+=======
+
+Translators:
+ Марко Костић [sr], Tim Sabsch [de], Rūdolfs Mazurs [lv], Matej Urbančič [sl],
+ Balázs Úr [hu], Claude Paroz [fr], Fran Dieguez [gl], Changwoo Ryu [ko],
+ Ryuta Fujii [ja], Fabio Tomat [fur], Goran Vidović [hr]
+
+3.33.91
+=======
+* Misc. bug fixes and cleanups [Florian; !88, !90, !91, !92]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Asier Sarasua Garmendia [eu], Anders Jonsson [sv], Marek Černocký [cs],
+ Kukuh Syafaat [id], Jiri Grönroos [fi], Florentina Mușat [ro],
+ Aurimas Černius [lt], Daniel Mustieles [es], Piotr Drąg [pl], Jordi Mas [ca],
+ Danial Behzadi [fa]
+3.33.90
+=======
+* window-list: Support showing windows from all workspaces [Florian; #154]
+* Misc. bug fixes and cleanups [Florian; !86, !87]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Jor Teron [mjw]
+
+3.33.4
+======
+* Make GNOME Classic more classic:
+ - Disable GNOME 3 overview [Florian; !69]
+ - Add window picker button to window list [Florian; !73, !80]
+ - Style improvements and fixes [Jakub; #169, !82]
+ - Support horizontal workspace layout in window list [Florian; !70]
+ - Add draggable previews to window list workspace switcher [Florian; !74]
+ - Arrange workspaces horizontally [Florian; !72]
+* workspace-indicator: Support horizontal workspace layout [Florian; !71]
+* workspace-indicator: Add draggable previews [Florian; !77]
+* Misc. bug fixes and cleanups [Florian; !75, !76, !79, !78, #168, !84]
+
+Contributors:
+ Florian Müllner, Jakub Steiner, Jor Teron
+
+Translators:
+ Jor Teron [mjw]
+
+3.33.3
+======
+* Misc. bug fixes [Florian, Marco; !67, !68]
+
+Contributors:
+ Florian Müllner, Marco Trevisan (Treviño)
+
+3.33.2
+======
+* Misc. bug fixes and cleanups [Florian; !66]
+
+Contributors:
+ Florian Müllner
+
+3.33.1
+======
+* Misc. bug fixes [Florian; !64]
+
+Contributors:
+ Florian Müllner
+
+3.32.1
+======
+* Fix windowsNavigator extension after ES6 port [Florian; #143]
+* screenshot-window-sizer: Add phone screenshot sizes [Adrien; !65]
+* Misc. bug fixes and cleanups [Fabian; !62]
+
+Contributors:
+ Florian Müllner, Adrien Plazas, Fabian P. Schmidt
+
+3.32.0
+======
+
+Contributors:
+ Florian Müllner
+
+Translations:
+ Victor Ibragimov [tg], Kristjan SCHMIDT [eo], Mart Raudsepp [et]
+
+3.31.92
+=======
+* Misc. bug fixes and cleanups [Florian; !57, !58, !59, !60]
+
+Contributors:
+ Florian Müllner
+
+3.31.91
+=======
+* apps-menu: Remove outdated legacy-tray handling [Florian; !53]
+* user-theme: Allow using XDG user data dir [Tomasz; !55]
+* Misc. bug fixes and cleanups [Florian; !52, !54, !56]
+
+Contributors:
+ Tomasz Gąsior, Florian Müllner
+
+Translators:
+ Matej Urbančič [sl], Gun Chleoc [gd]
+
+3.31.90
+=======
+* Misc. bug fixes and cleanups [Florian; !49, !50, !51]
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Ryuta Fujii [ja], Charles Monzat [fr], Pieter Schalk Schoeman [af]
+
+3.31.2
+======
+* Remove obsolete alternate-tab extension [Florian; #786496]
+* Adjust to gnome-shell changes [Florian; #113]
+
+Contributors:
+ Florian Müllner
+
+3.30.1
+======
+* apps-menu: Fix height on HiDPI systems [Florian; #102]
+* window-list: Only switch between windows on active workspace when scrolling
+ [Florian; #78]
+
+Contributors:
+ Florian Müllner
+
+3.30.0
+======
+* Bump version
+
+3.29.91
+=======
+* Misc. bug fixes [Florian; #90]
+
+Contributors:
+ Florian Müllner
+
+3.29.90
+=======
+* Misc. bug fixes [Florian; #786496]
+
+Contributors:
+ Florian Müllner
+
+3.29.3
+======
+* Adjust to global.screen removal [Jonas; #759538]
+
+Contributors:
+ Jonas Ådahl, Florian Müllner
+
+3.29.2
+======
+* Misc. bug fixes [Florian; #69]
+
+Contributors:
+ Florian Müllner
+
+3.28.1
+======
+* Misc. bug fixes [Xiaoguang, Florian; #59, #62]
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Dz Chen [zh_CN]
+
+3.28.0
+======
+
+Contributors:
+ Florian Müllner, Xiaoguang Wang
+
+Translators:
+ Aman Alam [pa], Bruce Cowan [en_GB]
+
+3.27.92
+=======
+
+Contributors:
+ Florian Müllner
+
+Translators:
+ Piotr Drąg [es], GNOME Translation Robot [gd], Daniel Șerbănescu [ro]
+
+3.27.91
+=======
+* places-menu: Support unmounting ejectable places [Rémy; #17]
+* apps-menu: Support separators and custom sort order [Florian; #27]
+* Port to meson [Florian; #31, #45]
+* window-list: Fix missing icons on wayland [Florian; #10]
+* places-menu: Fix terminating gnome-shell with recent gjs [Florian; #44]
+* auto-move: Make it work with wayland windows [Florian; #33]
+* Classic theme fixes [Florian, Jonas; #26, #41, #39, #40]
+* Require sassc for classic styling [Florian; !28]
+* Misc. bug fixes [Piotr, Florian; #772211, #32, #30]
+
+Contributors:
+ Jeremy Bicha, Piotr Drąg, Jonas Kümmerlin, Rémy Lefevre, Iñigo Martínez,
+ Florian Müllner
+
+Translators:
+ Matej Urbančič [sl], Kjartan Maraas [nb]
+
+3.27.1
+======
+* updated translations (ca@valencia)
+
+3.26.1
+======
+* native-window-placement: Adjust to gnome-shell changes
+* updated translations: el, fa, ru, sv
+
+3.26.0
+======
+* updated translations (be, bg, ca, da, eu, fi, is, it, ko, lv, ml,
+ nl, pt_BR, vi, zh_TW)
+
+3.25.91
+=======
+* updated translations (ca, fr, it, pl, pt_BR, sr, sr@latin, tr)
+
+3.25.90
+=======
+* updated translations (es, gl, hr, hu, kk, sl, sv, sv)
+
+3.25.4
+======
+* screenshot-window-sizer: Fix backward cycling
+* updated translations (ar, be, ca, cs, de, fur, id, lt, pl, sk)
+
+3.25.3
+======
+* places-menu: Use mount operation if necessary
+* window-list: Respect MWM hints
+* updated translations (es, fur, kk)
+
+3.25.2
+======
+* places-menu: Make URI launching asynchronous
+* updated translations (de, fur, hr, hu, id, sl)
+
+3.25.1
+======
+* apps-menu: Mark copied launchers as trusted
+* places-menu: Make icon lookup asynchronous
+* updated translations (hr)
+
+3.24.1
+======
+* apps-menu: Allow creating desktop launchers via DND
+* updated translations (el, vi)
+
+3.24.0
+======
+* updated translations (lv, tr)
+
+3.23.92
+=======
+* update classic theme
+* updated translations (be, ko, ca, da, cs, ru, lt)
+
+3.23.91
+=======
+* updated translations (de, es, eu, fi, fr, fur, gl, hu, id, it, kk, nb, pl, pt_BR,
+ sk, sr, sr@latin, sv, uk, zh_TW)
+
+3.23.90
+=======
+* window-list: Improve styling
+* window-list: Hide workspace indicator when there's a single (static) workspace
+* new translation (be)
+
+3.23.2
+======
+* alternateTab: Don't take over 'switch-group' shortcut
+* updated translations (zh_CN)
+
+3.22.1
+======
+* window-list: Update icon on app changes
+
+3.22.0
+======
+* updated translations (en_GB)
+
+3.21.92
+=======
+* update style
+* updated translations (pl, vi)
+
+3.21.91
+=======
+* updated translations (pl)
+
+3.21.90
+=======
+* updated translations (es, gu)
+
+3.21.4
+======
+* apps-menu: Fix entries from non-standard AppDir directories
+
+3.21.3
+======
+* adjust to gnome-shell changes
+* updated translations (oc)
+
+3.21.2
+======
+* version bump, nothing to see here
+
+3.20.1
+======
+* update classic style
+* updated translations (gd, oc)
+
+3.20.0
+======
+* version bump, nothing to see here
+
+3.19.92
+=======
+* version bump, nothing to see here
+
+3.19.91
+=======
+* updated translations (oc)
+
+3.19.90
+=======
+* version bump, nothing to see here
+
+3.19.4
+======
+* screenshot-window-sizer: HiDPI support
+* Fix gnome-shell component in classic session
+* updated translations (lt)
+
+3.19.3
+======
+* native-window-placement: Don't let border overlap title
+* apps-menu: Fix handling of .desktop files in subdirectories
+* updated translations (is)
+
+3.19.2
+======
+* updated translations (gd)
+
+3.19.1
+======
+* Fix some theme issues
+
+3.18.1
+======
+* window-list: Fix accessibility of window buttons
+* apps-menu: Fix unreliable highlight
+* updated translations (ar)
+
+3.18.0
+======
+* Bump version
+
+3.17.92
+=======
+* places: Include DESKTOP when desktop icons are enabled
+* updated translations (fa)
+
+3.17.91
+=======
+* updated translations (nl, pl, zh_TW)
+
+3.17.90
+=======
+* window-list: Improve application ordering
+* workspace-indicator: Use consistent workspace numbering
+
+3.17.4
+======
+* updated translations (fur)
+
+3.17.3
+======
+* window-list: Adjust with text-scaling-factor
+* classic style updates
+* updated translations (pt, ro)
+
+3.17.2
+======
+* updated translations (oc, pt, zh_CN)
+
+3.17.1
+======
+* style updates
+* updated translations (oc)
+
+3.16.1
+======
+* window-list: Fix workspace indicators popup menu position
+* apps-menu: Fix taking over panel-main-menu shortcut
+* updated translations (et, ja, lv)
+
+3.16.0
+======
+* updated translations (ca)
+
+3.15.92
+=======
+* classic: Update theme
+* update for mutter API changes
+* updated translations (bg, bs, da, fi, pa, ru, sr, sr@latin, tg)
+
+3.15.91
+=======
+* classic: Update theme
+* systemMonitor extension was removed, as the message tray where it
+ put its indicator no longer exists
+* window-list: Adjust for gnome-shell changes
+* updated translations (gl, it, kk, ko, lt, pl, sk, uk, zh_TW)
+
+3.15.90
+=======
+* classic: Visual refresh based on new shell theme
+* window-list: Adjust for gnome-shell changes
+* updated translations (an, el, eo, eu, fr, he, is, sv, tr)
+
+3.15.4
+======
+* window-list: Improve interaction with system modal dialogs
+* updated translations (cs, de, es, eu, fur, hu, id, nb, pt_BR, ru, sl, vi)
+
+3.15.3.1
+========
+* adjust to gnome-shell change
+
+3.15.3
+======
+* classic-mode: Add high-contrast theme variant, drop .desktop file
+* places-menu: Fix error when XDG user directories are not set up
+* window-list: Add option to show on all monitors
+* updated translations (eu, hu, kk, ro, tr)
+
+3.15.2
+======
+* removable-drive, user-theme, window-list: Update for gnome-shell changes
+* apps-menu: Fix some visual glitches
+* Fix classic mode style
+* updated translations (an, cs, he, vi)
+
+3.15.1
+======
+ * updated translations (es, nb)
+
+3.14.1
+======
+ * alternateTab: Fix dismissing popup with Escape
+ * some improvements to the window-list
+ (spacing in app buttons, no flash when closing windows with auto-grouping)
+ * updated translations (lv, it, pt, bg)
+
+3.14.0
+======
+ * updated translations (bn_IN, hi, kn, sr, sr@latin, uk)
+
+3.13.92
+=======
+* new extension: screenshot-window-sizer
+* window-list: Don't add sticky windows more than once
+* updated translations (da, de, fi, ko, mr, ms, ne, pa, pl, sk, sv, ta, te, tr)
+
+3.13.91
+=======
+* window-list: restore fitts'ability of workspace button
+* updated for gnome-shell changes
+* updated translations (cs, kk, fr, or, fa, ja, gu, id)
+
+3.13.90
+=======
+* updated translations (as, ca, eu, nl, zh_CN, zh_HK, zh_TW)
+
+3.13.4
+======
+* Updated for gnome-shell changes
+* updated translations (el, gl, ru)
+
+3.13.3
+======
+* Tweak preference UIs some more
+* Fix classic mode schema overrides
+* updated translations (es, he, hu, lt, nb, pt_BR, sl, tr)
+
+3.13.2
+======
+* Fix sorting of grouped buttons in window list
+* Tweak preference UIs
+* updated translations (en_GB)
+
+3.13.1
+======
+* add DesktopNames key to the classic session file
+* classic theme: remove rounded corners from tile previews
+* window-list: don't shift message tray on other monitors
+* auto-move-windows: several fixes and updates for api changes
+* launch-new-instances: updates for api changes
+* updated translations (ja, km)
+
+3.12.0
+======
+* updated translations (zh_HK, zh_TW)
+
+3.11.92
+=======
+* nothing to see here, move on
+
+3.11.91
+=======
+* updated translations (ko, fur)
+
+3.11.90
+=======
+* several fixes and improvements to the window-list
+ (can be scrolled, works correctly with the OSD
+ keyboard, filters skip-taskbar windows, does not
+ force all notifications to bold)
+* drive-menu fixed not to show shadowed mounts
+* updates for gnome-shell changes (launch-new-instance,
+ auto-move-windows, places-menu)
+* build system fixes for systems without /bin/bash
+* updated translations (or, tr, uk)
+
+3.11.5
+======
+* updates for gnome-shell changes
+* updated translations (kn)
+
+3.11.4
+======
+* classic mode now supports session saving
+* updates for gnome-shell changes
+* updated translations (ar, kn)
+
+3.11.3
+======
+* workspace-indicator is vertically aligned now
+* updated translations (ar, eo, ta, te)
+
+3.11.2
+======
+* updated translations (zh_CN)
+
+3.11.1
+======
+* ignore shadowed mounts in drive-menu extension
+* updates for gnome-shell/gjs changes
+* updated translations (el, th)
+
+3.10.1
+======
+* updated translations (af, ca, ca@valencia, de, et, eu, fa,
+ hu, lt, lv, nb, nl, pa, pt, sk, sr, sr@latin, tr)
+
+3.10.0
+======
+* updated translations (as, cs, da, es, et, fi, fr, gl,
+ he, id, it, kk, lt, lv, pa, pl, pt, pt_BR, ru, sl, tg,
+ uk, zh_HK, zh_TW)
+
+3.9.92
+======
+* more updates and fixes for gnome-shell master changes
+ and regressions (systemMonitor, window-list, apps-menu)
+* lots of updated translations (ar, as, cs, da, de, el, es,
+ eu, fi, gl, he, hu, it, ja, kk, ko, lv, nb, nl, pa, pl,
+ pt_BR, ru, sk, sr, sr@latin, ta, tg)
+
+3.9.91
+======
+* update the classic mode session and theme to work with the
+ new system menu
+* the usual round of updates and fixes for gnome-shell
+ API changes
+* updated translations (de, it, lt, nl, pl, pt_BR, sk,
+ zh_HK, zh_TW)
+
+3.9.90
+======
+* xrandr-indicator was removed, as the implementation
+ was incompatible with the new DisplayConfig mutter API
+* various extensions were updated for the 3.9.90 gnome-shell API
+* updated translations (cs, es, fur, gl, he, hu, id, ja, sl, tg,
+ zh_CN, zh_HK, zh_TW)
+
+3.9.5
+=====
+* alternative-status-menu was removed entirely, as
+ it does not fit in the designs of the new unified
+ status menu
+* updated translations (as, gu, it, ru)
+
+3.9.4
+=====
+* apps-menu: fixed handling of hot corner in case
+ of screen reconfiguration
+* alternative-status-menu now correctly honors polkit
+ for hibernation
+* user-menu now loads themes from $XDG_DATA_HOME too
+* translation updates (de, id, pt_BR, vi, zh_CN, ml)
+
+3.9.3
+=====
+* classic mode mini extensions were replaced with a
+ GSettings override specified in the .json file
+* styling of classic mode improved
+* native-window-placement is back working on 3.9
+* misc bug fixes
+* traslation updates (an, cs, el, es, gl, nb, pl, sk, tg)
+
+3.9.2
+=====
+* apps-menu: appearance of the scrollbars was improved
+* window-list is a little taller in classic mode (to account
+ for the workspace switcher)
+* alternative-status-menu honors again the dconf configuration
+* translation updates (sr)
+
+3.9.1
+=====
+* updates to window-list, xrandr-indicator,
+ workspace-indicator, windowsNavigator for gnome-shell
+ changes
+* translation updates (cs, es, lt, pl, pt_BR, sl)
+
+3.8.1
+=====
+* many improvements to window-list:
+ - windows are activated by DND over them
+ - window buttons now have the right size,
+ even if the text is smaller or larger
+ than the ideal
+ - window buttons can be grouped automatically
+ when the panel becomes crowded
+ - added a workspace switcher menu
+* added keyboard navigation to apps-menu
+* small tweaks to classic-mode theme, in particular
+ for menus
+* translation updates (gl, ko, sr)
+
+3.8.0
+=====
+* translation updates (hu, ja, fi, it)
+
+3.7.92
+======
+* misc bug fixes to app-menu and window-list
+* translation updates (de, sl, pt_BR, ru)
+
+3.7.91
+======
+* various updates for shell changes
+* update window-list to always use application icons
+* update apps-menu to not load subdirectories as
+ separate categories
+* translation updates (lt, zh_CN)
+
+3.7.90
+======
+* various fixes to make places-menu behave more
+ like Nautilus, including showing the machine
+ name in place of File System
+* various updates for shell changes
+* alternative-status-menu no longer supports
+ ConsoleKit systems, you need to install logind
+ to have suspend or hibernate
+* translation updates (es, cz, pl, sr)
+
+3.7.5.1
+=======
+* new extension forgotten in previous NEWS entry:
+ windows-list
+* also forgotten previously: classic mode got a new
+ GNOME2 style
+* build fixes
+
+3.7.5
+=====
+* places-menu is back in the classic extensions, with
+ a new old GNOME-2 look
+* classic mode moved the date menu to right, where we
+ all know it rightly belongs
+* apps-menu received a face-lift, with the inclusion
+ of a reduced form of AxeMenu
+* new extension in the classic set: launch-new-instance,
+ which modifies the behavior of clicking in the dash
+ and app launcher
+* alternate-tab, native-window-placement and windowsNavigator
+ updated for gnome-shell changes
+* translation updates (es, cz, pl)
+
+3.7.4
+=====
+* a separate configure switch has been added to enable
+ classic mode session definitions
+* places-menu is no longer part of the classic-mode
+ extension set
+* updated translations (ar, gl, hu, lt, pt_BR, sr)
+
+3.7.3
+=====
+* new extensions: default-min-max, static-workspaces
+* alternate-tab now uses the built-in window switcher and just
+ takes over the switch-application keybinding
+* workspace-indicator: is no longer part of classic-mode
+* we now install classic-mode data files for gdm, gnome-session
+ and gnome-shell, so if you enable classic-mode you get a new
+ session option in GDM
+* updated translations (ar, es, pl, ru, sl, zh_HK, zh_TW)
+
+3.7.2
+=====
+* fixed crashes with places-menu, windowsNavigator, alternate-tab
+ and native-window-placement
+* alternate-tab now hides attached modal dialogs
+* places-menu has restored support for Nautilus 3.4
+* the default for hibernate is now to show in alternative-status-menu
+* some extensions are now tagged as "classic", and can be chosen with
+ --enable-extensions=classic-mode
+* dock and gajim were removed at the beginning of the 3.7.1 cycle,
+ as they were buggy and unmaintained
+* updated translations (ar, cs, de, el, es, gl, id, lt, lv, pa, pl
+ ru, sk, sl, sr, sr@latin)
+
+3.6.1
+=====
+* fixed alternative-status-menu for the new lock screen
+* squashed some alternate-tab warnings
+* drive-menu now works with 3.6 again
+* updated translations (ar, cs, el, es, gl, id, lv, pl, sl)
+
+3.6.0
+=====
+* major rework in places menu, to make it work without
+ removed supporting code in the shell and to make it look like
+ the nautilus sidebar
+ (similar work would be needed for drive-menu, not done yet)
+* updated translations (ca, cs, de, el, en_GB, es, fi, hu, id, lt,
+ pl, pt_BR, ru, sl, sr)
+
+3.5.91
+======
+* various crashers were fixed in alternative-tab
+* auto-move-windows now can be made to work with static workspaces
+* place-menu is now on the left and uses symbolic icons like Files
+* StIconType usage was removed from all extensions, after it was
+ removed in core
+* systemMonitor, xrandr-indicator, apps-menu, places-menu,
+ alternative-status-menu were updated for the newer shell
+* updated translations (es, gl, it, pl, sl)
+
+3.5.90
+======
+* alternate-tab has been reworked again, the old mode switch
+ was removed and the all&thumbnails code extended to handle
+ icons and filtering to the workspace
+* alternate-tab thumbnails now reflect the aspect ratio of the windows
+* systemMonitor now shows a tooltip above the indicator
+* native-window-placement, systemMonitor and windowsNavigator have been updated
+ for the newer shell
+* updated translations (es, pa)
+
+3.5.5
+=====
+* convenience module has been relicensed to BSD,
+ for compatibility with GPLv3 extensions
+* alternate-tab has been refactored and seen various
+ improvements to all&thumbnails mode, including a new
+ overlaid application icon
+* updated translations (lt, id, sr)
+
+3.5.4
+=====
+* updated translations (de, es, ar, sl, lv, zh_CN)
+
+3.5.2
+=====
+* removable-drive-menu is now a11y friendly
+* the dock can now be placed on any monitor, not just the primary
+* dock is now clipped to its monitor
+* alternative-status-menu now exposes GSettings for Suspend
+ and Hibernate visibility - no UI yet
+* more gnome-shell API changes (places-menu, removable-drive-menu,
+ alternative-status-menu)
+* miscellaneous bug fixes (native-window-placement, gajim,
+ auto-move-windows)
+* updated translations
+
+3.4.0
+=====
+* build system improvements
+* updated translations (ar, cs, fr)
+
+3.3.92
+======
+* various updates for gnome-shell API changes (dock,
+ native-window-placement)
+* local-install is now a make rule, not a shell script
+* updated translations (zh, es, sw, ga, hu, it, no, pt_BR, de, sl,
+ pl, la, fi, sr)
+
+3.3.90
+======
+* system wide installation via "make install" is possible
+ again
+* alternate-tab can now pre-activate the selected window
+* auto-move-windows, workspace-indicator and example gained
+ new preference dialogs
+* workspace-indicator: fixed a bug wrt focus stealing prevention
+* updated translations (es, pt_BR, it, sl, gl, sr)
+
+3.3.5
+=====
+* improvements to the build system and convenience module,
+ making it easier for other extensions to use, and bringing
+ it up to date with gnome-shell changes
+* all extensions were ported to the Lang.Class framework
+ (except xrandr-indicator, which is pending GDBus merge)
+* alternate-tab and dock were slightly refactored to clean up
+ some old code
+
+3.3.4
+=====
+* improved styling of windowsNavigator tooltips
+* fixed windowsNavigator when used with the numeric keypad
+* fixed native-window-placement with custom button layout
+* updated translations (pt_BR, cz)
+
+3.3.3
+=====
+* windowsNavigator was fixed to work with azerty keyboards
+* drive-menu was changed to use media-eject icon instead of media-optical
+* dock: the default value of hide-effect is now move
+* dock: if autohide is disabled, now it pushes maximized windows aside
+* dock was updated to match current core shell styling
+* native-window-placement: position stategy setting was removed
+* alternative-status-menu no longer conflicts with other extensions
+ in the user menu
+* various other minor bug fixes
+* updated translations (zh, uk, es, it, cz, sl, sk, fi)
+
+3.3.2
+=====
+* all extensions are now self-contained, including l10n and settings
+* introduce a convenience module that can be shared among all extensions
+* you can know build an installable zip file with make zip-file
+* apps-menu no longer shows NoDisplay apps
+* alternative-status-menu, alternate-tab: fix for master shell
+
+3.2.1
+=====
+* dock: added "move" hide effect
+* systemMonitor: now it enables/disables properly
+* systemMonitor: improved styling
+* alternate-tab: both modes now work with gnome-shell 3.2
+* various other bug fixes
+* updated translations
+
+3.2.0
+=====
+
+* various: update for gnome-shell API changes
+
+3.1.91
+======
+
+* gajim: update for gnome-shell API changes
+
+3.1.90
+======
+
+* All extensions have been ported to the new extension
+ system (including live enable/disable)
+* Updated translations
+* xrandr-indicator no longer requires a specific gjs version
+* windowsNavigator fixed for more than 2 workspaces
+
+3.1.4
+=====
+
+* New extension: a menu for changing workspace (workspace-indicator)
+* systemMonitor: lower the requirement on libgtop
+* auto-move-windows: open overview when last window on
+ last workspace is closed
+* dock: implement autohiding, with various configurable
+ effects
+* alternate-tab: more configurable implementations available
+* native-window-placement: don't rearrange the windows when
+ the workspace switcher is shown/hidden
+* update for gnome-shell 3.1.4 API changes
+
+3.1.3
+=====
+
+* New extension: a menu for removable drives (drive-menu
+* New extensions: GNOME 2 like menus for apps and places
+ (apps-menu, places-menu)
+* New extension: additional configurability for the window
+ layout in the overview, including a mechanism similar to
+ KDE4 (native-window-placement)
+* New extension: a message tray indicator for CPU and memory
+ usage (uses libgtop) (systemMonitor)
+* user-theme: fixed resetting theme
+* user-theme: support themes installed in /usr/share/themes
+* alternative-status-menu: ported to gnome-shell master
+* dock: ported to gnome-shell master
+* dock: make position configurable (can be left or right)
+* Updated translations
+
+3.0.2
+=====
+
+* Updated translations.
+* Fixed bug #647386 (reverting of user-theme to default)
+* Fixed bug #647599 (support globally installed themes)
+* Added license and README
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/extension.js b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/extension.js
new file mode 100644
index 0000000..9501335
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/extension.js
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: 2011 John Stowers <john.stowers@gmail.com>
+// SPDX-FileCopyrightText: 2011 Giovanni Campagna <gcampagna@src.gnome.org>
+// SPDX-FileCopyrightText: 2011 Elad Alfassa <el.il@doom.co.il>
+// SPDX-FileCopyrightText: 2014 Florian Müllner <fmuellner@gnome.org>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+import Gio from 'gi://Gio';
+
+import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js';
+
+import * as Main from 'resource:///org/gnome/shell/ui/main.js';
+
+import {getThemeDirs, getModeThemeDirs} from './util.js';
+
+const SETTINGS_KEY = 'name';
+
+export default class ThemeManager extends Extension {
+ enable() {
+ this._settings = this.getSettings();
+ this._settings.connectObject(`changed::${SETTINGS_KEY}`,
+ this._changeTheme.bind(this), this);
+ this._changeTheme();
+ }
+
+ disable() {
+ this._settings?.disconnectObject();
+ this._settings = null;
+
+ Main.setThemeStylesheet(null);
+ Main.loadTheme();
+ }
+
+ _changeTheme() {
+ let stylesheet = null;
+ const themeName = this._settings.get_string(SETTINGS_KEY);
+
+ if (themeName) {
+ const stylesheetPaths = getThemeDirs()
+ .map(dir => `${dir}/${themeName}/gnome-shell/gnome-shell.css`);
+
+ stylesheetPaths.push(...getModeThemeDirs()
+ .map(dir => `${dir}/${themeName}.css`));
+
+ stylesheet = stylesheetPaths.find(path => {
+ const file = Gio.file_new_for_path(path);
+ return file.query_exists(null);
+ });
+ }
+
+ if (stylesheet)
+ log(`loading user theme: ${stylesheet}`);
+ else
+ log('loading default theme (Adwaita)');
+ Main.setThemeStylesheet(stylesheet);
+ Main.loadTheme();
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/metadata.json b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/metadata.json
new file mode 100644
index 0000000..2b14a6b
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/metadata.json
@@ -0,0 +1,17 @@
+{
+ "_generated": "Generated by SweetTooth, do not edit",
+ "description": "Load shell themes from user directory.",
+ "extension-id": "user-theme",
+ "gettext-domain": "gnome-shell-extension-user-theme",
+ "name": "User Themes",
+ "original-authors": [
+ "john.stowers@gmail.com"
+ ],
+ "settings-schema": "org.gnome.shell.extensions.user-theme",
+ "shell-version": [
+ "50"
+ ],
+ "url": "https://gitlab.gnome.org/GNOME/gnome-shell-extensions",
+ "uuid": "user-theme@gnome-shell-extensions.gcampax.github.com",
+ "version": 72
+} \ No newline at end of file
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/prefs.js b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/prefs.js
new file mode 100644
index 0000000..29f4993
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/prefs.js
@@ -0,0 +1,136 @@
+// SPDX-FileCopyrightText: 2020 Florian Müllner <fmuellner@gnome.org>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+// we use async/await here to not block the mainloop, not to parallelize
+/* eslint-disable no-await-in-loop */
+
+import Adw from 'gi://Adw';
+import Gio from 'gi://Gio';
+import GLib from 'gi://GLib';
+import GObject from 'gi://GObject';
+import Gtk from 'gi://Gtk';
+
+import {ExtensionPreferences} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
+
+import {getThemeDirs, getModeThemeDirs} from './util.js';
+
+Gio._promisify(Gio.File.prototype, 'enumerate_children_async');
+Gio._promisify(Gio.File.prototype, 'query_info_async');
+Gio._promisify(Gio.FileEnumerator.prototype, 'next_files_async');
+
+class UserThemePrefsWidget extends Adw.PreferencesGroup {
+ static {
+ GObject.registerClass(this);
+ }
+
+ constructor(settings) {
+ super({title: 'Themes'});
+
+ this._actionGroup = new Gio.SimpleActionGroup();
+ this.insert_action_group('theme', this._actionGroup);
+
+ this._settings = settings;
+ this._actionGroup.add_action(
+ this._settings.create_action('name'));
+
+ this.connect('destroy', () => (this._settings = null));
+
+ this._rows = new Map();
+ this._addTheme(''); // default
+
+ this._collectThemes();
+ }
+
+ async _collectThemes() {
+ for (const dirName of getThemeDirs()) {
+ const dir = Gio.File.new_for_path(dirName);
+ for (const name of await this._enumerateDir(dir)) {
+ if (this._rows.has(name))
+ continue;
+
+ const file = dir.resolve_relative_path(
+ `${name}/gnome-shell/gnome-shell.css`);
+ try {
+ await file.query_info_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_NAME,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT, null);
+ this._addTheme(name);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
+ logError(e);
+ }
+ }
+ }
+
+ for (const dirName of getModeThemeDirs()) {
+ const dir = Gio.File.new_for_path(dirName);
+ for (const filename of await this._enumerateDir(dir)) {
+ if (!filename.endsWith('.css'))
+ continue;
+
+ const name = filename.slice(0, -4);
+ if (!this._rows.has(name))
+ this._addTheme(name);
+ }
+ }
+ }
+
+ _addTheme(name) {
+ const row = new ThemeRow(name);
+ this._rows.set(name, row);
+
+ this.add(row);
+ }
+
+ async _enumerateDir(dir) {
+ const fileInfos = [];
+ let fileEnum;
+
+ try {
+ fileEnum = await dir.enumerate_children_async(
+ Gio.FILE_ATTRIBUTE_STANDARD_NAME,
+ Gio.FileQueryInfoFlags.NONE,
+ GLib.PRIORITY_DEFAULT, null);
+ } catch (e) {
+ if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
+ logError(e);
+ return [];
+ }
+
+ let infos;
+ do {
+ infos = await fileEnum.next_files_async(100,
+ GLib.PRIORITY_DEFAULT, null);
+ fileInfos.push(...infos);
+ } while (infos.length > 0);
+
+ return fileInfos.map(info => info.get_name());
+ }
+}
+
+class ThemeRow extends Adw.ActionRow {
+ static {
+ GObject.registerClass(this);
+ }
+
+ constructor(name) {
+ const check = new Gtk.CheckButton({
+ action_name: 'theme.name',
+ action_target: new GLib.Variant('s', name),
+ });
+
+ super({
+ title: name || 'Default',
+ activatable_widget: check,
+ });
+ this.add_prefix(check);
+ }
+}
+
+export default class UserThemePrefs extends ExtensionPreferences {
+ getPreferencesWidget() {
+ return new UserThemePrefsWidget(this.getSettings());
+ }
+}
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/gschemas.compiled b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/gschemas.compiled
new file mode 100644
index 0000000..4e8a5ca
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/gschemas.compiled
Binary files differ
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/org.gnome.shell.extensions.user-theme.gschema.xml b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/org.gnome.shell.extensions.user-theme.gschema.xml
new file mode 100644
index 0000000..cf2dc20
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/schemas/org.gnome.shell.extensions.user-theme.gschema.xml
@@ -0,0 +1,15 @@
+<!--
+SPDX-FileCopyrightText: 2016 Florian Müllner <fmuellner@gnome.org>
+
+SPDX-License-Identifier: GPL-2.0-or-later
+-->
+
+<schemalist gettext-domain="gnome-shell-extensions">
+ <schema id="org.gnome.shell.extensions.user-theme" path="/org/gnome/shell/extensions/user-theme/">
+ <key name="name" type="s">
+ <default>""</default>
+ <summary>Theme name</summary>
+ <description>The name of the theme, to be loaded from ~/.themes/name/gnome-shell</description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/util.js b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/util.js
new file mode 100644
index 0000000..e02b464
--- /dev/null
+++ b/raveos-gnome-theme/theme-data/extensions/installed/user-theme@gnome-shell-extensions.gcampax.github.com/util.js
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: 2020 Florian Müllner <fmuellner@gnome.org>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+import GLib from 'gi://GLib';
+
+const fn = (...args) => GLib.build_filenamev(args);
+
+/**
+ * @returns {string[]} - an ordered list of theme directories
+ */
+export function getThemeDirs() {
+ return [
+ fn(GLib.get_home_dir(), '.themes'),
+ fn(GLib.get_user_data_dir(), 'themes'),
+ ...GLib.get_system_data_dirs().map(dir => fn(dir, 'themes')),
+ ];
+}
+
+/**
+ * @returns {string[]} - an ordered list of mode theme directories
+ */
+export function getModeThemeDirs() {
+ return GLib.get_system_data_dirs()
+ .map(dir => fn(dir, 'gnome-shell', 'theme'));
+}