[gnome-shell/wip/background-rework: 5/13] background: add slide show support
- From: Ray Strode <halfline src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/background-rework: 5/13] background: add slide show support
- Date: Wed, 13 Feb 2013 21:29:42 +0000 (UTC)
commit 94a325a1310068695d99eb31cd989fa9010e27bc
Author: Ray Strode <rstrode redhat com>
Date: Mon Feb 4 16:50:36 2013 -0500
background: add slide show support
gnome-desktop's background drawing code supports an
XML format for presenting backgrounds based on time of day,
monitor geometry, etc. Now that we don't use gnome-desktop for drawing the
background, we need to implement that support ourselves to maintain
feature parity.
This commit implements that.
js/ui/background.js | 275 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 274 insertions(+), 1 deletions(-)
---
diff --git a/js/ui/background.js b/js/ui/background.js
index b658d1f..b4933bc 100644
--- a/js/ui/background.js
+++ b/js/ui/background.js
@@ -27,6 +27,8 @@ const BACKGROUND_STYLE_KEY = 'picture-options';
const PICTURE_OPACITY_KEY = 'picture-opacity';
const PICTURE_URI_KEY = 'picture-uri';
+const SLIDE_OPACITY_STEP_INCREMENT = 4.0;
+
let _backgroundCache = null;
const BackgroundCache = new Lang.Class({
@@ -153,6 +155,25 @@ const BackgroundCache = new Lang.Class({
}
return content;
+ },
+
+ getSlideShow: function(filename) {
+ if (this._slideShowFilename == filename) {
+ return this._slideShow;
+ }
+
+ let slideShow = new SlideShow({ layoutManager: this._layoutManager,
+ filename: filename });
+
+ if (slideShow.load()) {
+ this._monitorFile(filename);
+ this._slideShowFilename = filename;
+ this._slideShow = slideShow;
+ } else {
+ slideShow = null;
+ }
+
+ return slideShow;
}
});
Signals.addSignalMethods(BackgroundCache.prototype);
@@ -249,6 +270,70 @@ const Background = new Lang.Class({
return false;
},
+ _updateSlideContent: function(style) {
+ this._slideShow.update();
+
+ let slideFiles = this._slideShow.getSlideFiles(this._layoutManager.monitors[this._monitorIndex]);
+
+ if (!slideFiles)
+ return;
+
+ let fromContent = this._cache.getImageContent({ monitorIndex: this._monitorIndex,
+ effects: this._effects,
+ style: style,
+ filename: slideFiles[0] });
+
+ if (fromContent) {
+ if (!this._fromImage) {
+ this._fromImage = new Meta.BackgroundActor(global.screen, this._monitorIndex);
+ this.actor.add_child(this._fromImage);
+ }
+
+ if (this._fromImage.content != fromContent)
+ this._fromImage.content = fromContent;
+ }
+
+ if (slideFiles.length > 1) {
+ let toContent = this._cache.getImageContent({ monitorIndex: this._monitorIndex,
+ effects: this._effects,
+ style: style,
+ filename: slideFiles[1] });
+
+ if (toContent) {
+ if (!this._toImage) {
+ this._toImage = new Meta.BackgroundActor(global.screen, this._monitorIndex);
+ this.actor.add_child(this._toImage);
+ }
+
+ this._toImage.opacity = this._slideShow.slideProgress * 255;
+
+ if (this._toImage.content != toContent)
+ this._toImage.content = toContent;
+ } else {
+ this._toImage = null;
+ }
+ }
+ },
+
+ _loadSlides: function(filename, style) {
+ this._slideShow = this._cache.getSlideShow(filename);
+
+ if (!this._slideShow)
+ return false;
+
+ this._updateSlideContent(style);
+ this._watchCacheFile(filename);
+
+ let interval = Math.max(250, SLIDE_OPACITY_STEP_INCREMENT / this._slideShow.duration);
+ this._slideUpdateTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
+ interval,
+ Lang.bind(this, function() {
+ this._updateSlideContent(style);
+ return true;
+ }));
+ return true;
+ },
+
_load: function () {
this._cache = getBackgroundCache(this._layoutManager);
@@ -259,7 +344,9 @@ const Background = new Lang.Class({
let style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
if (style != GDesktopEnums.BackgroundStyle.NONE) {
- this._loadImage(filename, style);
+ if (!this._loadImage(filename, style)) {
+ this._loadSlides(filename, style);
+ }
}
if (!this._settings.get_boolean(DRAW_BACKGROUND_KEY)) {
@@ -312,3 +399,189 @@ const StillFrame = new Lang.Class({
}
});
Signals.addSignalMethods(StillFrame.prototype);
+
+const SlideShow = new Lang.Class({
+ Name: 'SlideShow',
+
+ _init: function(params) {
+ params = Params.parse(params, { layoutManager: null,
+ filename: null });
+ this.filename = params.filename;
+ this._slides = [];
+ this.duration = 0.0;
+ this._layoutManager = params.layoutManager;
+ this.slideProgress = 0.0;
+ },
+
+ _loadFileElement: function(fileElement) {
+ if (!fileElement)
+ return [];
+
+ let files = [];
+ let elements = fileElement.children();
+ if (elements.length() == 1 && elements.children().length() == 0) {
+ files.push({ path: elements[0].toString() });
+ } else {
+ for (let i = 0; i < elements.length(); i++) {
+ if (elements[i].name() == 'size' &&
+ elements[i] width &&
+ elements[i] height &&
+ elements[i].children().length() == 0) {
+ files.push({ path: elements[i].toString(),
+ width: parseFloat(elements[i] width),
+ height: parseFloat(elements[i] height) });
+ }
+ }
+ }
+
+ return files;
+ },
+
+ load: function() {
+ let file = Gio.File.new_for_path(this.filename);
+
+ let [result, text] = file.load_contents(null);
+
+ if (!result)
+ return false;
+
+ let rootElement;
+ try {
+ rootElement = XML(text.toString());
+ } catch(e) {
+ return false;
+ }
+
+ if (rootElement.length() != 1 || rootElement.name() != 'background')
+ return false;
+
+ let elements = rootElement.children();
+ for (let i = 0; i < elements.length(); i++) {
+ if (elements[i].name() == 'starttime') {
+ let startDate = new Date(parseInt(elements[i].year),
+ parseInt(elements[i].month) - 1,
+ parseInt(elements[i].day),
+ parseInt(elements[i].hour),
+ parseInt(elements[i].minute),
+ parseInt(elements[i].second));
+
+ this._startTime = startDate.getTime();
+ } else if (elements[i].name() == 'static') {
+ let slide = {};
+ slide['start-time'] = this._startTime + this.duration;
+ slide['duration'] = parseFloat(elements[i].duration) * 1000.0;
+ this.duration += slide['duration'];
+ slide['start-files'] = this._loadFileElement(elements[i].file);
+
+ this._slides.push(slide);
+ } else if (elements[i].name() == 'transition' &&
+ elements[i] type == 'overlay') {
+ let slide = {};
+ slide['start-time'] = this._startTime + this.duration;
+ slide['duration'] = parseFloat(elements[i].duration) * 1000.0;
+ this.duration += slide['duration'];
+ slide['start-files'] = this._loadFileElement(elements[i].from);
+ slide['end-files'] = this._loadFileElement(elements[i].to);
+
+ this._slides.push(slide);
+ }
+ }
+
+ return true;
+ },
+
+ _findFileForSize: function(files, width, height) {
+ // Find the file entry that best matches the given size.
+ // Do two passes; the first pass only considers entries
+ // that are larger than the given size.
+ // We are looking for the image that best matches the aspect ratio.
+ // When two images have the same aspect ratio, prefer the one whose
+ // width is closer to the given width.
+ // (algorithm taken from gnome-desktop)
+ let aspectRatio = (1.0 * width) / height;
+
+ let bestAspectCloseness;
+ let bestFile = null;
+ for (let pass = 0; pass < 2; pass++) {
+ for (let i = 0; i < files.length; i++) {
+ let file = files[i];
+
+ if (pass == 0 && ((file['width'] < width) || (file['height'] < height)))
+ continue;
+
+ let candidateAspectRatio;
+ if (!file['width'] || !file['height'])
+ candidateAspectRatio = 1.0;
+ else
+ candidateAspectRatio = file['width'] / file['height'];
+
+ let aspectCloseness = Math.abs(aspectRatio - candidateAspectRatio);
+
+ if ((bestAspectCloseness == undefined) ||
+ (aspectCloseness < bestAspectCloseness)) {
+ bestAspectCloseness = aspectCloseness;
+ bestFile = file;
+
+ } else if (aspectCloseness == bestAspectCloseness) {
+ if (Math.abs(file['width'] - width) < Math.abs(bestFile['width'] - width)) {
+ bestFile = file;
+ }
+ }
+
+ if (bestFile)
+ break;
+ }
+ }
+
+ return bestFile;
+ },
+
+ update: function() {
+ this._slide = null;
+
+ // The slideshow loops indefinitely, so roll back the
+ // current time as many slideshow intervals as necessary
+ // to find the effective time offset of the passed in time.
+ let timeOffset = (Date.now() - this._startTime) % this.duration;
+
+ let effectiveTime = this._startTime + timeOffset;
+
+ for (let i = 0; i < this._slides.length; i++) {
+ let slide = this._slides[i];
+
+ let slideStartTime = slide['start-time'];
+ let duration = slide['duration'];
+
+ if ((effectiveTime > slideStartTime) && effectiveTime < (slideStartTime + duration)) {
+ this._slide = slide;
+ this.slideProgress = (effectiveTime - slideStartTime) / duration;
+ }
+ }
+ },
+
+ getSlideFiles: function(monitor) {
+ if (!this._slide)
+ return null;
+
+ let file = this._findFileForSize(this._slide['start-files'],
+ monitor.width,
+ monitor.height);
+ if (!file)
+ return null;
+
+ let files = [];
+ files.push(file['path']);
+
+ if (this._slide['end-files']) {
+ file = this._findFileForSize(this._slide['end-files'],
+ monitor.width,
+ monitor.height);
+ if (file)
+ files.push(file['path']);
+ }
+
+ return files;
+ },
+});
+Signals.addSignalMethods(SlideShow.prototype);
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]