Commit 84d75cb5 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

[x11] Add support for _GTK_FRAME_EXTENTS

Summary:
KDE is known for having a strong view on the client-side decorations vs
server-side decorations issue. The main argument raised against CSD is
that desktop will look less consistent when clients start drawing window
decorations by themselves, which is somewhat true. It all ties to how
well each toolkit is integrated with the desktop environment.

KDE doesn't control the desktop market on Linux. Another big "player"
is GNOME. Both KDE and GNOME have very polarized views on in which
direction desktop should move forward. The KDE community is pushing more
toward server-side decorations while the GNOME community is pushing
more toward client-side decorations. Both communities have developed
great applications and it's not rare to see a GNOME application being
used in KDE Plasma. The only problem is that these different views are
not left behind the curtain and our users pay the price. Resizing GTK
clients in Plasma became practically impossible due to resize borders
having small hit area.

When a client draws its window decoration, it's more likely that it also
draws the drop-shadow around the decoration. The compositor must know
the extents of the shadow so things like snapping and so on work as
expected. And here lies the problem... While the xdg-shell protocol has
a way to specify such things, the NetWM spec doesn't have anything like
that. There's _GTK_FRAME_EXTENTS in the wild, however the problem with
it is that it's a proprietary atom, which is specific only to GTK apps.

Due to that, _GTK_FRAME_EXTENTS wasn't implemented because implementing
anything like that would require major changes in how we think about
geometry.

Recent xdg-shell window geometry patches adjusted geometry abstractions
in kwin to such a degree that it's very easy to add support for client
side decorated clients on X11. We just have to make sure that the
X11Client class provides correct buffer geometry and frame geometry when
the gtk frame extents are set.

Even though the X11 code is feature frozen, I still think it's worth
to have _GTK_FRAME_EXTENTS support in kwin because it will fix the resize
issues. Also, because KWin/Wayland is unfortunately far from becoming
default, it will help us with testing some implementation bits of the
window geometry from xdg-shell.

BUG: 390550
FIXED-IN: 5.18.0

Test Plan:
Things like quick tiling, maximizing, tiling scripts and so on work as
expected with GTK clients.

Reviewers: #kwin, davidedmundson

Reviewed By: #kwin, davidedmundson

Subscribers: cblack, trmdi, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D24660
parent 4fbb777a
......@@ -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);
}
}
......@@ -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;
}
......
......@@ -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"))
......
......@@ -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;
......
......@@ -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)
......
......@@ -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));
}
if (isDecorated()) {
bufferSize = frameSize;
} else {
bufferSize = m_clientGeometry.size();
}
QSize s(w, h);
if (!areGeometryUpdatesBlocked() && s != rules()->checkSize(s)) {
qCDebug(KWIN_CORE) << "forced size fail:" << s << ":" << rules()->checkSize(s);
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()
......
......@@ -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;
......
......@@ -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
......
......@@ -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()
......
......@@ -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);
}
......
......@@ -31,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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();
}