This commit is contained in:
2024-07-08 22:46:35 +02:00
parent 02f44c49d2
commit 27254d817a
56249 changed files with 808097 additions and 1 deletions

View File

@ -0,0 +1,255 @@
import Shell from 'gi://Shell';
import Clutter from 'gi://Clutter';
import * as Main from 'resource:///org/gnome/shell/ui/main.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;
const transparent = Clutter.Color.from_pixel(0x00000000);
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: 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: 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}`);
}
};

View File

@ -0,0 +1,451 @@
import Meta from 'gi://Meta';
import Gio from 'gi://Gio';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import { ApplicationsService } from '../dbus/services.js';
import { PaintSignals } from '../conveniences/paint_signals.js';
import { DummyPipeline } from '../conveniences/dummy_pipeline.js';
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();
}
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
)
window_actor.hide();
});
}
);
}
}
/// Iterate through all existing windows and add blur as needed.
update_all_windows() {
// 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 position and size when the window size changes
this.connections.connect(
meta_window, 'size-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);
}
/// 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) {
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
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}`);
// 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 && !blacklist.includes(window_wm_class))
|| (!enable_all && whitelist.includes(window_wm_class))
)
&& [
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();
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'
);
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)
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);
}
}));
}
/// Compute the size and position for a blur actor.
/// If `scale-monitor-framebuffer` experimental feature if on, we don't need to manage scaling.
/// Else, on wayland, we need to divide by the scale to get the correct result.
compute_allocation(meta_window) {
const scale_monitor_framebuffer = this.mutter_gsettings.get_strv('experimental-features')
.includes('scale-monitor-framebuffer');
const is_wayland = Meta.is_wayland_compositor();
const monitor_index = meta_window.get_monitor();
// check if the window is using wayland, or xwayland/xorg for rendering
const scale = !scale_monitor_framebuffer && is_wayland && meta_window.get_client_type() == 0
? Main.layoutManager.monitors[monitor_index].geometry_scale
: 1;
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
};
}
/// 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}`);
}
};

View File

@ -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}`);
}
};

View File

@ -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}`);
}
};

View File

@ -0,0 +1,209 @@
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;
}
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 extension was enabled prior, as
// the functions would call themselves and cause infinite recursion
if (!this.enabled) {
// 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.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);
}
/// 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.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)
);
// make sure to absolutely not do this if the component was not enabled
// prior, as this would cause infinite recursion
if (this.enabled) {
// restore original behavior
if (this._original_PrepareSwitch)
wac_proto._prepareWorkspaceSwitch = this._original_PrepareSwitch;
if (this._original_FinishSwitch)
wac_proto._finishWorkspaceSwitch = this._original_FinishSwitch;
}
this.connections.disconnect_all();
this.enabled = false;
}
_log(str) {
if (this.settings.DEBUG)
console.log(`[Blur my Shell > overview] ${str}`);
}
_warn(str) {
console.warn(`[Blur my Shell > overview] ${str}`);
}
};

View File

@ -0,0 +1,542 @@
import St from 'gi://St';
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"
];
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() {
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();
// update the classname if the panel to have or have not light text
this.update_light_text_classname();
// connect to monitors change
this.connections.connect(Main.layoutManager, 'monitors-changed',
_ => this.reset()
);
this.enabled = true;
}
reset() {
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();
} else {
// if no dash-to-panel, blur the main and only panel
this.maybe_blur_panel(Main.panel);
}
}
blur_dtp_panels() {
// FIXME when Dash to Panel changes its size, it seems it creates new
// panels; but I can't get to delete old widgets
// blur every panel found
global.dashToPanel.panels.forEach(p => {
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);
};
/// 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 monitor = Main.layoutManager.findMonitorForActor(panel);
if (!monitor)
return;
let [width, height] = panel_box.get_size();
// if static blur, need to clip the background
if (actors.static_blur) {
// 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;
let y = p_y + p_p_y - monitor.y;
background.set_clip(x, y, width, height);
background.x = -x;
background.y = -y;
} else {
background.x = panel.x;
background.y = panel.y;
background.width = width;
background.height = 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 (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 (
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;
// if so, and if in the same monitor, then it overlaps
if (same_monitor
&&
window_vertical_pos < panel_bottom + 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_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() {
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}`);
}
};

View File

@ -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'
);
// 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}`);
}
};

View File

@ -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}`);
}
};