|
| 1 | +/* |
| 2 | +* SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | +* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) |
| 4 | + */ |
| 5 | + |
| 6 | +public class Gala.BlurManager : Object { |
| 7 | + private struct BlurData { |
| 8 | + Clutter.Actor actor; |
| 9 | + BackgroundBlurEffect blur_effect; |
| 10 | + uint left; |
| 11 | + uint right; |
| 12 | + uint top; |
| 13 | + uint bottom; |
| 14 | + uint clip_radius; |
| 15 | + } |
| 16 | + |
| 17 | + private const int BLUR_RADIUS = 12; |
| 18 | + |
| 19 | + private static BlurManager instance; |
| 20 | + |
| 21 | + public static void init (WindowManagerGala wm) { |
| 22 | + if (instance != null) { |
| 23 | + return; |
| 24 | + } |
| 25 | + |
| 26 | + instance = new BlurManager (wm); |
| 27 | + } |
| 28 | + |
| 29 | + public static unowned BlurManager? get_instance () { |
| 30 | + return instance; |
| 31 | + } |
| 32 | + |
| 33 | + public WindowManagerGala wm { get; construct; } |
| 34 | + |
| 35 | + private GLib.HashTable<Meta.Window, BlurData?> blurred_windows = new GLib.HashTable<Meta.Window, BlurData?> (null, null); |
| 36 | + |
| 37 | + private BlurManager (WindowManagerGala wm) { |
| 38 | + Object (wm: wm); |
| 39 | + } |
| 40 | + |
| 41 | + construct { |
| 42 | + wm.get_display ().window_created.connect ((window) => { |
| 43 | + window.notify["mutter-hints"].connect ((obj, pspec) => parse_mutter_hints ((Meta.Window) obj)); |
| 44 | + parse_mutter_hints (window); |
| 45 | + }); |
| 46 | + |
| 47 | + unowned var monitor_manager = wm.get_display ().get_context ().get_backend ().get_monitor_manager (); |
| 48 | + monitor_manager.monitors_changed.connect (update_monitors); |
| 49 | + } |
| 50 | + |
| 51 | + /** |
| 52 | + * Blurs the given region of the given window. |
| 53 | + */ |
| 54 | + public void add_blur (Meta.Window window, uint left, uint right, uint top, uint bottom, uint clip_radius) { |
| 55 | + unowned var window_actor = (Meta.WindowActor) window.get_compositor_private (); |
| 56 | + if (window_actor == null) { |
| 57 | + critical ("Cannot blur actor: Actor is null"); |
| 58 | + return; |
| 59 | + } |
| 60 | + |
| 61 | + var monitor_scaling_factor = wm.get_display ().get_monitor_scale (window.get_monitor ()); |
| 62 | + |
| 63 | + var blur_data = blurred_windows[window]; |
| 64 | + if (blur_data == null) { |
| 65 | + var blur_effect = new BackgroundBlurEffect (BLUR_RADIUS, clip_radius, monitor_scaling_factor); |
| 66 | + |
| 67 | + var blurred_actor = new Clutter.Actor (); |
| 68 | + blurred_actor.add_effect (blur_effect); |
| 69 | + window_actor.insert_child_below (blurred_actor, null); |
| 70 | + |
| 71 | + blur_data = { blurred_actor, blur_effect, left, right, top, bottom, clip_radius }; |
| 72 | + blurred_windows[window] = blur_data; |
| 73 | + |
| 74 | + window.size_changed.connect (on_size_changed); |
| 75 | + } |
| 76 | + |
| 77 | + var buffer_rect = window.get_buffer_rect (); |
| 78 | + var frame_rect = window.get_frame_rect (); |
| 79 | + var x_shadow_size = (frame_rect.x - buffer_rect.x) / monitor_scaling_factor; |
| 80 | + var y_shadow_size = (frame_rect.y - buffer_rect.y) / monitor_scaling_factor; |
| 81 | + |
| 82 | + blur_data.actor.set_position (x_shadow_size + left, y_shadow_size + top); |
| 83 | + blur_data.actor.set_size (frame_rect.width - left - right, frame_rect.height - top - bottom); |
| 84 | + blur_data.blur_effect.monitor_scale = monitor_scaling_factor; |
| 85 | + } |
| 86 | + |
| 87 | + public void remove_blur (Meta.Window window) { |
| 88 | + var blur_data = blurred_windows[window]; |
| 89 | + if (blur_data == null) { |
| 90 | + return; |
| 91 | + } |
| 92 | + |
| 93 | + var actor = blur_data.actor; |
| 94 | + actor.remove_effect (blur_data.blur_effect); |
| 95 | + |
| 96 | + unowned var parent = actor.get_parent (); |
| 97 | + if (parent != null) { |
| 98 | + parent.remove_child (actor); |
| 99 | + } |
| 100 | + |
| 101 | + blurred_windows.remove (window); |
| 102 | + } |
| 103 | + |
| 104 | + private void on_size_changed (Meta.Window window) { |
| 105 | + var blur_data = blurred_windows[window]; |
| 106 | + if (blur_data == null) { |
| 107 | + return; |
| 108 | + } |
| 109 | + |
| 110 | + add_blur (window, blur_data.left, blur_data.right, blur_data.top, blur_data.bottom, blur_data.clip_radius); |
| 111 | + } |
| 112 | + |
| 113 | + private void update_monitors () { |
| 114 | + foreach (unowned var window in blurred_windows.get_keys ()) { |
| 115 | + var blur_data = blurred_windows[window]; |
| 116 | + |
| 117 | + var monitor_scaling_factor = window.display.get_monitor_scale (window.get_monitor ()); |
| 118 | + blur_data.blur_effect.monitor_scale = monitor_scaling_factor; |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + //X11 only |
| 123 | + private void parse_mutter_hints (Meta.Window window) { |
| 124 | + if (window.mutter_hints == null) { |
| 125 | + return; |
| 126 | + } |
| 127 | + |
| 128 | + var mutter_hints = window.mutter_hints.split (":"); |
| 129 | + foreach (var mutter_hint in mutter_hints) { |
| 130 | + var split = mutter_hint.split ("="); |
| 131 | + |
| 132 | + if (split.length != 2) { |
| 133 | + continue; |
| 134 | + } |
| 135 | + |
| 136 | + var key = split[0]; |
| 137 | + var val = split[1]; |
| 138 | + |
| 139 | + switch (key) { |
| 140 | + case "blur": |
| 141 | + var split_val = val.split (","); |
| 142 | + if (split_val.length != 5) { |
| 143 | + break; |
| 144 | + } |
| 145 | + |
| 146 | + uint parsed_left = 0, parsed_right = 0, parsed_top = 0, parsed_bottom = 0, parsed_clip_radius = 0; |
| 147 | + if ( |
| 148 | + uint.try_parse (split_val[0], out parsed_left) && |
| 149 | + uint.try_parse (split_val[1], out parsed_right) && |
| 150 | + uint.try_parse (split_val[2], out parsed_top) && |
| 151 | + uint.try_parse (split_val[3], out parsed_bottom) && |
| 152 | + uint.try_parse (split_val[4], out parsed_clip_radius) |
| 153 | + ) { |
| 154 | + add_blur (window, parsed_left, parsed_right, parsed_top, parsed_bottom, parsed_clip_radius); |
| 155 | + } else { |
| 156 | + warning ("Failed to parse %s as width and height", val); |
| 157 | + } |
| 158 | + |
| 159 | + break; |
| 160 | + default: |
| 161 | + break; |
| 162 | + } |
| 163 | + } |
| 164 | + } |
| 165 | +} |
0 commit comments