Skip to content

Commit

Permalink
Add blurred background (#698)
Browse files Browse the repository at this point in the history
  • Loading branch information
lenemter authored Feb 20, 2024
1 parent 85bbb72 commit 8cc1282
Show file tree
Hide file tree
Showing 19 changed files with 1,322 additions and 176 deletions.
63 changes: 63 additions & 0 deletions compositor/Background/Animation.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io)
* 2014 Tom Beckmann
*/

namespace GreeterCompositor {
public class Animation : Object {
public string filename { get; construct; }
public string[] key_frame_files { get; private set; default = {}; }
public double transition_progress { get; private set; default = 0.0; }
public double transition_duration { get; private set; default = 0.0; }
public bool loaded { get; private set; default = false; }

private Gnome.BGSlideShow? show = null;

public Animation (string filename) {
Object (filename: filename);
}

public async void load () {
show = new Gnome.BGSlideShow (filename);

show.load_async (null, (obj, res) => {
loaded = true;

load.callback ();
});

yield;
}

#if HAS_MUTTER45
public void update (Mtk.Rectangle monitor) {
#else
public void update (Meta.Rectangle monitor) {
#endif
string[] key_frame_files = {};

if (show == null)
return;

if (show.get_num_slides () < 1)
return;

double progress, duration;
bool is_fixed;
string file1, file2;
show.get_current_slide (monitor.width, monitor.height, out progress, out duration, out is_fixed, out file1, out file2);

transition_duration = duration;
transition_progress = progress;

if (file1 != null)
key_frame_files += file1;

if (file2 != null)
key_frame_files += file2;

this.key_frame_files = key_frame_files;
}
}
}
240 changes: 240 additions & 0 deletions compositor/Background/Background.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io)
* 2014 Tom Beckmann
*/

