diff --git a/client.cpp b/client.cpp index 7230cc5f01..7ba49bc165 100644 --- a/client.cpp +++ b/client.cpp @@ -2404,7 +2404,7 @@ QRect Client::decorationRect() const } } -KDecorationDefines::Position Client::titlebarPosition() +KDecorationDefines::Position Client::titlebarPosition() const { Position titlePos = PositionCenter; // PositionTop is returned by the default implementation // this will hint errors in the metaobject usage ;-) diff --git a/client.h b/client.h index a8076a16c6..14856aa4f3 100644 --- a/client.h +++ b/client.h @@ -599,7 +599,7 @@ public: bool decorationHasAlpha() const; - Position titlebarPosition(); + Position titlebarPosition() const; enum CoordinateMode { DecorationRelative, // Relative to the top left corner of the decoration diff --git a/composite.cpp b/composite.cpp index 40c18633af..4945edd90d 100644 --- a/composite.cpp +++ b/composite.cpp @@ -269,7 +269,6 @@ void Compositor::slotCompositingOptionsInitialized() } // render at least once - compositeTimer.stop(); performCompositing(); } @@ -533,7 +532,6 @@ void Compositor::addRepaintFull() void Compositor::timerEvent(QTimerEvent *te) { if (te->timerId() == compositeTimer.timerId()) { - compositeTimer.stop(); performCompositing(); } else QObject::timerEvent(te); @@ -581,10 +579,12 @@ void Compositor::performCompositing() if (repaints_region.isEmpty() && !windowRepaintsPending()) { m_scene->idle(); + m_timeSinceLastVBlank = fpsInterval - (options->vBlankTime() + 1); // means "start now" // Note: It would seem here we should undo suspended unredirect, but when scenes need // it for some reason, e.g. transformations or translucency, the next pass that does not // need this anymore and paints normally will also reset the suspended unredirect. // Otherwise the window would not be painted normally anyway. + compositeTimer.stop(); return; } @@ -602,6 +602,8 @@ void Compositor::performCompositing() m_timeSinceLastVBlank = m_scene->paint(repaints, windows); + compositeTimer.stop(); // stop here to ensure *we* cause the next repaint schedule - not some effect through m_scene->paint() + // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle() // is called the next time. If there would be nothing pending, it will not restart the timer and // scheduleRepaint() would restart it again somewhen later, called from functions that @@ -661,10 +663,29 @@ void Compositor::setCompositeTimer() waitTime = nanoToMilli(padding - options->vBlankTime()); } } - else // w/o vsync we just jump to the next demanded tick - // the "1" will ensure we don't block out the eventloop - the system's just not faster - // "0" would be sufficient, but the compositor isn't the WMs only task - waitTime = (m_timeSinceLastVBlank > fpsInterval) ? 1 : nanoToMilli(fpsInterval - m_timeSinceLastVBlank); + else { // w/o blocking vsync we just jump to the next demanded tick + if (fpsInterval > m_timeSinceLastVBlank) { + waitTime = nanoToMilli(fpsInterval - m_timeSinceLastVBlank); + if (!waitTime) { + waitTime = 1; // will ensure we don't block out the eventloop - the system's just not faster ... + } + }/* else if (m_scene->syncsToVBlank() && m_timeSinceLastVBlank - fpsInterval < (vBlankInterval<<1)) { + // NOTICE - "for later" ------------------------------------------------------------------ + // It can happen that we push two frames within one refresh cycle. + // Swapping will then block even with triple buffering when the GPU does not discard but + // queues frames + // now here's the mean part: if we take that as "OMG, we're late - next frame ASAP", + // there'll immediately be 2 frames in the pipe, swapping will block, we think we're + // late ... ewww + // so instead we pad to the clock again and add 2ms safety to ensure the pipe is really + // free + // NOTICE: obviously m_timeSinceLastVBlank can be too big because we're too slow as well + // So if this code was enabled, we'd needlessly half the framerate once more (15 instead of 30) + waitTime = nanoToMilli(vBlankInterval - (m_timeSinceLastVBlank - fpsInterval)%vBlankInterval) + 2; + }*/ else { + waitTime = 1; // ... "0" would be sufficient, but the compositor isn't the WMs only task + } + } compositeTimer.start(qMin(waitTime, 250u), this); // force 4fps minimum } diff --git a/eglonxbackend.cpp b/eglonxbackend.cpp index 29ef5206b1..aa57c9522d 100644 --- a/eglonxbackend.cpp +++ b/eglonxbackend.cpp @@ -26,6 +26,8 @@ along with this program. If not, see . #include // Qt #include +// system +#include namespace KWin { @@ -320,6 +322,14 @@ SceneOpenGL::TexturePrivate *EglOnXBackend::createBackendTexture(SceneOpenGL::Te void EglOnXBackend::prepareRenderingFrame() { + if (gs_tripleBufferNeedsDetection) { + // the composite timer floors the repaint frequency. This can pollute our triple buffering + // detection because the glXSwapBuffers call for the new frame has to wait until the pending + // one scanned out. + // So we compensate for that by waiting an extra milisecond to give the driver the chance to + // fllush the buffer queue + usleep(1000); + } present(); startRenderTimer(); eglWaitNative(EGL_CORE_NATIVE_ENGINE); diff --git a/geometry.cpp b/geometry.cpp index f94e0dd099..3887bef5bd 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -395,8 +395,9 @@ QPoint Workspace::adjustClientPosition(Client* c, QPoint pos, bool unrestricted, if (options->windowSnapZone() || !borderSnapZone.isNull() || options->centerSnapZone()) { const bool sOWO = options->isSnapOnlyWhenOverlapping(); + const int screen = screens()->number(pos + c->rect().center()); if (maxRect.isNull()) - maxRect = clientArea(MovementArea, pos + c->rect().center(), c->desktop()); + maxRect = clientArea(MovementArea, screen, c->desktop()); const int xmin = maxRect.left(); const int xmax = maxRect.right() + 1; //desk size const int ymin = maxRect.top(); @@ -419,21 +420,25 @@ QPoint Workspace::adjustClientPosition(Client* c, QPoint pos, bool unrestricted, const int snapX = borderSnapZone.width() * snapAdjust; //snap trigger const int snapY = borderSnapZone.height() * snapAdjust; if (snapX || snapY) { + QRect geo = c->geometry(); const QPoint cp = c->clientPos(); - const QSize cs = c->geometry().size() - c->clientSize(); + const QSize cs = geo.size() - c->clientSize(); int padding[4] = { cp.x(), cs.width() - cp.x(), cp.y(), cs.height() - cp.y() }; - // snap to titlebar + // snap to titlebar / snap to window borders on inner screen edges Position titlePos = c->titlebarPosition(); - if (titlePos == PositionLeft || (c->maximizeMode() & MaximizeHorizontal)) + if (padding[0] && (titlePos == PositionLeft || (c->maximizeMode() & MaximizeHorizontal) || + screens()->intersecting(geo.translated(maxRect.x() - (padding[0] + geo.x()), 0)) > 1)) padding[0] = 0; - if (titlePos == PositionRight || (c->maximizeMode() & MaximizeHorizontal)) + if (padding[1] && (titlePos == PositionRight || (c->maximizeMode() & MaximizeHorizontal) || + screens()->intersecting(geo.translated(maxRect.right() + padding[1] - geo.right(), 0)) > 1)) padding[1] = 0; - if (titlePos == PositionTop || (c->maximizeMode() & MaximizeVertical)) + if (padding[2] && (titlePos == PositionTop || (c->maximizeMode() & MaximizeVertical) || + screens()->intersecting(geo.translated(0, maxRect.y() - (padding[2] + geo.y()))) > 1)) padding[2] = 0; - if (titlePos == PositionBottom || (c->maximizeMode() & MaximizeVertical)) + if (padding[3] && (titlePos == PositionBottom || (c->maximizeMode() & MaximizeVertical) || + screens()->intersecting(geo.translated(0, maxRect.bottom() + padding[3] - geo.bottom())) > 1)) padding[3] = 0; - if ((sOWO ? (cx < xmin) : true) && (qAbs(xmin - cx) < snapX)) { deltaX = xmin - cx; nx = xmin - padding[0]; @@ -842,19 +847,19 @@ void Client::keepInArea(QRect area, bool partial) if (area.width() < width() || area.height() < height()) resizeWithChecks(qMin(area.width(), width()), qMin(area.height(), height())); } + int tx = x(), ty = y(); if (geometry().right() > area.right() && width() <= area.width()) - move(area.right() - width() + 1, y()); + tx = area.right() - width() + 1; if (geometry().bottom() > area.bottom() && height() <= area.height()) - move(x(), area.bottom() - height() + 1); + ty = area.bottom() - height() + 1; if (!area.contains(geometry().topLeft())) { - int tx = x(); - int ty = y(); if (tx < area.x()) tx = area.x(); if (ty < area.y()) ty = area.y(); - move(tx, ty); } + if (tx != x() || ty != y()) + move(tx, ty); } /*! @@ -1480,16 +1485,16 @@ void Client::getWmNormalHints() // update to match restrictions QSize new_size = adjustedSize(); if (new_size != size() && !isFullScreen()) { - QRect orig_geometry = geometry(); + QRect origClientGeometry(pos() + clientPos(), clientSize()); resizeWithChecks(new_size); if ((!isSpecialWindow() || isToolbar()) && !isFullScreen()) { // try to keep the window in its xinerama screen if possible, // if that fails at least keep it visible somewhere QRect area = workspace()->clientArea(MovementArea, this); - if (area.contains(orig_geometry)) + if (area.contains(origClientGeometry)) keepInArea(area); area = workspace()->clientArea(WorkArea, this); - if (area.contains(orig_geometry)) + if (area.contains(origClientGeometry)) keepInArea(area); } } @@ -1671,7 +1676,7 @@ void Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh, in if (newScreen != rules()->checkScreen(newScreen)) return; // not allowed by rule - QRect orig_geometry = geometry(); + QRect origClientGeometry(pos() + clientPos(), clientSize()); GeometryUpdatesBlocker blocker(this); move(new_pos); plainResize(ns); @@ -1679,7 +1684,7 @@ void Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh, in updateFullScreenHack(QRect(new_pos, QSize(nw, nh))); QRect area = workspace()->clientArea(WorkArea, this); if (!from_tool && (!isSpecialWindow() || isToolbar()) && !isFullScreen() - && area.contains(orig_geometry)) + && area.contains(origClientGeometry)) keepInArea(area); // this is part of the kicker-xinerama-hack... it should be @@ -1701,7 +1706,7 @@ void Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh, in QSize ns = sizeForClientSize(QSize(nw, nh)); if (ns != size()) { // don't restore if some app sets its own size again - QRect orig_geometry = geometry(); + QRect origClientGeometry(pos() + clientPos(), clientSize()); GeometryUpdatesBlocker blocker(this); int save_gravity = xSizeHint.win_gravity; xSizeHint.win_gravity = gravity; @@ -1712,10 +1717,10 @@ void Client::configureRequest(int value_mask, int rx, int ry, int rw, int rh, in // try to keep the window in its xinerama screen if possible, // if that fails at least keep it visible somewhere QRect area = workspace()->clientArea(MovementArea, this); - if (area.contains(orig_geometry)) + if (area.contains(origClientGeometry)) keepInArea(area); area = workspace()->clientArea(WorkArea, this); - if (area.contains(orig_geometry)) + if (area.contains(origClientGeometry)) keepInArea(area); } } diff --git a/glxbackend.cpp b/glxbackend.cpp index 51542c5ccb..e1d313626f 100644 --- a/glxbackend.cpp +++ b/glxbackend.cpp @@ -34,6 +34,8 @@ along with this program. If not, see . #include // Qt #include +// system +#include namespace KWin { @@ -492,6 +494,14 @@ SceneOpenGL::TexturePrivate *GlxBackend::createBackendTexture(SceneOpenGL::Textu void GlxBackend::prepareRenderingFrame() { + if (gs_tripleBufferNeedsDetection) { + // the composite timer floors the repaint frequency. This can pollute our triple buffering + // detection because the glXSwapBuffers call for the new frame has to wait until the pending + // one scanned out. + // So we compensate for that by waiting an extra milisecond to give the driver the chance to + // fllush the buffer queue + usleep(1000); + } present(); startRenderTimer(); glXWaitX(); diff --git a/placement.cpp b/placement.cpp index f39624c9fc..c5ba79cf94 100644 --- a/placement.cpp +++ b/placement.cpp @@ -33,6 +33,7 @@ along with this program. If not, see . #include "client.h" #include "options.h" #include "rules.h" +#include "screens.h" #endif namespace KWin @@ -836,6 +837,13 @@ int Workspace::packPositionLeft(const Client* cl, int oldx, bool left_edge) cons if (oldx <= newx) // try another Xinerama screen newx = clientArea(MaximizeArea, QPoint(cl->geometry().left() - 1, cl->geometry().center().y()), cl->desktop()).left(); + if (cl->titlebarPosition() != KDecorationDefines::PositionLeft) { + QRect geo = cl->geometry(); + int rgt = newx - cl->clientPos().x(); + geo.moveRight(rgt); + if (screens()->intersecting(geo) < 2) + newx = rgt; + } if (oldx <= newx) return oldx; for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) { @@ -856,6 +864,13 @@ int Workspace::packPositionRight(const Client* cl, int oldx, bool right_edge) co if (oldx >= newx) // try another Xinerama screen newx = clientArea(MaximizeArea, QPoint(cl->geometry().right() + 1, cl->geometry().center().y()), cl->desktop()).right(); + if (cl->titlebarPosition() != KDecorationDefines::PositionRight) { + QRect geo = cl->geometry(); + int rgt = newx + cl->width() - (cl->clientSize().width() + cl->clientPos().x()); + geo.moveRight(rgt); + if (screens()->intersecting(geo) < 2) + newx = rgt; + } if (oldx >= newx) return oldx; for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) { @@ -876,6 +891,13 @@ int Workspace::packPositionUp(const Client* cl, int oldy, bool top_edge) const if (oldy <= newy) // try another Xinerama screen newy = clientArea(MaximizeArea, QPoint(cl->geometry().center().x(), cl->geometry().top() - 1), cl->desktop()).top(); + if (cl->titlebarPosition() != KDecorationDefines::PositionTop) { + QRect geo = cl->geometry(); + int top = newy - cl->clientPos().y(); + geo.moveTop(top); + if (screens()->intersecting(geo) < 2) + newy = top; + } if (oldy <= newy) return oldy; for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) { @@ -896,6 +918,13 @@ int Workspace::packPositionDown(const Client* cl, int oldy, bool bottom_edge) co if (oldy >= newy) // try another Xinerama screen newy = clientArea(MaximizeArea, QPoint(cl->geometry().center().x(), cl->geometry().bottom() + 1), cl->desktop()).bottom(); + if (cl->titlebarPosition() != KDecorationDefines::PositionBottom) { + QRect geo = cl->geometry(); + int btm = newy + cl->height() - (cl->clientSize().height() + cl->clientPos().y()); + geo.moveBottom(btm); + if (screens()->intersecting(geo) < 2) + newy = btm; + } if (oldy >= newy) return oldy; for (ClientList::ConstIterator it = clients.constBegin(), end = clients.constEnd(); it != end; ++it) { diff --git a/screens.cpp b/screens.cpp index 23caf59352..c351c7e12e 100644 --- a/screens.cpp +++ b/screens.cpp @@ -117,6 +117,17 @@ int Screens::current() const return m_current; } +int Screens::intersecting(const QRect &r) const +{ + int cnt = 0; + for (int i = 0; i < count(); ++i) { + if (geometry(i).intersects(r)) { + ++cnt; + } + } + return cnt; +} + DesktopWidgetScreens::DesktopWidgetScreens(QObject *parent) : Screens(parent) , m_desktop(QApplication::desktop()) diff --git a/screens.h b/screens.h index f05f818519..0ba11d1aa9 100644 --- a/screens.h +++ b/screens.h @@ -68,6 +68,8 @@ public: inline bool isChanging() { return m_changedTimer->isActive(); } + int intersecting(const QRect &r) const; + public Q_SLOTS: void reconfigure();