rewrite background system

This commit is contained in:
Tom Beckmann 2013-11-16 12:19:29 +01:00
parent 162890aae4
commit be906a98cd
8 changed files with 311 additions and 801 deletions

View File

@ -77,7 +77,6 @@ vala_precompile(VALA_C
src/TextShadowEffect.vala
src/Utils.vala
src/Zooming.vala
src/Background/Animation.vala
src/Background/Background.vala
src/Background/BackgroundCache.vala
src/Background/BackgroundManager.vala

View File

@ -1,76 +0,0 @@
//
// Copyright (C) 2013 Tom Beckmann, Rico Tzschichholz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
namespace Gala
{
public class Animation : Object
{
public string filename { get; construct set; }
public Meta.Screen screen { get; construct set; }
public Gee.LinkedList<string> key_frame_files { get; private set; }
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; }
Gnome.BGSlideShow? show = null;
public Animation (Meta.Screen screen, string filename)
{
Object (filename: filename, screen: screen);
key_frame_files = new Gee.LinkedList<string> ();
}
public async void load ()
{
show = new Gnome.BGSlideShow (filename);
//FIXME yield show.load_async (null);
show.load ();
loaded = true;
}
public void update (int monitor_index)
{
key_frame_files = new Gee.LinkedList<string> ();
if (show == null)
return;
if (show.get_num_slides () < 1)
return;
var monitor = screen.get_monitor_geometry (monitor_index);
bool is_fixed;
string file1, file2;
double progress, duration;
show.get_current_slide (monitor.width, monitor.height, out progress,
out duration, out is_fixed, out file1, out file2);
transition_progress = progress;
transition_duration = duration;
if (file1 != null)
key_frame_files.add (file1);
if (file2 != null)
key_frame_files.add (file2);
}
}
}

View File

