From 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 Mon Sep 17 00:00:00 2001 From: AlexanderCurl Date: Sat, 18 Apr 2026 17:46:06 +0100 Subject: Replaced file structures for themes in the correct format --- .../blur-my-shell@aunetx/components/appfolders.js | 265 +++++++++ .../components/applications.js | 614 +++++++++++++++++++++ .../components/coverflow_alt_tab.js | 91 +++ .../components/dash_to_dock.js | 421 ++++++++++++++ .../blur-my-shell@aunetx/components/lockscreen.js | 89 +++ .../blur-my-shell@aunetx/components/overview.js | 226 ++++++++ .../blur-my-shell@aunetx/components/panel.js | 577 +++++++++++++++++++ .../blur-my-shell@aunetx/components/screenshot.js | 110 ++++ .../blur-my-shell@aunetx/components/window_list.js | 148 +++++ 9 files changed, 2541 insertions(+) create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/appfolders.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/applications.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/coverflow_alt_tab.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/dash_to_dock.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/lockscreen.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/overview.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/panel.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/screenshot.js create mode 100644 raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components/window_list.js (limited to 'raveos-gnome-theme/theme-data/extensions/installed/blur-my-shell@aunetx/components') 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 -- cgit v1.3