diff --git a/abstract_client.cpp b/abstract_client.cpp
index 673005a00e..7c61bf9bd2 100644
--- a/abstract_client.cpp
+++ b/abstract_client.cpp
@@ -2009,4 +2009,42 @@ QMargins AbstractClient::frameMargins() const
return QMargins(borderLeft(), borderTop(), borderRight(), borderBottom());
}
+QPoint AbstractClient::framePosToClientPos(const QPoint &point) const
+{
+ return point + QPoint(borderLeft(), borderTop());
+}
+
+QPoint AbstractClient::clientPosToFramePos(const QPoint &point) const
+{
+ return point - QPoint(borderLeft(), borderTop());
+}
+
+QSize AbstractClient::frameSizeToClientSize(const QSize &size) const
+{
+ const int width = size.width() - borderLeft() - borderRight();
+ const int height = size.height() - borderTop() - borderBottom();
+ return QSize(width, height);
+}
+
+QSize AbstractClient::clientSizeToFrameSize(const QSize &size) const
+{
+ const int width = size.width() + borderLeft() + borderRight();
+ const int height = size.height() + borderTop() + borderBottom();
+ return QSize(width, height);
+}
+
+QRect AbstractClient::frameRectToClientRect(const QRect &rect) const
+{
+ const QPoint position = framePosToClientPos(rect.topLeft());
+ const QSize size = frameSizeToClientSize(rect.size());
+ return QRect(position, size);
+}
+
+QRect AbstractClient::clientRectToFrameRect(const QRect &rect) const
+{
+ const QPoint position = clientPosToFramePos(rect.topLeft());
+ const QSize size = clientSizeToFrameSize(rect.size());
+ return QRect(position, size);
+}
+
}
diff --git a/abstract_client.h b/abstract_client.h
index 32e0356075..db48176e40 100644
--- a/abstract_client.h
+++ b/abstract_client.h
@@ -624,7 +624,7 @@ public:
void updateLayer();
enum ForceGeometry_t { NormalGeometrySet, ForceGeometrySet };
- void move(int x, int y, ForceGeometry_t force = NormalGeometrySet);
+ virtual void move(int x, int y, ForceGeometry_t force = NormalGeometrySet);
void move(const QPoint &p, ForceGeometry_t force = NormalGeometrySet);
virtual void resizeWithChecks(int w, int h, ForceGeometry_t force = NormalGeometrySet) = 0;
void resizeWithChecks(const QSize& s, ForceGeometry_t force = NormalGeometrySet);
@@ -655,6 +655,43 @@ public:
QSize adjustedSize(const QSize&, Sizemode mode = SizemodeAny) const;
QSize adjustedSize() const;
+ /**
+ * Calculates the matching client position for the given frame position @p point.
+ */
+ virtual QPoint framePosToClientPos(const QPoint &point) const;
+ /**
+ * Calculates the matching frame position for the given client position @p point.
+ */
+ virtual QPoint clientPosToFramePos(const QPoint &point) const;
+ /**
+ * Calculates the matching client size for the given frame size @p size.
+ *
+ * Notice that size constraints won't be applied.
+ *
+ * Default implementation returns the frame size with frame margins being excluded.
+ */
+ virtual QSize frameSizeToClientSize(const QSize &size) const;
+ /**
+ * Calculates the matching frame size for the given client size @p size.
+ *
+ * Notice that size constraints won't be applied.
+ *
+ * Default implementation returns the client size with frame margins being included.
+ */
+ virtual QSize clientSizeToFrameSize(const QSize &size) const;
+ /**
+ * Calculates the matching client rect for the given frame rect @p rect.
+ *
+ * Notice that size constraints won't be applied.
+ */
+ QRect frameRectToClientRect(const QRect &rect) const;
+ /**
+ * Calculates the matching frame rect for the given client rect @p rect.
+ *
+ * Notice that size constraints won't be applied.
+ */
+ QRect clientRectToFrameRect(const QRect &rect) const;
+
bool isMove() const {
return isMoveResize() && moveResizePointerMode() == PositionCenter;
}
diff --git a/atoms.cpp b/atoms.cpp
index 9e65624455..22705e1c9e 100644
--- a/atoms.cpp
+++ b/atoms.cpp
@@ -64,7 +64,6 @@ Atoms::Atoms()
, kde_color_sheme(QByteArrayLiteral("_KDE_NET_WM_COLOR_SCHEME"))
, kde_skip_close_animation(QByteArrayLiteral("_KDE_NET_WM_SKIP_CLOSE_ANIMATION"))
, kde_screen_edge_show(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"))
- , gtk_frame_extents(QByteArrayLiteral("_GTK_FRAME_EXTENTS"))
, kwin_dbus_service(QByteArrayLiteral("_ORG_KDE_KWIN_DBUS_SERVICE"))
, utf8_string(QByteArrayLiteral("UTF8_STRING"))
, text(QByteArrayLiteral("TEXT"))
diff --git a/atoms.h b/atoms.h
index e31f5adad7..9749731a08 100644
--- a/atoms.h
+++ b/atoms.h
@@ -73,7 +73,6 @@ public:
Xcb::Atom kde_color_sheme;
Xcb::Atom kde_skip_close_animation;
Xcb::Atom kde_screen_edge_show;
- Xcb::Atom gtk_frame_extents;
Xcb::Atom kwin_dbus_service;
Xcb::Atom utf8_string;
Xcb::Atom text;
diff --git a/events.cpp b/events.cpp
index 8347581258..cb30d74020 100644
--- a/events.cpp
+++ b/events.cpp
@@ -482,6 +482,9 @@ bool X11Client::windowEvent(xcb_generic_event_t *e)
if (dirtyProperties2 & NET::WM2DesktopFileName) {
setDesktopFileName(QByteArray(info->desktopFileName()));
}
+ if (dirtyProperties2 & NET::WM2GTKFrameExtents) {
+ setClientFrameExtents(info->gtkFrameExtents());
+ }
}
const uint8_t eventType = e->response_type & ~0x80;
@@ -754,8 +757,6 @@ void X11Client::propertyNotifyEvent(xcb_property_notify_event_t *e)
updateColorScheme();
else if (e->atom == atoms->kde_screen_edge_show)
updateShowOnScreenEdge();
- else if (e->atom == atoms->gtk_frame_extents)
- detectGtkFrameExtents();
else if (e->atom == atoms->kde_net_wm_appmenu_service_name)
checkApplicationMenuServiceName();
else if (e->atom == atoms->kde_net_wm_appmenu_object_path)
diff --git a/geometry.cpp b/geometry.cpp
index 335c11b589..bdef7a8224 100644
--- a/geometry.cpp
+++ b/geometry.cpp
@@ -1323,8 +1323,7 @@ void AbstractClient::checkOffscreenPosition(QRect* geom, const QRect& screenArea
QSize AbstractClient::adjustedSize(const QSize& frame, Sizemode mode) const
{
// first, get the window size for the given frame size s
- QSize wsize(frame.width() - (borderLeft() + borderRight()),
- frame.height() - (borderTop() + borderBottom()));
+ QSize wsize = frameSizeToClientSize(frame);
if (wsize.isEmpty())
wsize = QSize(qMax(wsize.width(), 1), qMax(wsize.height(), 1));
@@ -1507,11 +1506,11 @@ QSize X11Client::sizeForClientSize(const QSize& wsize, Sizemode mode, bool nofra
h = h1;
}
+ QSize size(w, h);
if (!noframe) {
- w += borderLeft() + borderRight();
- h += borderTop() + borderBottom();
+ size = clientSizeToFrameSize(size);
}
- return rules()->checkSize(QSize(w, h));
+ return rules()->checkSize(size);
}
/**
@@ -1532,7 +1531,7 @@ void X11Client::getWmNormalHints()
// update to match restrictions
QSize new_size = adjustedSize();
if (new_size != size() && !isFullScreen()) {
- QRect origClientGeometry(pos() + clientPos(), clientSize());
+ QRect origClientGeometry = m_clientGeometry;
resizeWithChecks(new_size);
if ((!isSpecialWindow() || isToolbar()) && !isFullScreen()) {
// try to keep the window in its xinerama screen if possible,
@@ -1575,10 +1574,10 @@ void X11Client::sendSyntheticConfigureNotify()
c.response_type = XCB_CONFIGURE_NOTIFY;
c.event = window();
c.window = window();
- c.x = x() + clientPos().x();
- c.y = y() + clientPos().y();
- c.width = clientSize().width();
- c.height = clientSize().height();
+ c.x = m_clientGeometry.x();
+ c.y = m_clientGeometry.y();
+ c.width = m_clientGeometry.width();
+ c.height = m_clientGeometry.height();
c.border_width = 0;
c.above_sibling = XCB_WINDOW_NONE;
c.override_redirect = 0;
@@ -1586,15 +1585,17 @@ void X11Client::sendSyntheticConfigureNotify()
xcb_flush(connection());
}
-const QPoint X11Client::calculateGravitation(bool invert, int gravity) const
+QPoint X11Client::gravityAdjustment(xcb_gravity_t gravity) const
{
- int dx, dy;
- dx = dy = 0;
+ int dx = 0;
+ int dy = 0;
- if (gravity == 0) // default (nonsense) value for the argument
- gravity = m_geometryHints.windowGravity();
-
-// dx, dy specify how the client window moves to make space for the frame
+ // dx, dy specify how the client window moves to make space for the frame.
+ // In general we have to compute the reference point and from that figure
+ // out how much we need to shift the client, however given that we ignore
+ // the border width attribute and the extents of the server-side decoration
+ // are known in advance, we can simplify the math quite a bit and express
+ // the required window gravity adjustment in terms of border sizes.
switch(gravity) {
case XCB_GRAVITY_NORTH_WEST: // move down right
default:
@@ -1614,7 +1615,9 @@ const QPoint X11Client::calculateGravitation(bool invert, int gravity) const
dy = 0;
break;
case XCB_GRAVITY_CENTER:
- break; // will be handled specially
+ dx = (borderLeft() - borderRight()) / 2;
+ dy = (borderTop() - borderBottom()) / 2;
+ break;
case XCB_GRAVITY_STATIC: // don't move
dx = 0;
dy = 0;
@@ -1636,15 +1639,18 @@ const QPoint X11Client::calculateGravitation(bool invert, int gravity) const
dy = -borderBottom();
break;
}
- if (gravity != XCB_GRAVITY_CENTER) {
- // translate from client movement to frame movement
- dx -= borderLeft();
- dy -= borderTop();
- } else {
- // center of the frame will be at the same position client center without frame would be
- dx = - (borderLeft() + borderRight()) / 2;
- dy = - (borderTop() + borderBottom()) / 2;
- }
+
+ return QPoint(dx, dy);
+}
+
+const QPoint X11Client::calculateGravitation(bool invert) const
+{
+ const QPoint adjustment = gravityAdjustment(m_geometryHints.windowGravity());
+
+ // translate from client movement to frame movement
+ const int dx = adjustment.x() - borderLeft();
+ const int dy = adjustment.y() - borderTop();
+
if (!invert)
return QPoint(x() + dx, y() + dy);
else
@@ -1700,23 +1706,25 @@ void X11Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh,
if (gravity == 0) // default (nonsense) value for the argument
gravity = m_geometryHints.windowGravity();
if (value_mask & configurePositionMask) {
- QPoint new_pos = calculateGravitation(true, gravity); // undo gravitation
+ QPoint new_pos = framePosToClientPos(pos());
+ new_pos -= gravityAdjustment(xcb_gravity_t(gravity));
if (value_mask & XCB_CONFIG_WINDOW_X) {
new_pos.setX(rx);
}
if (value_mask & XCB_CONFIG_WINDOW_Y) {
new_pos.setY(ry);
}
-
// clever(?) workaround for applications like xv that want to set
// the location to the current location but miscalculate the
// frame size due to kwin being a double-reparenting window
// manager
- if (new_pos.x() == x() + clientPos().x() && new_pos.y() == y() + clientPos().y()
+ if (new_pos.x() == m_clientGeometry.x() && new_pos.y() == m_clientGeometry.y()
&& gravity == XCB_GRAVITY_NORTH_WEST && !from_tool) {
new_pos.setX(x());
new_pos.setY(y());
}
+ new_pos += gravityAdjustment(xcb_gravity_t(gravity));
+ new_pos = clientPosToFramePos(new_pos);
int nw = clientSize().width();
int nh = clientSize().height();
@@ -1732,11 +1740,10 @@ void X11Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh,
if (newScreen != rules()->checkScreen(newScreen))
return; // not allowed by rule
- QRect origClientGeometry(pos() + clientPos(), clientSize());
+ QRect origClientGeometry = m_clientGeometry;
GeometryUpdatesBlocker blocker(this);
move(new_pos);
plainResize(ns);
- setFrameGeometry(QRect(calculateGravitation(false, gravity), size()));
QRect area = workspace()->clientArea(WorkArea, this);
if (!from_tool && (!isSpecialWindow() || isToolbar()) && !isFullScreen()
&& area.contains(origClientGeometry))
@@ -1762,7 +1769,7 @@ void X11Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh,
QSize ns = sizeForClientSize(QSize(nw, nh));
if (ns != size()) { // don't restore if some app sets its own size again
- QRect origClientGeometry(pos() + clientPos(), clientSize());
+ QRect origClientGeometry = m_clientGeometry;
GeometryUpdatesBlocker blocker(this);
resizeWithChecks(ns, xcb_gravity_t(gravity));
if (!from_tool && (!isSpecialWindow() || isToolbar()) && !isFullScreen()) {
@@ -1938,25 +1945,34 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for
// Such code is wrong and should be changed to handle the case when the window is shaded,
// for example using X11Client::clientSize()
+ QRect frameGeometry(x, y, w, h);
+ QRect bufferGeometry;
+
if (shade_geometry_change)
; // nothing
else if (isShade()) {
- if (h == borderTop() + borderBottom()) {
+ if (frameGeometry.height() == borderTop() + borderBottom()) {
qCDebug(KWIN_CORE) << "Shaded geometry passed for size:";
} else {
- client_size = QSize(w - borderLeft() - borderRight(), h - borderTop() - borderBottom());
- h = borderTop() + borderBottom();
+ m_clientGeometry = frameRectToClientRect(frameGeometry);
+ frameGeometry.setHeight(borderTop() + borderBottom());
}
} else {
- client_size = QSize(w - borderLeft() - borderRight(), h - borderTop() - borderBottom());
+ m_clientGeometry = frameRectToClientRect(frameGeometry);
}
- QRect g(x, y, w, h);
- if (!areGeometryUpdatesBlocked() && g != rules()->checkGeometry(g)) {
- qCDebug(KWIN_CORE) << "forced geometry fail:" << g << ":" << rules()->checkGeometry(g);
+ if (isDecorated()) {
+ bufferGeometry = frameGeometry;
+ } else {
+ bufferGeometry = m_clientGeometry;
}
- if (force == NormalGeometrySet && geom == g && pendingGeometryUpdate() == PendingGeometryNone)
+ if (!areGeometryUpdatesBlocked() && frameGeometry != rules()->checkGeometry(frameGeometry)) {
+ qCDebug(KWIN_CORE) << "forced geometry fail:" << frameGeometry << ":" << rules()->checkGeometry(frameGeometry);
+ }
+ if (!canUpdateGeometry(frameGeometry, bufferGeometry, force)) {
return;
- geom = g;
+ }
+ m_bufferGeometry = bufferGeometry;
+ geom = frameGeometry;
if (areGeometryUpdatesBlocked()) {
if (pendingGeometryUpdate() == PendingGeometryForced)
{} // maximum, nothing needed
@@ -1966,11 +1982,11 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for
setPendingGeometryUpdate(PendingGeometryNormal);
return;
}
- QSize oldClientSize = m_frame.geometry().size();
- bool resized = (frameGeometryBeforeUpdateBlocking().size() != geom.size() || pendingGeometryUpdate() == PendingGeometryForced);
+ const QRect oldBufferGeometry = bufferGeometryBeforeUpdateBlocking();
+ bool resized = (oldBufferGeometry.size() != m_bufferGeometry.size() || pendingGeometryUpdate() == PendingGeometryForced);
if (resized) {
resizeDecoration();
- m_frame.setGeometry(x, y, w, h);
+ m_frame.setGeometry(m_bufferGeometry);
if (!isShade()) {
QSize cs = clientSize();
m_wrapper.setGeometry(QRect(clientPos(), cs));
@@ -1986,9 +2002,9 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for
if (compositing()) // Defer the X update until we leave this mode
needsXWindowMove = true;
else
- m_frame.move(x, y); // sendSyntheticConfigureNotify() on finish shall be sufficient
+ m_frame.move(m_bufferGeometry.topLeft()); // sendSyntheticConfigureNotify() on finish shall be sufficient
} else {
- m_frame.move(x, y);
+ m_frame.move(m_bufferGeometry.topLeft());
sendSyntheticConfigureNotify();
}
@@ -2002,11 +2018,9 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for
screens()->setCurrent(this);
workspace()->updateStackingOrder();
- // need to regenerate decoration pixmaps when
- // - size is changed
- if (resized) {
- if (oldClientSize != QSize(w,h))
- discardWindowPixmap();
+ // Need to regenerate decoration pixmaps when the buffer size is changed.
+ if (oldBufferGeometry.size() != m_bufferGeometry.size()) {
+ discardWindowPixmap();
}
emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking());
addRepaintDuringGeometryUpdates();
@@ -2017,28 +2031,37 @@ void X11Client::setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t for
void X11Client::plainResize(int w, int h, ForceGeometry_t force)
{
+ QSize frameSize(w, h);
+ QSize bufferSize;
+
// this code is also duplicated in X11Client::setGeometry(), and it's also commented there
if (shade_geometry_change)
; // nothing
else if (isShade()) {
- if (h == borderTop() + borderBottom()) {
+ if (frameSize.height() == borderTop() + borderBottom()) {
qCDebug(KWIN_CORE) << "Shaded geometry passed for size:";
} else {
- client_size = QSize(w - borderLeft() - borderRight(), h - borderTop() - borderBottom());
- h = borderTop() + borderBottom();
+ m_clientGeometry.setSize(frameSizeToClientSize(frameSize));
+ frameSize.setHeight(borderTop() + borderBottom());
}
} else {
- client_size = QSize(w - borderLeft() - borderRight(), h - borderTop() - borderBottom());
+ m_clientGeometry.setSize(frameSizeToClientSize(frameSize));
}
- QSize s(w, h);
- if (!areGeometryUpdatesBlocked() && s != rules()->checkSize(s)) {
- qCDebug(KWIN_CORE) << "forced size fail:" << s << ":" << rules()->checkSize(s);
+ if (isDecorated()) {
+ bufferSize = frameSize;
+ } else {
+ bufferSize = m_clientGeometry.size();
+ }
+ if (!areGeometryUpdatesBlocked() && frameSize != rules()->checkSize(frameSize)) {
+ qCDebug(KWIN_CORE) << "forced size fail:" << frameSize << ":" << rules()->checkSize(frameSize);
}
// resuming geometry updates is handled only in setGeometry()
Q_ASSERT(pendingGeometryUpdate() == PendingGeometryNone || areGeometryUpdatesBlocked());
- if (force == NormalGeometrySet && geom.size() == s)
+ if (!canUpdateSize(frameSize, bufferSize, force)) {
return;
- geom.setSize(s);
+ }
+ m_bufferGeometry.setSize(bufferSize);
+ geom.setSize(frameSize);
if (areGeometryUpdatesBlocked()) {
if (pendingGeometryUpdate() == PendingGeometryForced)
{} // maximum, nothing needed
@@ -2048,10 +2071,8 @@ void X11Client::plainResize(int w, int h, ForceGeometry_t force)
setPendingGeometryUpdate(PendingGeometryNormal);
return;
}
- QSize oldClientSize = m_frame.geometry().size();
resizeDecoration();
- m_frame.resize(w, h);
-// resizeDecoration( s );
+ m_frame.resize(m_bufferGeometry.size());
if (!isShade()) {
QSize cs = clientSize();
m_wrapper.setGeometry(QRect(clientPos(), cs));
@@ -2063,8 +2084,9 @@ void X11Client::plainResize(int w, int h, ForceGeometry_t force)
updateWindowRules(Rules::Position|Rules::Size);
screens()->setCurrent(this);
workspace()->updateStackingOrder();
- if (oldClientSize != QSize(w,h))
+ if (bufferGeometryBeforeUpdateBlocking().size() != m_bufferGeometry.size()) {
discardWindowPixmap();
+ }
emit geometryShapeChanged(this, frameGeometryBeforeUpdateBlocking());
addRepaintDuringGeometryUpdates();
updateGeometryBeforeUpdateBlocking();
@@ -2105,12 +2127,6 @@ void AbstractClient::move(int x, int y, ForceGeometry_t force)
emit geometryChanged();
}
-void X11Client::doMove(int x, int y)
-{
- m_frame.move(x, y);
- sendSyntheticConfigureNotify();
-}
-
void AbstractClient::blockGeometryUpdates(bool block)
{
if (block) {
@@ -2657,7 +2673,7 @@ void X11Client::leaveMoveResize()
{
if (needsXWindowMove) {
// Do the deferred move
- m_frame.move(geom.topLeft());
+ m_frame.move(m_bufferGeometry.topLeft());
needsXWindowMove = false;
}
if (!isResize())
@@ -3110,8 +3126,8 @@ void X11Client::doResizeSync()
syncRequest.isPending = true; // limit the resizes to 30Hz to take pointless load from X11
syncRequest.timeout->start(33); // and the client, the mouse is still moved at full speed
} // and no human can control faster resizes anyway
- const QRect &moveResizeGeom = moveResizeGeometry();
- m_client.setGeometry(0, 0, moveResizeGeom.width() - (borderLeft() + borderRight()), moveResizeGeom.height() - (borderTop() + borderBottom()));
+ const QRect moveResizeClientGeometry = frameRectToClientRect(moveResizeGeometry());
+ m_client.setGeometry(0, 0, moveResizeClientGeometry.width(), moveResizeClientGeometry.height());
}
void AbstractClient::performMoveResize()
diff --git a/manage.cpp b/manage.cpp
index 9741f4390b..ce30263b3a 100644
--- a/manage.cpp
+++ b/manage.cpp
@@ -95,11 +95,11 @@ bool X11Client::manage(xcb_window_t w, bool isMapped)
NET::WM2InitialMappingState |
NET::WM2IconPixmap |
NET::WM2OpaqueRegion |
- NET::WM2DesktopFileName;
+ NET::WM2DesktopFileName |
+ NET::WM2GTKFrameExtents;
auto wmClientLeaderCookie = fetchWmClientLeader();
auto skipCloseAnimationCookie = fetchSkipCloseAnimation();
- auto gtkFrameExtentsCookie = fetchGtkFrameExtents();
auto showOnScreenEdgeCookie = fetchShowOnScreenEdge();
auto colorSchemeCookie = fetchColorScheme();
auto firstInTabBoxCookie = fetchFirstInTabBox();
@@ -138,9 +138,9 @@ bool X11Client::manage(xcb_window_t w, bool isMapped)
if (Xcb::Extensions::self()->isShapeAvailable())
xcb_shape_select_input(connection(), window(), true);
detectShape(window());
- readGtkFrameExtents(gtkFrameExtentsCookie);
detectNoBorder();
fetchIconicName();
+ setClientFrameExtents(info->gtkFrameExtents());
// Needs to be done before readTransient() because of reading the group
checkGroup();
@@ -323,8 +323,14 @@ bool X11Client::manage(xcb_window_t w, bool isMapped)
if (isMovable() && (geom.x() > area.right() || geom.y() > area.bottom()))
placementDone = false; // Weird, do not trust.
- if (placementDone)
- move(geom.x(), geom.y()); // Before gravitating
+ if (placementDone) {
+ QPoint position = geom.topLeft();
+ // Session contains the position of the frame geometry before gravitating.
+ if (!session) {
+ position = clientPosToFramePos(position);
+ }
+ move(position);
+ }
// Create client group if the window will have a decoration
bool dontKeepInArea = false;
diff --git a/netinfo.cpp b/netinfo.cpp
index 4fa340a5f4..65c91b21dd 100644
--- a/netinfo.cpp
+++ b/netinfo.cpp
@@ -110,7 +110,8 @@ RootInfo *RootInfo::create()
NET::WM2FullPlacement |
NET::WM2FullscreenMonitors |
NET::WM2KDEShadow |
- NET::WM2OpaqueRegion;
+ NET::WM2OpaqueRegion |
+ NET::WM2GTKFrameExtents;
#ifdef KWIN_BUILD_ACTIVITIES
properties2 |= NET::WM2Activities;
#endif
diff --git a/plugins/platforms/x11/standalone/glxbackend.cpp b/plugins/platforms/x11/standalone/glxbackend.cpp
index 424311a743..3897525554 100644
--- a/plugins/platforms/x11/standalone/glxbackend.cpp
+++ b/plugins/platforms/x11/standalone/glxbackend.cpp
@@ -861,7 +861,7 @@ bool GlxTexture::loadTexture(xcb_pixmap_t pixmap, const QSize &size, xcb_visuali
bool GlxTexture::loadTexture(WindowPixmap *pixmap)
{
Toplevel *t = pixmap->toplevel();
- return loadTexture(pixmap->pixmap(), t->size(), t->visual());
+ return loadTexture(pixmap->pixmap(), t->bufferGeometry().size(), t->visual());
}
OpenGLBackend *GlxTexture::backend()
diff --git a/toplevel.cpp b/toplevel.cpp
index 811b6b14ca..d66aac3ff2 100644
--- a/toplevel.cpp
+++ b/toplevel.cpp
@@ -408,8 +408,11 @@ void Toplevel::getDamageRegionReply()
region += QRect(reply->extents.x, reply->extents.y,
reply->extents.width, reply->extents.height);
+ const QRect bufferRect = bufferGeometry();
+ const QRect frameRect = frameGeometry();
+
damage_region += region;
- repaints_region += region;
+ repaints_region += region.translated(bufferRect.topLeft() - frameRect.topLeft());
free(reply);
}
diff --git a/x11client.cpp b/x11client.cpp
index f4ce2278b8..e5e0aa90d0 100644
--- a/x11client.cpp
+++ b/x11client.cpp
@@ -31,6 +31,7 @@ along with this program. If not, see .
#include "deleted.h"
#include "focuschain.h"
#include "group.h"
+#include "screens.h"
#include "shadow.h"
#ifdef KWIN_BUILD_TABBOX
#include "tabbox.h"
@@ -130,7 +131,6 @@ X11Client::X11Client()
, needsXWindowMove(false)
, m_decoInputExtent()
, m_focusOutTimer(nullptr)
- , m_clientSideDecorated(false)
{
// TODO: Do all as initialization
syncRequest.counter = syncRequest.alarm = XCB_NONE;
@@ -158,7 +158,6 @@ X11Client::X11Client()
//client constructed be connected to the workspace wrapper
geom = QRect(0, 0, 100, 100); // So that decorations don't start with size being (0,0)
- client_size = QSize(100, 100);
connect(clientMachine(), &ClientMachine::localhostChanged, this, &X11Client::updateCaption);
connect(options, &Options::condensedTitleChanged, this, &X11Client::updateCaption);
@@ -256,7 +255,7 @@ void X11Client::releaseWindow(bool on_shutdown)
m_client.deleteProperty(atoms->kde_net_wm_user_creation_time);
m_client.deleteProperty(atoms->net_frame_extents);
m_client.deleteProperty(atoms->kde_net_wm_frame_strut);
- m_client.reparent(rootWindow(), x(), y());
+ m_client.reparent(rootWindow(), m_bufferGeometry.x(), m_bufferGeometry.y());
xcb_change_save_set(c, XCB_SET_MODE_DELETE, m_client);
m_client.selectInput(XCB_EVENT_MASK_NO_EVENT);
if (on_shutdown)
@@ -549,21 +548,29 @@ void X11Client::updateFrameExtents()
info->setFrameExtents(strut);
}
-Xcb::Property X11Client::fetchGtkFrameExtents() const
+void X11Client::setClientFrameExtents(const NETStrut &strut)
{
- return Xcb::Property(false, m_client, atoms->gtk_frame_extents, XCB_ATOM_CARDINAL, 0, 4);
-}
+ const QMargins clientFrameExtents(strut.left, strut.top, strut.right, strut.bottom);
+ if (m_clientFrameExtents == clientFrameExtents) {
+ return;
+ }
-void X11Client::readGtkFrameExtents(Xcb::Property &prop)
-{
- m_clientSideDecorated = !prop.isNull() && prop->type != 0;
- emit clientSideDecoratedChanged();
-}
+ const bool wasClientSideDecorated = isClientSideDecorated();
+ m_clientFrameExtents = clientFrameExtents;
-void X11Client::detectGtkFrameExtents()
-{
- Xcb::Property prop = fetchGtkFrameExtents();
- readGtkFrameExtents(prop);
+ // We should resize the client when its custom frame extents are changed so
+ // the logical bounds remain the same. This however means that we will send
+ // several configure requests to the application upon restoring it from the
+ // maximized or fullscreen state. Notice that a client-side decorated client
+ // cannot be shaded, therefore it's okay not to use the adjusted size here.
+ setFrameGeometry(frameGeometry());
+
+ if (wasClientSideDecorated != isClientSideDecorated()) {
+ emit clientSideDecoratedChanged();
+ }
+
+ // This will invalidate the window quads cache.
+ emit geometryShapeChanged(this, frameGeometry());
}
/**
@@ -607,6 +614,11 @@ bool X11Client::noBorder() const
bool X11Client::userCanSetNoBorder() const
{
+ // Client-side decorations and server-side decorations are mutually exclusive.
+ if (isClientSideDecorated()) {
+ return false;
+ }
+
return !isFullScreen() && !isShade();
}
@@ -1973,7 +1985,7 @@ xcb_window_t X11Client::frameId() const
QRect X11Client::bufferGeometry() const
{
- return geom;
+ return m_bufferGeometry;
}
QMargins X11Client::bufferMargins() const
@@ -1981,6 +1993,70 @@ QMargins X11Client::bufferMargins() const
return QMargins(borderLeft(), borderTop(), borderRight(), borderBottom());
}
+QPoint X11Client::framePosToClientPos(const QPoint &point) const
+{
+ int x = point.x();
+ int y = point.y();
+
+ if (isDecorated()) {
+ x += borderLeft();
+ y += borderTop();
+ } else {
+ x -= m_clientFrameExtents.left();
+ y -= m_clientFrameExtents.top();
+ }
+
+ return QPoint(x, y);
+}
+
+QPoint X11Client::clientPosToFramePos(const QPoint &point) const
+{
+ int x = point.x();
+ int y = point.y();
+
+ if (isDecorated()) {
+ x -= borderLeft();
+ y -= borderTop();
+ } else {
+ x += m_clientFrameExtents.left();
+ y += m_clientFrameExtents.top();
+ }
+
+ return QPoint(x, y);
+}
+
+QSize X11Client::frameSizeToClientSize(const QSize &size) const
+{
+ int width = size.width();
+ int height = size.height();
+
+ if (isDecorated()) {
+ width -= borderLeft() + borderRight();
+ height -= borderTop() + borderBottom();
+ } else {
+ width += m_clientFrameExtents.left() + m_clientFrameExtents.right();
+ height += m_clientFrameExtents.top() + m_clientFrameExtents.bottom();
+ }
+
+ return QSize(width, height);
+}
+
+QSize X11Client::clientSizeToFrameSize(const QSize &size) const
+{
+ int width = size.width();
+ int height = size.height();
+
+ if (isDecorated()) {
+ width += borderLeft() + borderRight();
+ height += borderTop() + borderBottom();
+ } else {
+ width -= m_clientFrameExtents.left() + m_clientFrameExtents.right();
+ height -= m_clientFrameExtents.top() + m_clientFrameExtents.bottom();
+ }
+
+ return QSize(width, height);
+}
+
Xcb::Property X11Client::fetchShowOnScreenEdge() const
{
return Xcb::Property(false, window(), atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 0, 1);
@@ -2075,7 +2151,7 @@ void X11Client::addDamage(const QRegion &damage)
setupWindowManagementInterface();
}
}
- repaints_region += damage;
+ repaints_region += damage.translated(bufferGeometry().topLeft() - frameGeometry().topLeft());
Toplevel::addDamage(damage);
}
@@ -2140,5 +2216,84 @@ void X11Client::handleSync()
addRepaintFull();
}
+bool X11Client::canUpdatePosition(const QPoint &frame, const QPoint &buffer, ForceGeometry_t force) const
+{
+ // Obey forced geometry updates.
+ if (force != NormalGeometrySet) {
+ return true;
+ }
+ // Server-side geometry and our geometry are out of sync.
+ if (bufferGeometry().topLeft() != buffer) {
+ return true;
+ }
+ if (frameGeometry().topLeft() != frame) {
+ return true;
+ }
+ return false;
+}
+
+bool X11Client::canUpdateSize(const QSize &frame, const QSize &buffer, ForceGeometry_t force) const
+{
+ // Obey forced geometry updates.
+ if (force != NormalGeometrySet) {
+ return true;
+ }
+ // Server-side geometry and our geometry are out of sync.
+ if (bufferGeometry().size() != buffer) {
+ return true;
+ }
+ if (frameGeometry().size() != frame) {
+ return true;
+ }
+ return false;
+}
+
+bool X11Client::canUpdateGeometry(const QRect &frame, const QRect &buffer, ForceGeometry_t force) const
+{
+ if (canUpdatePosition(frame.topLeft(), buffer.topLeft(), force)) {
+ return true;
+ }
+ if (canUpdateSize(frame.size(), buffer.size(), force)) {
+ return true;
+ }
+ return pendingGeometryUpdate() != PendingGeometryNone;
+}
+
+void X11Client::move(int x, int y, ForceGeometry_t force)
+{
+ const QPoint framePosition(x, y);
+ m_clientGeometry.moveTopLeft(framePosToClientPos(framePosition));
+ const QPoint bufferPosition = isDecorated() ? framePosition : m_clientGeometry.topLeft();
+ // resuming geometry updates is handled only in setGeometry()
+ Q_ASSERT(pendingGeometryUpdate() == PendingGeometryNone || areGeometryUpdatesBlocked());
+ if (!areGeometryUpdatesBlocked() && framePosition != rules()->checkPosition(framePosition)) {
+ qCDebug(KWIN_CORE) << "forced position fail:" << framePosition << ":" << rules()->checkPosition(framePosition);
+ }
+ if (!canUpdatePosition(framePosition, bufferPosition, force)) {
+ return;
+ }
+ m_bufferGeometry.moveTopLeft(bufferPosition);
+ geom.moveTopLeft(framePosition);
+ if (areGeometryUpdatesBlocked()) {
+ if (pendingGeometryUpdate() == PendingGeometryForced) {
+ // Maximum, nothing needed.
+ } else if (force == ForceGeometrySet) {
+ setPendingGeometryUpdate(PendingGeometryForced);
+ } else {
+ setPendingGeometryUpdate(PendingGeometryNormal);
+ }
+ return;
+ }
+ m_frame.move(m_bufferGeometry.topLeft());
+ sendSyntheticConfigureNotify();
+ updateWindowRules(Rules::Position);
+ screens()->setCurrent(this);
+ workspace()->updateStackingOrder();
+ // client itself is not damaged
+ addRepaintDuringGeometryUpdates();
+ updateGeometryBeforeUpdateBlocking();
+ emit geometryChanged();
+}
+
} // namespace
diff --git a/x11client.h b/x11client.h
index 3107e2d986..06867d9054 100644
--- a/x11client.h
+++ b/x11client.h
@@ -92,6 +92,11 @@ public:
QRect bufferGeometry() const override;
QMargins bufferMargins() const override;
+ QPoint framePosToClientPos(const QPoint &point) const override;
+ QPoint clientPosToFramePos(const QPoint &point) const override;
+ QSize frameSizeToClientSize(const QSize &size) const override;
+ QSize clientSizeToFrameSize(const QSize &size) const override;
+
bool isTransient() const override;
bool groupTransient() const override;
bool wasOriginallyGroupTransient() const;
@@ -176,6 +181,8 @@ public:
void updateShape();
+ using AbstractClient::move;
+ void move(int x, int y, ForceGeometry_t force = NormalGeometrySet) override;
using AbstractClient::setFrameGeometry;
void setFrameGeometry(int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet) override;
/// plainResize() simply resizes
@@ -220,7 +227,8 @@ public:
void updateMouseGrab() override;
xcb_window_t moveResizeGrabWindow() const;
- const QPoint calculateGravitation(bool invert, int gravity = 0) const; // FRAME public?
+ QPoint gravityAdjustment(xcb_gravity_t gravity) const;
+ const QPoint calculateGravitation(bool invert) const;
void NETMoveResize(int x_root, int y_root, NET::Direction direction);
void NETMoveResizeWindow(int flags, int x, int y, int width, int height);
@@ -360,7 +368,6 @@ protected:
void doSetSkipSwitcher() override;
bool belongsToDesktop() const override;
void setGeometryRestore(const QRect &geo) override;
- void doMove(int x, int y) override;
bool doStartMoveResize() override;
void doPerformMoveResize() override;
bool isWaitingForMoveResizeSync() const override;
@@ -436,11 +443,12 @@ private:
void embedClient(xcb_window_t w, xcb_visualid_t visualid, xcb_colormap_t colormap, uint8_t depth);
void detectNoBorder();
- Xcb::Property fetchGtkFrameExtents() const;
- void readGtkFrameExtents(Xcb::Property &prop);
- void detectGtkFrameExtents();
void destroyDecoration() override;
void updateFrameExtents();
+ void setClientFrameExtents(const NETStrut &strut);
+ bool canUpdatePosition(const QPoint &frame, const QPoint &buffer, ForceGeometry_t force) const;
+ bool canUpdateSize(const QSize &frame, const QSize &buffer, ForceGeometry_t force) const;
+ bool canUpdateGeometry(const QRect &frame, const QRect &buffer, ForceGeometry_t force) const;
void internalShow();
void internalHide();
@@ -514,6 +522,8 @@ private:
} m_fullscreenMode;
MaximizeMode max_mode;
+ QRect m_bufferGeometry = QRect(0, 0, 100, 100);
+ QRect m_clientGeometry = QRect(0, 0, 100, 100);
QRect geom_restore;
QRect geom_fs_restore;
QTimer* shadeHoverTimer;
@@ -525,7 +535,6 @@ private:
xcb_timestamp_t m_pingTimestamp;
xcb_timestamp_t m_userTime;
NET::Actions allowed_actions;
- QSize client_size;
bool shade_geometry_change;
SyncRequest syncRequest;
static bool check_active_modal; ///< \see X11Client::checkActiveModal()
@@ -548,10 +557,11 @@ private:
QTimer *m_focusOutTimer;
QList m_connections;
- bool m_clientSideDecorated;
QMetaObject::Connection m_edgeRemoveConnection;
QMetaObject::Connection m_edgeGeometryTrackingConnection;
+
+ QMargins m_clientFrameExtents;
};
inline xcb_window_t X11Client::wrapperId() const
@@ -561,7 +571,7 @@ inline xcb_window_t X11Client::wrapperId() const
inline bool X11Client::isClientSideDecorated() const
{
- return m_clientSideDecorated;
+ return !m_clientFrameExtents.isNull();
}
inline bool X11Client::groupTransient() const
@@ -648,7 +658,7 @@ inline bool X11Client::isManaged() const
inline QSize X11Client::clientSize() const
{
- return client_size;
+ return m_clientGeometry.size();
}
inline void X11Client::plainResize(const QSize& s, ForceGeometry_t force)