From c6608bb5fc0622e970f4fa4c3a3f08c9ec327b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 15 May 2023 14:18:58 +0200 Subject: [PATCH 1/5] background, layout, overview: Catch errors on loading failure Ensure we properly log errors on async calls failures --- js/ui/background.js | 2 +- js/ui/layout.js | 4 ++-- js/ui/overview.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/js/ui/background.js b/js/ui/background.js index a1f660108..5730eb2ee 100644 --- a/js/ui/background.js +++ b/js/ui/background.js @@ -516,7 +516,7 @@ var Background = GObject.registerClass({ return; } - this._loadFile(this._file); + this._loadFile(this._file).catch(logError); } }); diff --git a/js/ui/layout.js b/js/ui/layout.js index d7f73dcac..dc19a9f79 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -593,7 +593,7 @@ var LayoutManager = GObject.registerClass({ this._updateMonitors(); this._updateBoxes(); this._updateHotCorners(); - this._updateBackgrounds(); + this._updateBackgrounds().catch(logError); this._updateFullscreen(); this._updateVisibility(); this._queueUpdateRegions(); @@ -678,7 +678,7 @@ var LayoutManager = GObject.registerClass({ if (this.primaryMonitor) { this._systemBackground.show(); global.stage.show(); - this._prepareStartupAnimation(); + this._prepareStartupAnimation().catch(logError); return GLib.SOURCE_REMOVE; } else { return GLib.SOURCE_CONTINUE; diff --git a/js/ui/overview.js b/js/ui/overview.js index c2c2e5326..15f0148a4 100644 --- a/js/ui/overview.js +++ b/js/ui/overview.js @@ -95,7 +95,7 @@ class OverviewActor extends St.BoxLayout { } runStartupAnimation(callback) { - this._controls.runStartupAnimation(callback); + this._controls.runStartupAnimation(callback).catch(logError); } get dash() { From 9e369bbbb526ce43f2fd2e18490c8948f1ed6ef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 15 May 2023 14:27:31 +0200 Subject: [PATCH 2/5] layout: Reject loading background promise if monitors had changed If monitors changed while the background is loading, the "loaded" event we're waiting from background managers may never arrive because we had already destroyed such managers, so we may end up waiting forever during _prepareStartupAnimation(). To prevent this to happen, let's cancel the waiting promise when monitors are changed. --- js/ui/layout.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/js/ui/layout.js b/js/ui/layout.js index dc19a9f79..002d5dc33 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -514,11 +514,21 @@ var LayoutManager = GObject.registerClass({ } _waitLoaded(bgManager) { - return new Promise(resolve => { + return new Promise((resolve, reject) => { + let monitorsChangedId; const id = bgManager.connect('loaded', () => { + this.disconnect(monitorsChangedId); bgManager.disconnect(id); resolve(); }); + + monitorsChangedId = this.connect('monitors-changed', () => { + bgManager.disconnect(id); + this.disconnect(monitorsChangedId); + + reject(new GLib.Error(Gio.IOErrorEnum, + Gio.IOErrorEnum.CANCELLED, 'Loading was cancelled')); + }); }); } @@ -539,7 +549,7 @@ var LayoutManager = GObject.registerClass({ bgManager.backgroundActor.hide(); } - return Promise.all(this._bgManagers.map(this._waitLoaded)); + return Promise.all(this._bgManagers.map(bgManager => this._waitLoaded(bgManager))); } _updateKeyboardBox() { From 66239334c35206da6af768bb3caf442b4068b0da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 15 May 2023 14:45:54 +0200 Subject: [PATCH 3/5] layout: Restart startup animation preparation if backgrounds changed In case monitors changed while we were waiting for the backgrounds update to be completed, are now getting a cancelled error. So in such case, we should re-prepare the startup animation so that it gets restarted with the proper monitors and screen geometries without the risk of being stuck waiting for changes. --- js/ui/layout.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/js/ui/layout.js b/js/ui/layout.js index 002d5dc33..1c1cb1819 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -716,6 +716,7 @@ var LayoutManager = GObject.registerClass({ async _prepareStartupAnimation() { // During the initial transition, add a simple actor to block all events, // so they don't get delivered to X11 windows that have been transformed. + this._coverPane?.destroy(); this._coverPane = new Clutter.Actor({ opacity: 0, width: global.screen_width, @@ -752,7 +753,17 @@ var LayoutManager = GObject.registerClass({ global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); - await this._updateBackgrounds(); + try { + await this._updateBackgrounds(); + } catch (e) { + if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { + this._prepareStartupAnimation().catch(logError); + return; + } + + logError(e); + return; + } } // Hack: Work around grab issue when testing greeter UI in nested From 26da7c135ac03974efbeb07db1380853d1b3fc40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 15 May 2023 16:18:21 +0200 Subject: [PATCH 4/5] layout: Update cover pane and and clipping during startup animation If monitor changes while we're performing the startup animation we should keep its clipping and the cover pane sizes in sync --- js/ui/layout.js | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/js/ui/layout.js b/js/ui/layout.js index 1c1cb1819..506724ef9 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -738,20 +738,23 @@ var LayoutManager = GObject.registerClass({ } else { this.keyboardBox.hide(); - let monitor = this.primaryMonitor; + const prepareMonitor = monitor => { + if (!Main.sessionMode.hasOverview) { + const x = monitor.x + monitor.width / 2.0; + const y = monitor.y + monitor.height / 2.0; - if (!Main.sessionMode.hasOverview) { - const x = monitor.x + monitor.width / 2.0; - const y = monitor.y + monitor.height / 2.0; + this.uiGroup.set_pivot_point( + x / global.screen_width, + y / global.screen_height); + this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75; + this.uiGroup.opacity = 0; + } - this.uiGroup.set_pivot_point( - x / global.screen_width, - y / global.screen_height); - this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75; - this.uiGroup.opacity = 0; - } + global.window_group.set_clip(monitor.x, monitor.y, + monitor.width, monitor.height); + }; - global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); + prepareMonitor(this.primaryMonitor); try { await this._updateBackgrounds(); @@ -764,6 +767,14 @@ var LayoutManager = GObject.registerClass({ logError(e); return; } + + this._startupMonitorsChangedId = this.connect('monitors-changed', () => { + this._coverPane.set({ + width: global.screen_width, + height: global.screen_height, + }); + prepareMonitor(this.primaryMonitor); + }); } // Hack: Work around grab issue when testing greeter UI in nested @@ -820,6 +831,11 @@ var LayoutManager = GObject.registerClass({ this.keyboardBox.show(); + if (this._startupMonitorsChangedId) { + this.disconnect(this._startupMonitorsChangedId); + this._startupMonitorsChangedId = 0; + } + if (!Main.sessionMode.isGreeter) { this._showSecondaryBackgrounds(); global.window_group.remove_clip(); From cd2eee92ed69c1a37a01e7612ae9952f48b3d685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Mon, 15 May 2023 16:22:52 +0200 Subject: [PATCH 5/5] layout: Avoid recreating backgrounds if we only need to wait for them In case backgrounds have just been created because of a monitors change there's no need to update them again, but we can just wait for being loaded. As per this, if loading is startup animation is restarted because of monitors changes, we can just wait until we've a background set again --- js/ui/layout.js | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/js/ui/layout.js b/js/ui/layout.js index 506724ef9..05ee89112 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -539,7 +539,7 @@ var LayoutManager = GObject.registerClass({ this._bgManagers = []; if (Main.sessionMode.isGreeter) - return Promise.resolve(); + return; for (let i = 0; i < this.monitors.length; i++) { let bgManager = this._createBackgroundManager(i); @@ -548,8 +548,14 @@ var LayoutManager = GObject.registerClass({ if (i != this.primaryIndex && this._startingUp) bgManager.backgroundActor.hide(); } + } - return Promise.all(this._bgManagers.map(bgManager => this._waitLoaded(bgManager))); + _waitBackgroundsLoaded() { + if (this._bgManagers.every(bgManager => bgManager.isLoaded)) + return Promise.resolve(); + + return Promise.all(this._bgManagers.map(bgManager => + this._waitLoaded(bgManager))); } _updateKeyboardBox() { @@ -603,7 +609,7 @@ var LayoutManager = GObject.registerClass({ this._updateMonitors(); this._updateBoxes(); this._updateHotCorners(); - this._updateBackgrounds().catch(logError); + this._updateBackgrounds(); this._updateFullscreen(); this._updateVisibility(); this._queueUpdateRegions(); @@ -716,7 +722,6 @@ var LayoutManager = GObject.registerClass({ async _prepareStartupAnimation() { // During the initial transition, add a simple actor to block all events, // so they don't get delivered to X11 windows that have been transformed. - this._coverPane?.destroy(); this._coverPane = new Clutter.Actor({ opacity: 0, width: global.screen_width, @@ -756,18 +761,6 @@ var LayoutManager = GObject.registerClass({ prepareMonitor(this.primaryMonitor); - try { - await this._updateBackgrounds(); - } catch (e) { - if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { - this._prepareStartupAnimation().catch(logError); - return; - } - - logError(e); - return; - } - this._startupMonitorsChangedId = this.connect('monitors-changed', () => { this._coverPane.set({ width: global.screen_width, @@ -775,6 +768,23 @@ var LayoutManager = GObject.registerClass({ }); prepareMonitor(this.primaryMonitor); }); + + this._updateBackgrounds(); + + while (true) { + try { + // eslint-disable-next-line no-await-in-loop + await this._waitBackgroundsLoaded(); + break; + } catch (e) { + if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) { + logError(e); + this.disconnect(this._startupMonitorsChangedId); + this._startupMonitorsChangedId = 0; + return; + } + } + } } // Hack: Work around grab issue when testing greeter UI in nested