From 586ebcd5be68efe7a3cf3734817c26ff2face7de Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Mon, 24 Jun 2013 19:13:05 +0100 Subject: [PATCH] background: fix asynchronous management of background loading operations This fixes a blue background being drawn when switching the monitors configuration using hardware keys (clone/multimonitor/external/internal). The problem is that the shell gather all background loading requests under the same meta_background_load_file_async call using one GCancellable (the first one to come). So when the shell receives a batch of 12 or so XRandr events, it creates 12 new background managers which end up trying to load 12 times the same background picture. All of these requests are batched into the same meta_background_load_file_async using the first GCancellable received on the first request. Unfortunately, when the first request is cancelled by the following event indicating a new monitor setup, all of the background picture requests are dropped on the floor, and nothing gets loaded (hence the blue screen background). https://bugzilla.gnome.org/show_bug.cgi?id=703001 --- js/ui/background.js | 69 +++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/js/ui/background.js b/js/ui/background.js index 1d9ab7c21..13343c6c8 100644 --- a/js/ui/background.js +++ b/js/ui/background.js @@ -142,23 +142,20 @@ const BackgroundCache = new Lang.Class({ cancellable: null, onFinished: null }); - for (let i = 0; i < this._pendingFileLoads.length; i++) { - if (this._pendingFileLoads[i].filename == params.filename && - this._pendingFileLoads[i].style == params.style) { - this._pendingFileLoads[i].callers.push({ shouldCopy: true, - monitorIndex: params.monitorIndex, - effects: params.effects, - onFinished: params.onFinished }); - return; - } - } + let fileLoad = { filename: params.filename, + style: params.style, + shouldCopy: false, + monitorIndex: params.monitorIndex, + effects: params.effects, + onFinished: params.onFinished, + cancellable: new Gio.Cancellable(), }; + this._pendingFileLoads.push(fileLoad); - this._pendingFileLoads.push({ filename: params.filename, - style: params.style, - callers: [{ shouldCopy: false, - monitorIndex: params.monitorIndex, - effects: params.effects, - onFinished: params.onFinished }] }); + if (params.cancellable) { + params.cancellable.connect(Lang.bind(this, function(c) { + fileLoad.cancellable.cancel(); + })); + } let content = new Meta.Background({ meta_screen: global.screen, monitor: params.monitorIndex, @@ -166,9 +163,19 @@ const BackgroundCache = new Lang.Class({ content.load_file_async(params.filename, params.style, - params.cancellable, + fileLoad.cancellable, Lang.bind(this, function(object, result) { + if (fileLoad.cancellable.is_cancelled()) { + if (params.cancellable && params.cancellable.is_cancelled()) { + if (params.onFinished) + params.onFinished(null); + this._removePendingFileLoad(fileLoad); + return; + } + return; + } + try { content.load_file_finish(result); @@ -178,22 +185,25 @@ const BackgroundCache = new Lang.Class({ content = null; } + let needsCopy = false; for (let i = 0; i < this._pendingFileLoads.length; i++) { let pendingLoad = this._pendingFileLoads[i]; if (pendingLoad.filename != params.filename || pendingLoad.style != params.style) continue; - for (let j = 0; j < pendingLoad.callers.length; j++) { - if (pendingLoad.callers[j].onFinished) { - if (content && pendingLoad.callers[j].shouldCopy) { - content = object.copy(pendingLoad.callers[j].monitorIndex, - pendingLoad.callers[j].effects); + if (pendingLoad.cancellable.is_cancelled()) + continue; - } - - pendingLoad.callers[j].onFinished(content); + pendingLoad.cancellable.cancel(); + if (pendingLoad.onFinished) { + if (content && needsCopy) { + content = object.copy(pendingLoad.monitorIndex, + pendingLoad.effects); } + + needsCopy = true; + pendingLoad.onFinished(content); } this._pendingFileLoads.splice(i, 1); @@ -201,6 +211,15 @@ const BackgroundCache = new Lang.Class({ })); }, + _removePendingFileLoad: function(fileLoad) { + for (let i = 0; i < this._pendingFileLoads.length; i++) { + if (this._pendingFileLoads[i].cancellable == fileLoad.cancellable) { + this._pendingFileLoads.splice(i, 1); + break; + } + } + }, + getImageContent: function(params) { params = Params.parse(params, { monitorIndex: 0, style: null,