Oreon-Lime-R2/blur-my-shell/blur-my-shell-29/applications.js

274 lines
10 KiB
JavaScript
Raw Normal View History

'use strict';
const { Shell, Clutter, Meta, GLib } = imports.gi;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Settings = Me.imports.settings;
const Utils = Me.imports.utilities;
const PaintSignals = Me.imports.paint_signals;
const BlurMode = Shell.BlurMode;
var ApplicationsBlur = class ApplicationsBlur {
constructor(connections, prefs) {
this.connections = connections;
this.prefs = prefs;
this.paint_signals = new PaintSignals.PaintSignals(connections);
this.windowMap = new Map();
this.blurActorMap = new Map();
}
enable() {
this._log("blurring applications...");
// iterate through existing windows and add blur as needed
for (let workspace = 0; workspace < global.workspace_manager.get_n_workspaces(); ++workspace) {
let metaWorkspace = global.workspace_manager.get_workspace_by_index(workspace);
let windows = metaWorkspace.list_windows();
windows.forEach((meta_window) => {
let window_actor = meta_window.get_compositor_private();
this.track_new(window_actor, meta_window);
})
}
// blur every new window
this.connections.connect(global.display, 'window-created', (meta_display, meta_window) => {
this._log("window created");
if (meta_window) {
let window_actor = meta_window.get_compositor_private();
this.track_new(window_actor, meta_window);
}
});
// workaround for blur being in front of target windows
this.connections.connect(global.display, 'notify::focus-window', () => {
if (this.blurActorMap.size > 0) {
let callbackId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
this.blurActorMap.forEach((blurActor, pid) => {
let actor = this.windowMap.get(pid).get_compositor_private();
if (actor !== null && actor.get_parent() === blurActor.get_parent()) {
global.window_group.set_child_below_sibling(blurActor, actor);
}
})
})
}
})
}
// Blurs and add the needed signals to every new tracked window
track_new(window_actor, meta_window) {
// TODO change the pid to a more unique one, to prevent collisions?
let pid = Date.now();
window_actor['blur_provider_pid'] = pid;
meta_window['blur_provider_pid'] = pid;
// remove the blur when the window is destroyed
this.connections.connect(window_actor, 'destroy', (window_actor) => {
let pid = window_actor.blur_provider_pid;
if (this.blurActorMap.has(pid)) {
this.remove_blur(pid)
}
});
// update the blur when the mutter-hint is changed
this.connections.connect(meta_window, 'notify::mutter-hints', (meta_window) => {
this._log("mutter-hint changed");
let pid = meta_window.blur_provider_pid;
let window_actor = meta_window.get_compositor_private();
this.update_blur(window_actor, meta_window, pid)
});
this.update_blur(window_actor, meta_window, pid);
}
// This method is basically a catch-all for blurring windows. It handles the decisions
// to call different methods that handle removing, setting, and updating blur
update_blur(window_actor, meta_window, pid) {
let mutter_hint = meta_window.get_mutter_hints();
// check if the window wants blur and update it accordingly
if (mutter_hint != null && mutter_hint.includes("blur-provider")) {
// get blur effect parameters
let sigma = this.parse_sigma_value(mutter_hint);
let brightness = this.prefs.BRIGHTNESS.get();
// if the provided sigma value is incorrect
if (sigma == null || sigma < 0 || sigma > 111) {
this._log("sigma value is null or outside of range (0-111), defaulting to extension setting")
sigma = this.prefs.SIGMA.get();
}
// check wether the actor is already blurred and act consequently
if (this.blurActorMap.has(pid)) {
if (sigma == 0) {
// actor already blurred, should not be anymore
this.remove_blur(pid);
} else {
// actor already blurred, update it
this.update_blur_effect(
this.blurActorMap.get(pid),
sigma,
brightness
);
}
} else if (sigma != 0) {
// actor not blurred, blur it
this.set_blur(
pid,
window_actor,
meta_window,
this.create_blur_effect(sigma, brightness)
)
}
} else if (this.blurActorMap.has(pid)) {
// remove blur if the mutter_hint no longer contains our blur-provider value
this.remove_blur(pid);
}
}
// Returns a new blur effect
create_blur_effect(sigma, brightness) {
return new Shell.BlurEffect({
sigma: sigma,
brightness: brightness,
mode: BlurMode.BACKGROUND
});
}
// Add the blur effect to the actor
set_blur(pid, window_actor, meta_window, blurEffect) {
let blurActor = this.create_blur_actor(meta_window, window_actor, blurEffect);
// if hacks are selected, force the repaint the window to prevent artifacts
if (this.prefs.HACKS_LEVEL.get() >= 1) {
this._log("applications hack level 1 or 2");
this.paint_signals.disconnect_all();
this.paint_signals.connect(blurActor, blurEffect);
} else {
this.paint_signals.disconnect_all();
}
global.window_group.insert_child_below(blurActor, window_actor);
// registed the blur actor/effect
blurActor['blur_provider_pid'] = pid;
this.blurActorMap.set(pid, blurActor);
this.windowMap.set(pid, meta_window);
// hide if window become invisible
this.connections.connect(window_actor, 'notify::visible', (window_actor) => {
let pid = window_actor.blur_provider_pid;
if (window_actor.visible) {
this.blurActorMap.get(pid).show();
} else {
this.blurActorMap.get(pid).hide();
}
});
}
// Returns a new already blurred widget, configured to follow the size and
// position of its target window
create_blur_actor(meta_window, window_actor, blurEffect) {
// create the constraints in size and position to its target window
let frame = meta_window.get_frame_rect();
let buffer = meta_window.get_buffer_rect();
const offsetX = frame.x - buffer.x;
const offsetY = frame.y - buffer.y;
const offsetWidth = buffer.width - frame.width;
const offsetHeight = buffer.height - frame.height;
let constraintPosX = new Clutter.BindConstraint({
source: window_actor,
coordinate: Clutter.BindCoordinate.X,
offset: offsetX
});
let constraintPosY = new Clutter.BindConstraint({
source: window_actor,
coordinate: Clutter.BindCoordinate.Y,
offset: offsetY
});
let constraintSizeX = new Clutter.BindConstraint({
source: window_actor,
coordinate: Clutter.BindCoordinate.WIDTH,
offset: -offsetWidth
});
let constraintSizeY = new Clutter.BindConstraint({
source: window_actor,
coordinate: Clutter.BindCoordinate.HEIGHT,
offset: -offsetHeight
});
// create the actor and add the constraints
let blurActor = new Clutter.Actor();
blurActor.add_constraint(constraintPosX);
blurActor.add_constraint(constraintPosY);
blurActor.add_constraint(constraintSizeX);
blurActor.add_constraint(constraintSizeY);
// add the effect
blurActor.add_effect_with_name('blur-effect', blurEffect);
return blurActor;
}
// Updates the blur effect by overwriting its sigma and brightness values
update_blur_effect(blur_actor, sigma, brightness) {
let effect = blur_actor.get_effect('blur-effect');
effect.sigma = sigma;
effect.brightness = brightness;
}
// Removes the blur actor from the shell and unregister it
remove_blur(pid) {
// global.window_group is null when restarting the shell, causing an innocent crash crash
if (global.window_group == null)
return
global.window_group.remove_actor(this.blurActorMap.get(pid));
this.blurActorMap.delete(pid);
this.windowMap.delete(pid);
}
// When given the xprop property, returns either a valid sigma value between 0 and 111
// or null if the parsing is incorrect
parse_sigma_value(property) {
let result = property.match("(blur-provider=)(\\d{1,3})")
if (result == null) {
return null;
} else {
return result[2];
}
}
disable() {
this._log("removing blur from applications...");
this.blurActorMap.forEach(((value, key) => {
this.remove_blur(key)
}));
this.connections.disconnect_all();
this.paint_signals.disconnect_all();
}
// Updates each blur effect to use new sigma value
set_sigma(s) {
this.blurActorMap.forEach((actor, _) => {
actor.get_effect('blur-effect').set_sigma(s)
})
}
// Updates each blur effect to use new brightness value
set_brightness(b) {
this.blurActorMap.forEach((actor, _) => {
actor.get_effect('blur-effect').set_brightness(b)
})
}
_log(str) {
if (this.prefs.DEBUG.get())
log(`[Blur my Shell] ${str}`)
}
}