@ -1,312 +1,255 @@
//
// Copyright (C) 2013 Tom Beckmann, Rico Tzschichholz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Code has been ported from gnome-shell's js/ui/background.js
namespace Gala
{
public class Background : Object
/**
* Group that holds a pattern at the very bottom and then an image showing the
* current wallpaper above (and one more additional image for transitions).
* It listens to changes on the provided settings object and updates accordingly.
*/
public class Background : Meta.BackgroundGroup
{
const string BACKGROUND_SCHEMA = "org.gnome.desktop.background";
const string PRIMARY_COLOR_KEY = "primary-color";
const string SECONDARY_COLOR_KEY = "secondary-color";
const string COLOR_SHADING_TYPE_KEY = "color-shading-type";
const string BACKGROUND_STYLE_KEY = "picture-options";
const string PICTURE_OPACITY_KEY = "picture-opacity";
const string PICTURE_URI_KEY = "picture-uri";
Meta.BackgroundActor pattern;
Meta.BackgroundActor? image = null;
const uint FADE_ANIMATION_TIME = 1000;
const uint ANIMATION_TRANSITION_DURATION = 1500;
// These parameters affect how often we redraw.
// The first is how different (percent crossfaded) the slide show
// has to look before redrawing and the second is the minimum
// frequency (in seconds) we're willing to wake up
public Meta.Screen screen { get; construct set; }
public int monitor { get; construct set; }
public Settings settings { get; construct set; }
Gnome.BGSlideShow? animation = null;
Meta.BackgroundActor? second_image = null;
double animation_duration = 0.0;
double animation_progress = 0.0;
uint update_animation_timeout_id;
const double ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
const double ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
public Meta.BackgroundEffects effects { get; construct set; }
public Settings settings { get; construct set; }
public int monitor_index { get; construct set; }
public Meta.BackgroundGroup actor { get; private set; }
public bool is_loaded { get; private set; }
// those two are set by the BackgroundManager
internal ulong change_signal_id = 0;
internal ulong loaded_signal_id = 0;
float _brightness;
public float brightness {
get {
return _brightness;
}
set {
_brightness = value;
if (pattern != null && pattern.content != null)
(pattern.content as Meta.Background).brightness = value;
foreach (var image in images) {
if (image != null && image.content != null)
(image.content as Meta.Background).brightness = brightness;
}
}
}
float _vignette_sharpness;
public float vignette_sharpness {
get {
return _vignette_sharpness;
}
set {
_vignette_sharpness = value;
if (pattern != null && pattern.content != null)
(pattern.content as Meta.Background).vignette_sharpness = value;
foreach (var image in images) {
if (image != null && image.content != null)
(image.content as Meta.Background).vignette_sharpness = vignette_sharpness;
}
}
}
GDesktop.BackgroundStyle style;
Meta.BackgroundActor? pattern;
BackgroundCache cache;
Animation animation;
Cancellable? cancellable = null;
uint update_animation_timeout_id = 0;
Meta.BackgroundActor images[2];
Gee.HashMap<string,ulong> file_watches;
string filename;
uint num_pending_images;
public signal void changed ();
public signal void loaded ();
public Background (int monitor_index, Meta.BackgroundEffects effects, Settings settings)
public Background (Meta.Screen screen, int monitor, Settings settings)
{
Object (monitor_index: monitor_index, effects: effects, settings: settings);
actor = new Meta.BackgroundGroup ();
file_watches = new Gee.HashMap<string,ulong> ();
pattern = null;
// contains a single image for static backgrounds and
// two images (from and to) for slide shows
images = { null, null };
brightness = 1.0f;
vignette_sharpness = 0.2f;
cancellable = new Cancellable ();
is_loaded = false;
settings.changed.connect (() => {
changed ();
});
load ();
actor.destroy.connect (destroy);
}
public void destroy ()
{
if (cancellable != null)
cancellable.cancel ();
if (update_animation_timeout_id != 0) {
Source.remove (update_animation_timeout_id);
update_animation_timeout_id = 0;
}
foreach (var key in file_watches.keys) {
cache.disconnect (file_watches.get (key));
}
file_watches = null;
if (pattern != null) {
if (pattern.content != null)
cache.remove_pattern_content (pattern.content as Meta.Background);
pattern.destroy ();
pattern = null;
}
foreach (var image in images) {
if (image == null)
continue;
if (image.content != null)
cache.remove_image_content (image.content as Meta.Background);
image.destroy ();
}
}
public void set_loaded ()
{
if (is_loaded)
return;
is_loaded = true;
Idle.add (() => {
loaded ();
return false;
});
}
public void load_pattern ()
{
var color = Clutter.Color.from_string (settings.get_string (PRIMARY_COLOR_KEY));
var second_color = Clutter.Color.from_string (settings.get_string (SECONDARY_COLOR_KEY));
var shading_type = (GDesktop.BackgroundShading)settings.get_enum (COLOR_SHADING_TYPE_KEY);
var content = cache.get_pattern_content (monitor_index, color, second_color, shading_type, effects);
Object (screen: screen, monitor: monitor, settings: settings);
pattern = new Meta.BackgroundActor ();
actor.add_child (pattern);
pattern.add_constraint (new Clutter.BindConstraint (this, Clutter.BindCoordinate.ALL, 0));
add_child (pattern);
pattern.content = content;
load (null);
settings.changed.connect (load);
}
public void watch_cache_file (string filename)
/**
* (Re)loads all components if key_changed is null or only the key_changed component
*/
void load (string? key_changed)
{
if (file_watches.has_key (filename))
var all = key_changed == null;
var cache = BackgroundCache.get_default ();
// update images
if (all || key_changed == "picture-uri" || key_changed == "picture-options") {
var file = File.new_for_commandline_arg (settings.get_string ("picture-uri")).get_path ();
var style = style_string_to_enum (settings.get_string ("picture-options"));
// no image at all
if (style == GDesktop.BackgroundStyle.NONE) {
if (image != null) {
image.destroy ();
image = null;
}
if (second_image != null) {
second_image.destroy ();
second_image = null;
}
animation = null;
// animation
} else if (file.has_suffix (".xml")) {
animation = new Gnome.BGSlideShow (file);
try {
if (animation.load ()) {
update_animation ();
}
} catch (Error e) {
warning (e.message);
}
// normal wallpaper
} else {
animation = null;
if (second_image != null) {
second_image.destroy ();
second_image = null;
}
cache.load_image.begin (file, monitor, style, (obj, res) => {
var content = cache.load_image.end (res);
if (content != null) {
set_image (content);
// if loading failed, destroy our image and show the pattern
} else if (image != null) {
image.destroy ();
image = null;
}
});
}
}
// update image opacity
if (all || key_changed == "picture-opacity") {
if (image != null)
image.opacity = (uint8)(settings.get_int ("picture-opacity") / 100.0 * 255);
}
// update pattern
if (all
|| key_changed == "primary-color"
|| key_changed == "secondary-color"
|| key_changed == "color-shading-type") {
var primary_color = Clutter.Color.from_string (settings.get_string ("primary-color"));
var secondary_color = Clutter.Color.from_string (settings.get_string ("secondary-color"));
var shading_type = shading_string_to_enum (settings.get_string ("color-shading-type"));
pattern.content = cache.load_pattern (monitor, primary_color, secondary_color, shading_type);
}
}
void set_image (Meta.Background content)
{
var new_image = new Meta.BackgroundActor ();
new_image.add_constraint (new Clutter.BindConstraint (this, Clutter.BindCoordinate.ALL, 0));
new_image.content = content;
new_image.opacity = 0;
insert_child_above (new_image, null);
var dest_opacity = (uint8)(settings.get_int ("picture-opacity") / 100.0 * 255);
new_image.animate (Clutter.AnimationMode.EASE_OUT_QUAD, ANIMATION_TRANSITION_DURATION,
opacity: dest_opacity).completed.connect (() => {
if (image != null)
image.destroy ();
image = new_image;
});
}
/**
* translates the string returned from gsettings for the color-shading-type key to the
* appropriate GDesktop.BackgroundShading enum value
*/
GDesktop.BackgroundShading shading_string_to_enum (string shading)
{
switch (shading) {
case "horizontal":
return GDesktop.BackgroundShading.HORIZONTAL;
case "vertical":
return GDesktop.BackgroundShading.VERTICAL;
}
return GDesktop.BackgroundShading.SOLID;
}
/**
* translates the string returned from gsettings for the picture-options key to the
* appropriate GDesktop.BackgroundStyle enum value
*/
GDesktop.BackgroundStyle style_string_to_enum (string style)
{
switch (style) {
case "wallpaper":
return GDesktop.BackgroundStyle.WALLPAPER;
case "centered":
return GDesktop.BackgroundStyle.CENTERED;
case "scaled":
return GDesktop.BackgroundStyle.SCALED;
case "stretched":
return GDesktop.BackgroundStyle.STRETCHED;
case "zoom":
return GDesktop.BackgroundStyle.ZOOM;
case "spanned":
return GDesktop.BackgroundStyle.SPANNED;
}
return GDesktop.BackgroundStyle.NONE;
}
/**
* SlideShow animation related functions
*/
void update_animation ()
{
if (animation == null)
return;
var signal_id = cache.file_changed.connect ((changed_file) => {
if (changed_file == filename) {
changed ();
}
});
file_watches.set (filename, signal_id);
}
public void add_image (Meta.Background content, int index, string filename) {
content.brightness = brightness;
content.vignette_sharpness = vignette_sharpness;
var actor = new Meta.BackgroundActor ();
actor.content = content;
// The background pattern is the first actor in
// the group, and all images should be above that.
this.actor.insert_child_at_index (actor, index + 1);
images[index] = actor;
watch_cache_file (filename);
}
public void update_image (Meta.Background content, int index, string filename) {
content.brightness = brightness;
content.vignette_sharpness = vignette_sharpness;
cache.remove_image_content (images[index].content as Meta.Background);
images[index].content = content;
watch_cache_file (filename);
}
public void update_animation_progress ()
{
if (images[1] != null)
images[1].opacity = (uint)(animation.transition_progress * 255);
queue_update_animation();
}
public void update_animation ()
{
update_animation_timeout_id = 0;
animation.update (monitor_index);
var files = animation.key_frame_files;
var geom = screen.get_monitor_geometry (monitor);
if (files.size == 0) {
set_loaded ();
bool is_fixed;
string file_from, file_to;
double progress, duration;
animation.get_current_slide (geom.width, geom.height, out progress,
out duration, out is_fixed, out file_from, out file_to);
animation_duration = duration;
animation_progress = progress;
print ("Animation Update\nfrom: %s to: %s, progress: %f, duration: %f\n", file_from, file_to, progress, duration);
if (file_from == null && file_to == null) {
queue_update_animation ();
return;
}
num_pending_images = files.size;
for (var i = 0; i < files.size; i++) {
var image = images[i];
if (image != null && image.content != null &&
(image.content as Meta.Background).get_filename () == files.get (i)) {
num_pending_images--;
if (num_pending_images == 0)
update_animation_progress ();
continue;
}
cache.get_image_content (monitor_index, style, files[i], effects,
this, get_update_animation_callback (i), cancellable);
if (image == null || image.content == null
|| (image.content as Meta.Background).get_filename () != file_from) {
image = update_image (image, file_from, false);
}
if (second_image == null || second_image.content == null
|| (second_image.content as Meta.Background).get_filename () != file_to) {
second_image = update_image (second_image, file_to, true);
}
update_animation_progress ();
}
// FIXME wrap callback method to keep the i at the correct value, I suppose
// we should find a nicer way to do this
PendingFileLoadFinished get_update_animation_callback (int i) {
return (userdata, content) => {
var self = userdata as Background;
self.num_pending_images--;
if (content == null) {
self.set_loaded ();
if (self.num_pending_images == 0)
self.update_animation_progress ();
return;
}
if (self.images[i] == null) {
self.add_image (content, i, self.animation.key_frame_files.get (i));
} else {
self.update_image (content, i, self.animation.key_frame_files.get (i));
}
if (self.num_pending_images == 0) {
self.set_loaded ();
self.update_animation_progress ();
}
};
}
public void queue_update_animation ()
/**
* Returns the passed orig_image with the correct content or a new one if orig_image was null
*/
Meta.BackgroundActor? update_image (Meta.BackgroundActor? orig_image, string? file, bool topmost)
{
if (update_animation_timeout_id != 0)
return;
Meta.BackgroundActor image = null;
if (cancellable == null || cancellable.is_cancelled ())
return;
if (orig_image != null)
image = orig_image;
if (animation.transition_duration == 0.0)
if (file == null) {
if (image != null) {
image.destroy ();
image = null;
}
return null;
}
if (image == null) {
image = new Meta.BackgroundActor ();
image.add_constraint (new Clutter.BindConstraint (this, Clutter.BindCoordinate.ALL, 0));
if (topmost)
insert_child_above (image, null);
else
insert_child_above (image, pattern);
}
var cache = BackgroundCache.get_default ();
var style = style_string_to_enum (settings.get_string ("picture-options"));
cache.load_image.begin (file, monitor, style, (obj, res) => {
image.content = cache.load_image.end (res);
});
return image;
}
void queue_update_animation ()
{
if (update_animation_timeout_id != 0 || animation_duration == 0.0)
return;
var n_steps = 255 / ANIMATION_OPACITY_STEP_INCREMENT;
var time_per_step = (uint)((animation.transition_duration * 1000) / n_steps);
var time_per_step = (uint)((animation_duration * 1000) / n_steps);
var interval = uint.max ((uint)(ANIMATION_MIN_WAKEUP_INTERVAL * 1000), time_per_step);
if (interval > uint.MAX)
@ -319,62 +262,12 @@ namespace Gala
});
}
public void load_animation (string filename)
void update_animation_progress ()
{
cache.get_animation.begin (filename, (obj, res) => {
animation = cache.get_animation.end (res);
if (second_image != null)
second_image.opacity = (uint)(animation_progress * 255);
if (animation == null || cancellable.is_cancelled ()) {
set_loaded ();
return;
}
update_animation ();
watch_cache_file (filename);
});
}
public void load_file (string filename)
{
this.filename = filename;
cache.get_image_content (monitor_index, style, filename, effects, this, (userdata, content) => {
var self = userdata as Background;
if (content == null) {
if (!self.cancellable.is_cancelled ())
self.load_animation (self.filename);
return;
}
self.add_image (content, 0, self.filename);
self.set_loaded ();
}, cancellable);
}
public void load ()
{
cache = BackgroundCache.get_default ();
load_pattern ();
style = (GDesktop.BackgroundStyle)settings.get_enum (BACKGROUND_STYLE_KEY);
if (style == GDesktop.BackgroundStyle.NONE) {
set_loaded ();
return;
}
var uri = settings.get_string (PICTURE_URI_KEY);
string filename;
if (Uri.parse_scheme (uri) != null)
filename = File.new_for_uri (uri).get_path ();
else
filename = uri;
if (filename == null) {
set_loaded ();
return;
}
load_file (filename);
queue_update_animation ();
}
}
}

