From 6119b447469c71c36b15c0d0fa8c1cfa24289778 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Tue, 12 Mar 2013 15:27:51 -0400 Subject: [PATCH] Improve tracking of fullscreen windows It's possible in some corner cases for the status of the topwindow to change and make it not fullscreen without ::restacked being changed. One way that it could happen with the old code was if the layer of the top window changed from NORMAL to FULLSCREEN. Change the logic not to look at the layer, which is a function of Mutter's *intended* stacking, rather than the *actual* stacking, which is what ::restacked gives you. Instead, look at the top portion of the stack, down to the first non-override-redirect window, and see if their are any monitor-sized windows there. Connect to changes on the top portion of the stack, so we know if conditions change. https://bugzilla.gnome.org/show_bug.cgi?id=649748 --- js/ui/layout.js | 120 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 16 deletions(-) diff --git a/js/ui/layout.js b/js/ui/layout.js index 4ca2680a9..1fff9cc53 100644 --- a/js/ui/layout.js +++ b/js/ui/layout.js @@ -142,6 +142,7 @@ const LayoutManager = new Lang.Class({ this._updateRegionIdle = 0; this._trackedActors = []; + this._topActors = []; this._isPopupWindowVisible = false; this._startingUp = true; @@ -888,36 +889,78 @@ const LayoutManager = new Lang.Class({ }); }, + _disconnectTopActor: function(window) { + /* O-R status is immutable in Mutter */ + if (window.metaWindow.is_override_redirect()) { + window.disconnect(window._topActorPositionChangedId); + delete window._topActorPositionChangedId; + window.disconnect(window._topActorSizeChangedId); + delete window._topActorSizeChangedId; + } + }, + + _disconnectTopWindow: function(metaWindow) { + metaWindow.disconnect(metaWindow._topActorUnmanagedId); + delete window._topActorUnmanagedId; + metaWindow.disconnect(metaWindow._topActorNotifyFullscreenId); + delete window._topActorNotifyFullscreenId; + }, + + _topActorUnmanaged: function(metaWindow, window) { + /* Actor is already destroyed, so don't disconnect it */ + this._disconnectTopWindow(metaWindow); + + let i = this._topActors.indexOf(window); + this._topActors.splice(i, 1); + }, + + _topActorChanged: function() { + this._windowsRestacked(); + }, + _updateFullscreen: function() { + // Ordinary chrome should be visible unless the top window in + // the stack is monitor sized. We allow override-redirect + // windows to be above this "top window" because there can be + // O-R windows that are offscreen, or otherwise have no + // semantic meaning to the user. + // + // If we wanted to be extra clever, we could figure out when + // an OVERRIDE_REDIRECT window was trying to partially overlap + // us, and then adjust the input region and our clip region + // accordingly... + + // @windows is sorted bottom to top. let windows = this._getWindowActorsForWorkspace(global.screen.get_active_workspace()); // Reset all monitors to not fullscreen for (let i = 0; i < this.monitors.length; i++) this.monitors[i].inFullscreen = false; - // Ordinary chrome should be visible unless there is a window - // with layer FULLSCREEN, or a window with layer - // OVERRIDE_REDIRECT that covers the whole screen. - // ('override_redirect' is not actually a layer above all - // other windows, but this seems to be how mutter treats it - // currently...) If we wanted to be extra clever, we could - // figure out when an OVERRIDE_REDIRECT window was trying to - // partially overlap us, and then adjust the input region and - // our clip region accordingly... - - // @windows is sorted bottom to top. + let oldTopActors = this._topActors; + let newTopActors = []; for (let i = windows.length - 1; i > -1; i--) { let window = windows[i]; let metaWindow = window.meta_window; - let layer = metaWindow.get_layer(); - // Skip minimized windows - if (!metaWindow.showing_on_its_workspace()) + // Because we are working with the list of actors, not the list of + // windows, we have an uncomfortable corner case to deal with. + // If a window is being hidden, it's actor will be left on top + // of the actor stack until any animation is done. (See + // meta_compositor_sync_stack()). These windows are actually at + // the bottom of the window stack, so if they return and become + // relevant again, we'll get another ::restacked signal, so we + // can just ignore them. This check *DOES NOT* handle destroyed + // windows correctly, but we don't currently animate such windows. + // If bugs show up here, instead of making this more complex, + // add a function to get the window stack from MetaStackTracker. + if (!metaWindow.showing_on_its_workspace ()) continue; - if (layer == Meta.StackLayer.FULLSCREEN || - (layer == Meta.StackLayer.OVERRIDE_REDIRECT && metaWindow.is_monitor_sized())) { + newTopActors.push(window); + + if (metaWindow.is_monitor_sized()) { if (metaWindow.is_screen_sized()) { for (let i = 0; i < this.monitors.length; i++) this.monitors[i].inFullscreen = true; @@ -928,8 +971,53 @@ const LayoutManager = new Lang.Class({ this.monitors[index].inFullscreen = true; } } + break; + } + + if (!window.is_override_redirect()) + break; + } + + // Deal with windows being added or removed from the "top actors" set. + // These are the actors that a change to could cause a change in + // our computed fullscreen status without a change in the stack. + for (let i = 0; i < oldTopActors.length; i++) { + let window = oldTopActors[i]; + if (newTopActors.indexOf(window) < 0) { + this._disconnectTopActor(window); + this._disconnectTopWindow(window.metaWindow); } } + + for (let i = 0; i < newTopActors.length; i++) { + let window = newTopActors[i]; + + if (oldTopActors.indexOf(window) < 0) { + window.metaWindow._topActorUnmanagedId = + window.metaWindow.connect('unmanaged', + Lang.bind(this, this._topActorUnmanaged, window)); + window.metaWindow._topActorNotifyFullscreenId = + window.metaWindow.connect('notify::fullscreen', + Lang.bind(this, this._topActorChanged)); + + /* In almost all cases, meta_window_is_monitor_size() depends + * on position only for O-R windows. The remaining case is a non-OR, + * non-fullscreen window which is screen sized. That is highly + * unlikely and probably should be excluded in + * meta_window_is_monitor_size(). + */ + if (window.metaWindow.is_override_redirect()) { + window._topActorPositionChangedId = + window.connect('position-changed', + Lang.bind(this, this._topActorChanged)); + window._topActorSizeChangedId = + window.connect('size-changed', + Lang.bind(this, this._topActorChanged)); + } + } + } + + this._topActors = newTopActors; }, _windowsRestacked: function() {