[gnome] Update extensions

This commit is contained in:
2026-04-09 09:51:44 -04:00
parent dadc78f2f4
commit 505c67d292
61 changed files with 1729 additions and 365 deletions

View File

@ -1,10 +1,58 @@
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) {
@ -15,6 +63,27 @@ export const ApplicationsBlur = class ApplicationsBlur {
// 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() {
@ -97,7 +166,7 @@ export const ApplicationsBlur = class ApplicationsBlur {
let window_actor = meta_window.get_compositor_private();
if (
!meta_window.get_workspace().active
(!meta_window.get_workspace().active) || meta_window.minimized
)
window_actor.hide();
});
@ -108,6 +177,9 @@ export const ApplicationsBlur = class ApplicationsBlur {
/// 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) => {
@ -145,11 +217,17 @@ export const ApplicationsBlur = class ApplicationsBlur {
_ => this.check_blur(meta_window)
);
// update the position and size when the window size changes
// 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(
@ -158,6 +236,18 @@ export const ApplicationsBlur = class ApplicationsBlur {
);
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.
@ -167,11 +257,39 @@ export const ApplicationsBlur = class ApplicationsBlur {
const meta_window = this.meta_window_map.get(pid);
const blur_actor = meta_window.blur_actor;
if (blur_actor) {
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;
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
@ -184,11 +302,14 @@ export const ApplicationsBlur = class ApplicationsBlur {
/// 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;
const whitelist = this.settings.applications.WHITELIST;
const blacklist = this.settings.applications.BLACKLIST;
if (window_wm_class)
this._log(`pid ${meta_window.bms_pid} associated to wm class name ${window_wm_class}`);
@ -197,8 +318,8 @@ export const ApplicationsBlur = class ApplicationsBlur {
// or if we are in whitelist mode and the window is whitelisted
if (
window_wm_class !== ""
&& ((enable_all && !blacklist.includes(window_wm_class))
|| (!enable_all && whitelist.includes(window_wm_class))
&& ((enable_all && !matchesAnyPattern(window_wm_class, this._compiled_blacklist))
|| (!enable_all && matchesAnyPattern(window_wm_class, this._compiled_whitelist))
)
&& [
Meta.FrameType.NORMAL,
@ -222,23 +343,37 @@ export const ApplicationsBlur = class ApplicationsBlur {
const pid = meta_window.bms_pid;
const window_actor = meta_window.get_compositor_private();
const pipeline = new DummyPipeline(this.effects_manager, this.settings.applications);
let [blur_actor, bg_manager] = pipeline.create_background_with_effect(
window_actor, 'bms-application-blurred-widget'
);
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;
meta_window.bg_manager = bg_manager;
// 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);
}
// make sure window is blurred in overview
if (this.settings.applications.BLUR_ON_OVERVIEW)
@ -328,8 +463,7 @@ export const ApplicationsBlur = class ApplicationsBlur {
) {
window_actor.show();
window_actor.get_last_child().hide();
}
else if (
} else if (
window_actor.visible
)
window_actor.get_last_child().show();
@ -358,18 +492,29 @@ export const ApplicationsBlur = class ApplicationsBlur {
}));
}
/// Compute the size and position for a blur actor.
/// 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_allocation(meta_window) {
const scale_monitor_framebuffer = this.mutter_gsettings.get_strv('experimental-features')
.includes('scale-monitor-framebuffer');
const is_wayland = Meta.is_wayland_compositor();
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 || Meta.is_wayland_compositor();
const monitor_index = meta_window.get_monitor();
// check if the window is using wayland, or xwayland/xorg for rendering
const scale = !scale_monitor_framebuffer && is_wayland && meta_window.get_client_type() == 0
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();
@ -382,6 +527,17 @@ export const ApplicationsBlur = class ApplicationsBlur {
};
}
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.
@ -448,4 +604,8 @@ export const ApplicationsBlur = class ApplicationsBlur {
if (this.settings.DEBUG)
console.log(`[Blur my Shell > applications] ${str}`);
}
_warn(str) {
console.warn(`[Blur my Shell > applications] ${str}`);
}
};

View File

@ -66,7 +66,7 @@ export const CoverflowAltTabBlur = class CoverflowAltTabBlur {
}
remove_background_actors() {
this.background_actors.forEach((actor) => actor.destroy);
this.background_actors.forEach((actor) => actor.destroy());
this.background_actors = [];
this.background_managers.forEach((background_manager) => {

View File

@ -16,6 +16,9 @@ const PANEL_STYLES = [
"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) {
@ -28,6 +31,11 @@ export const PanelBlur = class PanelBlur {
}
enable() {
if (this.enabled) {
this._log("blur already enabled");
return;
}
this._log("blurring top panel");
// check for panels when Dash to Panel is activated
@ -64,6 +72,11 @@ export const PanelBlur = class PanelBlur {
}
reset() {
if (!this.enabled) {
this._log("reset called but blur is not enabled");
return;
}
this._log("resetting...");
this.disable();
@ -77,6 +90,10 @@ export const PanelBlur = class PanelBlur {
// 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);
@ -92,24 +109,15 @@ export const PanelBlur = class PanelBlur {
if (!global.dashToPanel?.panels) {
return GLib.SOURCE_REMOVE;
}
this._log("Blurring Dash to Panel panels after idle.");
// blur every panel found
// blur every panel, except Main.panel (this is handled in blur_existing_panels() method above)
global.dashToPanel.panels.forEach(p => {
this.maybe_blur_panel(p.panel);
if (p.panel != Main.panel)
this.maybe_blur_panel(p.panel);
});
// if main panel is not included in the previous panels, blur it
if (
!global.dashToPanel.panels
.map(p => p.panel)
.includes(Main.panel)
&&
this.settings.dash_to_panel.BLUR_ORIGINAL_PANEL
)
this.maybe_blur_panel(Main.panel);
return GLib.SOURCE_REMOVE;
});
};
@ -377,6 +385,11 @@ export const PanelBlur = class PanelBlur {
/// 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
@ -408,7 +421,7 @@ export const PanelBlur = class PanelBlur {
/// Update the visibility of the blur effect
update_visibility() {
if (
Main.panel.has_style_pseudo_class('overview')
isMainPanelAlive && Main.panel.has_style_pseudo_class('overview')
|| !Main.sessionMode.hasWindows
) {
this.actors_list.forEach(
@ -533,6 +546,11 @@ export const PanelBlur = class PanelBlur {
}
disable() {
if (!this.enabled) {
this._log("blur already removed");
return;
}
this._log("removing blur from top panel");
this.disconnect_from_windows_and_overview();