View File

@ -1,281 +1,101 @@
//
// Copyright (C) 2013 Tom Beckmann, Rico Tzschichholz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
/* TODO
- GNOME monitors the images and removes them from cache when they change
so they reload. Do we need that?
*/
namespace Gala
{
public delegate void PendingFileLoadFinished (Object userdata, Meta.Background? content);
struct PendingFileLoad
{
string filename;
GDesktop.BackgroundStyle style;
Gee.LinkedList<PendingFileLoadCaller?> callers;
}
struct PendingFileLoadCaller
{
bool should_copy;
int monitor_index;
Meta.BackgroundEffects effects;
PendingFileLoadFinished on_finished;
Object userdata;
}
public class BackgroundCache : Object
{
static BackgroundCache? instance = null;
public Meta.Screen screen { get; construct set; }
Meta.Screen screen;
Gee.LinkedList<Meta.Background> patterns;
Gee.LinkedList<Meta.Background> images;
Gee.LinkedList<PendingFileLoad?> pending_file_loads;
Gee.HashMap<string,FileMonitor> file_monitors;
string animation_filename;
Animation animation;
public signal void file_changed (string filename);
public BackgroundCache (Meta.Screen _screen)
struct WaitingCallback
{
screen = _screen;
patterns = new Gee.LinkedList<Meta.Background> ();
images = new Gee.LinkedList<Meta.Background> ();
pending_file_loads = new Gee.LinkedList<PendingFileLoad?> ();
file_monitors = new Gee.HashMap<string,FileMonitor> ();
SourceFunc func;
string hash;
}
public Meta.Background get_pattern_content (int monitor_index, Clutter.Color color,
Clutter.Color second_color, GDesktop.BackgroundShading shading_type, Meta.BackgroundEffects effects)
Gee.HashMap<string,Meta.Background> image_cache;
Gee.HashMap<string,Meta.Background> pattern_cache;
Gee.LinkedList<WaitingCallback?> waiting_callbacks;
BackgroundCache (Meta.Screen screen)
{
Meta.Background? content = null, candidate_content = null;
Object (screen: screen);
foreach (var pattern in patterns) {
if (pattern == null)
continue;
image_cache = new Gee.HashMap<string,Meta.Background> ();
pattern_cache = new Gee.HashMap<string,Meta.Background> ();
waiting_callbacks = new Gee.LinkedList<WaitingCallback?> ();
}
if (pattern.get_shading() != shading_type)
continue;
public async Meta.Background? load_image (string file, int monitor,
GDesktop.BackgroundStyle style)
{
string hash = file + "#" + ((int)style).to_string ();
Meta.Background? content = image_cache.get (hash);
if (color.equal(pattern.get_color ()))
continue;
if (content != null) {
/*FIXME apparently we can just copy the content at any point
print ("FILENAME: %s\n", content.get_filename ());
// the content has been created, but the file is still loading, so we wait
if (content.get_filename () == null) {
waiting_callbacks.add ({ load_image.callback, hash });
yield;
}*/
if (shading_type != GDesktop.BackgroundShading.SOLID &&
!second_color.equal(pattern.get_second_color ()))
continue;
candidate_content = pattern;
if (effects != pattern.effects)
continue;
break;
return content.copy (monitor, Meta.BackgroundEffects.NONE);
}
if (candidate_content != null) {
content = candidate_content.copy (monitor_index, effects);
} else {
content = new Meta.Background (screen, monitor_index, effects);
content = new Meta.Background (screen, monitor, Meta.BackgroundEffects.NONE);
if (shading_type == GDesktop.BackgroundShading.SOLID) {
content.load_color (color);
} else {
content.load_gradient (shading_type, color, second_color);
try {
yield content.load_file_async (file, style, null);
} catch (Error e) {
warning (e.message);
return null;
}
image_cache.set (hash, content);
foreach (var callback in waiting_callbacks) {
if (callback.hash == hash) {
callback.func ();
waiting_callbacks.remove (callback);
}
}
patterns.add (content);
return content;
}
public void monitor_file (string filename)
public Meta.Background load_pattern (int monitor, Clutter.Color primary, Clutter.Color secondary,
GDesktop.BackgroundShading shading_type)
{
if (file_monitors.has_key (filename))
return;
string hash = primary.to_string () + secondary.to_string () +
((int)shading_type).to_string ();
Meta.Background? content = pattern_cache.get (hash);
var file = File.new_for_path (filename);
try {
var monitor = file.monitor (FileMonitorFlags.NONE);
if (content != null)
return content.copy (monitor, Meta.BackgroundEffects.NONE);
//TODO maybe do this in a cleaner way
ulong signal_id = 0;
signal_id = monitor.changed.connect (() => {
foreach (var image in images) {
if (image.get_filename () == filename)
images.remove (image);
}
content = new Meta.Background (screen, monitor, Meta.BackgroundEffects.NONE);
if (shading_type == GDesktop.BackgroundShading.SOLID)
content.load_color (primary);
else
content.load_gradient (shading_type, primary, secondary);
monitor.disconnect (signal_id);
pattern_cache.set (hash, content);
file_changed (filename);
});
file_monitors.set (filename, monitor);
} catch (Error e) { warning (e.message); }
return content;
}
public void remove_content (Gee.LinkedList<Meta.Background> content_list, Meta.Background content) {
content_list.remove (content);
}
static BackgroundCache? instance = null;
public void remove_pattern_content (Meta.Background content) {
remove_content (patterns, content);
}
public void remove_image_content (Meta.Background content) {
var filename = content.get_filename();
if (filename != null && file_monitors.has_key (filename))
//TODO disconnect filemonitor and delete it properly
file_monitors.unset (filename);
remove_content(images, content);
}
//FIXME as we may have to get a number of callbacks fired when this finishes,
// we can't use vala's async system, but use a callback based system instead
public void load_image_content (int monitor_index,
GDesktop.BackgroundStyle style, string filename, Meta.BackgroundEffects effects,
Object userdata, PendingFileLoadFinished on_finished, Cancellable? cancellable = null)
{
foreach (var pending_file_load in pending_file_loads) {
if (pending_file_load.filename == filename &&
pending_file_load.style == style) {
pending_file_load.callers.add ({true, monitor_index, effects, on_finished, userdata});
return;
}
}
PendingFileLoad load = {filename, style, new Gee.LinkedList<PendingFileLoadCaller?> ()};
load.callers.add ({false, monitor_index, effects, on_finished, userdata});
pending_file_loads.add (load);
var content = new Meta.Background (screen, monitor_index, effects);
content.load_file_async.begin (filename, style, cancellable, (obj, res) => {
try {
content.load_file_async.end (res);
monitor_file (filename);
images.add (content);
} catch (Error e) {
content = null;
}
foreach (var pending_load in pending_file_loads) {
if (pending_load.filename != filename ||
pending_load.style != style)
continue;
foreach (var caller in pending_load.callers) {
if (caller.on_finished != null) {
if (content != null && caller.should_copy) {
content = (obj as Meta.Background).copy (caller.monitor_index, caller.effects);
}
caller.on_finished (caller.userdata, content);
}
}
pending_file_loads.remove (pending_load);
}
});
}
public void get_image_content (int monitor_index, GDesktop.BackgroundStyle style,
string filename, Meta.BackgroundEffects effects, Object userdata,
PendingFileLoadFinished on_finished, Cancellable? cancellable = null)
{
Meta.Background content = null, candidate_content = null;
foreach (var image in images) {
if (image == null)
continue;
if (image.get_style () != style)
continue;
if (image.get_filename () != filename)
continue;
if (style == GDesktop.BackgroundStyle.SPANNED &&
image.monitor != monitor_index)
continue;
candidate_content = image;
if (effects != image.effects)
continue;
break;
}
if (candidate_content != null) {
content = candidate_content.copy (monitor_index, effects);
if (cancellable != null && cancellable.is_cancelled ())
content = null;
else
images.add (content);
on_finished (userdata, content);
} else {
load_image_content (monitor_index, style, filename, effects, userdata, on_finished, cancellable);
}
}
public async Animation get_animation (string filename)
{
Animation animation;
if (animation_filename == filename) {
animation = this.animation;
//FIXME do we need those Idles?
Idle.add (() => {
get_animation.callback ();
return false;
});
} else {
animation = new Animation (screen, filename);
yield animation.load ();
monitor_file (filename);
animation_filename = filename;
this.animation = animation;
Idle.add (() => {
get_animation.callback ();
return false;
});
}
yield;
return animation;
}
public static void init (Meta.Screen screen)
{
instance = new BackgroundCache (screen);
}
public static BackgroundCache get_default ()
requires (instance != null)
{
return instance;
}

View File

@ -1,120 +1,33 @@
//
// Copyright (C) 2013 Tom Beckmann, Rico Tzschichholz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
namespace Gala
{
public class BackgroundManager : Object
public class BackgroundManager : Meta.BackgroundGroup
{
const string BACKGROUND_SCHEMA = "org.gnome.desktop.background";
const uint FADE_ANIMATION_TIME = 1000;
public Meta.BackgroundEffects effects { get; construct set; }
public int monitor_index { get; construct set; }
public bool control_position { get; construct set; }
public Settings settings { get; construct set; }
public Meta.Screen screen { get; construct set; }
public Clutter.Actor container { get; construct set; }
public Background background { get; private set; }
Background? new_background = null;
public signal void changed ();
public BackgroundManager (Meta.Screen screen, Clutter.Actor container, int monitor_index,
Meta.BackgroundEffects effects, bool control_position, string settings_schema = BACKGROUND_SCHEMA)
public BackgroundManager (Meta.Screen screen)
{
Object (settings: new Settings (settings_schema),
container: container,
effects: effects,
monitor_index: monitor_index,
screen: screen,
control_position: control_position);
Object (screen: screen);
background = create_background ();
update ();
screen.monitors_changed.connect (update);
}
public void destroy ()
void update ()
{
if (new_background != null) {
new_background.actor.destroy();
new_background = null;
remove_all_children ();
var settings = BackgroundSettings.get_default ().schema;
for (var i = 0; i < screen.get_n_monitors (); i++) {
var geom = screen.get_monitor_geometry (i);
var background = new Background (screen, i, settings);
background.set_position (geom.x, geom.y);
background.set_size (geom.width, geom.height);
add_child (background);
}
if (background != null) {
background.actor.destroy();
background = null;
}
}
public void update_background (Background background, int monitor_index) {
var new_background = create_background ();
new_background.vignette_sharpness = background.vignette_sharpness;
new_background.brightness = background.brightness;
new_background.actor.visible = background.actor.visible;
new_background.loaded_signal_id = new_background.loaded.connect (() => {
new_background.disconnect (new_background.loaded_signal_id);
new_background.loaded_signal_id = 0;
background.actor.animate(Clutter.AnimationMode.EASE_OUT_QUAD, FADE_ANIMATION_TIME,
opacity : 0).completed.connect (() => {
if (this.new_background == new_background) {
this.background = new_background;
this.new_background = null;
} else {
new_background.actor.destroy ();
}
background.actor.destroy ();
changed ();
});
});
this.new_background = new_background;
}
public Background create_background ()
{
var background = new Background (monitor_index, effects, settings);
container.add_child (background.actor);
var monitor = screen.get_monitor_geometry (monitor_index);
background.actor.set_size(monitor.width, monitor.height);
if (control_position) {
background.actor.set_position (monitor.x, monitor.y);
background.actor.lower_bottom ();
}
background.change_signal_id = background.changed.connect (() => {
background.disconnect (background.change_signal_id);
update_background (background, monitor_index);
background.change_signal_id = 0;
});
background.actor.destroy.connect (() => {
if (background.change_signal_id != 0)
background.disconnect (background.change_signal_id);
if (background.loaded_signal_id != 0)
background.disconnect (background.loaded_signal_id);
});
return background;
}
}
}

View File

@ -1,37 +1,14 @@
//
// Copyright (C) 2013 Tom Beckmann, Rico Tzschichholz
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
namespace Gala
{
public class SystemBackground : Object
public class SystemBackground : Meta.BackgroundActor
{
public Meta.BackgroundActor actor { get; private set; }
public signal void loaded ();
public SystemBackground ()
{
actor = new Meta.BackgroundActor ();
BackgroundCache.get_default ().get_image_content (0, GDesktop.BackgroundStyle.WALLPAPER,
Config.PKGDATADIR + "/texture.png", Meta.BackgroundEffects.NONE, this, (userdata, content) => {
var self = userdata as SystemBackground;
self.actor.content = content;
self.loaded ();
var cache = BackgroundCache.get_default ();
cache.load_image.begin (Config.PKGDATADIR + "/texture.png", 0,
GDesktop.BackgroundStyle.WALLPAPER, (obj, res) => {
content = cache.load_image.end (res);
});
}
}

View File

@ -121,9 +121,9 @@ namespace Gala
*/
var system_background = new SystemBackground ();
system_background.actor.add_constraint (new Clutter.BindConstraint (stage,
system_background.add_constraint (new Clutter.BindConstraint (stage,
Clutter.BindCoordinate.ALL, 0));
stage.insert_child_below (system_background.actor, null);
stage.insert_child_below (system_background, null);
ui_group = new Clutter.Actor ();
ui_group.reactive = true;
@ -133,12 +133,9 @@ namespace Gala
stage.remove_child (window_group);
ui_group.add_child (window_group);
background_group = new Meta.BackgroundGroup ();
background_group = new BackgroundManager (screen);
window_group.add_child (background_group);
window_group.set_child_below_sibling (background_group, null);
setup_background_managers ();
screen.monitors_changed.connect (setup_background_managers);
#endif
workspace_view = new WorkspaceView (this);
@ -327,17 +324,6 @@ namespace Gala
Utils.set_input_area (screen, InputArea.NONE);
}
void setup_background_managers ()
{
background_group.destroy_all_children ();
var screen = get_screen ();
for (var i = 0; i < screen.get_n_monitors (); i++) {
new BackgroundManager (screen, background_group, i,
Meta.BackgroundEffects.NONE, true);
}
}
public uint32[] get_all_xids ()
{
var list = new Gee.ArrayList<uint32> ();

View File

@ -95,8 +95,8 @@ namespace Gala
// FIXME find a nice way to draw a border around it, maybe combinable with the indicator using a ShaderEffect
#if HAS_MUTTER38
//FIXME we probably want to keep the BackgroundManager and not just save its actor
wallpaper = new BackgroundManager (screen, this, 0, Meta.BackgroundEffects.NONE, false).background.actor;
wallpaper = new BackgroundManager (screen);
wallpaper.background_color = { 255, 0, 0, 255 };
#else
wallpaper = new Clone (Compositor.get_background_actor_for_screen (screen));
#endif
@ -126,9 +126,7 @@ namespace Gala
windows.clip_to_allocation = true;
add_child (indicator);
#if !HAS_MUTTER38
add_child (wallpaper);
#endif
add_child (windows);
add_child (icons);
add_child (close_button);