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
This commit is contained in:
Owen W. Taylor 2013-03-12 15:27:51 -04:00
parent 7af4a7bf88
commit 6119b44746

View File

@ -142,6 +142,7 @@ const LayoutManager = new Lang.Class({
this._updateRegionIdle = 0; this._updateRegionIdle = 0;
this._trackedActors = []; this._trackedActors = [];
this._topActors = [];
this._isPopupWindowVisible = false; this._isPopupWindowVisible = false;
this._startingUp = true; 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() { _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()); let windows = this._getWindowActorsForWorkspace(global.screen.get_active_workspace());
// Reset all monitors to not fullscreen // Reset all monitors to not fullscreen
for (let i = 0; i < this.monitors.length; i++) for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = false; this.monitors[i].inFullscreen = false;
// Ordinary chrome should be visible unless there is a window let oldTopActors = this._topActors;
// with layer FULLSCREEN, or a window with layer let newTopActors = [];
// 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.
for (let i = windows.length - 1; i > -1; i--) { for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i]; let window = windows[i];
let metaWindow = window.meta_window; let metaWindow = window.meta_window;
let layer = metaWindow.get_layer();
// Skip minimized windows // Because we are working with the list of actors, not the list of
if (!metaWindow.showing_on_its_workspace()) // 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; continue;
if (layer == Meta.StackLayer.FULLSCREEN || newTopActors.push(window);
(layer == Meta.StackLayer.OVERRIDE_REDIRECT && metaWindow.is_monitor_sized())) {
if (metaWindow.is_monitor_sized()) {
if (metaWindow.is_screen_sized()) { if (metaWindow.is_screen_sized()) {
for (let i = 0; i < this.monitors.length; i++) for (let i = 0; i < this.monitors.length; i++)
this.monitors[i].inFullscreen = true; this.monitors[i].inFullscreen = true;
@ -928,8 +971,53 @@ const LayoutManager = new Lang.Class({
this.monitors[index].inFullscreen = true; 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() { _windowsRestacked: function() {