Ensure that Toplevel::output() stays always in sync with geometry

Currently, if geometry updates are blocked, the Toplevel.output property
won't be updated. On the other hand, it's reasonable to use the output
property instead of manually looking up the output in window management
code, e.g. Workspace::clientArea().

In other words, using the Toplevel.output property is like walking on a
mine field, things can blow up. You can't use Toplevel.output even if it
makes perfect sense.

This change ensures that Toplevel.output property is always kept in sync
with the frame geometry. Unfortunately, this means that the output
property no longer can be updated when the frameGeometryChanged() signal
is emitted. It has to be done in moveResizeInternal() method.
This commit is contained in:
Vlad Zahorodnii 2021-11-26 12:03:14 +02:00 committed by Marco Martin
parent 6d56fa9820
commit 510a41eeb8
10 changed files with 30 additions and 28 deletions

View file

@ -63,8 +63,6 @@ AbstractClient::AbstractClient()
{
connect(this, &AbstractClient::clientStartUserMovedResized, this, &AbstractClient::moveResizedChanged);
connect(this, &AbstractClient::clientFinishUserMovedResized, this, &AbstractClient::moveResizedChanged);
connect(this, &AbstractClient::clientStartUserMovedResized, this, &AbstractClient::removeCheckOutputConnection);
connect(this, &AbstractClient::clientFinishUserMovedResized, this, &AbstractClient::setupCheckOutputConnection);
connect(this, &AbstractClient::windowShown, this, &AbstractClient::hiddenChanged);
connect(this, &AbstractClient::windowHidden, this, &AbstractClient::hiddenChanged);
@ -1041,7 +1039,6 @@ void AbstractClient::finishInteractiveMoveResize(bool cancel)
if (cancel) {
moveResize(initialInteractiveMoveResizeGeometry());
}
checkOutput(); // needs to be done because clientFinishUserMovedResized has not yet re-activated online alignment
if (output() != interactiveMoveResizeStartOutput()) {
workspace()->sendClientToOutput(this, output()); // checks rule validity
if (isFullScreen() || maximizeMode() != MaximizeRestore) {

View file

@ -1306,6 +1306,7 @@ void Unmanaged::configureNotifyEvent(xcb_configure_notify_event_t *e)
m_clientGeometry = newgeom;
m_frameGeometry = newgeom;
m_bufferGeometry = newgeom;
checkOutput();
Q_EMIT bufferGeometryChanged(this, old);
Q_EMIT clientGeometryChanged(this, old);
Q_EMIT frameGeometryChanged(this, old);

View file

@ -10,6 +10,7 @@
#include "internal_client.h"
#include "decorations/decorationbridge.h"
#include "deleted.h"
#include "platform.h"
#include "surfaceitem.h"
#include "workspace.h"
@ -472,6 +473,7 @@ void InternalClient::commitGeometry(const QRect &rect)
// The client geometry and the buffer geometry are the same.
const QRect oldClientGeometry = m_clientGeometry;
const QRect oldFrameGeometry = m_frameGeometry;
const AbstractOutput *oldOutput = m_output;
m_clientGeometry = frameRectToClientRect(rect);
m_frameGeometry = rect;
@ -481,6 +483,7 @@ void InternalClient::commitGeometry(const QRect &rect)
return;
}
m_output = kwinApp()->platform()->outputAt(rect.center());
syncGeometryToInternalWindow();
if (oldClientGeometry != m_clientGeometry) {
@ -490,6 +493,9 @@ void InternalClient::commitGeometry(const QRect &rect)
if (oldFrameGeometry != m_frameGeometry) {
Q_EMIT frameGeometryChanged(this, oldFrameGeometry);
}
if (oldOutput != m_output) {
Q_EMIT screenChanged();
}
Q_EMIT geometryShapeChanged(this, oldFrameGeometry);
}

View file

@ -47,7 +47,6 @@ Toplevel::Toplevel()
, m_skipCloseAnimation(false)
{
connect(screens(), &Screens::changed, this, &Toplevel::screenChanged);
setupCheckOutputConnection();
connect(this, &Toplevel::bufferGeometryChanged, this, &Toplevel::inputTransformationChanged);
// Only for compatibility reasons, drop in the next major release.
@ -381,22 +380,6 @@ void Toplevel::deleteEffectWindow()
effect_window = nullptr;
}
void Toplevel::checkOutput()
{
setOutput(kwinApp()->platform()->outputAt(frameGeometry().center()));
}
void Toplevel::setupCheckOutputConnection()
{
connect(this, &Toplevel::frameGeometryChanged, this, &Toplevel::checkOutput);
checkOutput();
}
void Toplevel::removeCheckOutputConnection()
{
disconnect(this, &Toplevel::frameGeometryChanged, this, &Toplevel::checkOutput);
}
int Toplevel::screen() const
{
return kwinApp()->platform()->enabledOutputs().indexOf(m_output);

View file

@ -640,13 +640,6 @@ Q_SIGNALS:
void visibleGeometryChanged();
protected Q_SLOTS:
/**
* Checks whether the screen number for this Toplevel changed and updates if needed.
* Any method changing the geometry of the Toplevel should call this method.
*/
void checkOutput();
void setupCheckOutputConnection();
void removeCheckOutputConnection();
void setReadyForPainting();
protected:
@ -677,6 +670,8 @@ protected:
void deleteShadow();
void deleteEffectWindow();
void setDepth(int depth);
AbstractOutput *m_output = nullptr;
QRect m_frameGeometry;
QRect m_clientGeometry;
QRect m_bufferGeometry;
@ -704,7 +699,6 @@ private:
QRegion opaque_region;
mutable QRegion m_shapeRegion;
mutable bool m_shapeRegionIsValid = false;
AbstractOutput *m_output = nullptr;
bool m_skipCloseAnimation;
quint32 m_pendingSurfaceId = 0;
QPointer<KWaylandServer::SurfaceInterface> m_surface;

View file

@ -11,6 +11,7 @@
#include "deleted.h"
#include "effects.h"
#include "platform.h"
#include "surfaceitem_x11.h"
#include "utils/common.h"
#include "workspace.h"
@ -219,6 +220,11 @@ QWindow *Unmanaged::findInternalWindow() const
return nullptr;
}
void Unmanaged::checkOutput()
{
setOutput(kwinApp()->platform()->outputAt(frameGeometry().center()));
}
void Unmanaged::damageNotifyEvent()
{
Q_ASSERT(kwinApp()->operationMode() == Application::OperationModeX11);

View file

@ -46,6 +46,7 @@ private:
void configureNotifyEvent(xcb_configure_notify_event_t *e);
void damageNotifyEvent();
QWindow *findInternalWindow() const;
void checkOutput();
void associate();
void initialize();
bool m_outline = false;

View file

@ -7,6 +7,7 @@
*/
#include "waylandclient.h"
#include "platform.h"
#include "screens.h"
#include "wayland_server.h"
#include "workspace.h"
@ -287,6 +288,7 @@ void WaylandClient::updateGeometry(const QRect &rect)
const QRect oldClientGeometry = m_clientGeometry;
const QRect oldFrameGeometry = m_frameGeometry;
const QRect oldBufferGeometry = m_bufferGeometry;
const AbstractOutput *oldOutput = m_output;
m_clientGeometry = frameRectToClientRect(rect);
m_frameGeometry = rect;
@ -308,6 +310,7 @@ void WaylandClient::updateGeometry(const QRect &rect)
return;
}
m_output = kwinApp()->platform()->outputAt(rect.center());
updateWindowRules(Rules::Position | Rules::Size);
if (changedGeometries & WaylandGeometryBuffer) {
@ -319,6 +322,9 @@ void WaylandClient::updateGeometry(const QRect &rect)
if (changedGeometries & WaylandGeometryFrame) {
Q_EMIT frameGeometryChanged(this, oldFrameGeometry);
}
if (oldOutput != m_output) {
Q_EMIT screenChanged();
}
Q_EMIT geometryShapeChanged(this, oldFrameGeometry);
}

View file

@ -4177,6 +4177,8 @@ void X11Client::moveResizeInternal(const QRect &rect, MoveResizeMode mode)
if (pendingMoveResizeMode() == MoveResizeMode::None && m_lastBufferGeometry == m_bufferGeometry && m_lastFrameGeometry == m_frameGeometry && m_lastClientGeometry == m_clientGeometry) {
return;
}
m_output = kwinApp()->platform()->outputAt(frameGeometry.center());
if (areGeometryUpdatesBlocked()) {
setPendingMoveResizeMode(mode);
return;
@ -4185,6 +4187,7 @@ void X11Client::moveResizeInternal(const QRect &rect, MoveResizeMode mode)
const QRect oldBufferGeometry = m_lastBufferGeometry;
const QRect oldFrameGeometry = m_lastFrameGeometry;
const QRect oldClientGeometry = m_lastClientGeometry;
const AbstractOutput *oldOutput = m_lastOutput;
updateServerGeometry();
updateWindowRules(Rules::Position | Rules::Size);
@ -4192,6 +4195,7 @@ void X11Client::moveResizeInternal(const QRect &rect, MoveResizeMode mode)
m_lastBufferGeometry = m_bufferGeometry;
m_lastFrameGeometry = m_frameGeometry;
m_lastClientGeometry = m_clientGeometry;
m_lastOutput = m_output;
if (isActive()) {
workspace()->setActiveOutput(output());
@ -4207,6 +4211,9 @@ void X11Client::moveResizeInternal(const QRect &rect, MoveResizeMode mode)
if (oldFrameGeometry != m_frameGeometry) {
Q_EMIT frameGeometryChanged(this, oldFrameGeometry);
}
if (oldOutput != m_output) {
Q_EMIT screenChanged();
}
Q_EMIT geometryShapeChanged(this, oldFrameGeometry);
}

View file

@ -537,6 +537,7 @@ private:
QMetaObject::Connection m_edgeGeometryTrackingConnection;
QMargins m_clientFrameExtents;
AbstractOutput *m_lastOutput = nullptr;
QRect m_lastBufferGeometry;
QRect m_lastFrameGeometry;
QRect m_lastClientGeometry;