summaryrefslogtreecommitdiff
path: root/raveos-theme/gnome/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
diff options
context:
space:
mode:
authornippy <you@example.com>2026-04-18 13:49:56 +0200
committernippy <you@example.com>2026-04-18 13:49:56 +0200
commit5d94c0a7d44a2255b81815a52a7056a94a39842d (patch)
tree759bdea9645c6a62f9e1e4c001f7d81cccd120d2 /raveos-theme/gnome/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
parente79cdf210b267f21a186255ce1a4d50029439d54 (diff)
downloadRaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.tar.gz
RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.zip
update Raveos themes
Diffstat (limited to 'raveos-theme/gnome/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js')
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js614
1 files changed, 614 insertions, 0 deletions
diff --git a/raveos-theme/gnome/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js b/raveos-theme/gnome/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js
new file mode 100644
index 0000000..e783046
--- /dev/null
+++ b/raveos-theme/gnome/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}`);
+ }
+};