namespace GreeterCompositor {
public class Background : Object {
private const double ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
private const double ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;

public signal void changed ();
public signal void loaded ();

public Meta.Display display { get; construct; }
public int monitor_index { get; construct; }
public BackgroundSource background_source { get; construct; }
public bool is_loaded { get; private set; default = false; }
public GDesktop.BackgroundStyle style { get; construct; }
public string? filename { get; construct; }
public Meta.Background background { get; private set; }

private Animation? animation = null;
private Gee.HashMap<string,ulong> file_watches;
private Cancellable cancellable;
private uint update_animation_timeout_id = 0;

public Background (Meta.Display display, int monitor_index, string? filename,
BackgroundSource background_source, GDesktop.BackgroundStyle style) {
Object (display: display,
monitor_index: monitor_index,
background_source: background_source,
style: style,
filename: filename);
}

construct {
background = new Meta.Background (display);
background.set_data<unowned Background> ("delegate", this);

file_watches = new Gee.HashMap<string,ulong> ();
cancellable = new Cancellable ();

background_source.changed.connect (settings_changed);

load ();
}

public void destroy () {
cancellable.cancel ();
remove_animation_timeout ();

var cache = BackgroundCache.get_default ();

foreach (var watch in file_watches.values) {
cache.disconnect (watch);
}

background_source.changed.disconnect (settings_changed);
}

public void update_resolution () {
if (animation != null) {
remove_animation_timeout ();
update_animation ();
}
}

private void set_loaded () {
if (is_loaded)
return;

is_loaded = true;

Idle.add (() => {
loaded ();
return Source.REMOVE;
});
}

private void load_pattern () {
string color_string;
var settings = background_source.gnome_background_settings;

color_string = settings.get_string ("primary-color");
var color = Clutter.Color.from_string (color_string);

var shading_type = settings.get_enum ("color-shading-type");

if (shading_type == GDesktop.BackgroundShading.SOLID) {
background.set_color (color);
} else {
color_string = settings.get_string ("secondary-color");
var second_color = Clutter.Color.from_string (color_string);
background.set_gradient ((GDesktop.BackgroundShading) shading_type, color, second_color);
}
}

private void watch_file (string filename) {
if (file_watches.has_key (filename))
return;

var cache = BackgroundCache.get_default ();

cache.monitor_file (filename);

file_watches[filename] = cache.file_changed.connect ((changed_file) => {
if (changed_file == filename) {
var image_cache = Meta.BackgroundImageCache.get_default ();
image_cache.purge (File.new_for_path (changed_file));
changed ();
}
});
}

private void remove_animation_timeout () {
if (update_animation_timeout_id != 0) {
Source.remove (update_animation_timeout_id);
update_animation_timeout_id = 0;
}
}

private void finish_animation (string[] files) {
set_loaded ();

if (files.length > 1)
background.set_blend (File.new_for_path (files[0]), File.new_for_path (files[1]), animation.transition_progress, style);
else if (files.length > 0)
background.set_file (File.new_for_path (files[0]), style);
else
background.set_file (null, style);

queue_update_animation ();
}

private void update_animation () {
update_animation_timeout_id = 0;

animation.update (display.get_monitor_geometry (monitor_index));
var files = animation.key_frame_files;

var cache = Meta.BackgroundImageCache.get_default ();
var num_pending_images = files.length;
for (var i = 0; i < files.length; i++) {
watch_file (files[i]);

var image = cache.load (File.new_for_path (files[i]));

if (image.is_loaded ()) {
num_pending_images--;
if (num_pending_images == 0) {
finish_animation (files);
}
} else {
ulong handler = 0;
handler = image.loaded.connect (() => {
image.disconnect (handler);
if (--num_pending_images == 0) {
finish_animation (files);
}
});
}
}
}

private void queue_update_animation () {
if (update_animation_timeout_id != 0)
return;

if (cancellable == null || cancellable.is_cancelled ())
return;

if (animation.transition_duration == 0)
return;

var n_steps = 255.0 / ANIMATION_OPACITY_STEP_INCREMENT;
var time_per_step = (animation.transition_duration * 1000) / n_steps;

var interval = (uint32) Math.fmax (ANIMATION_MIN_WAKEUP_INTERVAL * 1000, time_per_step);

if (interval > uint32.MAX)
return;

update_animation_timeout_id = Timeout.add (interval, () => {
update_animation_timeout_id = 0;
update_animation ();
return Source.REMOVE;
});
}

private async void load_animation (string filename) {
animation = yield BackgroundCache.get_default ().get_animation (filename);

if (animation == null || cancellable.is_cancelled ()) {
set_loaded ();
return;
}

update_animation ();
watch_file (filename);
}

private void load_image (string filename) {
background.set_file (File.new_for_path (filename), style);
watch_file (filename);

var cache = Meta.BackgroundImageCache.get_default ();
var image = cache.load (File.new_for_path (filename));
if (image.is_loaded ())
set_loaded ();
else {
ulong handler = 0;
handler = image.loaded.connect (() => {
set_loaded ();
image.disconnect (handler);
});
}
}

private void load_file (string filename) {
if (filename.has_suffix (".xml"))
load_animation.begin (filename);
else
load_image (filename);
}

private void load () {
load_pattern ();

if (filename == null)
set_loaded ();
else
load_file (filename);
}

private void settings_changed () {
changed ();
}
}
}
90 changes: 90 additions & 0 deletions compositor/Background/BackgroundCache.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2024 elementary, Inc. (https://elementary.io)
* 2014 Tom Beckmann
*/

namespace GreeterCompositor {
public class BackgroundCache : Object {
private static BackgroundCache? instance = null;

public static unowned BackgroundCache get_default () {
if (instance == null)
instance = new BackgroundCache ();

return instance;
}

public signal void file_changed (string filename);

private Gee.HashMap<string,FileMonitor> file_monitors;
private BackgroundSource background_source;

private Animation animation;

public BackgroundCache () {
Object ();
}

construct {
file_monitors = new Gee.HashMap<string,FileMonitor> ();
}

public void monitor_file (string filename) {
if (file_monitors.has_key (filename))
return;

var file = File.new_for_path (filename);
try {
var monitor = file.monitor (FileMonitorFlags.NONE, null);
monitor.changed.connect (() => {
file_changed (filename);
});

file_monitors[filename] = monitor;
} catch (Error e) {
warning ("Failed to monitor %s: %s", filename, e.message);
}
}

public async Animation get_animation (string filename) {
if (animation != null && animation.filename == filename) {
Idle.add (() => {
get_animation.callback ();
return Source.REMOVE;
});
yield;

return animation;
}

var animation = new Animation (filename);

yield animation.load ();

Idle.add (() => {
get_animation.callback ();
return Source.REMOVE;
});
yield;

return animation;
}

public BackgroundSource get_background_source (Meta.Display display) {
if (background_source == null) {
background_source = new BackgroundSource (display);
background_source.use_count = 1;
} else
background_source.use_count++;

return background_source;
}

public void release_background_source () {
if (--background_source.use_count == 0) {
background_source.destroy ();
}
}
}
}
Loading

0 comments on commit 8cc1282

Please sign in to comment.