diff --git a/effects/backgroundcontrast/contrast.cpp b/effects/backgroundcontrast/contrast.cpp index 3705a64d1..a6fbfa57d 100644 --- a/effects/backgroundcontrast/contrast.cpp +++ b/effects/backgroundcontrast/contrast.cpp @@ -1,486 +1,486 @@ /* * Copyright © 2010 Fredrik Höglund * Copyright © 2011 Philipp Knechtges * Copyright 2014 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "contrast.h" #include "contrastshader.h" // KConfigSkeleton #include #include #include #include #include namespace KWin { static const QByteArray s_contrastAtomName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION"); ContrastEffect::ContrastEffect() { shader = ContrastShader::create(); reconfigure(ReconfigureAll); // ### Hackish way to announce support. // Should be included in _NET_SUPPORTED instead. if (shader && shader->isValid()) { net_wm_contrast_region = effects->announceSupportProperty(s_contrastAtomName, this); KWayland::Server::Display *display = effects->waylandDisplay(); if (display) { m_contrastManager = display->createContrastManager(this); m_contrastManager->create(); } } else { net_wm_contrast_region = 0; } - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long))); - connect(effects, SIGNAL(screenGeometryChanged(QSize)), this, SLOT(slotScreenGeometryChanged())); + connect(effects, &EffectsHandler::windowAdded, this, &ContrastEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowDeleted, this, &ContrastEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::propertyNotify, this, &ContrastEffect::slotPropertyNotify); + connect(effects, &EffectsHandler::screenGeometryChanged, this, &ContrastEffect::slotScreenGeometryChanged); connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this] { if (shader && shader->isValid()) { net_wm_contrast_region = effects->announceSupportProperty(s_contrastAtomName, this); } } ); // Fetch the contrast regions for all windows for (EffectWindow *window: effects->stackingOrder()) { updateContrastRegion(window); } } ContrastEffect::~ContrastEffect() { delete shader; } void ContrastEffect::slotScreenGeometryChanged() { effects->makeOpenGLContextCurrent(); if (!supported()) { effects->reloadEffect(this); return; } for (EffectWindow *window: effects->stackingOrder()) { updateContrastRegion(window); } } void ContrastEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) if (shader) shader->init(); if (!shader || !shader->isValid()) { effects->removeSupportProperty(s_contrastAtomName, this); delete m_contrastManager; m_contrastManager = nullptr; } } void ContrastEffect::updateContrastRegion(EffectWindow *w) { QRegion region; float colorTransform[16]; QByteArray value; if (net_wm_contrast_region != XCB_ATOM_NONE) { value = w->readProperty(net_wm_contrast_region, net_wm_contrast_region, 32); if (value.size() > 0 && !((value.size() - (16 * sizeof(uint32_t))) % ((4 * sizeof(uint32_t))))) { const uint32_t *cardinals = reinterpret_cast(value.constData()); const float *floatCardinals = reinterpret_cast(value.constData()); unsigned int i = 0; for (; i < ((value.size() - (16 * sizeof(uint32_t)))) / sizeof(uint32_t);) { int x = cardinals[i++]; int y = cardinals[i++]; int w = cardinals[i++]; int h = cardinals[i++]; region += QRect(x, y, w, h); } for (unsigned int j = 0; j < 16; ++j) { colorTransform[j] = floatCardinals[i + j]; } QMatrix4x4 colorMatrix(colorTransform); m_colorMatrices[w] = colorMatrix; } } KWayland::Server::SurfaceInterface *surf = w->surface(); if (surf && surf->contrast()) { region = surf->contrast()->region(); m_colorMatrices[w] = colorMatrix(surf->contrast()->contrast(), surf->contrast()->intensity(), surf->contrast()->saturation()); } //!value.isNull() full window in X11 case, surf->contrast() //valid, full window in wayland case if (region.isEmpty() && (!value.isNull() || (surf && surf->contrast()))) { // Set the data to a dummy value. // This is needed to be able to distinguish between the value not // being set, and being set to an empty region. w->setData(WindowBackgroundContrastRole, 1); } else w->setData(WindowBackgroundContrastRole, region); } void ContrastEffect::slotWindowAdded(EffectWindow *w) { KWayland::Server::SurfaceInterface *surf = w->surface(); if (surf) { m_contrastChangedConnections[w] = connect(surf, &KWayland::Server::SurfaceInterface::contrastChanged, this, [this, w] () { if (w) { updateContrastRegion(w); } }); } updateContrastRegion(w); } void ContrastEffect::slotWindowDeleted(EffectWindow *w) { if (m_contrastChangedConnections.contains(w)) { disconnect(m_contrastChangedConnections[w]); m_contrastChangedConnections.remove(w); m_colorMatrices.remove(w); } } void ContrastEffect::slotPropertyNotify(EffectWindow *w, long atom) { if (w && atom == net_wm_contrast_region && net_wm_contrast_region != XCB_ATOM_NONE) { updateContrastRegion(w); } } QMatrix4x4 ContrastEffect::colorMatrix(qreal contrast, qreal intensity, qreal saturation) { QMatrix4x4 satMatrix; //saturation QMatrix4x4 intMatrix; //intensity QMatrix4x4 contMatrix; //contrast //Saturation matrix if (!qFuzzyCompare(saturation, 1.0)) { const qreal rval = (1.0 - saturation) * .2126; const qreal gval = (1.0 - saturation) * .7152; const qreal bval = (1.0 - saturation) * .0722; satMatrix = QMatrix4x4(rval + saturation, rval, rval, 0.0, gval, gval + saturation, gval, 0.0, bval, bval, bval + saturation, 0.0, 0, 0, 0, 1.0); } //IntensityMatrix if (!qFuzzyCompare(intensity, 1.0)) { intMatrix.scale(intensity, intensity, intensity); } //Contrast Matrix if (!qFuzzyCompare(contrast, 1.0)) { const float transl = (1.0 - contrast) / 2.0; contMatrix = QMatrix4x4(contrast, 0, 0, 0.0, 0, contrast, 0, 0.0, 0, 0, contrast, 0.0, transl, transl, transl, 1.0); } QMatrix4x4 colorMatrix = contMatrix * satMatrix * intMatrix; //colorMatrix = colorMatrix.transposed(); return colorMatrix; } bool ContrastEffect::enabledByDefault() { GLPlatform *gl = GLPlatform::instance(); if (gl->isIntel() && gl->chipClass() < SandyBridge) return false; if (gl->isSoftwareEmulation()) { return false; } return true; } bool ContrastEffect::supported() { bool supported = effects->isOpenGLCompositing() && GLRenderTarget::supported(); if (supported) { int maxTexSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); const QSize screenSize = effects->virtualScreenSize(); if (screenSize.width() > maxTexSize || screenSize.height() > maxTexSize) supported = false; } return supported; } QRegion ContrastEffect::contrastRegion(const EffectWindow *w) const { QRegion region; const QVariant value = w->data(WindowBackgroundContrastRole); if (value.isValid()) { const QRegion appRegion = qvariant_cast(value); if (!appRegion.isEmpty()) { region |= appRegion.translated(w->contentsRect().topLeft()) & w->decorationInnerRect(); } else { // An empty region means that the blur effect should be enabled // for the whole window. region = w->decorationInnerRect(); } } return region; } void ContrastEffect::uploadRegion(QVector2D *&map, const QRegion ®ion) { for (const QRect &r : region) { const QVector2D topLeft(r.x(), r.y()); const QVector2D topRight(r.x() + r.width(), r.y()); const QVector2D bottomLeft(r.x(), r.y() + r.height()); const QVector2D bottomRight(r.x() + r.width(), r.y() + r.height()); // First triangle *(map++) = topRight; *(map++) = topLeft; *(map++) = bottomLeft; // Second triangle *(map++) = bottomLeft; *(map++) = bottomRight; *(map++) = topRight; } } void ContrastEffect::uploadGeometry(GLVertexBuffer *vbo, const QRegion ®ion) { const int vertexCount = region.rectCount() * 6; if (!vertexCount) return; QVector2D *map = (QVector2D *) vbo->map(vertexCount * sizeof(QVector2D)); uploadRegion(map, region); vbo->unmap(); const GLVertexAttrib layout[] = { { VA_Position, 2, GL_FLOAT, 0 }, { VA_TexCoord, 2, GL_FLOAT, 0 } }; vbo->setAttribLayout(layout, 2, sizeof(QVector2D)); } void ContrastEffect::prePaintScreen(ScreenPrePaintData &data, int time) { m_paintedArea = QRegion(); m_currentContrast = QRegion(); effects->prePaintScreen(data, time); } void ContrastEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { // this effect relies on prePaintWindow being called in the bottom to top order effects->prePaintWindow(w, data, time); if (!w->isPaintingEnabled()) { return; } if (!shader || !shader->isValid()) { return; } const QRegion oldPaint = data.paint; // we don't have to blur a region we don't see m_currentContrast -= data.clip; // if we have to paint a non-opaque part of this window that intersects with the // currently blurred region (which is not cached) we have to redraw the whole region if ((data.paint-data.clip).intersects(m_currentContrast)) { data.paint |= m_currentContrast; } // in case this window has regions to be blurred const QRect screen = effects->virtualScreenGeometry(); const QRegion contrastArea = contrastRegion(w).translated(w->pos()) & screen; // we are not caching the window // if this window or an window underneath the modified area is painted again we have to // do everything if (m_paintedArea.intersects(contrastArea) || data.paint.intersects(contrastArea)) { data.paint |= contrastArea; // we have to check again whether we do not damage a blurred area // of a window we do not cache if (contrastArea.intersects(m_currentContrast)) { data.paint |= m_currentContrast; } } m_currentContrast |= contrastArea; // m_paintedArea keep track of all repainted areas m_paintedArea -= data.clip; m_paintedArea |= data.paint; } bool ContrastEffect::shouldContrast(const EffectWindow *w, int mask, const WindowPaintData &data) const { if (!shader || !shader->isValid()) return false; if (effects->activeFullScreenEffect() && !w->data(WindowForceBackgroundContrastRole).toBool()) return false; if (w->isDesktop()) return false; bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0); bool translated = data.xTranslation() || data.yTranslation(); if ((scaled || (translated || (mask & PAINT_WINDOW_TRANSFORMED))) && !w->data(WindowForceBackgroundContrastRole).toBool()) return false; if (!w->hasAlpha()) return false; return true; } void ContrastEffect::drawWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { const QRect screen = GLRenderTarget::virtualScreenGeometry(); if (shouldContrast(w, mask, data)) { QRegion shape = region & contrastRegion(w).translated(w->pos()) & screen; // let's do the evil parts - someone wants to blur behind a transformed window const bool translated = data.xTranslation() || data.yTranslation(); const bool scaled = data.xScale() != 1 || data.yScale() != 1; if (scaled) { QPoint pt = shape.boundingRect().topLeft(); QVector shapeRects = shape.rects(); shape = QRegion(); // clear foreach (QRect r, shapeRects) { r.moveTo(pt.x() + (r.x() - pt.x()) * data.xScale() + data.xTranslation(), pt.y() + (r.y() - pt.y()) * data.yScale() + data.yTranslation()); r.setWidth(r.width() * data.xScale()); r.setHeight(r.height() * data.yScale()); shape |= r; } shape = shape & region; //Only translated, not scaled } else if (translated) { shape = shape.translated(data.xTranslation(), data.yTranslation()); shape = shape & region; } if (!shape.isEmpty()) { doContrast(w, shape, screen, data.opacity(), data.screenProjectionMatrix()); } } // Draw the window over the contrast area effects->drawWindow(w, mask, region, data); } void ContrastEffect::paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity) { //FIXME: this is a no-op for now, it should figure out the right contrast, intensity, saturation effects->paintEffectFrame(frame, region, opacity, frameOpacity); } void ContrastEffect::doContrast(EffectWindow *w, const QRegion& shape, const QRect& screen, const float opacity, const QMatrix4x4 &screenProjection) { const QRegion actualShape = shape & screen; const QRect r = actualShape.boundingRect(); qreal scale = GLRenderTarget::virtualScreenScale(); // Upload geometry for the horizontal and vertical passes GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); uploadGeometry(vbo, actualShape); vbo->bindArrays(); // Create a scratch texture and copy the area in the back buffer that we're // going to blur into it GLTexture scratch(GL_RGBA8, r.width() * scale, r.height() * scale); scratch.setFilter(GL_LINEAR); scratch.setWrapMode(GL_CLAMP_TO_EDGE); scratch.bind(); const QRect sg = GLRenderTarget::virtualScreenGeometry(); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (r.x() - sg.x()) * scale, (sg.height() - (r.y() - sg.y() + r.height())) * scale, scratch.width(), scratch.height()); // Draw the texture on the offscreen framebuffer object, while blurring it horizontally shader->setColorMatrix(m_colorMatrices.value(w)); shader->bind(); shader->setOpacity(opacity); // Set up the texture matrix to transform from screen coordinates // to texture coordinates. QMatrix4x4 textureMatrix; textureMatrix.scale(1.0 / r.width(), -1.0 / r.height(), 1); textureMatrix.translate(-r.x(), -r.height() - r.y(), 0); shader->setTextureMatrix(textureMatrix); shader->setModelViewProjectionMatrix(screenProjection); vbo->draw(GL_TRIANGLES, 0, actualShape.rectCount() * 6); scratch.unbind(); scratch.discard(); vbo->unbindArrays(); if (opacity < 1.0) { glDisable(GL_BLEND); } shader->unbind(); } } // namespace KWin diff --git a/effects/blur/blur.cpp b/effects/blur/blur.cpp index be4d4fa20..c0e771dc0 100644 --- a/effects/blur/blur.cpp +++ b/effects/blur/blur.cpp @@ -1,771 +1,771 @@ /* * Copyright © 2010 Fredrik Höglund * Copyright © 2011 Philipp Knechtges * Copyright © 2018 Alex Nemeth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. if not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "blur.h" #include "blurshader.h" // KConfigSkeleton #include "blurconfig.h" #include #include #include #include // for QGuiApplication #include #include // for ceil() #include #include #include #include #include #include namespace KWin { static const QByteArray s_blurAtomName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"); BlurEffect::BlurEffect() { initConfig(); m_shader = new BlurShader(this); initBlurStrengthValues(); reconfigure(ReconfigureAll); // ### Hackish way to announce support. // Should be included in _NET_SUPPORTED instead. if (m_shader && m_shader->isValid() && m_renderTargetsValid) { net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); KWayland::Server::Display *display = effects->waylandDisplay(); if (display) { m_blurManager = display->createBlurManager(this); m_blurManager->create(); } } else { net_wm_blur_region = 0; } - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long))); - connect(effects, SIGNAL(screenGeometryChanged(QSize)), this, SLOT(slotScreenGeometryChanged())); + connect(effects, &EffectsHandler::windowAdded, this, &BlurEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowDeleted, this, &BlurEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::propertyNotify, this, &BlurEffect::slotPropertyNotify); + connect(effects, &EffectsHandler::screenGeometryChanged, this, &BlurEffect::slotScreenGeometryChanged); connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this] { if (m_shader && m_shader->isValid() && m_renderTargetsValid) { net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this); } } ); // Fetch the blur regions for all windows foreach (EffectWindow *window, effects->stackingOrder()) updateBlurRegion(window); } BlurEffect::~BlurEffect() { deleteFBOs(); } void BlurEffect::slotScreenGeometryChanged() { effects->makeOpenGLContextCurrent(); updateTexture(); // Fetch the blur regions for all windows foreach (EffectWindow *window, effects->stackingOrder()) updateBlurRegion(window); effects->doneOpenGLContextCurrent(); } bool BlurEffect::renderTargetsValid() const { return !m_renderTargets.isEmpty() && std::find_if(m_renderTargets.cbegin(), m_renderTargets.cend(), [](const GLRenderTarget *target) { return !target->valid(); }) == m_renderTargets.cend(); } void BlurEffect::deleteFBOs() { qDeleteAll(m_renderTargets); m_renderTargets.clear(); m_renderTextures.clear(); } void BlurEffect::updateTexture() { deleteFBOs(); /* Reserve memory for: * - The original sized texture (1) * - The downsized textures (m_downSampleIterations) * - The helper texture (1) */ m_renderTargets.reserve(m_downSampleIterations + 2); m_renderTextures.reserve(m_downSampleIterations + 2); for (int i = 0; i <= m_downSampleIterations; i++) { m_renderTextures.append(GLTexture(GL_RGBA8, effects->virtualScreenSize() / (1 << i))); m_renderTextures.last().setFilter(GL_LINEAR); m_renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); m_renderTargets.append(new GLRenderTarget(m_renderTextures.last())); } // This last set is used as a temporary helper texture m_renderTextures.append(GLTexture(GL_RGBA8, effects->virtualScreenSize())); m_renderTextures.last().setFilter(GL_LINEAR); m_renderTextures.last().setWrapMode(GL_CLAMP_TO_EDGE); m_renderTargets.append(new GLRenderTarget(m_renderTextures.last())); m_renderTargetsValid = renderTargetsValid(); // Prepare the stack for the rendering m_renderTargetStack.clear(); m_renderTargetStack.reserve(m_downSampleIterations * 2); // Upsample for (int i = 1; i < m_downSampleIterations; i++) { m_renderTargetStack.push(m_renderTargets[i]); } // Downsample for (int i = m_downSampleIterations; i > 0; i--) { m_renderTargetStack.push(m_renderTargets[i]); } // Copysample m_renderTargetStack.push(m_renderTargets[0]); // Generate the noise helper texture generateNoiseTexture(); } void BlurEffect::initBlurStrengthValues() { // This function creates an array of blur strength values that are evenly distributed // The range of the slider on the blur settings UI int numOfBlurSteps = 15; int remainingSteps = numOfBlurSteps; /* * Explanation for these numbers: * * The texture blur amount depends on the downsampling iterations and the offset value. * By changing the offset we can alter the blur amount without relying on further downsampling. * But there is a minimum and maximum value of offset per downsample iteration before we * get artifacts. * * The minOffset variable is the minimum offset value for an iteration before we * get blocky artifacts because of the downsampling. * * The maxOffset value is the maximum offset value for an iteration before we * get diagonal line artifacts because of the nature of the dual kawase blur algorithm. * * The expandSize value is the minimum value for an iteration before we reach the end * of a texture in the shader and sample outside of the area that was copied into the * texture from the screen. */ // {minOffset, maxOffset, expandSize} blurOffsets.append({1.0, 2.0, 10}); // Down sample size / 2 blurOffsets.append({2.0, 3.0, 20}); // Down sample size / 4 blurOffsets.append({2.0, 5.0, 50}); // Down sample size / 8 blurOffsets.append({3.0, 8.0, 150}); // Down sample size / 16 //blurOffsets.append({5.0, 10.0, 400}); // Down sample size / 32 //blurOffsets.append({7.0, ?.0}); // Down sample size / 64 float offsetSum = 0; for (int i = 0; i < blurOffsets.size(); i++) { offsetSum += blurOffsets[i].maxOffset - blurOffsets[i].minOffset; } for (int i = 0; i < blurOffsets.size(); i++) { int iterationNumber = std::ceil((blurOffsets[i].maxOffset - blurOffsets[i].minOffset) / offsetSum * numOfBlurSteps); remainingSteps -= iterationNumber; if (remainingSteps < 0) { iterationNumber += remainingSteps; } float offsetDifference = blurOffsets[i].maxOffset - blurOffsets[i].minOffset; for (int j = 1; j <= iterationNumber; j++) { // {iteration, offset} blurStrengthValues.append({i + 1, blurOffsets[i].minOffset + (offsetDifference / iterationNumber) * j}); } } } void BlurEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) BlurConfig::self()->read(); int blurStrength = BlurConfig::blurStrength() - 1; m_downSampleIterations = blurStrengthValues[blurStrength].iteration; m_offset = blurStrengthValues[blurStrength].offset; m_expandSize = blurOffsets[m_downSampleIterations - 1].expandSize; m_noiseStrength = BlurConfig::noiseStrength(); m_scalingFactor = qMax(1.0, QGuiApplication::primaryScreen()->logicalDotsPerInch() / 96.0); updateTexture(); if (!m_shader || !m_shader->isValid()) { effects->removeSupportProperty(s_blurAtomName, this); delete m_blurManager; m_blurManager = nullptr; } // Update all windows for the blur to take effect effects->addRepaintFull(); } void BlurEffect::updateBlurRegion(EffectWindow *w) const { QRegion region; QByteArray value; if (net_wm_blur_region != XCB_ATOM_NONE) { value = w->readProperty(net_wm_blur_region, XCB_ATOM_CARDINAL, 32); if (value.size() > 0 && !(value.size() % (4 * sizeof(uint32_t)))) { const uint32_t *cardinals = reinterpret_cast(value.constData()); for (unsigned int i = 0; i < value.size() / sizeof(uint32_t);) { int x = cardinals[i++]; int y = cardinals[i++]; int w = cardinals[i++]; int h = cardinals[i++]; region += QRect(x, y, w, h); } } } KWayland::Server::SurfaceInterface *surf = w->surface(); if (surf && surf->blur()) { region = surf->blur()->region(); } //!value.isNull() full window in X11 case, surf->blur() //valid, full window in wayland case if (region.isEmpty() && (!value.isNull() || (surf && surf->blur()))) { // Set the data to a dummy value. // This is needed to be able to distinguish between the value not // being set, and being set to an empty region. w->setData(WindowBlurBehindRole, 1); } else w->setData(WindowBlurBehindRole, region); } void BlurEffect::slotWindowAdded(EffectWindow *w) { KWayland::Server::SurfaceInterface *surf = w->surface(); if (surf) { windowBlurChangedConnections[w] = connect(surf, &KWayland::Server::SurfaceInterface::blurChanged, this, [this, w] () { if (w) { updateBlurRegion(w); } }); } updateBlurRegion(w); } void BlurEffect::slotWindowDeleted(EffectWindow *w) { auto it = windowBlurChangedConnections.find(w); if (it == windowBlurChangedConnections.end()) { return; } disconnect(*it); windowBlurChangedConnections.erase(it); } void BlurEffect::slotPropertyNotify(EffectWindow *w, long atom) { if (w && atom == net_wm_blur_region && net_wm_blur_region != XCB_ATOM_NONE) { updateBlurRegion(w); } } bool BlurEffect::enabledByDefault() { GLPlatform *gl = GLPlatform::instance(); if (gl->isIntel() && gl->chipClass() < SandyBridge) return false; if (gl->isSoftwareEmulation()) { return false; } return true; } bool BlurEffect::supported() { bool supported = effects->isOpenGLCompositing() && GLRenderTarget::supported() && GLRenderTarget::blitSupported(); if (supported) { int maxTexSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); const QSize screenSize = effects->virtualScreenSize(); if (screenSize.width() > maxTexSize || screenSize.height() > maxTexSize) supported = false; } return supported; } QRect BlurEffect::expand(const QRect &rect) const { return rect.adjusted(-m_expandSize, -m_expandSize, m_expandSize, m_expandSize); } QRegion BlurEffect::expand(const QRegion ®ion) const { QRegion expanded; for (const QRect &rect : region) { expanded += expand(rect); } return expanded; } QRegion BlurEffect::blurRegion(const EffectWindow *w) const { QRegion region; const QVariant value = w->data(WindowBlurBehindRole); if (value.isValid()) { const QRegion appRegion = qvariant_cast(value); if (!appRegion.isEmpty()) { if (w->decorationHasAlpha() && effects->decorationSupportsBlurBehind()) { region = w->shape(); region -= w->decorationInnerRect(); } region |= appRegion.translated(w->contentsRect().topLeft()) & w->decorationInnerRect(); } else { // An empty region means that the blur effect should be enabled // for the whole window. region = w->shape(); } } else if (w->decorationHasAlpha() && effects->decorationSupportsBlurBehind()) { // If the client hasn't specified a blur region, we'll only enable // the effect behind the decoration. region = w->shape(); region -= w->decorationInnerRect(); } return region; } void BlurEffect::uploadRegion(QVector2D *&map, const QRegion ®ion, const int downSampleIterations) { for (int i = 0; i <= downSampleIterations; i++) { const int divisionRatio = (1 << i); for (const QRect &r : region) { const QVector2D topLeft( r.x() / divisionRatio, r.y() / divisionRatio); const QVector2D topRight( (r.x() + r.width()) / divisionRatio, r.y() / divisionRatio); const QVector2D bottomLeft( r.x() / divisionRatio, (r.y() + r.height()) / divisionRatio); const QVector2D bottomRight((r.x() + r.width()) / divisionRatio, (r.y() + r.height()) / divisionRatio); // First triangle *(map++) = topRight; *(map++) = topLeft; *(map++) = bottomLeft; // Second triangle *(map++) = bottomLeft; *(map++) = bottomRight; *(map++) = topRight; } } } void BlurEffect::uploadGeometry(GLVertexBuffer *vbo, const QRegion &blurRegion, const QRegion &windowRegion) { const int vertexCount = ((blurRegion.rectCount() * (m_downSampleIterations + 1)) + windowRegion.rectCount()) * 6; if (!vertexCount) return; QVector2D *map = (QVector2D *) vbo->map(vertexCount * sizeof(QVector2D)); uploadRegion(map, blurRegion, m_downSampleIterations); uploadRegion(map, windowRegion, 0); vbo->unmap(); const GLVertexAttrib layout[] = { { VA_Position, 2, GL_FLOAT, 0 }, { VA_TexCoord, 2, GL_FLOAT, 0 } }; vbo->setAttribLayout(layout, 2, sizeof(QVector2D)); } void BlurEffect::prePaintScreen(ScreenPrePaintData &data, int time) { m_damagedArea = QRegion(); m_paintedArea = QRegion(); m_currentBlur = QRegion(); effects->prePaintScreen(data, time); } void BlurEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { // this effect relies on prePaintWindow being called in the bottom to top order effects->prePaintWindow(w, data, time); if (!w->isPaintingEnabled()) { return; } if (!m_shader || !m_shader->isValid()) { return; } // to blur an area partially we have to shrink the opaque area of a window QRegion newClip; const QRegion oldClip = data.clip; for (const QRect &rect : data.clip) { newClip |= rect.adjusted(m_expandSize, m_expandSize, -m_expandSize, -m_expandSize); } data.clip = newClip; const QRegion oldPaint = data.paint; // we don't have to blur a region we don't see m_currentBlur -= newClip; // if we have to paint a non-opaque part of this window that intersects with the // currently blurred region we have to redraw the whole region if ((data.paint - oldClip).intersects(m_currentBlur)) { data.paint |= m_currentBlur; } // in case this window has regions to be blurred const QRect screen = effects->virtualScreenGeometry(); const QRegion blurArea = blurRegion(w).translated(w->pos()) & screen; const QRegion expandedBlur = (w->isDock() ? blurArea : expand(blurArea)) & screen; // if this window or a window underneath the blurred area is painted again we have to // blur everything if (m_paintedArea.intersects(expandedBlur) || data.paint.intersects(blurArea)) { data.paint |= expandedBlur; // we keep track of the "damage propagation" m_damagedArea |= (w->isDock() ? (expandedBlur & m_damagedArea) : expand(expandedBlur & m_damagedArea)) & blurArea; // we have to check again whether we do not damage a blurred area // of a window if (expandedBlur.intersects(m_currentBlur)) { data.paint |= m_currentBlur; } } m_currentBlur |= expandedBlur; // we don't consider damaged areas which are occluded and are not // explicitly damaged by this window m_damagedArea -= data.clip; m_damagedArea |= oldPaint; // in contrast to m_damagedArea does m_paintedArea keep track of all repainted areas m_paintedArea -= data.clip; m_paintedArea |= data.paint; } bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const { if (!m_renderTargetsValid || !m_shader || !m_shader->isValid()) return false; if (effects->activeFullScreenEffect() && !w->data(WindowForceBlurRole).toBool()) return false; if (w->isDesktop()) return false; bool scaled = !qFuzzyCompare(data.xScale(), 1.0) && !qFuzzyCompare(data.yScale(), 1.0); bool translated = data.xTranslation() || data.yTranslation(); if ((scaled || (translated || (mask & PAINT_WINDOW_TRANSFORMED))) && !w->data(WindowForceBlurRole).toBool()) return false; bool blurBehindDecos = effects->decorationsHaveAlpha() && effects->decorationSupportsBlurBehind(); if (!w->hasAlpha() && w->opacity() >= 1.0 && !(blurBehindDecos && w->hasDecoration())) return false; return true; } void BlurEffect::drawWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { const QRect screen = GLRenderTarget::virtualScreenGeometry(); if (shouldBlur(w, mask, data)) { QRegion shape = region & blurRegion(w).translated(w->pos()) & screen; // let's do the evil parts - someone wants to blur behind a transformed window const bool translated = data.xTranslation() || data.yTranslation(); const bool scaled = data.xScale() != 1 || data.yScale() != 1; if (scaled) { QPoint pt = shape.boundingRect().topLeft(); QVector shapeRects = shape.rects(); shape = QRegion(); // clear foreach (QRect r, shapeRects) { r.moveTo(pt.x() + (r.x() - pt.x()) * data.xScale() + data.xTranslation(), pt.y() + (r.y() - pt.y()) * data.yScale() + data.yTranslation()); r.setWidth(r.width() * data.xScale()); r.setHeight(r.height() * data.yScale()); shape |= r; } shape = shape & region; //Only translated, not scaled } else if (translated) { shape = shape.translated(data.xTranslation(), data.yTranslation()); shape = shape & region; } if (!shape.isEmpty()) { doBlur(shape, screen, data.opacity(), data.screenProjectionMatrix(), w->isDock(), w->geometry()); } } // Draw the window over the blurred area effects->drawWindow(w, mask, region, data); } void BlurEffect::paintEffectFrame(EffectFrame *frame, QRegion region, double opacity, double frameOpacity) { const QRect screen = effects->virtualScreenGeometry(); bool valid = m_renderTargetsValid && m_shader && m_shader->isValid(); QRegion shape = frame->geometry().adjusted(-borderSize, -borderSize, borderSize, borderSize) & screen; if (valid && !shape.isEmpty() && region.intersects(shape.boundingRect()) && frame->style() != EffectFrameNone) { doBlur(shape, screen, opacity * frameOpacity, frame->screenProjectionMatrix(), false, frame->geometry()); } effects->paintEffectFrame(frame, region, opacity, frameOpacity); } void BlurEffect::generateNoiseTexture() { if (m_noiseStrength == 0) { return; } // Init randomness based on time qsrand((uint)QTime::currentTime().msec()); QImage noiseImage(QSize(256, 256), QImage::Format_Grayscale8); for (int y = 0; y < noiseImage.height(); y++) { uint8_t *noiseImageLine = (uint8_t *) noiseImage.scanLine(y); for (int x = 0; x < noiseImage.width(); x++) { noiseImageLine[x] = qrand() % m_noiseStrength + (128 - m_noiseStrength / 2); } } // The noise texture looks distorted when not scaled with integer noiseImage = noiseImage.scaled(noiseImage.size() * m_scalingFactor); m_noiseTexture = GLTexture(noiseImage); m_noiseTexture.setFilter(GL_NEAREST); m_noiseTexture.setWrapMode(GL_REPEAT); } void BlurEffect::doBlur(const QRegion& shape, const QRect& screen, const float opacity, const QMatrix4x4 &screenProjection, bool isDock, QRect windowRect) { // Blur would not render correctly on a secondary monitor because of wrong coordinates // BUG: 393723 const int xTranslate = -screen.x(); const int yTranslate = effects->virtualScreenSize().height() - screen.height() - screen.y(); const QRegion expandedBlurRegion = expand(shape) & expand(screen); // Upload geometry for the down and upsample iterations GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); uploadGeometry(vbo, expandedBlurRegion.translated(xTranslate, yTranslate), shape); vbo->bindArrays(); const QRect sourceRect = expandedBlurRegion.boundingRect() & screen; const QRect destRect = sourceRect.translated(xTranslate, yTranslate); GLRenderTarget::pushRenderTargets(m_renderTargetStack); int blurRectCount = expandedBlurRegion.rectCount() * 6; /* * If the window is a dock or panel we avoid the "extended blur" effect. * Extended blur is when windows that are not under the blurred area affect * the final blur result. * We want to avoid this on panels, because it looks really weird and ugly * when maximized windows or windows near the panel affect the dock blur. */ if (isDock) { m_renderTargets.last()->blitFromFramebuffer(sourceRect, destRect); copyScreenSampleTexture(vbo, blurRectCount, shape.translated(xTranslate, yTranslate), screenProjection); } else { m_renderTargets.first()->blitFromFramebuffer(sourceRect, destRect); // Remove the m_renderTargets[0] from the top of the stack that we will not use GLRenderTarget::popRenderTarget(); } downSampleTexture(vbo, blurRectCount); upSampleTexture(vbo, blurRectCount); // Modulate the blurred texture with the window opacity if the window isn't opaque if (opacity < 1.0) { glEnable(GL_BLEND); #if 1 // bow shape, always above y = x float o = 1.0f-opacity; o = 1.0f - o*o; #else // sigmoid shape, above y = x for x > 0.5, below y = x for x < 0.5 float o = 2.0f*opacity - 1.0f; o = 0.5f + o / (1.0f + qAbs(o)); #endif glBlendColor(0, 0, 0, o); glBlendFunc(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA); } upscaleRenderToScreen(vbo, blurRectCount * (m_downSampleIterations + 1), shape.rectCount() * 6, screenProjection, windowRect.topLeft()); if (opacity < 1.0) { glDisable(GL_BLEND); } vbo->unbindArrays(); } void BlurEffect::upscaleRenderToScreen(GLVertexBuffer *vbo, int vboStart, int blurRectCount, QMatrix4x4 screenProjection, QPoint windowPosition) { glActiveTexture(GL_TEXTURE0); m_renderTextures[1].bind(); if (m_noiseStrength > 0) { m_shader->bind(BlurShader::NoiseSampleType); m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale()); m_shader->setNoiseTextureSize(m_noiseTexture.size() * GLRenderTarget::virtualScreenScale()); m_shader->setTexturePosition(windowPosition * GLRenderTarget::virtualScreenScale()); glActiveTexture(GL_TEXTURE1); m_noiseTexture.bind(); } else { m_shader->bind(BlurShader::UpSampleType); m_shader->setTargetTextureSize(m_renderTextures[0].size() * GLRenderTarget::virtualScreenScale()); } m_shader->setOffset(m_offset); m_shader->setModelViewProjectionMatrix(screenProjection); //Render to the screen vbo->draw(GL_TRIANGLES, vboStart, blurRectCount); glActiveTexture(GL_TEXTURE0); m_shader->unbind(); } void BlurEffect::downSampleTexture(GLVertexBuffer *vbo, int blurRectCount) { QMatrix4x4 modelViewProjectionMatrix; m_shader->bind(BlurShader::DownSampleType); m_shader->setOffset(m_offset); for (int i = 1; i <= m_downSampleIterations; i++) { modelViewProjectionMatrix.setToIdentity(); modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); m_shader->setTargetTextureSize(m_renderTextures[i].size()); //Copy the image from this texture m_renderTextures[i - 1].bind(); vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); GLRenderTarget::popRenderTarget(); } m_shader->unbind(); } void BlurEffect::upSampleTexture(GLVertexBuffer *vbo, int blurRectCount) { QMatrix4x4 modelViewProjectionMatrix; m_shader->bind(BlurShader::UpSampleType); m_shader->setOffset(m_offset); for (int i = m_downSampleIterations - 1; i >= 1; i--) { modelViewProjectionMatrix.setToIdentity(); modelViewProjectionMatrix.ortho(0, m_renderTextures[i].width(), m_renderTextures[i].height(), 0 , 0, 65535); m_shader->setModelViewProjectionMatrix(modelViewProjectionMatrix); m_shader->setTargetTextureSize(m_renderTextures[i].size()); //Copy the image from this texture m_renderTextures[i + 1].bind(); vbo->draw(GL_TRIANGLES, blurRectCount * i, blurRectCount); GLRenderTarget::popRenderTarget(); } m_shader->unbind(); } void BlurEffect::copyScreenSampleTexture(GLVertexBuffer *vbo, int blurRectCount, QRegion blurShape, QMatrix4x4 screenProjection) { m_shader->bind(BlurShader::CopySampleType); m_shader->setModelViewProjectionMatrix(screenProjection); m_shader->setTargetTextureSize(effects->virtualScreenSize()); /* * This '1' sized adjustment is necessary do avoid windows affecting the blur that are * right next to this window. */ m_shader->setBlurRect(blurShape.boundingRect().adjusted(1, 1, -1, -1), effects->virtualScreenSize()); m_renderTextures.last().bind(); vbo->draw(GL_TRIANGLES, 0, blurRectCount); GLRenderTarget::popRenderTarget(); m_shader->unbind(); } } // namespace KWin diff --git a/effects/coverswitch/coverswitch.cpp b/effects/coverswitch/coverswitch.cpp index 4d1074dc3..29b7009c4 100644 --- a/effects/coverswitch/coverswitch.cpp +++ b/effects/coverswitch/coverswitch.cpp @@ -1,1005 +1,1005 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "coverswitch.h" // KConfigSkeleton #include "coverswitchconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { CoverSwitchEffect::CoverSwitchEffect() : mActivated(0) , angle(60.0) , animation(false) , start(false) , stop(false) , stopRequested(false) , startRequested(false) , zPosition(900.0) , scaleFactor(0.0) , direction(Left) , selected_window(0) , captionFrame(NULL) , primaryTabBox(false) , secondaryTabBox(false) { initConfig(); reconfigure(ReconfigureAll); // Caption frame captionFont.setBold(true); captionFont.setPointSize(captionFont.pointSize() * 2); if (effects->compositingType() == OpenGL2Compositing) { m_reflectionShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("coverswitch-reflection.glsl")); } else { m_reflectionShader = NULL; } - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(tabBoxAdded(int)), this, SLOT(slotTabBoxAdded(int))); - connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed())); - connect(effects, SIGNAL(tabBoxUpdated()), this, SLOT(slotTabBoxUpdated())); - connect(effects, SIGNAL(tabBoxKeyEvent(QKeyEvent*)), this, SLOT(slotTabBoxKeyEvent(QKeyEvent*))); + connect(effects, &EffectsHandler::windowClosed, this, &CoverSwitchEffect::slotWindowClosed); + connect(effects, &EffectsHandler::tabBoxAdded, this, &CoverSwitchEffect::slotTabBoxAdded); + connect(effects, &EffectsHandler::tabBoxClosed, this, &CoverSwitchEffect::slotTabBoxClosed); + connect(effects, &EffectsHandler::tabBoxUpdated, this, &CoverSwitchEffect::slotTabBoxUpdated); + connect(effects, &EffectsHandler::tabBoxKeyEvent, this, &CoverSwitchEffect::slotTabBoxKeyEvent); } CoverSwitchEffect::~CoverSwitchEffect() { delete captionFrame; delete m_reflectionShader; } bool CoverSwitchEffect::supported() { return effects->isOpenGLCompositing() && effects->animationsSupported(); } void CoverSwitchEffect::reconfigure(ReconfigureFlags) { CoverSwitchConfig::self()->read(); animationDuration = std::chrono::milliseconds( animationTime(200)); animateSwitch = CoverSwitchConfig::animateSwitch(); animateStart = CoverSwitchConfig::animateStart(); animateStop = CoverSwitchConfig::animateStop(); reflection = CoverSwitchConfig::reflection(); windowTitle = CoverSwitchConfig::windowTitle(); zPosition = CoverSwitchConfig::zPosition(); timeLine.setEasingCurve(QEasingCurve::InOutSine); timeLine.setDuration(animationDuration); // Defined outside the ui primaryTabBox = CoverSwitchConfig::tabBox(); secondaryTabBox = CoverSwitchConfig::tabBoxAlternative(); QColor tmp = CoverSwitchConfig::mirrorFrontColor(); mirrorColor[0][0] = tmp.redF(); mirrorColor[0][1] = tmp.greenF(); mirrorColor[0][2] = tmp.blueF(); mirrorColor[0][3] = 1.0; tmp = CoverSwitchConfig::mirrorRearColor(); mirrorColor[1][0] = tmp.redF(); mirrorColor[1][1] = tmp.greenF(); mirrorColor[1][2] = tmp.blueF(); mirrorColor[1][3] = -1.0; } void CoverSwitchEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (mActivated || stop || stopRequested) { data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; if (animation || start || stop) { timeLine.update(std::chrono::milliseconds(time)); } if (selected_window == NULL) abort(); } effects->prePaintScreen(data, time); } void CoverSwitchEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); if (mActivated || stop || stopRequested) { QList< EffectWindow* > tempList = currentWindowList; int index = tempList.indexOf(selected_window); if (animation || start || stop) { if (!start && !stop) { if (direction == Right) index++; else index--; if (index < 0) index = tempList.count() + index; if (index >= tempList.count()) index = index % tempList.count(); } foreach (Direction direction, scheduled_directions) { if (direction == Right) index++; else index--; if (index < 0) index = tempList.count() + index; if (index >= tempList.count()) index = index % tempList.count(); } } int leftIndex = index - 1; if (leftIndex < 0) leftIndex = tempList.count() - 1; int rightIndex = index + 1; if (rightIndex == tempList.count()) rightIndex = 0; EffectWindow* frontWindow = tempList[ index ]; leftWindows.clear(); rightWindows.clear(); bool evenWindows = (tempList.count() % 2 == 0) ? true : false; int leftWindowCount = 0; if (evenWindows) leftWindowCount = tempList.count() / 2 - 1; else leftWindowCount = (tempList.count() - 1) / 2; for (int i = 0; i < leftWindowCount; i++) { int tempIndex = (leftIndex - i); if (tempIndex < 0) tempIndex = tempList.count() + tempIndex; leftWindows.prepend(tempList[ tempIndex ]); } int rightWindowCount = 0; if (evenWindows) rightWindowCount = tempList.count() / 2; else rightWindowCount = (tempList.count() - 1) / 2; for (int i = 0; i < rightWindowCount; i++) { int tempIndex = (rightIndex + i) % tempList.count(); rightWindows.prepend(tempList[ tempIndex ]); } if (reflection) { // no reflections during start and stop animation // except when using a shader if ((!start && !stop) || effects->compositingType() == OpenGL2Compositing) paintScene(frontWindow, leftWindows, rightWindows, true); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // we can use a huge scale factor (needed to calculate the rearground vertices) // as we restrict with a PaintClipper painting on the current screen float reflectionScaleFactor = 100000 * tan(60.0 * M_PI / 360.0f) / area.width(); const float width = area.width(); const float height = area.height(); float vertices[] = { -width * 0.5f, height, 0.0, width * 0.5f, height, 0.0, width*reflectionScaleFactor, height, -5000, -width*reflectionScaleFactor, height, -5000 }; // foreground if (start) { mirrorColor[0][3] = timeLine.value(); } else if (stop) { mirrorColor[0][3] = 1.0 - timeLine.value(); } else { mirrorColor[0][3] = 1.0; } int y = 0; // have to adjust the y values to fit OpenGL // in OpenGL y==0 is at bottom, in Qt at top if (effects->numScreens() > 1) { QRect fullArea = effects->clientArea(FullArea, 0, 1); if (fullArea.height() != area.height()) { if (area.y() == 0) y = fullArea.height() - area.height(); else y = fullArea.height() - area.y() - area.height(); } } // use scissor to restrict painting of the reflection plane to current screen glScissor(area.x(), y, area.width(), area.height()); glEnable(GL_SCISSOR_TEST); if (m_reflectionShader && m_reflectionShader->isValid()) { ShaderManager::instance()->pushShader(m_reflectionShader); QMatrix4x4 windowTransformation = data.projectionMatrix(); windowTransformation.translate(area.x() + area.width() * 0.5f, 0.0, 0.0); m_reflectionShader->setUniform(GLShader::ModelViewProjectionMatrix, windowTransformation); m_reflectionShader->setUniform("u_frontColor", QVector4D(mirrorColor[0][0], mirrorColor[0][1], mirrorColor[0][2], mirrorColor[0][3])); m_reflectionShader->setUniform("u_backColor", QVector4D(mirrorColor[1][0], mirrorColor[1][1], mirrorColor[1][2], mirrorColor[1][3])); // TODO: make this one properly QVector verts; QVector texcoords; verts.reserve(18); texcoords.reserve(12); texcoords << 1.0 << 0.0; verts << vertices[6] << vertices[7] << vertices[8]; texcoords << 1.0 << 0.0; verts << vertices[9] << vertices[10] << vertices[11]; texcoords << 0.0 << 0.0; verts << vertices[0] << vertices[1] << vertices[2]; texcoords << 0.0 << 0.0; verts << vertices[0] << vertices[1] << vertices[2]; texcoords << 0.0 << 0.0; verts << vertices[3] << vertices[4] << vertices[5]; texcoords << 1.0 << 0.0; verts << vertices[6] << vertices[7] << vertices[8]; GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setData(6, 3, verts.data(), texcoords.data()); vbo->render(GL_TRIANGLES); ShaderManager::instance()->popShader(); } glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); } paintScene(frontWindow, leftWindows, rightWindows); // Render the caption frame if (windowTitle) { double opacity = 1.0; if (start) opacity = timeLine.value(); else if (stop) opacity = 1.0 - timeLine.value(); if (animation) captionFrame->setCrossFadeProgress(timeLine.value()); captionFrame->render(region, opacity); } } } void CoverSwitchEffect::postPaintScreen() { if ((mActivated && (animation || start)) || stop || stopRequested) { if (timeLine.done()) { timeLine.reset(); if (stop) { stop = false; effects->setActiveFullScreenEffect(0); foreach (EffectWindow * window, referrencedWindows) { window->unrefWindow(); } referrencedWindows.clear(); currentWindowList.clear(); if (startRequested) { startRequested = false; mActivated = true; effects->refTabBox(); currentWindowList = effects->currentTabBoxWindowList(); if (animateStart) { start = true; } } } else if (!scheduled_directions.isEmpty()) { direction = scheduled_directions.dequeue(); if (start) { animation = true; start = false; } } else { animation = false; start = false; if (stopRequested) { stopRequested = false; stop = true; } } } effects->addRepaintFull(); } effects->postPaintScreen(); } void CoverSwitchEffect::paintScene(EffectWindow* frontWindow, const EffectWindowList& leftWindows, const EffectWindowList& rightWindows, bool reflectedWindows) { // LAYOUT // one window in the front. Other windows left and right rotated // for odd number of windows: left: (n-1)/2; front: 1; right: (n-1)/2 // for even number of windows: left: n/2; front: 1; right: n/2 -1 // // ANIMATION // forward (alt+tab) // all left windows are moved to next position // top most left window is rotated and moved to front window position // front window is rotated and moved to next right window position // right windows are moved to next position // last right window becomes totally transparent in half the time // appears transparent on left side and becomes totally opaque again // backward (alt+shift+tab) same as forward but opposite direction int width = area.width(); int leftWindowCount = leftWindows.count(); int rightWindowCount = rightWindows.count(); // Problem during animation: a window which is painted after another window // appears in front of the other // so during animation the painting order has to be rearreanged // paint sequence no animation: left, right, front // paint sequence forward animation: right, front, left if (!animation) { paintWindows(leftWindows, true, reflectedWindows); paintWindows(rightWindows, false, reflectedWindows); paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows); } else { if (direction == Right) { if (timeLine.value() < 0.5) { // paint in normal way paintWindows(leftWindows, true, reflectedWindows); paintWindows(rightWindows, false, reflectedWindows); paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows); } else { paintWindows(rightWindows, false, reflectedWindows); paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows); paintWindows(leftWindows, true, reflectedWindows, rightWindows.at(0)); } } else { paintWindows(leftWindows, true, reflectedWindows); if (timeLine.value() < 0.5) { paintWindows(rightWindows, false, reflectedWindows); paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows); } else { EffectWindow* leftWindow; if (leftWindowCount > 0) { leftWindow = leftWindows.at(0); paintFrontWindow(frontWindow, width, leftWindowCount, rightWindowCount, reflectedWindows); } else leftWindow = frontWindow; paintWindows(rightWindows, false, reflectedWindows, leftWindow); } } } } void CoverSwitchEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (mActivated || stop || stopRequested) { if (!(mask & PAINT_WINDOW_TRANSFORMED) && !w->isDesktop()) { if ((start || stop) && w->isDock()) { data.setOpacity(1.0 - timeLine.value()); if (stop) data.setOpacity(timeLine.value()); } else return; } } if ((start || stop) && (!w->isOnCurrentDesktop() || w->isMinimized())) { if (stop) // Fade out windows not on the current desktop data.setOpacity(1.0 - timeLine.value()); else // Fade in Windows from other desktops when animation is started data.setOpacity(timeLine.value()); } effects->paintWindow(w, mask, region, data); } void CoverSwitchEffect::slotTabBoxAdded(int mode) { if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; if (!mActivated) { effects->setShowingDesktop(false); // only for windows mode if (((mode == TabBoxWindowsMode && primaryTabBox) || (mode == TabBoxWindowsAlternativeMode && secondaryTabBox) || (mode == TabBoxCurrentAppWindowsMode && primaryTabBox) || (mode == TabBoxCurrentAppWindowsAlternativeMode && secondaryTabBox)) && effects->currentTabBoxWindowList().count() > 0) { effects->startMouseInterception(this, Qt::ArrowCursor); activeScreen = effects->activeScreen(); if (!stop && !stopRequested) { effects->refTabBox(); effects->setActiveFullScreenEffect(this); scheduled_directions.clear(); selected_window = effects->currentTabBoxWindow(); currentWindowList = effects->currentTabBoxWindowList(); direction = Left; mActivated = true; if (animateStart) { start = true; } // Calculation of correct area area = effects->clientArea(FullScreenArea, activeScreen, effects->currentDesktop()); const QSize screenSize = effects->virtualScreenSize(); scaleFactor = (zPosition + 1100) * 2.0 * tan(60.0 * M_PI / 360.0f) / screenSize.width(); if (screenSize.width() - area.width() != 0) { // one of the screens is smaller than the other (horizontal) if (area.width() < screenSize.width() - area.width()) scaleFactor *= (float)area.width() / (float)(screenSize.width() - area.width()); else if (area.width() != screenSize.width() - area.width()) { // vertical layout with different width // but we don't want to catch screens with same width and different height if (screenSize.height() != area.height()) scaleFactor *= (float)area.width() / (float)(screenSize.width()); } } if (effects->numScreens() > 1) { // unfortunatelly we have to change the projection matrix in dual screen mode // code is adapted from SceneOpenGL2::createProjectionMatrix() QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * std::tan(fovy * M_PI / 360.0f); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; if (area.width() != fullRect.width()) { if (area.x() == 0) { // horizontal layout: left screen xmin *= (float)area.width() / (float)fullRect.width(); xmax *= (fullRect.width() - 0.5f * area.width()) / (0.5f * fullRect.width()); } else { // horizontal layout: right screen xmin *= (fullRect.width() - 0.5f * area.width()) / (0.5f * fullRect.width()); xmax *= (float)area.width() / (float)fullRect.width(); } } if (area.height() != fullRect.height()) { if (area.y() == 0) { // vertical layout: top screen ymin *= (fullRect.height() - 0.5f * area.height()) / (0.5f * fullRect.height()); ymax *= (float)area.height() / (float)fullRect.height(); } else { // vertical layout: bottom screen ymin *= (float)area.height() / (float)fullRect.height(); ymax *= (fullRect.height() - 0.5f * area.height()) / (0.5f * fullRect.height()); } } m_projectionMatrix = QMatrix4x4(); m_projectionMatrix.frustum(xmin, xmax, ymin, ymax, zNear, zFar); const float scaleFactor = 1.1f / zNear; // Create a second matrix that transforms screen coordinates // to world coordinates. QMatrix4x4 matrix; matrix.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1); matrix.scale( (xmax - xmin) * scaleFactor / fullRect.width(), -(ymax - ymin) * scaleFactor / fullRect.height(), 0.001); // Combine the matrices m_projectionMatrix *= matrix; m_modelviewMatrix = QMatrix4x4(); m_modelviewMatrix.translate(area.x(), area.y(), 0.0); } // Setup caption frame geometry if (windowTitle) { QRect frameRect = QRect(area.width() * 0.25f + area.x(), area.height() * 0.9f + area.y(), area.width() * 0.5f, QFontMetrics(captionFont).height()); if (!captionFrame) { captionFrame = effects->effectFrame(EffectFrameStyled); captionFrame->setFont(captionFont); captionFrame->enableCrossFade(true); } captionFrame->setGeometry(frameRect); captionFrame->setIconSize(QSize(frameRect.height(), frameRect.height())); // And initial contents updateCaption(); } effects->addRepaintFull(); } else { startRequested = true; } } } } void CoverSwitchEffect::slotTabBoxClosed() { if (mActivated) { if (animateStop) { if (!animation && !start) { stop = true; } else if (start && scheduled_directions.isEmpty()) { start = false; stop = true; timeLine.setElapsed(timeLine.duration() - timeLine.elapsed()); } else { stopRequested = true; } } else { effects->setActiveFullScreenEffect(0); start = false; animation = false; timeLine.reset(); } mActivated = false; effects->unrefTabBox(); effects->stopMouseInterception(this); effects->addRepaintFull(); } } void CoverSwitchEffect::slotTabBoxUpdated() { if (mActivated) { if (animateSwitch && currentWindowList.count() > 1) { // determine the switch direction if (selected_window != effects->currentTabBoxWindow()) { if (selected_window != NULL) { int old_index = currentWindowList.indexOf(selected_window); int new_index = effects->currentTabBoxWindowList().indexOf(effects->currentTabBoxWindow()); Direction new_direction; int distance = new_index - old_index; if (distance > 0) new_direction = Left; if (distance < 0) new_direction = Right; if (effects->currentTabBoxWindowList().count() == 2) { new_direction = Left; distance = 1; } if (distance != 0) { distance = abs(distance); int tempDistance = effects->currentTabBoxWindowList().count() - distance; if (tempDistance < abs(distance)) { distance = tempDistance; if (new_direction == Left) new_direction = Right; else new_direction = Left; } if (!animation && !start) { animation = true; direction = new_direction; distance--; } for (int i = 0; i < distance; i++) { if (!scheduled_directions.isEmpty() && scheduled_directions.last() != new_direction) scheduled_directions.pop_back(); else scheduled_directions.enqueue(new_direction); if (scheduled_directions.count() == effects->currentTabBoxWindowList().count()) scheduled_directions.clear(); } } } selected_window = effects->currentTabBoxWindow(); currentWindowList = effects->currentTabBoxWindowList(); updateCaption(); } } effects->addRepaintFull(); } } void CoverSwitchEffect::paintWindowCover(EffectWindow* w, bool reflectedWindow, WindowPaintData& data) { QRect windowRect = w->geometry(); data.setYTranslation(area.height() - windowRect.y() - windowRect.height()); data.setZTranslation(-zPosition); if (start) { if (w->isMinimized()) { data.multiplyOpacity(timeLine.value()); } else { const QVector3D translation = data.translation() * timeLine.value(); data.setXTranslation(translation.x()); data.setYTranslation(translation.y()); data.setZTranslation(translation.z()); if (effects->numScreens() > 1) { QRect clientRect = effects->clientArea(FullScreenArea, w->screen(), effects->currentDesktop()); QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); if (w->screen() == activeScreen) { if (clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x()) { data.translate(- clientRect.x() * (1.0f - timeLine.value())); } if (clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y()) { data.translate(0.0, - clientRect.y() * (1.0f - timeLine.value())); } } else { if (clientRect.width() != fullRect.width() && clientRect.x() < area.x()) { data.translate(- clientRect.width() * (1.0f - timeLine.value())); } if (clientRect.height() != fullRect.height() && clientRect.y() < area.y()) { data.translate(0.0, - clientRect.height() * (1.0f - timeLine.value())); } } } data.setRotationAngle(data.rotationAngle() * timeLine.value()); } } if (stop) { if (w->isMinimized() && w != effects->activeWindow()) { data.multiplyOpacity(1.0 - timeLine.value()); } else { const QVector3D translation = data.translation() * (1.0 - timeLine.value()); data.setXTranslation(translation.x()); data.setYTranslation(translation.y()); data.setZTranslation(translation.z()); if (effects->numScreens() > 1) { QRect clientRect = effects->clientArea(FullScreenArea, w->screen(), effects->currentDesktop()); QRect rect = effects->clientArea(FullScreenArea, activeScreen, effects->currentDesktop()); QRect fullRect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); if (w->screen() == activeScreen) { if (clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x()) { data.translate(- clientRect.x() * timeLine.value()); } if (clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y()) { data.translate(0.0, - clientRect.y() * timeLine.value()); } } else { if (clientRect.width() != fullRect.width() && clientRect.x() < rect.x()) { data.translate(- clientRect.width() * timeLine.value()); } if (clientRect.height() != fullRect.height() && clientRect.y() < area.y()) { data.translate(0.0, - clientRect.height() * timeLine.value()); } } } data.setRotationAngle(data.rotationAngle() * (1.0 - timeLine.value())); } } if (reflectedWindow) { QMatrix4x4 reflectionMatrix; reflectionMatrix.scale(1.0, -1.0, 1.0); data.setModelViewMatrix(reflectionMatrix*data.modelViewMatrix()); data.setYTranslation(- area.height() - windowRect.y() - windowRect.height()); if (start) { data.multiplyOpacity(timeLine.value()); } else if (stop) { data.multiplyOpacity(1.0 - timeLine.value()); } effects->drawWindow(w, PAINT_WINDOW_TRANSFORMED, infiniteRegion(), data); } else { effects->paintWindow(w, PAINT_WINDOW_TRANSFORMED, infiniteRegion(), data); } } void CoverSwitchEffect::paintFrontWindow(EffectWindow* frontWindow, int width, int leftWindows, int rightWindows, bool reflectedWindow) { if (frontWindow == NULL) return; bool specialHandlingForward = false; WindowPaintData data(frontWindow); if (effects->numScreens() > 1) { data.setProjectionMatrix(m_projectionMatrix); data.setModelViewMatrix(m_modelviewMatrix); } data.setXTranslation(area.width() * 0.5 - frontWindow->geometry().x() - frontWindow->geometry().width() * 0.5); if (leftWindows == 0) { leftWindows = 1; if (!start && !stop) specialHandlingForward = true; } if (rightWindows == 0) { rightWindows = 1; } if (animation) { float distance = 0.0; const QSize screenSize = effects->virtualScreenSize(); if (direction == Right) { // move to right distance = -frontWindow->geometry().width() * 0.5f + area.width() * 0.5f + (((float)screenSize.width() * 0.5 * scaleFactor) - (float)area.width() * 0.5f) / rightWindows; data.translate(distance * timeLine.value()); data.setRotationAxis(Qt::YAxis); data.setRotationAngle(-angle * timeLine.value()); data.setRotationOrigin(QVector3D(frontWindow->geometry().width(), 0.0, 0.0)); } else { // move to left distance = frontWindow->geometry().width() * 0.5f - area.width() * 0.5f + ((float)width * 0.5f - ((float)screenSize.width() * 0.5 * scaleFactor)) / leftWindows; float factor = 1.0; if (specialHandlingForward) factor = 2.0; data.translate(distance * timeLine.value() * factor); data.setRotationAxis(Qt::YAxis); data.setRotationAngle(angle * timeLine.value()); } } if (specialHandlingForward && timeLine.value() < 0.5) { data.multiplyOpacity(1.0 - timeLine.value() * 2.0); } paintWindowCover(frontWindow, reflectedWindow, data); } void CoverSwitchEffect::paintWindows(const EffectWindowList& windows, bool left, bool reflectedWindows, EffectWindow* additionalWindow) { int width = area.width(); int windowCount = windows.count(); EffectWindow* window; int rotateFactor = 1; if (!left) { rotateFactor = -1; } const QSize screenSize = effects->virtualScreenSize(); float xTranslate = -((float)(width) * 0.5f - ((float)screenSize.width() * 0.5 * scaleFactor)); if (!left) xTranslate = ((float)screenSize.width() * 0.5 * scaleFactor) - (float)width * 0.5f; // handling for additional window from other side // has to appear on this side after half of the time if (animation && timeLine.value() >= 0.5 && additionalWindow != NULL) { WindowPaintData data(additionalWindow); if (effects->numScreens() > 1) { data.setProjectionMatrix(m_projectionMatrix); data.setModelViewMatrix(m_modelviewMatrix); } data.setRotationAxis(Qt::YAxis); data.setRotationAngle(angle * rotateFactor); if (left) { data.translate(-xTranslate - additionalWindow->geometry().x()); } else { data.translate(xTranslate + area.width() - additionalWindow->geometry().x() - additionalWindow->geometry().width()); data.setRotationOrigin(QVector3D(additionalWindow->geometry().width(), 0.0, 0.0)); } data.multiplyOpacity((timeLine.value() - 0.5) * 2.0); paintWindowCover(additionalWindow, reflectedWindows, data); } // normal behaviour for (int i = 0; i < windows.count(); i++) { window = windows.at(i); if (window == NULL || window->isDeleted()) { continue; } WindowPaintData data(window); if (effects->numScreens() > 1) { data.setProjectionMatrix(m_projectionMatrix); data.setModelViewMatrix(m_modelviewMatrix); } data.setRotationAxis(Qt::YAxis); data.setRotationAngle(angle); if (left) data.translate(-xTranslate + xTranslate * i / windowCount - window->geometry().x()); else data.translate(xTranslate + width - xTranslate * i / windowCount - window->geometry().x() - window->geometry().width()); if (animation) { if (direction == Right) { if ((i == windowCount - 1) && left) { // right most window on left side -> move to front // have to move one window distance plus half the difference between the window and the desktop size data.translate((xTranslate / windowCount + (width - window->geometry().width()) * 0.5f) * timeLine.value()); data.setRotationAngle(angle - angle * timeLine.value()); } // right most window does not have to be moved else if (!left && (i == 0)); // do nothing else { // all other windows - move to next position data.translate(xTranslate / windowCount * timeLine.value()); } } else { if ((i == windowCount - 1) && !left) { // left most window on right side -> move to front data.translate(- (xTranslate / windowCount + (width - window->geometry().width()) * 0.5f) * timeLine.value()); data.setRotationAngle(angle - angle * timeLine.value()); } // left most window does not have to be moved else if (i == 0 && left); // do nothing else { // all other windows - move to next position data.translate(- xTranslate / windowCount * timeLine.value()); } } } if (!left) data.setRotationOrigin(QVector3D(window->geometry().width(), 0.0, 0.0)); data.setRotationAngle(data.rotationAngle() * rotateFactor); // make window most to edge transparent if animation if (animation && i == 0 && ((direction == Left && left) || (direction == Right && !left))) { // only for the first half of the animation if (timeLine.value() < 0.5) { data.multiplyOpacity((1.0 - timeLine.value() * 2.0)); paintWindowCover(window, reflectedWindows, data); } } else { paintWindowCover(window, reflectedWindows, data); } } } void CoverSwitchEffect::windowInputMouseEvent(QEvent* e) { if (e->type() != QEvent::MouseButtonPress) return; // we don't want click events during animations if (animation) return; QMouseEvent* event = static_cast< QMouseEvent* >(e); switch (event->button()) { case Qt::XButton1: // wheel up selectPreviousWindow(); break; case Qt::XButton2: // wheel down selectNextWindow(); break; case Qt::LeftButton: case Qt::RightButton: case Qt::MidButton: default: QPoint pos = event->pos(); // determine if a window has been clicked // not interested in events above a fullscreen window (ignoring panel size) if (pos.y() < (area.height()*scaleFactor - area.height()) * 0.5f *(1.0f / scaleFactor)) return; // if there is no selected window (that is no window at all) we cannot click it if (!selected_window) return; if (pos.x() < (area.width()*scaleFactor - selected_window->width()) * 0.5f *(1.0f / scaleFactor)) { float availableSize = (area.width() * scaleFactor - area.width()) * 0.5f * (1.0f / scaleFactor); for (int i = 0; i < leftWindows.count(); i++) { int windowPos = availableSize / leftWindows.count() * i; if (pos.x() < windowPos) continue; if (i + 1 < leftWindows.count()) { if (pos.x() > availableSize / leftWindows.count()*(i + 1)) continue; } effects->setTabBoxWindow(leftWindows[i]); return; } } if (pos.x() > area.width() - (area.width()*scaleFactor - selected_window->width()) * 0.5f *(1.0f / scaleFactor)) { float availableSize = (area.width() * scaleFactor - area.width()) * 0.5f * (1.0f / scaleFactor); for (int i = 0; i < rightWindows.count(); i++) { int windowPos = area.width() - availableSize / rightWindows.count() * i; if (pos.x() > windowPos) continue; if (i + 1 < rightWindows.count()) { if (pos.x() < area.width() - availableSize / rightWindows.count()*(i + 1)) continue; } effects->setTabBoxWindow(rightWindows[i]); return; } } break; } } void CoverSwitchEffect::abort() { // it's possible that abort is called after tabbox has been closed // in this case the cleanup is already done (see bug 207554) if (mActivated) { effects->unrefTabBox(); effects->stopMouseInterception(this); } effects->setActiveFullScreenEffect(0); timeLine.reset(); mActivated = false; stop = false; stopRequested = false; effects->addRepaintFull(); captionFrame->free(); } void CoverSwitchEffect::slotWindowClosed(EffectWindow* c) { if (c == selected_window) selected_window = 0; // if the list is not empty, the effect is active if (!currentWindowList.isEmpty()) { c->refWindow(); referrencedWindows.append(c); currentWindowList.removeAll(c); leftWindows.removeAll(c); rightWindows.removeAll(c); } } bool CoverSwitchEffect::isActive() const { return (mActivated || stop || stopRequested) && !effects->isScreenLocked(); } void CoverSwitchEffect::updateCaption() { if (!selected_window || !windowTitle) { return; } if (selected_window->isDesktop()) { captionFrame->setText(i18nc("Special entry in alt+tab list for minimizing all windows", "Show Desktop")); static QPixmap pix = QIcon::fromTheme(QStringLiteral("user-desktop")).pixmap(captionFrame->iconSize()); captionFrame->setIcon(pix); } else { captionFrame->setText(selected_window->caption()); captionFrame->setIcon(selected_window->icon()); } } void CoverSwitchEffect::slotTabBoxKeyEvent(QKeyEvent *event) { if (event->type() == QEvent::KeyPress) { switch (event->key()) { case Qt::Key_Left: selectPreviousWindow(); break; case Qt::Key_Right: selectNextWindow(); break; default: // nothing break; } } } void CoverSwitchEffect::selectNextOrPreviousWindow(bool forward) { if (!mActivated || !selected_window) { return; } const int index = effects->currentTabBoxWindowList().indexOf(selected_window); int newIndex = index; if (forward) { ++newIndex; } else { --newIndex; } if (newIndex == effects->currentTabBoxWindowList().size()) { newIndex = 0; } else if (newIndex < 0) { newIndex = effects->currentTabBoxWindowList().size() -1; } if (index == newIndex) { return; } effects->setTabBoxWindow(effects->currentTabBoxWindowList().at(newIndex)); } } // namespace diff --git a/effects/cube/cube.cpp b/effects/cube/cube.cpp index 64c622d9b..1208f2e39 100644 --- a/effects/cube/cube.cpp +++ b/effects/cube/cube.cpp @@ -1,1740 +1,1740 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "cube.h" // KConfigSkeleton #include "cubeconfig.h" #include "cube_inside.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { CubeEffect::CubeEffect() : activated(false) , cube_painting(false) , keyboard_grab(false) , painting_desktop(1) , frontDesktop(0) , cubeOpacity(1.0) , opacityDesktopOnly(true) , displayDesktopName(false) , desktopNameFrame(NULL) , reflection(true) , desktopChangedWhileRotating(false) , paintCaps(true) , wallpaper(NULL) , texturedCaps(true) , capTexture(NULL) , reflectionPainting(false) , activeScreen(0) , bottomCap(false) , closeOnMouseRelease(false) , zoom(0.0) , zPosition(0.0) , useForTabBox(false) , tabBoxMode(false) , shortcutsRegistered(false) , mode(Cube) , useShaders(false) , cylinderShader(0) , sphereShader(0) , zOrderingFactor(0.0f) , mAddedHeightCoeff1(0.0f) , mAddedHeightCoeff2(0.0f) , m_cubeCapBuffer(NULL) , m_proxy(this) , m_cubeAction(new QAction(this)) , m_cylinderAction(new QAction(this)) , m_sphereAction(new QAction(this)) { initConfig(); desktopNameFont.setBold(true); desktopNameFont.setPointSize(14); if (effects->compositingType() == OpenGL2Compositing) { m_reflectionShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("cube-reflection.glsl")); m_capShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("cube-cap.glsl")); } else { m_reflectionShader = NULL; m_capShader = NULL; } m_textureMirrorMatrix.scale(1.0, -1.0, 1.0); m_textureMirrorMatrix.translate(0.0, -1.0, 0.0); - connect(effects, SIGNAL(tabBoxAdded(int)), this, SLOT(slotTabBoxAdded(int))); - connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed())); - connect(effects, SIGNAL(tabBoxUpdated()), this, SLOT(slotTabBoxUpdated())); + connect(effects, &EffectsHandler::tabBoxAdded, this, &CubeEffect::slotTabBoxAdded); + connect(effects, &EffectsHandler::tabBoxClosed, this, &CubeEffect::slotTabBoxClosed); + connect(effects, &EffectsHandler::tabBoxUpdated, this, &CubeEffect::slotTabBoxUpdated); reconfigure(ReconfigureAll); } bool CubeEffect::supported() { return effects->isOpenGLCompositing(); } void CubeEffect::reconfigure(ReconfigureFlags) { CubeConfig::self()->read(); foreach (ElectricBorder border, borderActivate) { effects->unreserveElectricBorder(border, this); } foreach (ElectricBorder border, borderActivateCylinder) { effects->unreserveElectricBorder(border, this); } foreach (ElectricBorder border, borderActivateSphere) { effects->unreserveElectricBorder(border, this); } borderActivate.clear(); borderActivateCylinder.clear(); borderActivateSphere.clear(); QList borderList = QList(); borderList.append(int(ElectricNone)); borderList = CubeConfig::borderActivate(); foreach (int i, borderList) { borderActivate.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } borderList.clear(); borderList.append(int(ElectricNone)); borderList = CubeConfig::borderActivateCylinder(); foreach (int i, borderList) { borderActivateCylinder.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } borderList.clear(); borderList.append(int(ElectricNone)); borderList = CubeConfig::borderActivateSphere(); foreach (int i, borderList) { borderActivateSphere.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } cubeOpacity = (float)CubeConfig::opacity() / 100.0f; opacityDesktopOnly = CubeConfig::opacityDesktopOnly(); displayDesktopName = CubeConfig::displayDesktopName(); reflection = CubeConfig::reflection(); // TODO: Rename rotationDuration to duration so we // can use animationTime(500). const int d = CubeConfig::rotationDuration() != 0 ? CubeConfig::rotationDuration() : 500; rotationDuration = std::chrono::milliseconds(static_cast(animationTime(d))); backgroundColor = CubeConfig::backgroundColor(); capColor = CubeConfig::capColor(); paintCaps = CubeConfig::caps(); closeOnMouseRelease = CubeConfig::closeOnMouseRelease(); zPosition = CubeConfig::zPosition(); useForTabBox = CubeConfig::tabBox(); invertKeys = CubeConfig::invertKeys(); invertMouse = CubeConfig::invertMouse(); capDeformationFactor = (float)CubeConfig::capDeformation() / 100.0f; useZOrdering = CubeConfig::zOrdering(); delete wallpaper; wallpaper = NULL; delete capTexture; capTexture = NULL; texturedCaps = CubeConfig::texturedCaps(); timeLine.setEasingCurve(QEasingCurve::InOutSine); timeLine.setDuration(rotationDuration); verticalTimeLine.setEasingCurve(QEasingCurve::InOutSine); verticalTimeLine.setDuration(rotationDuration); // do not connect the shortcut if we use cylinder or sphere if (!shortcutsRegistered) { QAction* cubeAction = m_cubeAction; cubeAction->setObjectName(QStringLiteral("Cube")); cubeAction->setText(i18n("Desktop Cube")); KGlobalAccel::self()->setDefaultShortcut(cubeAction, QList() << Qt::CTRL + Qt::Key_F11); KGlobalAccel::self()->setShortcut(cubeAction, QList() << Qt::CTRL + Qt::Key_F11); effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F11, cubeAction); effects->registerPointerShortcut(Qt::ControlModifier | Qt::AltModifier, Qt::LeftButton, cubeAction); cubeShortcut = KGlobalAccel::self()->shortcut(cubeAction); QAction* cylinderAction = m_cylinderAction; cylinderAction->setObjectName(QStringLiteral("Cylinder")); cylinderAction->setText(i18n("Desktop Cylinder")); KGlobalAccel::self()->setShortcut(cylinderAction, QList()); effects->registerGlobalShortcut(QKeySequence(), cylinderAction); cylinderShortcut = KGlobalAccel::self()->shortcut(cylinderAction); QAction* sphereAction = m_sphereAction; sphereAction->setObjectName(QStringLiteral("Sphere")); sphereAction->setText(i18n("Desktop Sphere")); KGlobalAccel::self()->setShortcut(sphereAction, QList()); sphereShortcut = KGlobalAccel::self()->shortcut(sphereAction); effects->registerGlobalShortcut(QKeySequence(), sphereAction); - connect(cubeAction, SIGNAL(triggered(bool)), this, SLOT(toggleCube())); - connect(cylinderAction, SIGNAL(triggered(bool)), this, SLOT(toggleCylinder())); - connect(sphereAction, SIGNAL(triggered(bool)), this, SLOT(toggleSphere())); + connect(cubeAction, &QAction::triggered, this, &CubeEffect::toggleCube); + connect(cylinderAction, &QAction::triggered, this, &CubeEffect::toggleCylinder); + connect(sphereAction, &QAction::triggered, this, &CubeEffect::toggleSphere); connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &CubeEffect::globalShortcutChanged); shortcutsRegistered = true; } // set the cap color on the shader if (m_capShader && m_capShader->isValid()) { ShaderBinder binder(m_capShader); m_capShader->setUniform(GLShader::Color, capColor); } // touch borders const QVector relevantBorders{ElectricLeft, ElectricTop, ElectricRight, ElectricBottom}; for (auto e : relevantBorders) { effects->unregisterTouchBorder(e, m_cubeAction); effects->unregisterTouchBorder(e, m_sphereAction); effects->unregisterTouchBorder(e, m_cylinderAction); } auto touchEdge = [&relevantBorders] (const QList touchBorders, QAction *action) { for (int i : touchBorders) { if (!relevantBorders.contains(ElectricBorder(i))) { continue; } effects->registerTouchBorder(ElectricBorder(i), action); } }; touchEdge(CubeConfig::touchBorderActivate(), m_cubeAction); touchEdge(CubeConfig::touchBorderActivateCylinder(), m_cylinderAction); touchEdge(CubeConfig::touchBorderActivateSphere(), m_sphereAction); } CubeEffect::~CubeEffect() { delete wallpaper; delete capTexture; delete cylinderShader; delete sphereShader; delete desktopNameFrame; delete m_reflectionShader; delete m_capShader; delete m_cubeCapBuffer; } QImage CubeEffect::loadCubeCap(const QString &capPath) { if (!texturedCaps) { return QImage(); } return QImage(capPath); } void CubeEffect::slotCubeCapLoaded() { QFutureWatcher *watcher = dynamic_cast*>(sender()); if (!watcher) { // not invoked from future watcher return; } QImage img = watcher->result(); if (!img.isNull()) { effects->makeOpenGLContextCurrent(); capTexture = new GLTexture(img); capTexture->setFilter(GL_LINEAR); if (!GLPlatform::instance()->isGLES()) { capTexture->setWrapMode(GL_CLAMP_TO_BORDER); } // need to recreate the VBO for the cube cap delete m_cubeCapBuffer; m_cubeCapBuffer = NULL; effects->addRepaintFull(); } watcher->deleteLater(); } QImage CubeEffect::loadWallPaper(const QString &file) { return QImage(file); } void CubeEffect::slotWallPaperLoaded() { QFutureWatcher *watcher = dynamic_cast*>(sender()); if (!watcher) { // not invoked from future watcher return; } QImage img = watcher->result(); if (!img.isNull()) { effects->makeOpenGLContextCurrent(); wallpaper = new GLTexture(img); effects->addRepaintFull(); } watcher->deleteLater(); } bool CubeEffect::loadShader() { effects->makeOpenGLContextCurrent(); if (!(GLPlatform::instance()->supports(GLSL) && (effects->compositingType() == OpenGL2Compositing))) return false; cylinderShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture | ShaderTrait::AdjustSaturation | ShaderTrait::Modulate, QStringLiteral("cylinder.vert"), QString()); if (!cylinderShader->isValid()) { qCCritical(KWINEFFECTS) << "The cylinder shader failed to load!"; return false; } else { ShaderBinder binder(cylinderShader); cylinderShader->setUniform("sampler", 0); QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); cylinderShader->setUniform("width", (float)rect.width() * 0.5f); } sphereShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture | ShaderTrait::AdjustSaturation | ShaderTrait::Modulate, QStringLiteral("sphere.vert"), QString()); if (!sphereShader->isValid()) { qCCritical(KWINEFFECTS) << "The sphere shader failed to load!"; return false; } else { ShaderBinder binder(sphereShader); sphereShader->setUniform("sampler", 0); QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); sphereShader->setUniform("width", (float)rect.width() * 0.5f); sphereShader->setUniform("height", (float)rect.height() * 0.5f); sphereShader->setUniform("u_offset", QVector2D(0, 0)); } return true; } void CubeEffect::startAnimation(AnimationState state) { QEasingCurve curve; /* If this is first and only animation -> EaseInOut * there is more -> EaseIn * If there was an animation before, and this is the last one -> EaseOut * there is more -> Linear */ if (animationState == AnimationState::None) { curve.setType(animations.empty() ? QEasingCurve::InOutSine : QEasingCurve::InCurve); } else { curve.setType(animations.empty() ? QEasingCurve::OutCurve : QEasingCurve::Linear); } timeLine.reset(); timeLine.setEasingCurve(curve); startAngle = currentAngle; startFrontDesktop = frontDesktop; animationState = state; } void CubeEffect::startVerticalAnimation(VerticalAnimationState state) { /* Ignore if there is nowhere to rotate */ if ((qFuzzyIsNull(verticalCurrentAngle - 90.0f) && state == VerticalAnimationState::Upwards) || (qFuzzyIsNull(verticalCurrentAngle + 90.0f) && state == VerticalAnimationState::Downwards)) { return; } verticalTimeLine.reset(); verticalStartAngle = verticalCurrentAngle; verticalAnimationState = state; } void CubeEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (activated) { data.mask |= PAINT_SCREEN_TRANSFORMED | Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS | PAINT_SCREEN_BACKGROUND_FIRST; if (animationState == AnimationState::None && !animations.empty()) { startAnimation(animations.dequeue()); } if (verticalAnimationState == VerticalAnimationState::None && !verticalAnimations.empty()) { startVerticalAnimation(verticalAnimations.dequeue()); } if (animationState != AnimationState::None || verticalAnimationState != VerticalAnimationState::None) { if (animationState != AnimationState::None) { timeLine.update(std::chrono::milliseconds(time)); } if (verticalAnimationState != VerticalAnimationState::None) { verticalTimeLine.update(std::chrono::milliseconds(time)); } rotateCube(); } } effects->prePaintScreen(data, time); } void CubeEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (activated) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); // background float clearColor[4]; glGetFloatv(GL_COLOR_CLEAR_VALUE, clearColor); glClearColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF(), 1.0); glClear(GL_COLOR_BUFFER_BIT); glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); // wallpaper if (wallpaper) { ShaderBinder binder(ShaderTrait::MapTexture); binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); wallpaper->bind(); wallpaper->render(region, rect); wallpaper->unbind(); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // some veriables needed for painting the caps float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); float zTranslate = zPosition + zoom; if (animationState == AnimationState::Start) { zTranslate *= timeLine.value(); } else if (animationState == AnimationState::Stop) { zTranslate *= (1.0 - timeLine.value()); } // reflection if (reflection) { // we can use a huge scale factor (needed to calculate the rearground vertices) float scaleFactor = 1000000 * tan(60.0 * M_PI / 360.0f) / rect.height(); m_reflectionMatrix.setToIdentity(); m_reflectionMatrix.scale(1.0, -1.0, 1.0); double translate = 0.0; if (mode == Cube) { double addedHeight1 = -rect.height() * cos(verticalCurrentAngle*M_PI/180.0f) - rect.width() * sin(fabs(verticalCurrentAngle)*M_PI/180.0f)/tan(M_PI/effects->numberOfDesktops()); double addedHeight2 = -rect.width() * sin(fabs(verticalCurrentAngle)*M_PI/180.0f)*tan(M_PI*0.5f/effects->numberOfDesktops()); if (verticalCurrentAngle > 0.0f && effects->numberOfDesktops() & 1) translate = cos(fabs(currentAngle)*effects->numberOfDesktops()*M_PI/360.0f) * addedHeight2 + addedHeight1 - float(rect.height()); else translate = sin(fabs(currentAngle)*effects->numberOfDesktops()*M_PI/360.0f) * addedHeight2 + addedHeight1 - float(rect.height()); } else if (mode == Cylinder) { double addedHeight1 = -rect.height() * cos(verticalCurrentAngle*M_PI/180.0f) - rect.width() * sin(fabs(verticalCurrentAngle)*M_PI/180.0f)/tan(M_PI/effects->numberOfDesktops()); translate = addedHeight1 - float(rect.height()); } else { float radius = (rect.width() * 0.5) / cos(cubeAngle * 0.5 * M_PI / 180.0); translate = -rect.height()-2*radius; } m_reflectionMatrix.translate(0.0f, translate, 0.0f); reflectionPainting = true; glEnable(GL_CULL_FACE); paintCap(true, -point - zTranslate, data.projectionMatrix()); // cube glCullFace(GL_BACK); paintCube(mask, region, data); glCullFace(GL_FRONT); paintCube(mask, region, data); paintCap(false, -point - zTranslate, data.projectionMatrix()); glDisable(GL_CULL_FACE); reflectionPainting = false; const float width = rect.width(); const float height = rect.height(); float vertices[] = { -width * 0.5f, height, 0.0, width * 0.5f, height, 0.0, width * scaleFactor, height, -5000, -width * scaleFactor, height, -5000 }; // foreground float alpha = 0.7; if (animationState == AnimationState::Start) { alpha = 0.3 + 0.4 * timeLine.value(); } else if (animationState == AnimationState::Stop) { alpha = 0.3 + 0.4 * (1.0 - timeLine.value()); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (m_reflectionShader && m_reflectionShader->isValid()) { // ensure blending is enabled - no attribute stack ShaderBinder binder(m_reflectionShader); QMatrix4x4 windowTransformation = data.projectionMatrix(); windowTransformation.translate(rect.x() + rect.width() * 0.5f, 0.0, 0.0); m_reflectionShader->setUniform(GLShader::ModelViewProjectionMatrix, windowTransformation); m_reflectionShader->setUniform("u_alpha", alpha); QVector verts; QVector texcoords; verts.reserve(18); texcoords.reserve(12); texcoords << 0.0 << 0.0; verts << vertices[6] << vertices[7] << vertices[8]; texcoords << 0.0 << 0.0; verts << vertices[9] << vertices[10] << vertices[11]; texcoords << 1.0 << 0.0; verts << vertices[0] << vertices[1] << vertices[2]; texcoords << 1.0 << 0.0; verts << vertices[0] << vertices[1] << vertices[2]; texcoords << 1.0 << 0.0; verts << vertices[3] << vertices[4] << vertices[5]; texcoords << 0.0 << 0.0; verts << vertices[6] << vertices[7] << vertices[8]; GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setData(6, 3, verts.data(), texcoords.data()); vbo->render(GL_TRIANGLES); } glDisable(GL_BLEND); } glEnable(GL_CULL_FACE); // caps paintCap(false, -point - zTranslate, data.projectionMatrix()); // cube glCullFace(GL_FRONT); paintCube(mask, region, data); glCullFace(GL_BACK); paintCube(mask, region, data); // cap paintCap(true, -point - zTranslate, data.projectionMatrix()); glDisable(GL_CULL_FACE); glDisable(GL_BLEND); // desktop name box - inspired from coverswitch if (displayDesktopName) { double opacity = 1.0; if (animationState == AnimationState::Start) { opacity = timeLine.value(); } else if (animationState == AnimationState::Stop) { opacity = 1.0 - timeLine.value(); } QRect screenRect = effects->clientArea(ScreenArea, activeScreen, frontDesktop); QRect frameRect = QRect(screenRect.width() * 0.33f + screenRect.x(), screenRect.height() * 0.95f + screenRect.y(), screenRect.width() * 0.34f, QFontMetrics(desktopNameFont).height()); if (!desktopNameFrame) { desktopNameFrame = effects->effectFrame(EffectFrameStyled); desktopNameFrame->setFont(desktopNameFont); } desktopNameFrame->setGeometry(frameRect); desktopNameFrame->setText(effects->desktopName(frontDesktop)); desktopNameFrame->render(region, opacity); } } else { effects->paintScreen(mask, region, data); } } void CubeEffect::rotateCube() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); m_rotationMatrix.setToIdentity(); float internalCubeAngle = 360.0f / effects->numberOfDesktops(); float zTranslate = zPosition + zoom; float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); /* Animations */ if (animationState == AnimationState::Start) { zTranslate *= timeLine.value(); } else if (animationState == AnimationState::Stop) { currentAngle = startAngle * (1.0 - timeLine.value()); zTranslate *= (1.0 - timeLine.value()); } else if (animationState != AnimationState::None) { /* Left or right */ float endAngle = animationState == AnimationState::Right ? internalCubeAngle : -internalCubeAngle; currentAngle = startAngle + timeLine.value() * (endAngle - startAngle); frontDesktop = startFrontDesktop; } /* Switching to next desktop: either by mouse or due to animation */ if (currentAngle > internalCubeAngle * 0.5f) { currentAngle -= internalCubeAngle; frontDesktop--; if (frontDesktop < 1) { frontDesktop = effects->numberOfDesktops(); } } if (currentAngle < -internalCubeAngle * 0.5f) { currentAngle += internalCubeAngle; frontDesktop++; if (frontDesktop > effects->numberOfDesktops()) { frontDesktop = 1; } } /* Vertical animations */ if (verticalAnimationState != VerticalAnimationState::None) { float verticalEndAngle = 0.0; if (verticalAnimationState == VerticalAnimationState::Upwards && verticalStartAngle >= 0.0) { verticalEndAngle = 90.0; } if (verticalAnimationState == VerticalAnimationState::Downwards && verticalStartAngle <= 0.0) { verticalEndAngle = -90.0; } // This also handles the "VerticalAnimationState::Stop" correctly, since it has endAngle = 0.0 verticalCurrentAngle = verticalStartAngle + verticalTimeLine.value() * (verticalEndAngle - verticalStartAngle); } /* Updating rotation matrix */ if (verticalAnimationState != VerticalAnimationState::None || verticalCurrentAngle != 0.0f) { m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate); m_rotationMatrix.rotate(verticalCurrentAngle, 1.0, 0.0, 0.0); m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate); } if (animationState != AnimationState::None || currentAngle != 0.0f) { m_rotationMatrix.translate(rect.width() / 2, rect.height() / 2, -point - zTranslate); m_rotationMatrix.rotate(currentAngle, 0.0, 1.0, 0.0); m_rotationMatrix.translate(-rect.width() / 2, -rect.height() / 2, point + zTranslate); } } void CubeEffect::paintCube(int mask, QRegion region, ScreenPaintData& data) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float internalCubeAngle = 360.0f / effects->numberOfDesktops(); cube_painting = true; float zTranslate = zPosition + zoom; if (animationState == AnimationState::Start) { zTranslate *= timeLine.value(); } else if (animationState == AnimationState::Stop) { zTranslate *= (1.0 - timeLine.value()); } // Rotation of the cube float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float point = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); for (int i = 0; i < effects->numberOfDesktops(); i++) { // start painting the cube painting_desktop = (i + frontDesktop) % effects->numberOfDesktops(); if (painting_desktop == 0) { painting_desktop = effects->numberOfDesktops(); } QMatrix4x4 matrix; matrix.translate(0, 0, -zTranslate); const QVector3D origin(rect.width() / 2, 0.0, -point); matrix.translate(origin); matrix.rotate(internalCubeAngle * i, 0, 1, 0); matrix.translate(-origin); m_currentFaceMatrix = matrix; effects->paintScreen(mask, region, data); } cube_painting = false; painting_desktop = effects->currentDesktop(); } void CubeEffect::paintCap(bool frontFirst, float zOffset, const QMatrix4x4 &projection) { if ((!paintCaps) || effects->numberOfDesktops() <= 2) return; GLenum firstCull = frontFirst ? GL_FRONT : GL_BACK; GLenum secondCull = frontFirst ? GL_BACK : GL_FRONT; const QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); // create the VBO if not yet created if (!m_cubeCapBuffer) { switch(mode) { case Cube: paintCubeCap(); break; case Cylinder: paintCylinderCap(); break; case Sphere: paintSphereCap(); break; default: // impossible break; } } QMatrix4x4 capMvp; QMatrix4x4 capMatrix; capMatrix.translate(rect.width() / 2, 0.0, zOffset); capMatrix.rotate((1 - frontDesktop) * 360.0f / effects->numberOfDesktops(), 0.0, 1.0, 0.0); capMatrix.translate(0.0, rect.height(), 0.0); if (mode == Sphere) { capMatrix.scale(1.0, -1.0, 1.0); } bool capShader = false; if (effects->compositingType() == OpenGL2Compositing && m_capShader && m_capShader->isValid()) { capShader = true; ShaderManager::instance()->pushShader(m_capShader); float opacity = cubeOpacity; if (animationState == AnimationState::Start) { opacity *= timeLine.value(); } else if (animationState == AnimationState::Stop) { opacity *= (1.0 - timeLine.value()); } m_capShader->setUniform("u_opacity", opacity); m_capShader->setUniform("u_mirror", 1); if (reflectionPainting) { capMvp = projection * m_reflectionMatrix * m_rotationMatrix; } else { capMvp = projection * m_rotationMatrix; } m_capShader->setUniform(GLShader::ModelViewProjectionMatrix, capMvp * capMatrix); m_capShader->setUniform("u_untextured", texturedCaps ? 0 : 1); if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) { capTexture->bind(); } } glEnable(GL_BLEND); glCullFace(firstCull); m_cubeCapBuffer->render(GL_TRIANGLES); if (mode == Sphere) { capMatrix.scale(1.0, -1.0, 1.0); } capMatrix.translate(0.0, -rect.height(), 0.0); if (capShader) { m_capShader->setUniform(GLShader::ModelViewProjectionMatrix, capMvp * capMatrix); m_capShader->setUniform("u_mirror", 0); } glCullFace(secondCull); m_cubeCapBuffer->render(GL_TRIANGLES); glDisable(GL_BLEND); if (capShader) { ShaderManager::instance()->popShader(); if (texturedCaps && effects->numberOfDesktops() > 3 && capTexture) { capTexture->unbind(); } } } void CubeEffect::paintCubeCap() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float z = rect.width() / 2 * tan(cubeAngle * 0.5f * M_PI / 180.0f); float zTexture = rect.width() / 2 * tan(45.0f * M_PI / 180.0f); float angle = 360.0f / effects->numberOfDesktops(); bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; QVector verts; QVector texCoords; for (int i = 0; i < effects->numberOfDesktops(); i++) { int triangleRows = effects->numberOfDesktops() * 5; float zTriangleDistance = z / (float)triangleRows; float widthTriangle = tan(angle * 0.5 * M_PI / 180.0) * zTriangleDistance; float currentWidth = 0.0; float cosValue = cos(i * angle * M_PI / 180.0); float sinValue = sin(i * angle * M_PI / 180.0); for (int j = 0; j < triangleRows; j++) { float previousWidth = currentWidth; currentWidth = tan(angle * 0.5 * M_PI / 180.0) * zTriangleDistance * (j + 1); int evenTriangles = 0; int oddTriangles = 0; for (int k = 0; k < floor(currentWidth / widthTriangle * 2 - 1 + 0.5f); k++) { float x1 = -previousWidth; float x2 = -currentWidth; float x3 = 0.0; float z1 = 0.0; float z2 = 0.0; float z3 = 0.0; if (k % 2 == 0) { x1 += evenTriangles * widthTriangle * 2; x2 += evenTriangles * widthTriangle * 2; x3 = x2 + widthTriangle * 2; z1 = j * zTriangleDistance; z2 = (j + 1) * zTriangleDistance; z3 = (j + 1) * zTriangleDistance; float xRot = cosValue * x1 - sinValue * z1; float zRot = sinValue * x1 + cosValue * z1; x1 = xRot; z1 = zRot; xRot = cosValue * x2 - sinValue * z2; zRot = sinValue * x2 + cosValue * z2; x2 = xRot; z2 = zRot; xRot = cosValue * x3 - sinValue * z3; zRot = sinValue * x3 + cosValue * z3; x3 = xRot; z3 = zRot; evenTriangles++; } else { x1 += oddTriangles * widthTriangle * 2; x2 += (oddTriangles + 1) * widthTriangle * 2; x3 = x1 + widthTriangle * 2; z1 = j * zTriangleDistance; z2 = (j + 1) * zTriangleDistance; z3 = j * zTriangleDistance; float xRot = cosValue * x1 - sinValue * z1; float zRot = sinValue * x1 + cosValue * z1; x1 = xRot; z1 = zRot; xRot = cosValue * x2 - sinValue * z2; zRot = sinValue * x2 + cosValue * z2; x2 = xRot; z2 = zRot; xRot = cosValue * x3 - sinValue * z3; zRot = sinValue * x3 + cosValue * z3; x3 = xRot; z3 = zRot; oddTriangles++; } float texX1 = 0.0; float texX2 = 0.0; float texX3 = 0.0; float texY1 = 0.0; float texY2 = 0.0; float texY3 = 0.0; if (texture) { if (capTexture->isYInverted()) { texX1 = x1 / (rect.width()) + 0.5; texY1 = 0.5 + z1 / zTexture * 0.5; texX2 = x2 / (rect.width()) + 0.5; texY2 = 0.5 + z2 / zTexture * 0.5; texX3 = x3 / (rect.width()) + 0.5; texY3 = 0.5 + z3 / zTexture * 0.5; texCoords << texX1 << texY1; } else { texX1 = x1 / (rect.width()) + 0.5; texY1 = 0.5 - z1 / zTexture * 0.5; texX2 = x2 / (rect.width()) + 0.5; texY2 = 0.5 - z2 / zTexture * 0.5; texX3 = x3 / (rect.width()) + 0.5; texY3 = 0.5 - z3 / zTexture * 0.5; texCoords << texX1 << texY1; } } verts << x1 << 0.0 << z1; if (texture) { texCoords << texX2 << texY2; } verts << x2 << 0.0 << z2; if (texture) { texCoords << texX3 << texY3; } verts << x3 << 0.0 << z3; } } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::paintCylinderCap() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float radian = (cubeAngle * 0.5) * M_PI / 180; float radius = (rect.width() * 0.5) * tan(radian); float segment = radius / 30.0f; bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; QVector verts; QVector texCoords; for (int i = 1; i <= 30; i++) { int steps = 72; for (int j = 0; j <= steps; j++) { const float azimuthAngle = (j * (360.0f / steps)) * M_PI / 180.0f; const float azimuthAngle2 = ((j + 1) * (360.0f / steps)) * M_PI / 180.0f; const float x1 = segment * (i - 1) * sin(azimuthAngle); const float x2 = segment * i * sin(azimuthAngle); const float x3 = segment * (i - 1) * sin(azimuthAngle2); const float x4 = segment * i * sin(azimuthAngle2); const float z1 = segment * (i - 1) * cos(azimuthAngle); const float z2 = segment * i * cos(azimuthAngle); const float z3 = segment * (i - 1) * cos(azimuthAngle2); const float z4 = segment * i * cos(azimuthAngle2); if (texture) { if (capTexture->isYInverted()) { texCoords << (radius + x1) / (radius * 2.0f) << (z1 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << (z2 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << (z3 + radius) / (radius * 2.0f); texCoords << (radius + x4) / (radius * 2.0f) << (z4 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << (z3 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << (z2 + radius) / (radius * 2.0f); } else { texCoords << (radius + x1) / (radius * 2.0f) << 1.0f - (z1 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << 1.0f - (z2 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << 1.0f - (z3 + radius) / (radius * 2.0f); texCoords << (radius + x4) / (radius * 2.0f) << 1.0f - (z4 + radius) / (radius * 2.0f); texCoords << (radius + x3) / (radius * 2.0f) << 1.0f - (z3 + radius) / (radius * 2.0f); texCoords << (radius + x2) / (radius * 2.0f) << 1.0f - (z2 + radius) / (radius * 2.0f); } } verts << x1 << 0.0 << z1; verts << x2 << 0.0 << z2; verts << x3 << 0.0 << z3; verts << x4 << 0.0 << z4; verts << x3 << 0.0 << z3; verts << x2 << 0.0 << z2; } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::paintSphereCap() { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float cubeAngle = (float)((float)(effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 180.0f); float zTexture = rect.width() / 2 * tan(45.0f * M_PI / 180.0f); float radius = (rect.width() * 0.5) / cos(cubeAngle * 0.5 * M_PI / 180.0); float angle = acos((rect.height() * 0.5) / radius) * 180.0 / M_PI; angle /= 30; bool texture = texturedCaps && effects->numberOfDesktops() > 3 && capTexture; QVector verts; QVector texCoords; for (int i = 0; i < 30; i++) { float topAngle = angle * i * M_PI / 180.0; float bottomAngle = angle * (i + 1) * M_PI / 180.0; float yTop = (rect.height() * 0.5 - radius * cos(topAngle)); yTop *= (1.0f-capDeformationFactor); float yBottom = rect.height() * 0.5 - radius * cos(bottomAngle); yBottom *= (1.0f - capDeformationFactor); for (int j = 0; j < 36; j++) { const float x1 = radius * sin(topAngle) * sin((90.0 + j * 10.0) * M_PI / 180.0); const float z1 = radius * sin(topAngle) * cos((90.0 + j * 10.0) * M_PI / 180.0); const float x2 = radius * sin(bottomAngle) * sin((90.0 + j * 10.0) * M_PI / 180.00); const float z2 = radius * sin(bottomAngle) * cos((90.0 + j * 10.0) * M_PI / 180.0); const float x3 = radius * sin(bottomAngle) * sin((90.0 + (j + 1) * 10.0) * M_PI / 180.0); const float z3 = radius * sin(bottomAngle) * cos((90.0 + (j + 1) * 10.0) * M_PI / 180.0); const float x4 = radius * sin(topAngle) * sin((90.0 + (j + 1) * 10.0) * M_PI / 180.0); const float z4 = radius * sin(topAngle) * cos((90.0 + (j + 1) * 10.0) * M_PI / 180.0); if (texture) { if (capTexture->isYInverted()) { texCoords << x4 / (rect.width()) + 0.5 << 0.5 + z4 / zTexture * 0.5; texCoords << x1 / (rect.width()) + 0.5 << 0.5 + z1 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 + z2 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 + z2 / zTexture * 0.5; texCoords << x3 / (rect.width()) + 0.5 << 0.5 + z3 / zTexture * 0.5; texCoords << x4 / (rect.width()) + 0.5 << 0.5 + z4 / zTexture * 0.5; } else { texCoords << x4 / (rect.width()) + 0.5 << 0.5 - z4 / zTexture * 0.5; texCoords << x1 / (rect.width()) + 0.5 << 0.5 - z1 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 - z2 / zTexture * 0.5; texCoords << x2 / (rect.width()) + 0.5 << 0.5 - z2 / zTexture * 0.5; texCoords << x3 / (rect.width()) + 0.5 << 0.5 - z3 / zTexture * 0.5; texCoords << x4 / (rect.width()) + 0.5 << 0.5 - z4 / zTexture * 0.5; } } verts << x4 << yTop << z4; verts << x1 << yTop << z1; verts << x2 << yBottom << z2; verts << x2 << yBottom << z2; verts << x3 << yBottom << z3; verts << x4 << yTop << z4; } } delete m_cubeCapBuffer; m_cubeCapBuffer = new GLVertexBuffer(GLVertexBuffer::Static); m_cubeCapBuffer->setData(verts.count() / 3, 3, verts.constData(), texture ? texCoords.constData() : NULL); } void CubeEffect::postPaintScreen() { effects->postPaintScreen(); if (!activated) return; bool animation = (animationState != AnimationState::None || verticalAnimationState != VerticalAnimationState::None); if (animationState != AnimationState::None && timeLine.done()) { /* An animation have just finished! */ if (animationState == AnimationState::Stop) { /* If the stop animation is finished, we're done */ if (keyboard_grab) effects->ungrabKeyboard(); keyboard_grab = false; effects->stopMouseInterception(this); effects->setCurrentDesktop(frontDesktop); effects->setActiveFullScreenEffect(0); delete m_cubeCapBuffer; m_cubeCapBuffer = NULL; if (desktopNameFrame) desktopNameFrame->free(); activated = false; // User can press Esc several times, and several Stop animations can be added to queue. We don't want it animationState = AnimationState::None; animations.clear(); verticalAnimationState = VerticalAnimationState::None; verticalAnimations.clear(); } else { if (!animations.empty()) startAnimation(animations.dequeue()); else animationState = AnimationState::None; } } /* Vertical animation have finished */ if (verticalAnimationState != VerticalAnimationState::None && verticalTimeLine.done()) { if (!verticalAnimations.empty()) { startVerticalAnimation(verticalAnimations.dequeue()); } else { verticalAnimationState = VerticalAnimationState::None; } } /* Repaint if there is any animation */ if (animation) { effects->addRepaintFull(); } } void CubeEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (activated) { if (cube_painting) { if (mode == Cylinder || mode == Sphere) { int leftDesktop = frontDesktop - 1; int rightDesktop = frontDesktop + 1; if (leftDesktop == 0) leftDesktop = effects->numberOfDesktops(); if (rightDesktop > effects->numberOfDesktops()) rightDesktop = 1; if (painting_desktop == frontDesktop) data.quads = data.quads.makeGrid(40); else if (painting_desktop == leftDesktop || painting_desktop == rightDesktop) data.quads = data.quads.makeGrid(100); else data.quads = data.quads.makeGrid(250); } if (w->isOnDesktop(painting_desktop)) { QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop); if (w->x() < rect.x()) { data.quads = data.quads.splitAtX(-w->x()); } if (w->x() + w->width() > rect.x() + rect.width()) { data.quads = data.quads.splitAtX(rect.width() - w->x()); } if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } if (useZOrdering && !w->isDesktop() && !w->isDock() && !w->isOnAllDesktops()) data.setTransformed(); w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } else { // check for windows belonging to the previous desktop int prev_desktop = painting_desktop - 1; if (prev_desktop == 0) prev_desktop = effects->numberOfDesktops(); if (w->isOnDesktop(prev_desktop) && mode == Cube && !useZOrdering) { QRect rect = effects->clientArea(FullArea, activeScreen, prev_desktop); if (w->x() + w->width() > rect.x() + rect.width()) { w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); data.quads = data.quads.splitAtX(rect.width() - w->x()); if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } data.setTransformed(); effects->prePaintWindow(w, data, time); return; } } // check for windows belonging to the next desktop int next_desktop = painting_desktop + 1; if (next_desktop > effects->numberOfDesktops()) next_desktop = 1; if (w->isOnDesktop(next_desktop) && mode == Cube && !useZOrdering) { QRect rect = effects->clientArea(FullArea, activeScreen, next_desktop); if (w->x() < rect.x()) { w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); data.quads = data.quads.splitAtX(-w->x()); if (w->y() < rect.y()) { data.quads = data.quads.splitAtY(-w->y()); } if (w->y() + w->height() > rect.y() + rect.height()) { data.quads = data.quads.splitAtY(rect.height() - w->y()); } data.setTransformed(); effects->prePaintWindow(w, data, time); return; } } w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } } } effects->prePaintWindow(w, data, time); } void CubeEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { ShaderManager *shaderManager = ShaderManager::instance(); if (activated && cube_painting) { region= infiniteRegion(); // we need to explicitly prevent any clipping, bug #325432 //qCDebug(KWINEFFECTS) << w->caption(); float opacity = cubeOpacity; if (animationState == AnimationState::Start) { opacity = 1.0 - (1.0 - opacity) * timeLine.value(); if (reflectionPainting) opacity = 0.5 + (cubeOpacity - 0.5) * timeLine.value(); // fade in windows belonging to different desktops if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop))) opacity = timeLine.value() * cubeOpacity; } else if (animationState == AnimationState::Stop) { opacity = 1.0 - (1.0 - opacity) * (1.0 - timeLine.value()); if (reflectionPainting) opacity = 0.5 + (cubeOpacity - 0.5) * (1.0 - timeLine.value()); // fade out windows belonging to different desktops if (painting_desktop == effects->currentDesktop() && (!w->isOnDesktop(painting_desktop))) opacity = cubeOpacity * (1.0 - timeLine.value()); } // z-Ordering if (!w->isDesktop() && !w->isDock() && useZOrdering && !w->isOnAllDesktops()) { float zOrdering = (effects->stackingOrder().indexOf(w) + 1) * zOrderingFactor; if (animationState == AnimationState::Start) { zOrdering *= timeLine.value(); } else if (animationState == AnimationState::Stop) { zOrdering *= (1.0 - timeLine.value()); } data.translate(0.0, 0.0, zOrdering); } // check for windows belonging to the previous desktop int prev_desktop = painting_desktop - 1; if (prev_desktop == 0) prev_desktop = effects->numberOfDesktops(); int next_desktop = painting_desktop + 1; if (next_desktop > effects->numberOfDesktops()) next_desktop = 1; if (w->isOnDesktop(prev_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) { QRect rect = effects->clientArea(FullArea, activeScreen, prev_desktop); WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() > rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; data.setXTranslation(-rect.width()); } if (w->isOnDesktop(next_desktop) && (mask & PAINT_WINDOW_TRANSFORMED)) { QRect rect = effects->clientArea(FullArea, activeScreen, next_desktop); WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (w->x() + quad.right() <= rect.x()) { new_quads.append(quad); } } data.quads = new_quads; data.setXTranslation(rect.width()); } QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop); if (animationState == AnimationState::Start || animationState == AnimationState::Stop) { // we have to change opacity values for fade in/out of windows which are shown on front-desktop if (prev_desktop == effects->currentDesktop() && w->x() < rect.x()) { if (animationState == AnimationState::Start) { opacity = timeLine.value() * cubeOpacity; } else if (animationState == AnimationState::Stop) { opacity = cubeOpacity * (1.0 - timeLine.value()); } } if (next_desktop == effects->currentDesktop() && w->x() + w->width() > rect.x() + rect.width()) { if (animationState == AnimationState::Start) { opacity = timeLine.value() * cubeOpacity; } else if (animationState == AnimationState::Stop) { opacity = cubeOpacity * (1.0 - timeLine.value()); } } } // HACK set opacity to 0.99 in case of fully opaque to ensure that windows are painted in correct sequence // bug #173214 if (opacity > 0.99f) opacity = 0.99f; if (opacityDesktopOnly && !w->isDesktop()) opacity = 0.99f; data.multiplyOpacity(opacity); if (w->isOnDesktop(painting_desktop) && w->x() < rect.x()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() > -w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->isOnDesktop(painting_desktop) && w->x() + w->width() > rect.x() + rect.width()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.right() <= rect.width() - w->x()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() < rect.y()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() > -w->y()) { new_quads.append(quad); } } data.quads = new_quads; } if (w->y() + w->height() > rect.y() + rect.height()) { WindowQuadList new_quads; foreach (const WindowQuad & quad, data.quads) { if (quad.bottom() <= rect.height() - w->y()) { new_quads.append(quad); } } data.quads = new_quads; } GLShader *currentShader = nullptr; if (mode == Cylinder) { shaderManager->pushShader(cylinderShader); cylinderShader->setUniform("xCoord", (float)w->x()); cylinderShader->setUniform("cubeAngle", (effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 90.0f); float factor = 0.0f; if (animationState == AnimationState::Start) { factor = 1.0f - timeLine.value(); } else if (animationState == AnimationState::Stop) { factor = timeLine.value(); } cylinderShader->setUniform("timeLine", factor); currentShader = cylinderShader; } if (mode == Sphere) { shaderManager->pushShader(sphereShader); sphereShader->setUniform("u_offset", QVector2D(w->x(), w->y())); sphereShader->setUniform("cubeAngle", (effects->numberOfDesktops() - 2) / (float)effects->numberOfDesktops() * 90.0f); float factor = 0.0f; if (animationState == AnimationState::Start) { factor = 1.0f - timeLine.value(); } else if (animationState == AnimationState::Stop) { factor = timeLine.value(); } sphereShader->setUniform("timeLine", factor); currentShader = sphereShader; } if (currentShader) { data.shader = currentShader; } data.setProjectionMatrix(data.screenProjectionMatrix()); if (reflectionPainting) { data.setModelViewMatrix(m_reflectionMatrix * m_rotationMatrix * m_currentFaceMatrix); } else { data.setModelViewMatrix(m_rotationMatrix * m_currentFaceMatrix); } } effects->paintWindow(w, mask, region, data); if (activated && cube_painting) { if (mode == Cylinder || mode == Sphere) { shaderManager->popShader(); } if (w->isDesktop() && effects->numScreens() > 1 && paintCaps) { QRect rect = effects->clientArea(FullArea, activeScreen, painting_desktop); QRegion paint = QRegion(rect); for (int i = 0; i < effects->numScreens(); i++) { if (i == w->screen()) continue; paint = paint.subtracted(QRegion(effects->clientArea(ScreenArea, i, painting_desktop))); } paint = paint.subtracted(QRegion(w->geometry())); // in case of free area in multiscreen setup fill it with cap color if (!paint.isEmpty()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); QVector verts; float quadSize = 0.0f; int leftDesktop = frontDesktop - 1; int rightDesktop = frontDesktop + 1; if (leftDesktop == 0) leftDesktop = effects->numberOfDesktops(); if (rightDesktop > effects->numberOfDesktops()) rightDesktop = 1; if (painting_desktop == frontDesktop) quadSize = 100.0f; else if (painting_desktop == leftDesktop || painting_desktop == rightDesktop) quadSize = 150.0f; else quadSize = 250.0f; foreach (const QRect & paintRect, paint.rects()) { for (int i = 0; i <= (paintRect.height() / quadSize); i++) { for (int j = 0; j <= (paintRect.width() / quadSize); j++) { verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << paintRect.y() + i*quadSize; verts << paintRect.x() + j*quadSize << paintRect.y() + i*quadSize; verts << paintRect.x() + j*quadSize << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height()); verts << paintRect.x() + j*quadSize << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height()); verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << qMin(paintRect.y() + (i + 1)*quadSize, (float)paintRect.y() + paintRect.height()); verts << qMin(paintRect.x() + (j + 1)*quadSize, (float)paintRect.x() + paintRect.width()) << paintRect.y() + i*quadSize; } } } bool capShader = false; if (effects->compositingType() == OpenGL2Compositing && m_capShader && m_capShader->isValid()) { capShader = true; ShaderManager::instance()->pushShader(m_capShader); m_capShader->setUniform("u_mirror", 0); m_capShader->setUniform("u_untextured", 1); QMatrix4x4 mvp = data.screenProjectionMatrix(); if (reflectionPainting) { mvp = mvp * m_reflectionMatrix * m_rotationMatrix * m_currentFaceMatrix; } else { mvp = mvp * m_rotationMatrix * m_currentFaceMatrix; } m_capShader->setUniform(GLShader::ModelViewProjectionMatrix, mvp); } GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); QColor color = capColor; capColor.setAlphaF(cubeOpacity); vbo->setColor(color); vbo->setData(verts.size() / 2, 2, verts.constData(), NULL); if (!capShader || mode == Cube) { // TODO: use sphere and cylinder shaders vbo->render(GL_TRIANGLES); } if (capShader) { ShaderManager::instance()->popShader(); } glDisable(GL_BLEND); } } } } bool CubeEffect::borderActivated(ElectricBorder border) { if (!borderActivate.contains(border) && !borderActivateCylinder.contains(border) && !borderActivateSphere.contains(border)) return false; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return false; if (borderActivate.contains(border)) { if (!activated || (activated && mode == Cube)) toggleCube(); else return false; } if (borderActivateCylinder.contains(border)) { if (!activated || (activated && mode == Cylinder)) toggleCylinder(); else return false; } if (borderActivateSphere.contains(border)) { if (!activated || (activated && mode == Sphere)) toggleSphere(); else return false; } return true; } void CubeEffect::toggleCube() { qCDebug(KWINEFFECTS) << "toggle cube"; toggle(Cube); } void CubeEffect::toggleCylinder() { qCDebug(KWINEFFECTS) << "toggle cylinder"; if (!useShaders) useShaders = loadShader(); if (useShaders) toggle(Cylinder); } void CubeEffect::toggleSphere() { qCDebug(KWINEFFECTS) << "toggle sphere"; if (!useShaders) useShaders = loadShader(); if (useShaders) toggle(Sphere); } void CubeEffect::toggle(CubeMode newMode) { if ((effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) || effects->numberOfDesktops() < 2) return; if (!activated) { mode = newMode; setActive(true); } else { setActive(false); } } void CubeEffect::grabbedKeyboardEvent(QKeyEvent* e) { // If either stop is running or is scheduled - ignore all events if ((!animations.isEmpty() && animations.last() == AnimationState::Stop) || animationState == AnimationState::Stop) { return; } // taken from desktopgrid.cpp if (e->type() == QEvent::KeyPress) { // check for global shortcuts // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155) if (mode == Cube && cubeShortcut.contains(e->key() + e->modifiers())) { toggleCube(); return; } if (mode == Cylinder && cylinderShortcut.contains(e->key() + e->modifiers())) { toggleCylinder(); return; } if (mode == Sphere && sphereShortcut.contains(e->key() + e->modifiers())) { toggleSphere(); return; } int desktop = -1; // switch by F or just if (e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35) desktop = e->key() - Qt::Key_F1 + 1; else if (e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9) desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0; if (desktop != -1) { if (desktop <= effects->numberOfDesktops()) { // we have to rotate to chosen desktop // and end effect when rotation finished rotateToDesktop(desktop); setActive(false); } return; } int key = e->key(); if (invertKeys) { if (key == Qt::Key_Left) key = Qt::Key_Right; else if (key == Qt::Key_Right) key = Qt::Key_Left; else if (key == Qt::Key_Up) key = Qt::Key_Down; else if (key == Qt::Key_Down) key = Qt::Key_Up; } switch(key) { // wrap only on autorepeat case Qt::Key_Left: qCDebug(KWINEFFECTS) << "left"; if (animations.count() < effects->numberOfDesktops()) animations.enqueue(AnimationState::Left); break; case Qt::Key_Right: qCDebug(KWINEFFECTS) << "right"; if (animations.count() < effects->numberOfDesktops()) animations.enqueue(AnimationState::Right); break; case Qt::Key_Up: qCDebug(KWINEFFECTS) << "up"; verticalAnimations.enqueue(VerticalAnimationState::Upwards); break; case Qt::Key_Down: qCDebug(KWINEFFECTS) << "down"; verticalAnimations.enqueue(VerticalAnimationState::Downwards); break; case Qt::Key_Escape: rotateToDesktop(effects->currentDesktop()); setActive(false); return; case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Space: setActive(false); return; case Qt::Key_Plus: case Qt::Key_Equal: zoom -= 10.0; zoom = qMax(-zPosition, zoom); rotateCube(); break; case Qt::Key_Minus: zoom += 10.0f; rotateCube(); break; default: break; } } effects->addRepaintFull(); } void CubeEffect::rotateToDesktop(int desktop) { // all scheduled animations will be removed as a speed up animations.clear(); verticalAnimations.clear(); // we want only startAnimation to finish gracefully // all the others can be interrupted if (animationState != AnimationState::Start) { animationState = AnimationState::None; } verticalAnimationState = VerticalAnimationState::None; // find the fastest rotation path from frontDesktop to desktop int rightRotations = frontDesktop - desktop; if (rightRotations < 0) { rightRotations += effects->numberOfDesktops(); } int leftRotations = desktop - frontDesktop; if (leftRotations < 0) { leftRotations += effects->numberOfDesktops(); } if (leftRotations <= rightRotations) { for (int i = 0; i < leftRotations; i++) { animations.enqueue(AnimationState::Left); } } else { for (int i = 0; i < rightRotations; i++) { animations.enqueue(AnimationState::Right); } } // we want the face of desktop to appear, it might need also vertical animation if (verticalCurrentAngle > 0.0f) { verticalAnimations.enqueue(VerticalAnimationState::Downwards); } if (verticalCurrentAngle < 0.0f) { verticalAnimations.enqueue(VerticalAnimationState::Upwards); } /* Start immediately, so there is no pause: * during that pause, actual frontDesktop might change * if user moves his mouse fast, leading to incorrect desktop */ if (animationState == AnimationState::None && !animations.empty()) { startAnimation(animations.dequeue()); } if (verticalAnimationState == VerticalAnimationState::None && !verticalAnimations.empty()) { startVerticalAnimation(verticalAnimations.dequeue()); } } void CubeEffect::setActive(bool active) { foreach (CubeInsideEffect * inside, m_cubeInsideEffects) { inside->setActive(true); } if (active) { QString capPath = CubeConfig::capPath(); if (texturedCaps && !capTexture && !capPath.isEmpty()) { QFutureWatcher *watcher = new QFutureWatcher(this); - connect(watcher, SIGNAL(finished()), SLOT(slotCubeCapLoaded())); + connect(watcher, &QFutureWatcher::finished, this, &CubeEffect::slotCubeCapLoaded); watcher->setFuture(QtConcurrent::run(this, &CubeEffect::loadCubeCap, capPath)); } QString wallpaperPath = CubeConfig::wallpaper().toLocalFile(); if (!wallpaper && !wallpaperPath.isEmpty()) { QFutureWatcher *watcher = new QFutureWatcher(this); - connect(watcher, SIGNAL(finished()), SLOT(slotWallPaperLoaded())); + connect(watcher, &QFutureWatcher::finished, this, &CubeEffect::slotWallPaperLoaded); watcher->setFuture(QtConcurrent::run(this, &CubeEffect::loadWallPaper, wallpaperPath)); } activated = true; activeScreen = effects->activeScreen(); keyboard_grab = effects->grabKeyboard(this); effects->startMouseInterception(this, Qt::OpenHandCursor); frontDesktop = effects->currentDesktop(); zoom = 0.0; zOrderingFactor = zPosition / (effects->stackingOrder().count() - 1); animations.enqueue(AnimationState::Start); animationState = AnimationState::None; verticalAnimationState = VerticalAnimationState::None; effects->setActiveFullScreenEffect(this); qCDebug(KWINEFFECTS) << "Cube is activated"; currentAngle = 0.0; verticalCurrentAngle = 0.0; if (reflection) { QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); float temporaryCoeff = float(rect.width()) / tan(M_PI / float(effects->numberOfDesktops())); mAddedHeightCoeff1 = sqrt(float(rect.height()) * float(rect.height()) + temporaryCoeff * temporaryCoeff); mAddedHeightCoeff2 = sqrt(float(rect.height()) * float(rect.height()) + float(rect.width()) * float(rect.width()) + temporaryCoeff * temporaryCoeff); } m_rotationMatrix.setToIdentity(); } else { animations.enqueue(AnimationState::Stop); } effects->addRepaintFull(); } void CubeEffect::windowInputMouseEvent(QEvent* e) { if (!activated) return; if (tabBoxMode) return; if ((!animations.isEmpty() && animations.last() == AnimationState::Stop) || animationState == AnimationState::Stop) return; QMouseEvent *mouse = dynamic_cast< QMouseEvent* >(e); if (!mouse) return; static QPoint oldpos; static QElapsedTimer dblClckTime; static int dblClckCounter(0); if (mouse->type() == QEvent::MouseMove && mouse->buttons().testFlag(Qt::LeftButton)) { const QPoint pos = mouse->pos(); QRect rect = effects->clientArea(FullArea, activeScreen, effects->currentDesktop()); bool repaint = false; // vertical movement only if there is not a rotation if (verticalAnimationState == VerticalAnimationState::None) { // display height corresponds to 180* int deltaY = pos.y() - oldpos.y(); float deltaVerticalDegrees = (float)deltaY / rect.height() * 180.0f; if (invertMouse) verticalCurrentAngle += deltaVerticalDegrees; else verticalCurrentAngle -= deltaVerticalDegrees; // don't get too excited verticalCurrentAngle = qBound(-90.0f, verticalCurrentAngle, 90.0f); if (deltaVerticalDegrees != 0.0) repaint = true; } // horizontal movement only if there is not a rotation if (animationState == AnimationState::None) { // display width corresponds to sum of angles of the polyhedron int deltaX = oldpos.x() - pos.x(); float deltaDegrees = (float)deltaX / rect.width() * 360.0f; if (deltaX == 0) { if (pos.x() == 0) deltaDegrees = 5.0f; if (pos.x() == rect.width() - 1) deltaDegrees = -5.0f; } if (invertMouse) currentAngle += deltaDegrees; else currentAngle -= deltaDegrees; if (deltaDegrees != 0.0) repaint = true; } if (repaint) { rotateCube(); effects->addRepaintFull(); } oldpos = pos; } else if (mouse->type() == QEvent::MouseButtonPress && mouse->button() == Qt::LeftButton) { oldpos = mouse->pos(); if (dblClckTime.elapsed() > QApplication::doubleClickInterval()) dblClckCounter = 0; if (!dblClckCounter) dblClckTime.start(); } else if (mouse->type() == QEvent::MouseButtonRelease) { effects->defineCursor(Qt::OpenHandCursor); if (mouse->button() == Qt::LeftButton && ++dblClckCounter == 2) { dblClckCounter = 0; if (dblClckTime.elapsed() < QApplication::doubleClickInterval()) { setActive(false); return; } } else if (mouse->button() == Qt::XButton1) { if (animations.count() < effects->numberOfDesktops()) { if (invertMouse) animations.enqueue(AnimationState::Right); else animations.enqueue(AnimationState::Left); } effects->addRepaintFull(); } else if (mouse->button() == Qt::XButton2) { if (animations.count() < effects->numberOfDesktops()) { if (invertMouse) animations.enqueue(AnimationState::Left); else animations.enqueue(AnimationState::Right); } effects->addRepaintFull(); } else if (mouse->button() == Qt::RightButton || (mouse->button() == Qt::LeftButton && closeOnMouseRelease)) { setActive(false); } } } void CubeEffect::slotTabBoxAdded(int mode) { if (activated) return; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; if (useForTabBox && mode == TabBoxDesktopListMode) { effects->refTabBox(); tabBoxMode = true; setActive(true); rotateToDesktop(effects->currentTabBoxDesktop()); } } void CubeEffect::slotTabBoxUpdated() { if (activated) { rotateToDesktop(effects->currentTabBoxDesktop()); effects->addRepaintFull(); } } void CubeEffect::slotTabBoxClosed() { if (activated) { effects->unrefTabBox(); tabBoxMode = false; setActive(false); } } void CubeEffect::globalShortcutChanged(QAction *action, const QKeySequence &seq) { if (action->objectName() == QStringLiteral("Cube")) { cubeShortcut.clear(); cubeShortcut.append(seq); } else if (action->objectName() == QStringLiteral("Cylinder")) { cylinderShortcut.clear(); cylinderShortcut.append(seq); } else if (action->objectName() == QStringLiteral("Sphere")) { sphereShortcut.clear(); sphereShortcut.append(seq); } } void* CubeEffect::proxy() { return &m_proxy; } void CubeEffect::registerCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.append(effect); } void CubeEffect::unregisterCubeInsideEffect(CubeInsideEffect* effect) { m_cubeInsideEffects.removeAll(effect); } bool CubeEffect::isActive() const { return activated && !effects->isScreenLocked(); } } // namespace diff --git a/effects/cube/cube_config.cpp b/effects/cube/cube_config.cpp index 422506dd9..0c663c555 100644 --- a/effects/cube/cube_config.cpp +++ b/effects/cube/cube_config.cpp @@ -1,121 +1,121 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "cube_config.h" // KConfigSkeleton #include "cubeconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(CubeEffectConfigFactory, "cube_config.json", registerPlugin();) namespace KWin { CubeEffectConfigForm::CubeEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } CubeEffectConfig::CubeEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("cube")), parent, args) { m_ui = new CubeEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); m_ui->tabWidget->setTabText(0, i18nc("@title:tab Basic Settings", "Basic")); m_ui->tabWidget->setTabText(1, i18nc("@title:tab Advanced Settings", "Advanced")); // Shortcut config. The shortcut belongs to the component "kwin"! m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); m_actionCollection->setConfigGroup(QStringLiteral("Cube")); m_actionCollection->setConfigGlobal(true); QAction* cubeAction = m_actionCollection->addAction(QStringLiteral("Cube")); cubeAction->setText(i18n("Desktop Cube")); cubeAction->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(cubeAction, QList() << Qt::CTRL + Qt::Key_F11); KGlobalAccel::self()->setShortcut(cubeAction, QList() << Qt::CTRL + Qt::Key_F11); QAction* cylinderAction = m_actionCollection->addAction(QStringLiteral("Cylinder")); cylinderAction->setText(i18n("Desktop Cylinder")); cylinderAction->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setShortcut(cylinderAction, QList()); QAction* sphereAction = m_actionCollection->addAction(QStringLiteral("Sphere")); sphereAction->setText(i18n("Desktop Sphere")); sphereAction->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setShortcut(sphereAction, QList()); m_ui->editor->addCollection(m_actionCollection); capsSelectionChanged(); - connect(m_ui->kcfg_Caps, SIGNAL(stateChanged(int)), this, SLOT(capsSelectionChanged())); + connect(m_ui->kcfg_Caps, &QCheckBox::stateChanged, this, &CubeEffectConfig::capsSelectionChanged); m_ui->kcfg_Wallpaper->setFilter(QStringLiteral("*.png *.jpeg *.jpg ")); CubeConfig::instance(KWIN_CONFIG); addConfig(CubeConfig::self(), m_ui); load(); } void CubeEffectConfig::save() { KCModule::save(); m_ui->editor->save(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("cube")); } void CubeEffectConfig::capsSelectionChanged() { if (m_ui->kcfg_Caps->checkState() == Qt::Checked) { // activate cap color m_ui->kcfg_CapColor->setEnabled(true); m_ui->capColorLabel->setEnabled(true); m_ui->kcfg_TexturedCaps->setEnabled(true); } else { // deactivate cap color m_ui->kcfg_CapColor->setEnabled(false); m_ui->capColorLabel->setEnabled(false); m_ui->kcfg_TexturedCaps->setEnabled(false); } } } // namespace #include "cube_config.moc" diff --git a/effects/desktopgrid/desktopgrid.cpp b/effects/desktopgrid/desktopgrid.cpp index d920bea93..e7eb3b997 100644 --- a/effects/desktopgrid/desktopgrid.cpp +++ b/effects/desktopgrid/desktopgrid.cpp @@ -1,1514 +1,1514 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak Copyright (C) 2008 Lucas Murray Copyright (C) 2009 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "desktopgrid.h" // KConfigSkeleton #include "desktopgridconfig.h" #include "../presentwindows/presentwindows_proxy.h" #include "../effect_builtins.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { // WARNING, TODO: This effect relies on the desktop layout being EWMH-compliant. DesktopGridEffect::DesktopGridEffect() : activated(false) , timeline() , keyboardGrab(false) , wasWindowMove(false) , wasWindowCopy(false) , wasDesktopMove(false) , isValidMove(false) , windowMove(NULL) , windowMoveDiff() , gridSize() , orientation(Qt::Horizontal) , activeCell(1, 1) , scale() , unscaledBorder() , scaledSize() , scaledOffset() , m_proxy(0) , m_activateAction(new QAction(this)) { initConfig(); // Load shortcuts QAction* a = m_activateAction; a->setObjectName(QStringLiteral("ShowDesktopGrid")); a->setText(i18n("Show Desktop Grid")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::CTRL + Qt::Key_F8); KGlobalAccel::self()->setShortcut(a, QList() << Qt::CTRL + Qt::Key_F8); shortcut = KGlobalAccel::self()->shortcut(a); effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F8, a); effects->registerTouchpadSwipeShortcut(SwipeDirection::Up, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(toggle())); + connect(a, &QAction::triggered, this, &DesktopGridEffect::toggle); connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &DesktopGridEffect::globalShortcutChanged); - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(numberDesktopsChanged(uint)), this, SLOT(slotNumberDesktopsChanged(uint))); - connect(effects, SIGNAL(windowGeometryShapeChanged(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowGeometryShapeChanged(KWin::EffectWindow*,QRect))); + connect(effects, &EffectsHandler::windowAdded, this, &DesktopGridEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowClosed, this, &DesktopGridEffect::slotWindowClosed); + connect(effects, &EffectsHandler::windowDeleted, this, &DesktopGridEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::numberDesktopsChanged, this, &DesktopGridEffect::slotNumberDesktopsChanged); + connect(effects, &EffectsHandler::windowGeometryShapeChanged, this, &DesktopGridEffect::slotWindowGeometryShapeChanged); connect(effects, &EffectsHandler::numberScreensChanged, this, &DesktopGridEffect::setup); // Load all other configuration details reconfigure(ReconfigureAll); } DesktopGridEffect::~DesktopGridEffect() { foreach (DesktopButtonsView *view, m_desktopButtonsViews) view->deleteLater(); m_desktopButtonsViews.clear(); } void DesktopGridEffect::reconfigure(ReconfigureFlags) { DesktopGridConfig::self()->read(); foreach (ElectricBorder border, borderActivate) { effects->unreserveElectricBorder(border, this); } borderActivate.clear(); foreach (int i, DesktopGridConfig::borderActivate()) { borderActivate.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } // TODO: rename zoomDuration to duration zoomDuration = animationTime(DesktopGridConfig::zoomDuration() != 0 ? DesktopGridConfig::zoomDuration() : 300); timeline.setCurveShape(QTimeLine::EaseInOutCurve); timeline.setDuration(zoomDuration); border = DesktopGridConfig::borderWidth(); desktopNameAlignment = Qt::Alignment(DesktopGridConfig::desktopNameAlignment()); layoutMode = DesktopGridConfig::layoutMode(); customLayoutRows = DesktopGridConfig::customLayoutRows(); m_usePresentWindows = DesktopGridConfig::presentWindows(); // deactivate and activate all touch border const QVector relevantBorders{ElectricLeft, ElectricTop, ElectricRight, ElectricBottom}; for (auto e : relevantBorders) { effects->unregisterTouchBorder(e, m_activateAction); } const auto touchBorders = DesktopGridConfig::touchBorderActivate(); for (int i : touchBorders) { if (!relevantBorders.contains(ElectricBorder(i))) { continue; } effects->registerTouchBorder(ElectricBorder(i), m_activateAction); } } //----------------------------------------------------------------------------- // Screen painting void DesktopGridEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (timeline.currentValue() != 0 || activated || (isUsingPresentWindows() && isMotionManagerMovingWindows())) { if (activated) timeline.setCurrentTime(timeline.currentTime() + time); else timeline.setCurrentTime(timeline.currentTime() - time); for (int i = 0; i < effects->numberOfDesktops(); i++) { if (i == highlightedDesktop - 1) hoverTimeline[i]->setCurrentTime(hoverTimeline[i]->currentTime() + time); else hoverTimeline[i]->setCurrentTime(hoverTimeline[i]->currentTime() - time); } if (isUsingPresentWindows()) { QList::iterator i; for (i = m_managers.begin(); i != m_managers.end(); ++i) (*i).calculate(time); } // PAINT_SCREEN_BACKGROUND_FIRST is needed because screen will be actually painted more than once, // so with normal screen painting second screen paint would erase parts of the first paint if (timeline.currentValue() != 0 || (isUsingPresentWindows() && isMotionManagerMovingWindows())) data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST; if (!activated && timeline.currentValue() == 0 && !(isUsingPresentWindows() && isMotionManagerMovingWindows())) finish(); } for (auto const &w : effects->stackingOrder()) { w->setData(WindowForceBlurRole, QVariant(true)); } effects->prePaintScreen(data, time); } void DesktopGridEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (timeline.currentValue() == 0 && !isUsingPresentWindows()) { effects->paintScreen(mask, region, data); return; } for (int desktop = 1; desktop <= effects->numberOfDesktops(); desktop++) { ScreenPaintData d = data; paintingDesktop = desktop; effects->paintScreen(mask, region, d); } // paint the add desktop button foreach (DesktopButtonsView *view, m_desktopButtonsViews) { if (!view->effectWindow) { EffectWindow *viewWindow = effects->findWindow(view->winId()); if (viewWindow) { viewWindow->setData(WindowForceBlurRole, QVariant(true)); view->effectWindow = viewWindow; } } if (view->effectWindow) { WindowPaintData d(view->effectWindow); d.multiplyOpacity(timeline.currentValue()); effects->drawWindow(view->effectWindow, PAINT_WINDOW_TRANSLUCENT, infiniteRegion(), d); } } if (isUsingPresentWindows() && windowMove && wasWindowMove) { // the moving window has to be painted on top of all desktops QPoint diff = cursorPos() - m_windowMoveStartPoint; QRect geo = m_windowMoveGeometry.translated(diff); WindowPaintData d(windowMove, data.projectionMatrix()); d *= QVector2D((qreal)geo.width() / (qreal)windowMove->width(), (qreal)geo.height() / (qreal)windowMove->height()); d += QPoint(geo.left() - windowMove->x(), geo.top() - windowMove->y()); effects->drawWindow(windowMove, PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_LANCZOS, infiniteRegion(), d); } if (desktopNameAlignment) { for (int screen = 0; screen < effects->numScreens(); screen++) { QRect screenGeom = effects->clientArea(ScreenArea, screen, 0); int desktop = 1; foreach (EffectFrame * frame, desktopNames) { QPointF posTL(scalePos(screenGeom.topLeft(), desktop, screen)); QPointF posBR(scalePos(screenGeom.bottomRight(), desktop, screen)); QRect textArea(posTL.x(), posTL.y(), posBR.x() - posTL.x(), posBR.y() - posTL.y()); textArea.adjust(textArea.width() / 10, textArea.height() / 10, -textArea.width() / 10, -textArea.height() / 10); int x, y; if (desktopNameAlignment & Qt::AlignLeft) x = textArea.x(); else if (desktopNameAlignment & Qt::AlignRight) x = textArea.right(); else x = textArea.center().x(); if (desktopNameAlignment & Qt::AlignTop) y = textArea.y(); else if (desktopNameAlignment & Qt::AlignBottom) y = textArea.bottom(); else y = textArea.center().y(); frame->setPosition(QPoint(x, y)); frame->render(region, timeline.currentValue(), 0.7); ++desktop; } } } } void DesktopGridEffect::postPaintScreen() { if (activated ? timeline.currentValue() != 1 : timeline.currentValue() != 0) effects->addRepaintFull(); // Repaint during zoom if (isUsingPresentWindows() && isMotionManagerMovingWindows()) effects->addRepaintFull(); if (activated) { for (int i = 0; i < effects->numberOfDesktops(); i++) { if (hoverTimeline[i]->currentValue() != 0.0 && hoverTimeline[i]->currentValue() != 1.0) { // Repaint during soft highlighting effects->addRepaintFull(); break; } } } for (auto &w : effects->stackingOrder()) { w->setData(WindowForceBlurRole, QVariant()); } effects->postPaintScreen(); } //----------------------------------------------------------------------------- // Window painting void DesktopGridEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (timeline.currentValue() != 0 || (isUsingPresentWindows() && isMotionManagerMovingWindows())) { if (w->isOnDesktop(paintingDesktop)) { w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); if (w->isMinimized() && isUsingPresentWindows()) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE); data.mask |= PAINT_WINDOW_TRANSFORMED; // Split windows at screen edges for (int screen = 0; screen < effects->numScreens(); screen++) { QRect screenGeom = effects->clientArea(ScreenArea, screen, 0); if (w->x() < screenGeom.x()) data.quads = data.quads.splitAtX(screenGeom.x() - w->x()); if (w->x() + w->width() > screenGeom.x() + screenGeom.width()) data.quads = data.quads.splitAtX(screenGeom.x() + screenGeom.width() - w->x()); if (w->y() < screenGeom.y()) data.quads = data.quads.splitAtY(screenGeom.y() - w->y()); if (w->y() + w->height() > screenGeom.y() + screenGeom.height()) data.quads = data.quads.splitAtY(screenGeom.y() + screenGeom.height() - w->y()); } if (windowMove && wasWindowMove && windowMove->findModal() == w) w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } else w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } effects->prePaintWindow(w, data, time); } void DesktopGridEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (timeline.currentValue() != 0 || (isUsingPresentWindows() && isMotionManagerMovingWindows())) { if (isUsingPresentWindows() && w == windowMove && wasWindowMove && ((!wasWindowCopy && sourceDesktop == paintingDesktop) || (sourceDesktop != highlightedDesktop && highlightedDesktop == paintingDesktop))) { return; // will be painted on top of all other windows } foreach (DesktopButtonsView *view, m_desktopButtonsViews) { if (view->effectWindow == w) { if (!activated && timeline.currentValue() < 0.05) { view->hide(); } return; // will be painted on top of all other windows } } qreal xScale = data.xScale(); qreal yScale = data.yScale(); data.multiplyBrightness(1.0 - (0.3 * (1.0 - hoverTimeline[paintingDesktop - 1]->currentValue()))); for (int screen = 0; screen < effects->numScreens(); screen++) { QRect screenGeom = effects->clientArea(ScreenArea, screen, 0); QRectF transformedGeo = w->geometry(); // Display all quads on the same screen on the same pass WindowQuadList screenQuads; bool quadsAdded = false; if (isUsingPresentWindows()) { WindowMotionManager& manager = m_managers[(paintingDesktop-1)*(effects->numScreens())+screen ]; if (manager.isManaging(w)) { foreach (const WindowQuad & quad, data.quads) screenQuads.append(quad); transformedGeo = manager.transformedGeometry(w); quadsAdded = true; if (!manager.areWindowsMoving() && timeline.currentValue() == 1.0) mask |= PAINT_WINDOW_LANCZOS; } else if (w->screen() != screen) quadsAdded = true; // we don't want parts of overlapping windows on the other screen if (w->isDesktop()) quadsAdded = false; } if (!quadsAdded) { foreach (const WindowQuad & quad, data.quads) { QRect quadRect( w->x() + quad.left(), w->y() + quad.top(), quad.right() - quad.left(), quad.bottom() - quad.top() ); if (quadRect.intersects(screenGeom)) screenQuads.append(quad); } } if (screenQuads.isEmpty()) continue; // Nothing is being displayed, don't bother WindowPaintData d = data; d.quads = screenQuads; QPointF newPos = scalePos(transformedGeo.topLeft().toPoint(), paintingDesktop, screen); double progress = timeline.currentValue(); d.setXScale(interpolate(1, xScale * scale[screen] * (float)transformedGeo.width() / (float)w->geometry().width(), progress)); d.setYScale(interpolate(1, yScale * scale[screen] * (float)transformedGeo.height() / (float)w->geometry().height(), progress)); d += QPoint(qRound(newPos.x() - w->x()), qRound(newPos.y() - w->y())); if (isUsingPresentWindows() && (w->isDock() || w->isSkipSwitcher())) { // fade out panels if present windows is used d.multiplyOpacity((1.0 - timeline.currentValue())); } if (isUsingPresentWindows() && w->isMinimized()) { d.multiplyOpacity(timeline.currentValue()); } if (effects->compositingType() == XRenderCompositing) { // More exact clipping as XRender displays the entire window instead of just the quad QPointF screenPosF = scalePos(screenGeom.topLeft(), paintingDesktop).toPoint(); QPoint screenPos( qRound(screenPosF.x()), qRound(screenPosF.y()) ); QSize screenSize( qRound(interpolate(screenGeom.width(), scaledSize[screen].width(), progress)), qRound(interpolate(screenGeom.height(), scaledSize[screen].height(), progress)) ); PaintClipper pc(effects->clientArea(ScreenArea, screen, 0) & QRect(screenPos, screenSize)); effects->paintWindow(w, mask, region, d); } else { if (w->isDesktop() && timeline.currentValue() == 1.0) { // desktop windows are not in a motion manager and can always be rendered with // lanczos sampling except for animations mask |= PAINT_WINDOW_LANCZOS; } effects->paintWindow(w, mask, effects->clientArea(ScreenArea, screen, 0), d); } } } else effects->paintWindow(w, mask, region, data); } //----------------------------------------------------------------------------- // User interaction void DesktopGridEffect::slotWindowAdded(EffectWindow* w) { if (!activated) return; if (isUsingPresentWindows()) { if (!isRelevantWithPresentWindows(w)) return; // don't add foreach (const int i, desktopList(w)) { WindowMotionManager& manager = m_managers[ i*effects->numScreens()+w->screen()]; manager.manage(w); m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager); } } effects->addRepaintFull(); } void DesktopGridEffect::slotWindowClosed(EffectWindow* w) { if (!activated && timeline.currentValue() == 0) return; if (w == windowMove) { effects->setElevatedWindow(windowMove, false); windowMove = NULL; } if (isUsingPresentWindows()) { foreach (const int i, desktopList(w)) { WindowMotionManager& manager = m_managers[i*effects->numScreens()+w->screen()]; manager.unmanage(w); m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager); } } effects->addRepaintFull(); } void DesktopGridEffect::slotWindowDeleted(EffectWindow* w) { if (w == windowMove) windowMove = 0; foreach (DesktopButtonsView *view, m_desktopButtonsViews) { if (view->effectWindow && view->effectWindow == w) { view->effectWindow = nullptr; break; } } if (isUsingPresentWindows()) { for (QList::iterator it = m_managers.begin(), end = m_managers.end(); it != end; ++it) { it->unmanage(w); } } } void DesktopGridEffect::slotWindowGeometryShapeChanged(EffectWindow* w, const QRect& old) { Q_UNUSED(old) if (!activated) return; if (w == windowMove && wasWindowMove) return; if (isUsingPresentWindows()) { foreach (const int i, desktopList(w)) { WindowMotionManager& manager = m_managers[i*effects->numScreens()+w->screen()]; m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager); } } } void DesktopGridEffect::windowInputMouseEvent(QEvent* e) { if ((e->type() != QEvent::MouseMove && e->type() != QEvent::MouseButtonPress && e->type() != QEvent::MouseButtonRelease) || timeline.currentValue() != 1) // Block user input during animations return; QMouseEvent* me = static_cast< QMouseEvent* >(e); if (!(wasWindowMove || wasDesktopMove)) { foreach (DesktopButtonsView *view, m_desktopButtonsViews) { if (view->geometry().contains(me->pos())) { const QPoint widgetPos = view->mapFromGlobal(me->pos()); QMouseEvent event(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers()); view->windowInputMouseEvent(&event); return; } } } if (e->type() == QEvent::MouseMove) { int d = posToDesktop(me->pos()); if (windowMove != NULL && (me->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance()) { // Handle window moving if (!wasWindowMove) { // Activate on move if (isUsingPresentWindows()) { foreach (const int i, desktopList(windowMove)) { WindowMotionManager& manager = m_managers[(i)*(effects->numScreens()) + windowMove->screen()]; if ((i + 1) == sourceDesktop) { const QRectF transformedGeo = manager.transformedGeometry(windowMove); const QPointF pos = scalePos(transformedGeo.topLeft().toPoint(), sourceDesktop, windowMove->screen()); const QSize size(scale[windowMove->screen()] *(float)transformedGeo.width(), scale[windowMove->screen()] *(float)transformedGeo.height()); m_windowMoveGeometry = QRect(pos.toPoint(), size); m_windowMoveStartPoint = me->pos(); } manager.unmanage(windowMove); if (EffectWindow* modal = windowMove->findModal()) { if (manager.isManaging(modal)) manager.unmanage(modal); } m_proxy->calculateWindowTransformations(manager.managedWindows(), windowMove->screen(), manager); } wasWindowMove = true; } } if (windowMove->isMovable() && !isUsingPresentWindows()) { wasWindowMove = true; int screen = effects->screenNumber(me->pos()); effects->moveWindow(windowMove, unscalePos(me->pos(), NULL) + windowMoveDiff, true, 1.0 / scale[screen]); } if (wasWindowMove) { if (!effects->waylandDisplay() || (me->modifiers() & Qt::ControlModifier)) { wasWindowCopy = true; effects->defineCursor(Qt::DragCopyCursor); } else { wasWindowCopy = false; effects->defineCursor(Qt::ClosedHandCursor); } if (d != highlightedDesktop) { auto desktops = windowMove->desktops(); if (!desktops.contains(d)) { desktops.append(d); } if (highlightedDesktop != sourceDesktop || !wasWindowCopy) { desktops.removeOne(highlightedDesktop); } effects->windowToDesktops(windowMove, desktops); const int screen = effects->screenNumber(me->pos()); if (screen != windowMove->screen()) effects->windowToScreen(windowMove, screen); } effects->addRepaintFull(); } } else if ((me->buttons() & Qt::LeftButton) && !wasDesktopMove && (me->pos() - dragStartPos).manhattanLength() > QApplication::startDragDistance()) { wasDesktopMove = true; effects->defineCursor(Qt::ClosedHandCursor); } if (d != highlightedDesktop) { // Highlight desktop if ((me->buttons() & Qt::LeftButton) && isValidMove && !wasWindowMove && d <= effects->numberOfDesktops()) { EffectWindowList windows = effects->stackingOrder(); EffectWindowList stack[3]; for (EffectWindowList::const_iterator it = windows.constBegin(), end = windows.constEnd(); it != end; ++it) { EffectWindow *w = const_cast(*it); // we're not really touching it here but below if (w->isOnAllDesktops()) continue; if (w->isOnDesktop(highlightedDesktop)) stack[0] << w; if (w->isOnDesktop(d)) stack[1] << w; if (w->isOnDesktop(m_originalMovingDesktop)) stack[2] << w; } const int desks[4] = {highlightedDesktop, d, m_originalMovingDesktop, highlightedDesktop}; for (int i = 0; i < 3; ++i ) { if (desks[i] == desks[i+1]) continue; foreach (EffectWindow *w, stack[i]) { auto desktops = w->desktops(); desktops.removeOne(desks[i]); desktops.append(desks[i+1]); effects->windowToDesktops(w, desktops); if (isUsingPresentWindows()) { m_managers[(desks[i]-1)*(effects->numScreens()) + w->screen()].unmanage(w); m_managers[(desks[i+1]-1)*(effects->numScreens()) + w->screen()].manage(w); } } } if (isUsingPresentWindows()) { for (int i = 0; i < effects->numScreens(); i++) { for (int j = 0; j < 3; ++j) { WindowMotionManager& manager = m_managers[(desks[j]-1)*(effects->numScreens()) + i ]; m_proxy->calculateWindowTransformations(manager.managedWindows(), i, manager); } } effects->addRepaintFull(); } } setHighlightedDesktop(d); } } if (e->type() == QEvent::MouseButtonPress) { if (me->buttons() == Qt::LeftButton) { isValidMove = true; dragStartPos = me->pos(); sourceDesktop = posToDesktop(me->pos()); bool isDesktop = (me->modifiers() & Qt::ShiftModifier); EffectWindow* w = isDesktop ? NULL : windowAt(me->pos()); if (w != NULL) isDesktop = w->isDesktop(); if (isDesktop) m_originalMovingDesktop = posToDesktop(me->pos()); else m_originalMovingDesktop = 0; if (w != NULL && !w->isDesktop() && (w->isMovable() || w->isMovableAcrossScreens() || isUsingPresentWindows())) { // Prepare it for moving windowMoveDiff = w->pos() - unscalePos(me->pos(), NULL); windowMove = w; effects->setElevatedWindow(windowMove, true); } } else if ((me->buttons() == Qt::MidButton || me->buttons() == Qt::RightButton) && windowMove == NULL) { EffectWindow* w = windowAt(me->pos()); if (w && w->isDesktop()) { w = nullptr; } if (w != NULL) { const int desktop = posToDesktop(me->pos()); if (w->isOnAllDesktops()) { effects->windowToDesktop(w, desktop); } else { effects->windowToDesktop(w, NET::OnAllDesktops); } const bool isOnAllDesktops = w->isOnAllDesktops(); if (isUsingPresentWindows()) { for (int i = 0; i < effects->numberOfDesktops(); i++) { if (i != desktop - 1) { WindowMotionManager& manager = m_managers[ i*effects->numScreens() + w->screen()]; if (isOnAllDesktops) manager.manage(w); else manager.unmanage(w); m_proxy->calculateWindowTransformations(manager.managedWindows(), w->screen(), manager); } } } effects->addRepaintFull(); } } } if (e->type() == QEvent::MouseButtonRelease && me->button() == Qt::LeftButton) { isValidMove = false; if (windowMove) effects->activateWindow(windowMove); if (wasWindowMove || wasDesktopMove) { // reset pointer effects->defineCursor(Qt::PointingHandCursor); } else { // click -> exit const int desk = posToDesktop(me->pos()); if (desk > effects->numberOfDesktops()) return; // don't quit when missing desktop setCurrentDesktop(desk); setActive(false); } if (windowMove) { if (wasWindowMove && isUsingPresentWindows()) { const int targetDesktop = posToDesktop(cursorPos()); foreach (const int i, desktopList(windowMove)) { WindowMotionManager& manager = m_managers[(i)*(effects->numScreens()) + windowMove->screen()]; manager.manage(windowMove); if (EffectWindow* modal = windowMove->findModal()) manager.manage(modal); if (i + 1 == targetDesktop) { // for the desktop the window is dropped on, we use the current geometry manager.setTransformedGeometry(windowMove, moveGeometryToDesktop(targetDesktop)); } m_proxy->calculateWindowTransformations(manager.managedWindows(), windowMove->screen(), manager); } effects->addRepaintFull(); } effects->setElevatedWindow(windowMove, false); windowMove = NULL; } wasWindowMove = false; wasWindowCopy = false; wasDesktopMove = false; } } void DesktopGridEffect::grabbedKeyboardEvent(QKeyEvent* e) { if (timeline.currentValue() != 1) // Block user input during animations return; if (windowMove != NULL) return; if (e->type() == QEvent::KeyPress) { // check for global shortcuts // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155) if (shortcut.contains(e->key() + e->modifiers())) { toggle(); return; } int desktop = -1; // switch by F or just if (e->key() >= Qt::Key_F1 && e->key() <= Qt::Key_F35) desktop = e->key() - Qt::Key_F1 + 1; else if (e->key() >= Qt::Key_0 && e->key() <= Qt::Key_9) desktop = e->key() == Qt::Key_0 ? 10 : e->key() - Qt::Key_0; if (desktop != -1) { if (desktop <= effects->numberOfDesktops()) { setHighlightedDesktop(desktop); setCurrentDesktop(desktop); setActive(false); } return; } switch(e->key()) { // Wrap only on autorepeat case Qt::Key_Left: setHighlightedDesktop(desktopToLeft(highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Right: setHighlightedDesktop(desktopToRight(highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Up: setHighlightedDesktop(desktopUp(highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Down: setHighlightedDesktop(desktopDown(highlightedDesktop, !e->isAutoRepeat())); break; case Qt::Key_Escape: setActive(false); return; case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Space: setCurrentDesktop(highlightedDesktop); setActive(false); return; case Qt::Key_Plus: slotAddDesktop(); break; case Qt::Key_Minus: slotRemoveDesktop(); break; default: break; } } } bool DesktopGridEffect::borderActivated(ElectricBorder border) { if (!borderActivate.contains(border)) return false; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return true; toggle(); return true; } //----------------------------------------------------------------------------- // Helper functions // Transform a point to its position on the scaled grid QPointF DesktopGridEffect::scalePos(const QPoint& pos, int desktop, int screen) const { if (screen == -1) screen = effects->screenNumber(pos); QRect screenGeom = effects->clientArea(ScreenArea, screen, 0); QPoint desktopCell; if (orientation == Qt::Horizontal) { desktopCell.setX((desktop - 1) % gridSize.width() + 1); desktopCell.setY((desktop - 1) / gridSize.width() + 1); } else { desktopCell.setX((desktop - 1) / gridSize.height() + 1); desktopCell.setY((desktop - 1) % gridSize.height() + 1); } double progress = timeline.currentValue(); QPointF point( interpolate( ( (screenGeom.width() + unscaledBorder[screen]) *(desktopCell.x() - 1) - (screenGeom.width() + unscaledBorder[screen]) *(activeCell.x() - 1) ) + pos.x(), ( (scaledSize[screen].width() + border) *(desktopCell.x() - 1) + scaledOffset[screen].x() + (pos.x() - screenGeom.x()) * scale[screen] ), progress), interpolate( ( (screenGeom.height() + unscaledBorder[screen]) *(desktopCell.y() - 1) - (screenGeom.height() + unscaledBorder[screen]) *(activeCell.y() - 1) ) + pos.y(), ( (scaledSize[screen].height() + border) *(desktopCell.y() - 1) + scaledOffset[screen].y() + (pos.y() - screenGeom.y()) * scale[screen] ), progress) ); return point; } // Detransform a point to its position on the full grid // TODO: Doesn't correctly interpolate (Final position is correct though), don't forget to copy to posToDesktop() QPoint DesktopGridEffect::unscalePos(const QPoint& pos, int* desktop) const { int screen = effects->screenNumber(pos); QRect screenGeom = effects->clientArea(ScreenArea, screen, 0); //double progress = timeline.currentValue(); double scaledX = /*interpolate( ( pos.x() - screenGeom.x() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.width() + unscaledBorder[screen] ) + activeCell.x() - 1,*/ (pos.x() - scaledOffset[screen].x() + double(border) / 2.0) / (scaledSize[screen].width() + border)/*, progress )*/; double scaledY = /*interpolate( ( pos.y() - screenGeom.y() + unscaledBorder[screen] / 2.0 ) / ( screenGeom.height() + unscaledBorder[screen] ) + activeCell.y() - 1,*/ (pos.y() - scaledOffset[screen].y() + double(border) / 2.0) / (scaledSize[screen].height() + border)/*, progress )*/; int gx = qBound(0, int(scaledX), gridSize.width() - 1); // Zero-based int gy = qBound(0, int(scaledY), gridSize.height() - 1); scaledX -= gx; scaledY -= gy; if (desktop != NULL) { if (orientation == Qt::Horizontal) *desktop = gy * gridSize.width() + gx + 1; else *desktop = gx * gridSize.height() + gy + 1; } return QPoint( qBound( screenGeom.x(), qRound( scaledX * (screenGeom.width() + unscaledBorder[screen]) - unscaledBorder[screen] / 2.0 + screenGeom.x() ), screenGeom.right() ), qBound( screenGeom.y(), qRound( scaledY * (screenGeom.height() + unscaledBorder[screen]) - unscaledBorder[screen] / 2.0 + screenGeom.y() ), screenGeom.bottom() ) ); } int DesktopGridEffect::posToDesktop(const QPoint& pos) const { // Copied from unscalePos() int screen = effects->screenNumber(pos); double scaledX = (pos.x() - scaledOffset[screen].x() + double(border) / 2.0) / (scaledSize[screen].width() + border); double scaledY = (pos.y() - scaledOffset[screen].y() + double(border) / 2.0) / (scaledSize[screen].height() + border); int gx = qBound(0, int(scaledX), gridSize.width() - 1); // Zero-based int gy = qBound(0, int(scaledY), gridSize.height() - 1); if (orientation == Qt::Horizontal) return gy * gridSize.width() + gx + 1; return gx * gridSize.height() + gy + 1; } EffectWindow* DesktopGridEffect::windowAt(QPoint pos) const { // Get stacking order top first EffectWindowList windows = effects->stackingOrder(); EffectWindowList::Iterator begin = windows.begin(); EffectWindowList::Iterator end = windows.end(); --end; while (begin < end) qSwap(*begin++, *end--); int desktop; pos = unscalePos(pos, &desktop); if (desktop > effects->numberOfDesktops()) return NULL; if (isUsingPresentWindows()) { const int screen = effects->screenNumber(pos); EffectWindow *w = m_managers.at((desktop - 1) * (effects->numScreens()) + screen).windowAtPoint(pos, false); if (w) return w; foreach (EffectWindow * w, windows) { if (w->isOnDesktop(desktop) && w->isDesktop() && w->geometry().contains(pos)) return w; } } else { foreach (EffectWindow * w, windows) { if (w->isOnDesktop(desktop) && w->isOnCurrentActivity() && !w->isMinimized() && w->geometry().contains(pos)) return w; } } return NULL; } void DesktopGridEffect::setCurrentDesktop(int desktop) { if (orientation == Qt::Horizontal) { activeCell.setX((desktop - 1) % gridSize.width() + 1); activeCell.setY((desktop - 1) / gridSize.width() + 1); } else { activeCell.setX((desktop - 1) / gridSize.height() + 1); activeCell.setY((desktop - 1) % gridSize.height() + 1); } if (effects->currentDesktop() != desktop) effects->setCurrentDesktop(desktop); } void DesktopGridEffect::setHighlightedDesktop(int d) { if (d == highlightedDesktop || d <= 0 || d > effects->numberOfDesktops()) return; if (highlightedDesktop > 0 && highlightedDesktop <= hoverTimeline.count()) hoverTimeline[highlightedDesktop-1]->setCurrentTime(qMin(hoverTimeline[highlightedDesktop-1]->currentTime(), hoverTimeline[highlightedDesktop-1]->duration())); highlightedDesktop = d; if (highlightedDesktop <= hoverTimeline.count()) hoverTimeline[highlightedDesktop-1]->setCurrentTime(qMax(hoverTimeline[highlightedDesktop-1]->currentTime(), 0)); effects->addRepaintFull(); } int DesktopGridEffect::desktopToRight(int desktop, bool wrap) const { // Copied from Workspace::desktopToRight() int dt = desktop - 1; if (orientation == Qt::Vertical) { dt += gridSize.height(); if (dt >= effects->numberOfDesktops()) { if (wrap) dt -= effects->numberOfDesktops(); else return desktop; } } else { int d = (dt % gridSize.width()) + 1; if (d >= gridSize.width()) { if (wrap) d -= gridSize.width(); else return desktop; } dt = dt - (dt % gridSize.width()) + d; } return dt + 1; } int DesktopGridEffect::desktopToLeft(int desktop, bool wrap) const { // Copied from Workspace::desktopToLeft() int dt = desktop - 1; if (orientation == Qt::Vertical) { dt -= gridSize.height(); if (dt < 0) { if (wrap) dt += effects->numberOfDesktops(); else return desktop; } } else { int d = (dt % gridSize.width()) - 1; if (d < 0) { if (wrap) d += gridSize.width(); else return desktop; } dt = dt - (dt % gridSize.width()) + d; } return dt + 1; } int DesktopGridEffect::desktopUp(int desktop, bool wrap) const { // Copied from Workspace::desktopUp() int dt = desktop - 1; if (orientation == Qt::Horizontal) { dt -= gridSize.width(); if (dt < 0) { if (wrap) dt += effects->numberOfDesktops(); else return desktop; } } else { int d = (dt % gridSize.height()) - 1; if (d < 0) { if (wrap) d += gridSize.height(); else return desktop; } dt = dt - (dt % gridSize.height()) + d; } return dt + 1; } int DesktopGridEffect::desktopDown(int desktop, bool wrap) const { // Copied from Workspace::desktopDown() int dt = desktop - 1; if (orientation == Qt::Horizontal) { dt += gridSize.width(); if (dt >= effects->numberOfDesktops()) { if (wrap) dt -= effects->numberOfDesktops(); else return desktop; } } else { int d = (dt % gridSize.height()) + 1; if (d >= gridSize.height()) { if (wrap) d -= gridSize.height(); else return desktop; } dt = dt - (dt % gridSize.height()) + d; } return dt + 1; } //----------------------------------------------------------------------------- // Activation void DesktopGridEffect::toggle() { setActive(!activated); } void DesktopGridEffect::setActive(bool active) { if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; // Only one fullscreen effect at a time thanks if (active && isMotionManagerMovingWindows()) return; // Still moving windows from last usage - don't activate if (activated == active) return; // Already in that state activated = active; if (activated) { effects->setShowingDesktop(false); if (timeline.currentValue() == 0) setup(); } else { if (isUsingPresentWindows()) { QList::iterator it; for (it = m_managers.begin(); it != m_managers.end(); ++it) { foreach (EffectWindow * w, (*it).managedWindows()) { (*it).moveWindow(w, w->geometry()); } } } QTimer::singleShot(zoomDuration + 1, this, [this] { if (activated) return; foreach (DesktopButtonsView *view, m_desktopButtonsViews) { view->hide(); } } ); setHighlightedDesktop(effects->currentDesktop()); // Ensure selected desktop is highlighted } effects->addRepaintFull(); } void DesktopGridEffect::setup() { if (!isActive()) return; if (!keyboardGrab) { keyboardGrab = effects->grabKeyboard(this); effects->startMouseInterception(this, Qt::PointingHandCursor); effects->setActiveFullScreenEffect(this); } setHighlightedDesktop(effects->currentDesktop()); // Soft highlighting qDeleteAll(hoverTimeline); hoverTimeline.clear(); for (int i = 0; i < effects->numberOfDesktops(); i++) { QTimeLine *newTimeline = new QTimeLine(zoomDuration, this); newTimeline->setCurveShape(QTimeLine::EaseInOutCurve); hoverTimeline.append(newTimeline); } hoverTimeline[effects->currentDesktop() - 1]->setCurrentTime(hoverTimeline[effects->currentDesktop() - 1]->duration()); // Create desktop name textures if enabled if (desktopNameAlignment) { QFont font; font.setBold(true); font.setPointSize(12); for (int i = 0; i < effects->numberOfDesktops(); i++) { EffectFrame* frame = effects->effectFrame(EffectFrameUnstyled, false); frame->setFont(font); frame->setText(effects->desktopName(i + 1)); frame->setAlignment(desktopNameAlignment); desktopNames.append(frame); } } setupGrid(); setCurrentDesktop(effects->currentDesktop()); // setup the motion managers if (m_usePresentWindows) m_proxy = static_cast(effects->getProxy(BuiltInEffects::nameForEffect(BuiltInEffect::PresentWindows))); if (isUsingPresentWindows()) { m_proxy->reCreateGrids(); // revalidation on multiscreen, bug #351724 for (int i = 1; i <= effects->numberOfDesktops(); i++) { for (int j = 0; j < effects->numScreens(); j++) { WindowMotionManager manager; foreach (EffectWindow * w, effects->stackingOrder()) { if (w->isOnDesktop(i) && w->screen() == j &&isRelevantWithPresentWindows(w)) { manager.manage(w); } } m_proxy->calculateWindowTransformations(manager.managedWindows(), j, manager); m_managers.append(manager); } } } bool enableAdd = effects->numberOfDesktops() < 20; bool enableRemove = effects->numberOfDesktops() > 1; QVector::iterator it = m_desktopButtonsViews.begin(); const int n = DesktopGridConfig::showAddRemove() ? effects->numScreens() : 0; for (int i = 0; i < n; ++i) { DesktopButtonsView *view; if (it == m_desktopButtonsViews.end()) { view = new DesktopButtonsView(); m_desktopButtonsViews.append(view); it = m_desktopButtonsViews.end(); // changed through insert! - connect(view, SIGNAL(addDesktop()), SLOT(slotAddDesktop())); - connect(view, SIGNAL(removeDesktop()), SLOT(slotRemoveDesktop())); + connect(view, &DesktopButtonsView::addDesktop, this, &DesktopGridEffect::slotAddDesktop); + connect(view, &DesktopButtonsView::removeDesktop, this, &DesktopGridEffect::slotRemoveDesktop); } else { view = *it; ++it; } view->setAddDesktopEnabled(enableAdd); view->setRemoveDesktopEnabled(enableRemove); const QRect screenRect = effects->clientArea(FullScreenArea, i, 1); view->show(); // pseudo show must happen before geometry changes view->setPosition(screenRect.right() - border/3 - view->width(), screenRect.bottom() - border/3 - view->height()); } while (it != m_desktopButtonsViews.end()) { (*it)->deleteLater(); it = m_desktopButtonsViews.erase(it); } } void DesktopGridEffect::setupGrid() { // We need these variables for every paint so lets cache them int x, y; int numDesktops = effects->numberOfDesktops(); switch(layoutMode) { default: case LayoutPager: orientation = Qt::Horizontal; gridSize = effects->desktopGridSize(); // sanity check: pager may report incorrect size in case of one desktop if (numDesktops == 1) { gridSize = QSize(1, 1); } break; case LayoutAutomatic: y = sqrt(float(numDesktops)) + 0.5; x = float(numDesktops) / float(y) + 0.5; if (x * y < numDesktops) x++; orientation = Qt::Horizontal; gridSize.setWidth(x); gridSize.setHeight(y); break; case LayoutCustom: orientation = Qt::Horizontal; gridSize.setWidth(ceil(effects->numberOfDesktops() / double(customLayoutRows))); gridSize.setHeight(customLayoutRows); break; } scale.clear(); unscaledBorder.clear(); scaledSize.clear(); scaledOffset.clear(); for (int i = 0; i < effects->numScreens(); i++) { QRect geom = effects->clientArea(ScreenArea, i, 0); double sScale; if (gridSize.width() > gridSize.height()) sScale = (geom.width() - border * (gridSize.width() + 1)) / double(geom.width() * gridSize.width()); else sScale = (geom.height() - border * (gridSize.height() + 1)) / double(geom.height() * gridSize.height()); double sBorder = border / sScale; QSizeF size( double(geom.width()) * sScale, double(geom.height()) * sScale ); QPointF offset( geom.x() + (geom.width() - size.width() * gridSize.width() - border *(gridSize.width() - 1)) / 2.0, geom.y() + (geom.height() - size.height() * gridSize.height() - border *(gridSize.height() - 1)) / 2.0 ); scale.append(sScale); unscaledBorder.append(sBorder); scaledSize.append(size); scaledOffset.append(offset); } } void DesktopGridEffect::finish() { if (desktopNameAlignment) { qDeleteAll(desktopNames); desktopNames.clear(); } if (keyboardGrab) effects->ungrabKeyboard(); keyboardGrab = false; effects->stopMouseInterception(this); effects->setActiveFullScreenEffect(0); if (isUsingPresentWindows()) { while (!m_managers.isEmpty()) { m_managers.first().unmanageAll(); m_managers.removeFirst(); } m_proxy = 0; } } void DesktopGridEffect::globalShortcutChanged(QAction *action, const QKeySequence& seq) { if (action->objectName() != QStringLiteral("ShowDesktopGrid")) { return; } shortcut.clear(); shortcut.append(seq); } bool DesktopGridEffect::isMotionManagerMovingWindows() const { if (isUsingPresentWindows()) { QList::const_iterator it; for (it = m_managers.begin(); it != m_managers.end(); ++it) { if ((*it).areWindowsMoving()) return true; } } return false; } bool DesktopGridEffect::isUsingPresentWindows() const { return (m_proxy != NULL); } // transforms the geometry of the moved window to a geometry on the desktop // internal method only used when a window is dropped onto a desktop QRectF DesktopGridEffect::moveGeometryToDesktop(int desktop) const { QPointF point = unscalePos(m_windowMoveGeometry.topLeft() + cursorPos() - m_windowMoveStartPoint); const double scaleFactor = scale[ windowMove->screen()]; if (posToDesktop(m_windowMoveGeometry.topLeft() + cursorPos() - m_windowMoveStartPoint) != desktop) { // topLeft is not on the desktop - check other corners // if all corners are not on the desktop the window is bigger than the desktop - no matter what it will look strange if (posToDesktop(m_windowMoveGeometry.topRight() + cursorPos() - m_windowMoveStartPoint) == desktop) { point = unscalePos(m_windowMoveGeometry.topRight() + cursorPos() - m_windowMoveStartPoint) - QPointF(m_windowMoveGeometry.width(), 0) / scaleFactor; } else if (posToDesktop(m_windowMoveGeometry.bottomLeft() + cursorPos() - m_windowMoveStartPoint) == desktop) { point = unscalePos(m_windowMoveGeometry.bottomLeft() + cursorPos() - m_windowMoveStartPoint) - QPointF(0, m_windowMoveGeometry.height()) / scaleFactor; } else if (posToDesktop(m_windowMoveGeometry.bottomRight() + cursorPos() - m_windowMoveStartPoint) == desktop) { point = unscalePos(m_windowMoveGeometry.bottomRight() + cursorPos() - m_windowMoveStartPoint) - QPointF(m_windowMoveGeometry.width(), m_windowMoveGeometry.height()) / scaleFactor; } } return QRectF(point, m_windowMoveGeometry.size() / scaleFactor); } void DesktopGridEffect::slotAddDesktop() { effects->setNumberOfDesktops(effects->numberOfDesktops() + 1); } void DesktopGridEffect::slotRemoveDesktop() { effects->setNumberOfDesktops(effects->numberOfDesktops() - 1); } void DesktopGridEffect::slotNumberDesktopsChanged(uint old) { if (!activated) return; const uint desktop = effects->numberOfDesktops(); bool enableAdd = desktop < 20; bool enableRemove = desktop > 1; foreach (DesktopButtonsView *view, m_desktopButtonsViews) { view->setAddDesktopEnabled(enableAdd); view->setRemoveDesktopEnabled(enableRemove); } if (old < desktop) desktopsAdded(old); else desktopsRemoved(old); } void DesktopGridEffect::desktopsAdded(int old) { const int desktop = effects->numberOfDesktops(); for (int i = old; i <= effects->numberOfDesktops(); i++) { // add a timeline for the new desktop QTimeLine *newTimeline = new QTimeLine(zoomDuration, this); newTimeline->setCurveShape(QTimeLine::EaseInOutCurve); hoverTimeline.append(newTimeline); } // Create desktop name textures if enabled if (desktopNameAlignment) { QFont font; font.setBold(true); font.setPointSize(12); for (int i = old; i < desktop; i++) { EffectFrame* frame = effects->effectFrame(EffectFrameUnstyled, false); frame->setFont(font); frame->setText(effects->desktopName(i + 1)); frame->setAlignment(desktopNameAlignment); desktopNames.append(frame); } } if (isUsingPresentWindows()) { for (int i = old+1; i <= effects->numberOfDesktops(); ++i) { for (int j = 0; j < effects->numScreens(); ++j) { WindowMotionManager manager; foreach (EffectWindow * w, effects->stackingOrder()) { if (w->isOnDesktop(i) && w->screen() == j &&isRelevantWithPresentWindows(w)) { manager.manage(w); } } m_proxy->calculateWindowTransformations(manager.managedWindows(), j, manager); m_managers.append(manager); } } } setupGrid(); // and repaint effects->addRepaintFull(); } void DesktopGridEffect::desktopsRemoved(int old) { const int desktop = effects->numberOfDesktops(); for (int i = desktop; i < old; i++) { delete hoverTimeline.takeLast(); if (desktopNameAlignment) { delete desktopNames.last(); desktopNames.removeLast(); } if (isUsingPresentWindows()) { for (int j = 0; j < effects->numScreens(); ++j) { WindowMotionManager& manager = m_managers.last(); manager.unmanageAll(); m_managers.removeLast(); } } } // add removed windows to the last desktop if (isUsingPresentWindows()) { for (int j = 0; j < effects->numScreens(); ++j) { WindowMotionManager& manager = m_managers[(desktop-1)*(effects->numScreens())+j ]; foreach (EffectWindow * w, effects->stackingOrder()) { if (manager.isManaging(w)) continue; if (w->isOnDesktop(desktop) && w->screen() == j && isRelevantWithPresentWindows(w)) { manager.manage(w); } } m_proxy->calculateWindowTransformations(manager.managedWindows(), j, manager); } } setupGrid(); // and repaint effects->addRepaintFull(); } //TODO: kill this function? or at least keep a consistent numeration with desktops starting from 1 QVector DesktopGridEffect::desktopList(const EffectWindow *w) const { if (w->isOnAllDesktops()) { static QVector allDesktops; if (allDesktops.count() != effects->numberOfDesktops()) { allDesktops.resize(effects->numberOfDesktops()); for (int i = 0; i < effects->numberOfDesktops(); ++i) allDesktops[i] = i; } return allDesktops; } QVector desks; desks.resize(w->desktops().count()); int i = 0; for (const int desk : w->desktops()) { desks[i++] = desk-1; } return desks; } bool DesktopGridEffect::isActive() const { return (timeline.currentValue() != 0 || activated || (isUsingPresentWindows() && isMotionManagerMovingWindows())) && !effects->isScreenLocked(); } bool DesktopGridEffect::isRelevantWithPresentWindows(EffectWindow *w) const { if (w->isSpecialWindow() || w->isUtility()) { return false; } if (w->isSkipSwitcher()) { return false; } if (w->isDeleted()) { return false; } if (!w->acceptsFocus()) { return false; } if (!w->isCurrentTab()) { return false; } if (!w->isOnCurrentActivity()) { return false; } return true; } /************************************************ * DesktopButtonView ************************************************/ DesktopButtonsView::DesktopButtonsView(QWindow *parent) : QQuickView(parent) , effectWindow(nullptr) , m_visible(false) , m_posIsValid(false) { setFlags(Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint); setColor(Qt::transparent); rootContext()->setContextProperty(QStringLiteral("add"), QVariant(true)); rootContext()->setContextProperty(QStringLiteral("remove"), QVariant(true)); setSource(QUrl(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/desktopgrid/main.qml")))); if (QObject *item = rootObject()->findChild(QStringLiteral("addButton"))) { connect(item, SIGNAL(clicked()), SIGNAL(addDesktop())); } if (QObject *item = rootObject()->findChild(QStringLiteral("removeButton"))) { connect(item, SIGNAL(clicked()), SIGNAL(removeDesktop())); } } void DesktopButtonsView::windowInputMouseEvent(QMouseEvent *e) { if (e->type() == QEvent::MouseMove) { mouseMoveEvent(e); } else if (e->type() == QEvent::MouseButtonPress) { mousePressEvent(e); } else if (e->type() == QEvent::MouseButtonDblClick) { mouseDoubleClickEvent(e); } else if (e->type() == QEvent::MouseButtonRelease) { mouseReleaseEvent(e); } } void DesktopButtonsView::setAddDesktopEnabled(bool enable) { rootContext()->setContextProperty(QStringLiteral("add"), QVariant(enable)); } void DesktopButtonsView::setRemoveDesktopEnabled(bool enable) { rootContext()->setContextProperty(QStringLiteral("remove"), QVariant(enable)); } bool DesktopButtonsView::isVisible() const { return m_visible; } void DesktopButtonsView::show() { if (!m_visible && m_posIsValid) { setPosition(m_pos); m_posIsValid = false; } m_visible = true; QQuickView::show(); } void DesktopButtonsView::hide() { if (!m_posIsValid) { m_pos = position(); m_posIsValid = true; setPosition(-width(), -height()); } m_visible = false; } } // namespace diff --git a/effects/desktopgrid/desktopgrid_config.cpp b/effects/desktopgrid/desktopgrid_config.cpp index 3b175359e..2d8e917a9 100644 --- a/effects/desktopgrid/desktopgrid_config.cpp +++ b/effects/desktopgrid/desktopgrid_config.cpp @@ -1,140 +1,140 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2008 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "desktopgrid_config.h" // KConfigSkeleton #include "desktopgridconfig.h" #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(DesktopGridEffectConfigFactory, "desktopgrid_config.json", registerPlugin();) namespace KWin { DesktopGridEffectConfigForm::DesktopGridEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } DesktopGridEffectConfig::DesktopGridEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("desktopgrid")), parent, args) { m_ui = new DesktopGridEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); // Shortcut config. The shortcut belongs to the component "kwin"! m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); m_actionCollection->setConfigGroup(QStringLiteral("DesktopGrid")); m_actionCollection->setConfigGlobal(true); QAction* a = m_actionCollection->addAction(QStringLiteral("ShowDesktopGrid")); a->setText(i18n("Show Desktop Grid")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::CTRL + Qt::Key_F8); KGlobalAccel::self()->setShortcut(a, QList() << Qt::CTRL + Qt::Key_F8); m_ui->shortcutEditor->addCollection(m_actionCollection); m_ui->desktopNameAlignmentCombo->addItem(i18nc("Desktop name alignment:", "Disabled"), QVariant(Qt::Alignment(0))); m_ui->desktopNameAlignmentCombo->addItem(i18n("Top"), QVariant(Qt::AlignHCenter | Qt::AlignTop)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Top-Right"), QVariant(Qt::AlignRight | Qt::AlignTop)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Right"), QVariant(Qt::AlignRight | Qt::AlignVCenter)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Bottom-Right"), QVariant(Qt::AlignRight | Qt::AlignBottom)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Bottom"), QVariant(Qt::AlignHCenter | Qt::AlignBottom)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Bottom-Left"), QVariant(Qt::AlignLeft | Qt::AlignBottom)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Left"), QVariant(Qt::AlignLeft | Qt::AlignVCenter)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Top-Left"), QVariant(Qt::AlignLeft | Qt::AlignTop)); m_ui->desktopNameAlignmentCombo->addItem(i18n("Center"), QVariant(Qt::AlignCenter)); DesktopGridConfig::instance(KWIN_CONFIG); addConfig(DesktopGridConfig::self(), m_ui); - connect(m_ui->kcfg_LayoutMode, SIGNAL(currentIndexChanged(int)), this, SLOT(layoutSelectionChanged())); - connect(m_ui->desktopNameAlignmentCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); - connect(m_ui->shortcutEditor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(m_ui->kcfg_LayoutMode, qOverload(&KComboBox::currentIndexChanged), this, &DesktopGridEffectConfig::layoutSelectionChanged); + connect(m_ui->desktopNameAlignmentCombo, qOverload(&KComboBox::currentIndexChanged), this, qOverload<>(&DesktopGridEffectConfig::changed)); + connect(m_ui->shortcutEditor, &KShortcutsEditor::keyChange, this, qOverload<>(&DesktopGridEffectConfig::changed)); load(); layoutSelectionChanged(); } DesktopGridEffectConfig::~DesktopGridEffectConfig() { // If save() is called undoChanges() has no effect m_ui->shortcutEditor->undoChanges(); } void DesktopGridEffectConfig::save() { m_ui->shortcutEditor->save(); DesktopGridConfig::setDesktopNameAlignment(m_ui->desktopNameAlignmentCombo->itemData(m_ui->desktopNameAlignmentCombo->currentIndex()).toInt()); KCModule::save(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("desktopgrid")); } void DesktopGridEffectConfig::load() { KCModule::load(); m_ui->desktopNameAlignmentCombo->setCurrentIndex(m_ui->desktopNameAlignmentCombo->findData(QVariant(DesktopGridConfig::desktopNameAlignment()))); } void DesktopGridEffectConfig::layoutSelectionChanged() { if (m_ui->kcfg_LayoutMode->currentIndex() == DesktopGridEffect::LayoutCustom) { m_ui->layoutRowsLabel->setEnabled(true); m_ui->kcfg_CustomLayoutRows->setEnabled(true); } else { m_ui->layoutRowsLabel->setEnabled(false); m_ui->kcfg_CustomLayoutRows->setEnabled(false); } } void DesktopGridEffectConfig::defaults() { KCModule::defaults(); m_ui->desktopNameAlignmentCombo->setCurrentIndex(0); } } // namespace #include "desktopgrid_config.moc" diff --git a/effects/fallapart/fallapart.cpp b/effects/fallapart/fallapart.cpp index 5d3f0997b..d2dea06f7 100644 --- a/effects/fallapart/fallapart.cpp +++ b/effects/fallapart/fallapart.cpp @@ -1,197 +1,197 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "fallapart.h" // KConfigSkeleton #include "fallapartconfig.h" #include #include namespace KWin { bool FallApartEffect::supported() { return effects->isOpenGLCompositing() && effects->animationsSupported(); } FallApartEffect::FallApartEffect() { initConfig(); reconfigure(ReconfigureAll); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDataChanged(KWin::EffectWindow*,int)), this, SLOT(slotWindowDataChanged(KWin::EffectWindow*,int))); + connect(effects, &EffectsHandler::windowClosed, this, &FallApartEffect::slotWindowClosed); + connect(effects, &EffectsHandler::windowDeleted, this, &FallApartEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::windowDataChanged, this, &FallApartEffect::slotWindowDataChanged); } void FallApartEffect::reconfigure(ReconfigureFlags) { FallApartConfig::self()->read(); blockSize = FallApartConfig::blockSize(); } void FallApartEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (!windows.isEmpty()) data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; effects->prePaintScreen(data, time); } void FallApartEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (windows.contains(w) && isRealWindow(w)) { if (windows[ w ] < 1) { windows[ w ] += time / animationTime(1000.); data.setTransformed(); w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE); // Request the window to be divided into cells data.quads = data.quads.makeGrid(blockSize); } else { windows.remove(w); w->unrefWindow(); } } effects->prePaintWindow(w, data, time); } void FallApartEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (windows.contains(w) && isRealWindow(w)) { const qreal t = windows[w]; WindowQuadList new_quads; int cnt = 0; foreach (WindowQuad quad, data.quads) { // krazy:exclude=foreach // make fragments move in various directions, based on where // they are (left pieces generally move to the left, etc.) QPointF p1(quad[ 0 ].x(), quad[ 0 ].y()); double xdiff = 0; if (p1.x() < w->width() / 2) xdiff = -(w->width() / 2 - p1.x()) / w->width() * 100; if (p1.x() > w->width() / 2) xdiff = (p1.x() - w->width() / 2) / w->width() * 100; double ydiff = 0; if (p1.y() < w->height() / 2) ydiff = -(w->height() / 2 - p1.y()) / w->height() * 100; if (p1.y() > w->height() / 2) ydiff = (p1.y() - w->height() / 2) / w->height() * 100; double modif = t * t * 64; srandom(cnt); // change direction randomly but consistently xdiff += (rand() % 21 - 10); ydiff += (rand() % 21 - 10); for (int j = 0; j < 4; ++j) { quad[ j ].move(quad[ j ].x() + xdiff * modif, quad[ j ].y() + ydiff * modif); } // also make the fragments rotate around their center QPointF center((quad[ 0 ].x() + quad[ 1 ].x() + quad[ 2 ].x() + quad[ 3 ].x()) / 4, (quad[ 0 ].y() + quad[ 1 ].y() + quad[ 2 ].y() + quad[ 3 ].y()) / 4); double adiff = (rand() % 720 - 360) / 360. * 2 * M_PI; // spin randomly for (int j = 0; j < 4; ++j) { double x = quad[ j ].x() - center.x(); double y = quad[ j ].y() - center.y(); double angle = atan2(y, x); angle += windows[ w ] * adiff; double dist = sqrt(x * x + y * y); x = dist * cos(angle); y = dist * sin(angle); quad[ j ].move(center.x() + x, center.y() + y); } new_quads.append(quad); ++cnt; } data.quads = new_quads; data.multiplyOpacity(interpolate(1.0, 0.0, t)); } effects->paintWindow(w, mask, region, data); } void FallApartEffect::postPaintScreen() { if (!windows.isEmpty()) effects->addRepaintFull(); effects->postPaintScreen(); } bool FallApartEffect::isRealWindow(EffectWindow* w) { // TODO: isSpecialWindow is rather generic, maybe tell windowtypes separately? /* qCDebug(KWINEFFECTS) << "--" << w->caption() << "--------------------------------"; qCDebug(KWINEFFECTS) << "Tooltip:" << w->isTooltip(); qCDebug(KWINEFFECTS) << "Toolbar:" << w->isToolbar(); qCDebug(KWINEFFECTS) << "Desktop:" << w->isDesktop(); qCDebug(KWINEFFECTS) << "Special:" << w->isSpecialWindow(); qCDebug(KWINEFFECTS) << "TopMenu:" << w->isTopMenu(); qCDebug(KWINEFFECTS) << "Notific:" << w->isNotification(); qCDebug(KWINEFFECTS) << "Splash:" << w->isSplash(); qCDebug(KWINEFFECTS) << "Normal:" << w->isNormalWindow(); */ if (!w->isNormalWindow()) return false; return true; } void FallApartEffect::slotWindowClosed(EffectWindow* c) { if (!isRealWindow(c)) return; if (!c->isVisible()) return; const void* e = c->data(WindowClosedGrabRole).value(); if (e && e != this) return; c->setData(WindowClosedGrabRole, QVariant::fromValue(static_cast(this))); windows[ c ] = 0; c->refWindow(); } void FallApartEffect::slotWindowDeleted(EffectWindow* c) { windows.remove(c); } void FallApartEffect::slotWindowDataChanged(EffectWindow* w, int role) { if (role != WindowClosedGrabRole) { return; } if (w->data(role).value() == this) { return; } auto it = windows.find(w); if (it == windows.end()) { return; } it.key()->unrefWindow(); windows.erase(it); } bool FallApartEffect::isActive() const { return !windows.isEmpty(); } } // namespace diff --git a/effects/flipswitch/flipswitch.cpp b/effects/flipswitch/flipswitch.cpp index c72b953e5..890b6d881 100644 --- a/effects/flipswitch/flipswitch.cpp +++ b/effects/flipswitch/flipswitch.cpp @@ -1,988 +1,988 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008, 2009 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "flipswitch.h" // KConfigSkeleton #include "flipswitchconfig.h" #include #include #include #include #include #include #include #include #include namespace KWin { FlipSwitchEffect::FlipSwitchEffect() : m_selectedWindow(nullptr) , m_currentAnimationShape(QTimeLine::EaseInOutCurve) , m_active(false) , m_start(false) , m_stop(false) , m_animation(false) , m_hasKeyboardGrab(false) , m_captionFrame(NULL) { initConfig(); reconfigure(ReconfigureAll); // Caption frame m_captionFont.setBold(true); m_captionFont.setPointSize(m_captionFont.pointSize() * 2); QAction* flipSwitchCurrentAction = new QAction(this); flipSwitchCurrentAction->setObjectName(QStringLiteral("FlipSwitchCurrent")); flipSwitchCurrentAction->setText(i18n("Toggle Flip Switch (Current desktop)")); KGlobalAccel::self()->setShortcut(flipSwitchCurrentAction, QList()); m_shortcutCurrent = KGlobalAccel::self()->shortcut(flipSwitchCurrentAction); effects->registerGlobalShortcut(QKeySequence(), flipSwitchCurrentAction); - connect(flipSwitchCurrentAction, SIGNAL(triggered(bool)), this, SLOT(toggleActiveCurrent())); + connect(flipSwitchCurrentAction, &QAction::triggered, this, &FlipSwitchEffect::toggleActiveCurrent); QAction* flipSwitchAllAction = new QAction(this); flipSwitchAllAction->setObjectName(QStringLiteral("FlipSwitchAll")); flipSwitchAllAction->setText(i18n("Toggle Flip Switch (All desktops)")); KGlobalAccel::self()->setShortcut(flipSwitchAllAction, QList()); effects->registerGlobalShortcut(QKeySequence(), flipSwitchAllAction); m_shortcutAll = KGlobalAccel::self()->shortcut(flipSwitchAllAction); - connect(flipSwitchAllAction, SIGNAL(triggered(bool)), this, SLOT(toggleActiveAllDesktops())); + connect(flipSwitchAllAction, &QAction::triggered, this, &FlipSwitchEffect::toggleActiveAllDesktops); connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &FlipSwitchEffect::globalShortcutChanged); - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(tabBoxAdded(int)), this, SLOT(slotTabBoxAdded(int))); - connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed())); - connect(effects, SIGNAL(tabBoxUpdated()), this, SLOT(slotTabBoxUpdated())); - connect(effects, SIGNAL(tabBoxKeyEvent(QKeyEvent*)), this, SLOT(slotTabBoxKeyEvent(QKeyEvent*))); + connect(effects, &EffectsHandler::windowAdded, this, &FlipSwitchEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowClosed, this, &FlipSwitchEffect::slotWindowClosed); + connect(effects, &EffectsHandler::tabBoxAdded, this, &FlipSwitchEffect::slotTabBoxAdded); + connect(effects, &EffectsHandler::tabBoxClosed, this, &FlipSwitchEffect::slotTabBoxClosed); + connect(effects, &EffectsHandler::tabBoxUpdated, this, &FlipSwitchEffect::slotTabBoxUpdated); + connect(effects, &EffectsHandler::tabBoxKeyEvent, this, &FlipSwitchEffect::slotTabBoxKeyEvent); } FlipSwitchEffect::~FlipSwitchEffect() { delete m_captionFrame; } bool FlipSwitchEffect::supported() { return effects->isOpenGLCompositing() && effects->animationsSupported(); } void FlipSwitchEffect::reconfigure(ReconfigureFlags) { FlipSwitchConfig::self()->read(); m_tabbox = FlipSwitchConfig::tabBox(); m_tabboxAlternative = FlipSwitchConfig::tabBoxAlternative(); const int duration = animationTime(200); m_timeLine.setDuration(duration); m_startStopTimeLine.setDuration(duration); m_angle = FlipSwitchConfig::angle(); m_xPosition = FlipSwitchConfig::xPosition() / 100.0f; m_yPosition = FlipSwitchConfig::yPosition() / 100.0f; m_windowTitle = FlipSwitchConfig::windowTitle(); } void FlipSwitchEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (m_active) { data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; if (m_start) m_startStopTimeLine.setCurrentTime(m_startStopTimeLine.currentTime() + time); if (m_stop && m_scheduledDirections.isEmpty()) m_startStopTimeLine.setCurrentTime(m_startStopTimeLine.currentTime() - time); if (m_animation) m_timeLine.setCurrentTime(m_timeLine.currentTime() + time); } effects->prePaintScreen(data, time); } void FlipSwitchEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); if (m_active) { EffectWindowList tempList; if (m_mode == TabboxMode) tempList = effects->currentTabBoxWindowList(); else { // we have to setup the list // using stacking order directly is not possible // as not each window in stacking order is shown // TODO: store list instead of calculating in each frame? foreach (EffectWindow * w, effects->stackingOrder()) { if (m_windows.contains(w)) tempList.append(w); } } m_flipOrderedWindows.clear(); int index = tempList.indexOf(m_selectedWindow); int tabIndex = index; if (m_mode == TabboxMode) { foreach (SwitchingDirection direction, m_scheduledDirections) { // krazy:exclude=foreach if (direction == DirectionBackward) index++; else index--; if (index < 0) index = tempList.count() + index; if (index >= tempList.count()) index = index % tempList.count(); } tabIndex = index; EffectWindow* w = NULL; if (!m_scheduledDirections.isEmpty() && m_scheduledDirections.head() == DirectionBackward) { index--; if (index < 0) index = tempList.count() + index; w = tempList.at(index); } for (int i = index - 1; i >= 0; i--) m_flipOrderedWindows.append(tempList.at(i)); for (int i = effects->currentTabBoxWindowList().count() - 1; i >= index; i--) m_flipOrderedWindows.append(tempList.at(i)); if (w) { m_flipOrderedWindows.removeAll(w); m_flipOrderedWindows.append(w); } } else { foreach (SwitchingDirection direction, m_scheduledDirections) { // krazy:exclude=foreach if (direction == DirectionForward) index++; else index--; if (index < 0) index = tempList.count() - 1; if (index >= tempList.count()) index = 0; } tabIndex = index; EffectWindow* w = NULL; if (!m_scheduledDirections.isEmpty() && m_scheduledDirections.head() == DirectionBackward) { index++; if (index >= tempList.count()) index = 0; } // sort from stacking order for (int i = index + 1; i < tempList.count(); i++) m_flipOrderedWindows.append(tempList.at(i)); for (int i = 0; i <= index; i++) m_flipOrderedWindows.append(tempList.at(i)); if (w) { m_flipOrderedWindows.removeAll(w); m_flipOrderedWindows.append(w); } } int winMask = PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_TRANSLUCENT; // fade in/out one window at the end of the stack during animation if (m_animation && !m_scheduledDirections.isEmpty()) { EffectWindow* w = m_flipOrderedWindows.last(); if (ItemInfo *info = m_windows.value(w,0)) { WindowPaintData data(w); if (effects->numScreens() > 1) { data.setProjectionMatrix(m_projectionMatrix); data.setModelViewMatrix(m_modelviewMatrix); } data.setRotationAxis(Qt::YAxis); data.setRotationAngle(m_angle * m_startStopTimeLine.currentValue()); data.setOpacity(info->opacity); data.setBrightness(info->brightness); data.setSaturation(info->saturation); int distance = tempList.count() - 1; float zDistance = 500.0f; data.translate(- (w->x() - m_screenArea.x() + data.xTranslation()) * m_startStopTimeLine.currentValue()); data.translate(m_screenArea.width() * m_xPosition * m_startStopTimeLine.currentValue(), (m_screenArea.y() + m_screenArea.height() * m_yPosition - (w->y() + w->height() + data.yTranslation())) * m_startStopTimeLine.currentValue()); data.translate(- (m_screenArea.width() * 0.25f) * distance * m_startStopTimeLine.currentValue(), - (m_screenArea.height() * 0.10f) * distance * m_startStopTimeLine.currentValue(), - (zDistance * distance) * m_startStopTimeLine.currentValue()); if (m_scheduledDirections.head() == DirectionForward) data.multiplyOpacity(0.8 * m_timeLine.currentValue()); else data.multiplyOpacity(0.8 * (1.0 - m_timeLine.currentValue())); if (effects->numScreens() > 1) { adjustWindowMultiScreen(w, data); } effects->drawWindow(w, winMask, infiniteRegion(), data); } } foreach (EffectWindow *w, m_flipOrderedWindows) { ItemInfo *info = m_windows.value(w,0); if (!info) continue; WindowPaintData data(w); if (effects->numScreens() > 1) { data.setProjectionMatrix(m_projectionMatrix); data.setModelViewMatrix(m_modelviewMatrix); } data.setRotationAxis(Qt::YAxis); data.setRotationAngle(m_angle * m_startStopTimeLine.currentValue()); data.setOpacity(info->opacity); data.setBrightness(info->brightness); data.setSaturation(info->saturation); int windowIndex = tempList.indexOf(w); int distance; if (m_mode == TabboxMode) { if (windowIndex < tabIndex) distance = tempList.count() - (tabIndex - windowIndex); else if (windowIndex > tabIndex) distance = windowIndex - tabIndex; else distance = 0; } else { distance = m_flipOrderedWindows.count() - m_flipOrderedWindows.indexOf(w) - 1; if (!m_scheduledDirections.isEmpty() && m_scheduledDirections.head() == DirectionBackward) { distance--; } } if (!m_scheduledDirections.isEmpty() && m_scheduledDirections.head() == DirectionBackward) { if (w == m_flipOrderedWindows.last()) { distance = -1; data.multiplyOpacity(m_timeLine.currentValue()); } } float zDistance = 500.0f; data.translate(- (w->x() - m_screenArea.x() + data.xTranslation()) * m_startStopTimeLine.currentValue()); data.translate(m_screenArea.width() * m_xPosition * m_startStopTimeLine.currentValue(), (m_screenArea.y() + m_screenArea.height() * m_yPosition - (w->y() + w->height() + data.yTranslation())) * m_startStopTimeLine.currentValue()); data.translate(-(m_screenArea.width() * 0.25f) * distance * m_startStopTimeLine.currentValue(), -(m_screenArea.height() * 0.10f) * distance * m_startStopTimeLine.currentValue(), -(zDistance * distance) * m_startStopTimeLine.currentValue()); if (m_animation && !m_scheduledDirections.isEmpty()) { if (m_scheduledDirections.head() == DirectionForward) { data.translate((m_screenArea.width() * 0.25f) * m_timeLine.currentValue(), (m_screenArea.height() * 0.10f) * m_timeLine.currentValue(), zDistance * m_timeLine.currentValue()); if (distance == 0) data.multiplyOpacity((1.0 - m_timeLine.currentValue())); } else { data.translate(- (m_screenArea.width() * 0.25f) * m_timeLine.currentValue(), - (m_screenArea.height() * 0.10f) * m_timeLine.currentValue(), - zDistance * m_timeLine.currentValue()); } } data.multiplyOpacity((0.8 + 0.2 * (1.0 - m_startStopTimeLine.currentValue()))); if (effects->numScreens() > 1) { adjustWindowMultiScreen(w, data); } effects->drawWindow(w, winMask, infiniteRegion(), data); } if (m_windowTitle) { // Render the caption frame if (m_animation) { m_captionFrame->setCrossFadeProgress(m_timeLine.currentValue()); } m_captionFrame->render(region, m_startStopTimeLine.currentValue()); } } } void FlipSwitchEffect::postPaintScreen() { if (m_active) { if (m_start && m_startStopTimeLine.currentValue() == 1.0f) { m_start = false; if (!m_scheduledDirections.isEmpty()) { m_animation = true; m_timeLine.setCurrentTime(0); if (m_scheduledDirections.count() == 1) { m_currentAnimationShape = QTimeLine::EaseOutCurve; m_timeLine.setCurveShape(m_currentAnimationShape); } else { m_currentAnimationShape = QTimeLine::LinearCurve; m_timeLine.setCurveShape(m_currentAnimationShape); } } effects->addRepaintFull(); } if (m_stop && m_startStopTimeLine.currentValue() == 0.0f) { m_stop = false; m_active = false; m_captionFrame->free(); effects->setActiveFullScreenEffect(0); effects->addRepaintFull(); qDeleteAll(m_windows); m_windows.clear(); } if (m_animation && m_timeLine.currentValue() == 1.0f) { m_timeLine.setCurrentTime(0); m_scheduledDirections.dequeue(); if (m_scheduledDirections.isEmpty()) { m_animation = false; effects->addRepaintFull(); } else { if (m_scheduledDirections.count() == 1) { if (m_stop) m_currentAnimationShape = QTimeLine::LinearCurve; else m_currentAnimationShape = QTimeLine::EaseOutCurve; } else { m_currentAnimationShape = QTimeLine::LinearCurve; } m_timeLine.setCurveShape(m_currentAnimationShape); } } if (m_start || m_stop || m_animation) effects->addRepaintFull(); } effects->postPaintScreen(); } void FlipSwitchEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (m_active) { if (m_windows.contains(w)) { data.setTransformed(); data.setTranslucent(); if (!w->isOnCurrentDesktop()) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); if (w->isMinimized()) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE); if (!w->isCurrentTab()) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_TAB_GROUP); } else { if ((m_start || m_stop) && !w->isDesktop() && w->isOnCurrentDesktop()) data.setTranslucent(); else if (!w->isDesktop()) w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } } effects->prePaintWindow(w, data, time); } void FlipSwitchEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (m_active) { ItemInfo *info = m_windows.value(w,0); if (info) { info->opacity = data.opacity(); info->brightness = data.brightness(); info->saturation = data.saturation(); } // fade out all windows not in window list except the desktops const bool isFader = (m_start || m_stop) && !info && !w->isDesktop(); if (isFader) data.multiplyOpacity((1.0 - m_startStopTimeLine.currentValue())); // if not a fader or the desktop, skip painting here to prevent flicker if (!(isFader || w->isDesktop())) return; } effects->paintWindow(w, mask, region, data); } //************************************************************* // Tabbox handling //************************************************************* void FlipSwitchEffect::slotTabBoxAdded(int mode) { if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; // only for windows mode effects->setShowingDesktop(false); if (((mode == TabBoxWindowsMode && m_tabbox) || (mode == TabBoxWindowsAlternativeMode && m_tabboxAlternative) || (mode == TabBoxCurrentAppWindowsMode && m_tabbox) || (mode == TabBoxCurrentAppWindowsAlternativeMode && m_tabboxAlternative)) && (!m_active || (m_active && m_stop)) && !effects->currentTabBoxWindowList().isEmpty()) { setActive(true, TabboxMode); if (m_active) effects->refTabBox(); } } void FlipSwitchEffect::slotTabBoxClosed() { if (m_active) { setActive(false, TabboxMode); effects->unrefTabBox(); } } void FlipSwitchEffect::slotTabBoxUpdated() { if (m_active && !m_stop) { if (!effects->currentTabBoxWindowList().isEmpty()) { // determine the switch direction if (m_selectedWindow != effects->currentTabBoxWindow()) { if (m_selectedWindow != NULL) { int old_index = effects->currentTabBoxWindowList().indexOf(m_selectedWindow); int new_index = effects->currentTabBoxWindowList().indexOf(effects->currentTabBoxWindow()); SwitchingDirection new_direction; int distance = new_index - old_index; if (distance > 0) new_direction = DirectionForward; if (distance < 0) new_direction = DirectionBackward; if (effects->currentTabBoxWindowList().count() == 2) { new_direction = DirectionForward; distance = 1; } if (distance != 0) { distance = abs(distance); int tempDistance = effects->currentTabBoxWindowList().count() - distance; if (tempDistance < abs(distance)) { distance = tempDistance; if (new_direction == DirectionForward) new_direction = DirectionBackward; else new_direction = DirectionForward; } scheduleAnimation(new_direction, distance); } } m_selectedWindow = effects->currentTabBoxWindow(); updateCaption(); } } effects->addRepaintFull(); } } //************************************************************* // Window adding/removing handling //************************************************************* void FlipSwitchEffect::slotWindowAdded(EffectWindow* w) { if (m_active && isSelectableWindow(w)) { m_windows[ w ] = new ItemInfo; } } void FlipSwitchEffect::slotWindowClosed(EffectWindow* w) { if (m_selectedWindow == w) m_selectedWindow = 0; if (m_active) { QHash< const EffectWindow*, ItemInfo* >::iterator it = m_windows.find(w); if (it != m_windows.end()) { delete *it; m_windows.erase(it); } } } //************************************************************* // Activation //************************************************************* void FlipSwitchEffect::setActive(bool activate, FlipSwitchMode mode) { if (activate) { // effect already active, do some sanity checks if (m_active) { if (m_stop) { if (mode != m_mode) { // only the same mode may reactivate the effect return; } } else { // active, but not scheduled for closing -> abort return; } } m_mode = mode; foreach (EffectWindow * w, effects->stackingOrder()) { if (isSelectableWindow(w) && !m_windows.contains(w)) m_windows[ w ] = new ItemInfo; } if (m_windows.isEmpty()) return; effects->setActiveFullScreenEffect(this); m_active = true; m_start = true; m_startStopTimeLine.setCurveShape(QTimeLine::EaseInOutCurve); m_activeScreen = effects->activeScreen(); m_screenArea = effects->clientArea(ScreenArea, m_activeScreen, effects->currentDesktop()); if (effects->numScreens() > 1) { // unfortunatelly we have to change the projection matrix in dual screen mode // code is copied from Coverswitch QRect fullRect = effects->clientArea(FullArea, m_activeScreen, effects->currentDesktop()); float fovy = 60.0f; float aspect = 1.0f; float zNear = 0.1f; float zFar = 100.0f; float ymax = zNear * std::tan(fovy * M_PI / 360.0f); float ymin = -ymax; float xmin = ymin * aspect; float xmax = ymax * aspect; if (m_screenArea.width() != fullRect.width()) { if (m_screenArea.x() == 0) { // horizontal layout: left screen xmin *= (float)m_screenArea.width() / (float)fullRect.width(); xmax *= (fullRect.width() - 0.5f * m_screenArea.width()) / (0.5f * fullRect.width()); } else { // horizontal layout: right screen xmin *= (fullRect.width() - 0.5f * m_screenArea.width()) / (0.5f * fullRect.width()); xmax *= (float)m_screenArea.width() / (float)fullRect.width(); } } if (m_screenArea.height() != fullRect.height()) { if (m_screenArea.y() == 0) { // vertical layout: top screen ymin *= (fullRect.height() - 0.5f * m_screenArea.height()) / (0.5f * fullRect.height()); ymax *= (float)m_screenArea.height() / (float)fullRect.height(); } else { // vertical layout: bottom screen ymin *= (float)m_screenArea.height() / (float)fullRect.height(); ymax *= (fullRect.height() - 0.5f * m_screenArea.height()) / (0.5f * fullRect.height()); } } m_projectionMatrix = QMatrix4x4(); m_projectionMatrix.frustum(xmin, xmax, ymin, ymax, zNear, zFar); const float scaleFactor = 1.1f / zNear; // Create a second matrix that transforms screen coordinates // to world coordinates. QMatrix4x4 matrix; matrix.translate(xmin * scaleFactor, ymax * scaleFactor, -1.1); matrix.scale( (xmax - xmin) * scaleFactor / fullRect.width(), -(ymax - ymin) * scaleFactor / fullRect.height(), 0.001); // Combine the matrices m_projectionMatrix *= matrix; m_modelviewMatrix = QMatrix4x4(); m_modelviewMatrix.translate(m_screenArea.x(), m_screenArea.y(), 0.0); } if (m_stop) { // effect is still closing from last usage m_stop = false; } else { // things to do only when there is no closing animation m_scheduledDirections.clear(); } switch(m_mode) { case TabboxMode: m_selectedWindow = effects->currentTabBoxWindow(); effects->startMouseInterception(this, Qt::ArrowCursor); break; case CurrentDesktopMode: m_selectedWindow = effects->activeWindow(); effects->startMouseInterception(this, Qt::BlankCursor); m_hasKeyboardGrab = effects->grabKeyboard(this); break; case AllDesktopsMode: m_selectedWindow = effects->activeWindow(); effects->startMouseInterception(this, Qt::BlankCursor); m_hasKeyboardGrab = effects->grabKeyboard(this); break; } // Setup caption frame geometry QRect frameRect = QRect(m_screenArea.width() * 0.25f + m_screenArea.x(), m_screenArea.height() * 0.1f + m_screenArea.y() - QFontMetrics(m_captionFont).height(), m_screenArea.width() * 0.5f, QFontMetrics(m_captionFont).height()); if (!m_captionFrame) { m_captionFrame = effects->effectFrame(EffectFrameStyled); m_captionFrame->setFont(m_captionFont); m_captionFrame->enableCrossFade(true); } m_captionFrame->setGeometry(frameRect); m_captionFrame->setIconSize(QSize(frameRect.height(), frameRect.height())); updateCaption(); effects->addRepaintFull(); } else { // only deactivate if mode is current mode if (mode != m_mode) return; if (m_start && m_scheduledDirections.isEmpty()) { m_start = false; } m_stop = true; if (m_animation) { m_startStopTimeLine.setCurveShape(QTimeLine::EaseOutCurve); if (m_scheduledDirections.count() == 1) { if (m_currentAnimationShape == QTimeLine::EaseInOutCurve) m_currentAnimationShape = QTimeLine::EaseInCurve; else if (m_currentAnimationShape == QTimeLine::EaseOutCurve) m_currentAnimationShape = QTimeLine::LinearCurve; m_timeLine.setCurveShape(m_currentAnimationShape); } } else m_startStopTimeLine.setCurveShape(QTimeLine::EaseInOutCurve); effects->stopMouseInterception(this); if (m_hasKeyboardGrab) { effects->ungrabKeyboard(); m_hasKeyboardGrab = false; } effects->addRepaintFull(); } } void FlipSwitchEffect::toggleActiveAllDesktops() { if (m_active) { if (m_stop) { // reactivate if stopping setActive(true, AllDesktopsMode); } else { // deactivate if not stopping setActive(false, AllDesktopsMode); } } else { setActive(true, AllDesktopsMode); } } void FlipSwitchEffect::toggleActiveCurrent() { if (m_active) { if (m_stop) { // reactivate if stopping setActive(true, CurrentDesktopMode); } else { // deactivate if not stopping setActive(false, CurrentDesktopMode); } } else { setActive(true, CurrentDesktopMode); } } //************************************************************* // Helper function //************************************************************* bool FlipSwitchEffect::isSelectableWindow(EffectWindow* w) const { // desktop windows might be included if ((w->isSpecialWindow() && !w->isDesktop()) || w->isUtility()) return false; if (w->isDesktop()) return (m_mode == TabboxMode && effects->currentTabBoxWindowList().contains(w)); if (w->isDeleted()) return false; if (!w->acceptsFocus()) return false; switch(m_mode) { case TabboxMode: return effects->currentTabBoxWindowList().contains(w); case CurrentDesktopMode: return w->isOnCurrentDesktop(); case AllDesktopsMode: //nothing special break; } return true; } void FlipSwitchEffect::scheduleAnimation(const SwitchingDirection& direction, int distance) { if (m_start) { // start is still active so change the shape to have a nice transition m_startStopTimeLine.setCurveShape(QTimeLine::EaseInCurve); } if (!m_animation && !m_start) { m_animation = true; m_scheduledDirections.enqueue(direction); distance--; // reset shape just to make sure m_currentAnimationShape = QTimeLine::EaseInOutCurve; m_timeLine.setCurveShape(m_currentAnimationShape); } for (int i = 0; i < distance; i++) { if (m_scheduledDirections.count() > 1 && m_scheduledDirections.last() != direction) m_scheduledDirections.pop_back(); else m_scheduledDirections.enqueue(direction); if (m_scheduledDirections.count() == m_windows.count() + 1) { SwitchingDirection temp = m_scheduledDirections.dequeue(); m_scheduledDirections.clear(); m_scheduledDirections.enqueue(temp); } } if (m_scheduledDirections.count() > 1) { QTimeLine::CurveShape newShape = QTimeLine::EaseInOutCurve; switch(m_currentAnimationShape) { case QTimeLine::EaseInOutCurve: newShape = QTimeLine::EaseInCurve; break; case QTimeLine::EaseOutCurve: newShape = QTimeLine::LinearCurve; break; default: newShape = m_currentAnimationShape; } if (newShape != m_currentAnimationShape) { m_currentAnimationShape = newShape; m_timeLine.setCurveShape(m_currentAnimationShape); } } } void FlipSwitchEffect::adjustWindowMultiScreen(const KWin::EffectWindow* w, WindowPaintData& data) { if (effects->numScreens() <= 1) return; QRect clientRect = effects->clientArea(FullScreenArea, w->screen(), effects->currentDesktop()); QRect rect = effects->clientArea(ScreenArea, m_activeScreen, effects->currentDesktop()); QRect fullRect = effects->clientArea(FullArea, m_activeScreen, effects->currentDesktop()); if (w->screen() == m_activeScreen) { if (clientRect.width() != fullRect.width() && clientRect.x() != fullRect.x()) { data.translate(- clientRect.x()); } if (clientRect.height() != fullRect.height() && clientRect.y() != fullRect.y()) { data.translate(0.0, - clientRect.y()); } } else { if (clientRect.width() != fullRect.width() && clientRect.x() < rect.x()) { data.translate(- (m_screenArea.x() - clientRect.x())); } if (clientRect.height() != fullRect.height() && clientRect.y() < m_screenArea.y()) { data.translate(0.0, - (m_screenArea.y() - clientRect.y())); } } } void FlipSwitchEffect::selectNextOrPreviousWindow(bool forward) { if (!m_active || !m_selectedWindow) { return; } const int index = effects->currentTabBoxWindowList().indexOf(m_selectedWindow); int newIndex = index; if (forward) { ++newIndex; } else { --newIndex; } if (newIndex == effects->currentTabBoxWindowList().size()) { newIndex = 0; } else if (newIndex < 0) { newIndex = effects->currentTabBoxWindowList().size() -1; } if (index == newIndex) { return; } effects->setTabBoxWindow(effects->currentTabBoxWindowList().at(newIndex)); } //************************************************************* // Keyboard handling //************************************************************* void FlipSwitchEffect::globalShortcutChanged(QAction *action, QKeySequence shortcut) { if (action->objectName() == QStringLiteral("FlipSwitchAll")) { m_shortcutAll.clear(); m_shortcutAll.append(shortcut); } else if (action->objectName() == QStringLiteral("FlipSwitchCurrent")) { m_shortcutCurrent.clear(); m_shortcutCurrent.append(shortcut); } } void FlipSwitchEffect::grabbedKeyboardEvent(QKeyEvent* e) { if (e->type() == QEvent::KeyPress) { // check for global shortcuts // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155) if (m_mode == CurrentDesktopMode && m_shortcutCurrent.contains(e->key() + e->modifiers())) { toggleActiveCurrent(); return; } if (m_mode == AllDesktopsMode && m_shortcutAll.contains(e->key() + e->modifiers())) { toggleActiveAllDesktops(); return; } switch(e->key()) { case Qt::Key_Escape: setActive(false, m_mode); return; case Qt::Key_Tab: { // find next window if (m_windows.isEmpty()) return; // sanity check bool found = false; for (int i = effects->stackingOrder().indexOf(m_selectedWindow) - 1; i >= 0; i--) { if (isSelectableWindow(effects->stackingOrder().at(i))) { m_selectedWindow = effects->stackingOrder().at(i); found = true; break; } } if (!found) { for (int i = effects->stackingOrder().count() - 1; i > effects->stackingOrder().indexOf(m_selectedWindow); i--) { if (isSelectableWindow(effects->stackingOrder().at(i))) { m_selectedWindow = effects->stackingOrder().at(i); found = true; break; } } } if (found) { updateCaption(); scheduleAnimation(DirectionForward); } break; } case Qt::Key_Backtab: { // find previous window if (m_windows.isEmpty()) return; // sanity check bool found = false; for (int i = effects->stackingOrder().indexOf(m_selectedWindow) + 1; i < effects->stackingOrder().count(); i++) { if (isSelectableWindow(effects->stackingOrder().at(i))) { m_selectedWindow = effects->stackingOrder().at(i); found = true; break; } } if (!found) { for (int i = 0; i < effects->stackingOrder().indexOf(m_selectedWindow); i++) { if (isSelectableWindow(effects->stackingOrder().at(i))) { m_selectedWindow = effects->stackingOrder().at(i); found = true; break; } } } if (found) { updateCaption(); scheduleAnimation(DirectionBackward); } break; } case Qt::Key_Return: case Qt::Key_Enter: case Qt::Key_Space: if (m_selectedWindow) effects->activateWindow(m_selectedWindow); setActive(false, m_mode); break; default: break; } effects->addRepaintFull(); } } void FlipSwitchEffect::slotTabBoxKeyEvent(QKeyEvent *event) { if (event->type() == QEvent::KeyPress) { switch (event->key()) { case Qt::Key_Up: case Qt::Key_Left: selectPreviousWindow(); break; case Qt::Key_Down: case Qt::Key_Right: selectNextWindow(); break; default: // nothing break; } } } bool FlipSwitchEffect::isActive() const { return m_active && !effects->isScreenLocked(); } void FlipSwitchEffect::updateCaption() { if (!m_selectedWindow) { return; } if (m_selectedWindow->isDesktop()) { m_captionFrame->setText(i18nc("Special entry in alt+tab list for minimizing all windows", "Show Desktop")); static QPixmap pix = QIcon::fromTheme(QStringLiteral("user-desktop")).pixmap(m_captionFrame->iconSize()); m_captionFrame->setIcon(pix); } else { m_captionFrame->setText(m_selectedWindow->caption()); m_captionFrame->setIcon(m_selectedWindow->icon()); } } //************************************************************* // Mouse handling //************************************************************* void FlipSwitchEffect::windowInputMouseEvent(QEvent* e) { if (e->type() != QEvent::MouseButtonPress) return; // we don't want click events during animations if (m_animation) return; QMouseEvent* event = static_cast< QMouseEvent* >(e); switch (event->button()) { case Qt::XButton1: // wheel up selectPreviousWindow(); break; case Qt::XButton2: // wheel down selectNextWindow(); break; case Qt::LeftButton: case Qt::RightButton: case Qt::MidButton: default: // TODO: Change window on mouse button click break; } } //************************************************************* // Item Info //************************************************************* FlipSwitchEffect::ItemInfo::ItemInfo() : deleted(false) , opacity(0.0) , brightness(0.0) , saturation(0.0) { } FlipSwitchEffect::ItemInfo::~ItemInfo() { } } // namespace diff --git a/effects/highlightwindow/highlightwindow.cpp b/effects/highlightwindow/highlightwindow.cpp index 355220c89..cd3ddd4a1 100644 --- a/effects/highlightwindow/highlightwindow.cpp +++ b/effects/highlightwindow/highlightwindow.cpp @@ -1,309 +1,313 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "highlightwindow.h" namespace KWin { HighlightWindowEffect::HighlightWindowEffect() : m_finishing(false) , m_fadeDuration(float(animationTime(150))) , m_monitorWindow(NULL) { m_atom = effects->announceSupportProperty("_KDE_WINDOW_HIGHLIGHT", this); - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long))); + connect(effects, &EffectsHandler::windowAdded, this, &HighlightWindowEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowClosed, this, &HighlightWindowEffect::slotWindowClosed); + connect(effects, &EffectsHandler::windowDeleted, this, &HighlightWindowEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::propertyNotify, this, + [this](EffectWindow *w, long atom) { + slotPropertyNotify(w, atom, nullptr); + } + ); connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this] { m_atom = effects->announceSupportProperty("_KDE_WINDOW_HIGHLIGHT", this); } ); } HighlightWindowEffect::~HighlightWindowEffect() { } static bool isInitiallyHidden(EffectWindow* w) { // Is the window initially hidden until it is highlighted? return w->isMinimized() || !w->isCurrentTab() || !w->isOnCurrentDesktop(); } void HighlightWindowEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { // Calculate window opacities QHash::iterator opacity = m_windowOpacity.find(w); if (!m_highlightedWindows.isEmpty()) { // Initial fade out and changing highlight animation if (opacity == m_windowOpacity.end()) opacity = m_windowOpacity.insertMulti(w, 0.0f); float oldOpacity = *opacity; if (m_highlightedWindows.contains(w)) *opacity = qMin(1.0f, oldOpacity + time / m_fadeDuration); else if (w->isNormalWindow() || w->isDialog()) // Only fade out windows *opacity = qMax(isInitiallyHidden(w) ? 0.0f : 0.15f, oldOpacity - time / m_fadeDuration); if (*opacity < 0.98f) data.setTranslucent(); if (oldOpacity != *opacity) effects->addRepaint(w->expandedGeometry()); } else if (m_finishing && m_windowOpacity.contains(w)) { // Final fading back in animation if (opacity == m_windowOpacity.end()) opacity = m_windowOpacity.insert(w, 0.0f); float oldOpacity = *opacity; if (isInitiallyHidden(w)) *opacity = qMax(0.0f, oldOpacity - time / m_fadeDuration); else *opacity = qMin(1.0f, oldOpacity + time / m_fadeDuration); if (*opacity < 0.98f) data.setTranslucent(); if (oldOpacity != *opacity) effects->addRepaint(w->expandedGeometry()); if (*opacity > 0.98f || *opacity < 0.02f) { m_windowOpacity.remove(w); // We default to 1.0 opacity = m_windowOpacity.end(); } } // Show tabbed windows and windows on other desktops if highlighted if (opacity != m_windowOpacity.end() && *opacity > 0.01) { if (w->isMinimized()) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE); if (!w->isCurrentTab()) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_TAB_GROUP); if (!w->isOnCurrentDesktop()) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); } effects->prePaintWindow(w, data, time); } void HighlightWindowEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { data.multiplyOpacity(m_windowOpacity.value(w, 1.0f)); effects->paintWindow(w, mask, region, data); } void HighlightWindowEffect::slotWindowAdded(EffectWindow* w) { if (!m_highlightedWindows.isEmpty()) { // The effect is activated thus we need to add it to the opacity hash foreach (const WId id, m_highlightedIds) { if (w == effects->findWindow(id)) { m_windowOpacity[w] = 1.0; // this window was demanded to be highlighted before it appeared return; } } m_windowOpacity[w] = 0.15; // this window is not currently highlighted } slotPropertyNotify(w, m_atom, w); // Check initial value } void HighlightWindowEffect::slotWindowClosed(EffectWindow* w) { if (m_monitorWindow == w) // The monitoring window was destroyed finishHighlighting(); } void HighlightWindowEffect::slotWindowDeleted(EffectWindow* w) { m_windowOpacity.remove(w); } void HighlightWindowEffect::slotPropertyNotify(EffectWindow* w, long a, EffectWindow *addedWindow) { if (a != m_atom || m_atom == XCB_ATOM_NONE) return; // Not our atom // if the window is null, the property was set on the root window - see events.cpp QByteArray byteData = w ? w->readProperty(m_atom, m_atom, 32) : effects->readRootProperty(m_atom, m_atom, 32); if (byteData.length() < 1) { // Property was removed, clearing highlight if (!addedWindow || w != addedWindow) finishHighlighting(); return; } auto* data = reinterpret_cast(byteData.data()); if (!data[0]) { // Purposely clearing highlight by issuing a NULL target finishHighlighting(); return; } m_monitorWindow = w; bool found = false; int length = byteData.length() / sizeof(data[0]); //foreach ( EffectWindow* e, m_highlightedWindows ) // effects->setElevatedWindow( e, false ); m_highlightedWindows.clear(); m_highlightedIds.clear(); for (int i = 0; i < length; i++) { m_highlightedIds << data[i]; EffectWindow* foundWin = effects->findWindow(data[i]); if (!foundWin) { qCDebug(KWINEFFECTS) << "Invalid window targetted for highlight. Requested:" << data[i]; continue; // might come in later. } m_highlightedWindows.append(foundWin); // TODO: We cannot just simply elevate the window as this will elevate it over // Plasma tooltips and other such windows as well //effects->setElevatedWindow( foundWin, true ); found = true; } if (!found) { finishHighlighting(); return; } prepareHighlighting(); if (w) m_windowOpacity[w] = 1.0; // Because it's not in stackingOrder() yet /* TODO: Finish thumbnails of offscreen windows, not sure if it's worth it though if ( !m_highlightedWindow->isOnCurrentDesktop() ) { // Window is offscreen, determine thumbnail position QRect screenArea = effects->clientArea( MaximizeArea ); // Workable area of the active screen QRect outerArea = outerArea.adjusted( outerArea.width() / 10, outerArea.height() / 10, -outerArea.width() / 10, -outerArea.height() / 10 ); // Add 10% margin around the edge QRect innerArea = outerArea.adjusted( outerArea.width() / 40, outerArea.height() / 40, -outerArea.width() / 40, -outerArea.height() / 40 ); // Outer edge of the thumbnail border (2.5%) QRect thumbArea = outerArea.adjusted( 20, 20, -20, -20 ); // Outer edge of the thumbnail (20px) // Determine the maximum size that we can make the thumbnail within the innerArea double areaAspect = double( thumbArea.width() ) / double( thumbArea.height() ); double windowAspect = aspectRatio( m_highlightedWindow ); QRect thumbRect; // Position doesn't matter right now, but it will later if ( windowAspect > areaAspect ) // Top/bottom will touch first thumbRect = QRect( 0, 0, widthForHeight( thumbArea.height() ), thumbArea.height() ); else // Left/right will touch first thumbRect = QRect( 0, 0, thumbArea.width(), heightForWidth( thumbArea.width() )); if ( thumbRect.width() >= m_highlightedWindow->width() ) // Area is larger than the window, just use the window's size thumbRect = m_highlightedWindow->geometry(); // Determine position of desktop relative to the current one QPoint direction = effects->desktopGridCoords( m_highlightedWindow->desktop() ) - effects->desktopGridCoords( effects->currentDesktop() ); // Draw a line from the center of the current desktop to the center of the target desktop. QPointF desktopLine( 0, 0, direction.x() * screenArea.width(), direction.y() * screenArea.height() ); desktopLeft.translate( screenArea.width() / 2, screenArea.height() / 2 ); // Move to the screen center // Take the point where the line crosses the outerArea, this will be the tip of our arrow QPointF arrowTip; QLineF testLine( // Top outerArea.x(), outerArea.y(), outerArea.x() + outerArea.width(), outerArea.y() ); if ( desktopLine.intersect( testLine, &arrowTip ) != QLineF::BoundedIntersection ) { testLine = QLineF( // Right outerArea.x() + outerArea.width(), outerArea.y(), outerArea.x() + outerArea.width(), outerArea.y() + outerArea.height() ); if ( desktopLine.intersect( testLine, &arrowTip ) != QLineF::BoundedIntersection ) { testLine = QLineF( // Bottom outerArea.x() + outerArea.width(), outerArea.y() + outerArea.height(), outerArea.x(), outerArea.y() + outerArea.height() ); if ( desktopLine.intersect( testLine, &arrowTip ) != QLineF::BoundedIntersection ) { testLine = QLineF( // Left outerArea.x(), outerArea.y() + outerArea.height(), outerArea.x(), outerArea.y() ); desktopLine.intersect( testLine, &arrowTip ); // Should never fail } } } m_arrowTip = arrowTip.toPoint(); } */ } void HighlightWindowEffect::prepareHighlighting() { // Create window data for every window. Just calling [w] creates it. m_finishing = false; foreach (EffectWindow * w, effects->stackingOrder()) { if (!m_windowOpacity.contains(w)) // Just in case we are still finishing from last time m_windowOpacity.insertMulti(w, isInitiallyHidden(w) ? 0.0 : 1.0); if (!m_highlightedWindows.isEmpty()) m_highlightedWindows.at(0)->addRepaintFull(); } } void HighlightWindowEffect::finishHighlighting() { m_finishing = true; m_monitorWindow = NULL; m_highlightedWindows.clear(); if (!m_windowOpacity.isEmpty()) m_windowOpacity.constBegin().key()->addRepaintFull(); } void HighlightWindowEffect::highlightWindows(const QVector &windows) { if (windows.isEmpty()) { finishHighlighting(); return; } m_monitorWindow = nullptr; m_highlightedWindows.clear(); m_highlightedIds.clear(); for (auto w : windows) { m_highlightedWindows << w; } prepareHighlighting(); } bool HighlightWindowEffect::isActive() const { return !(m_windowOpacity.isEmpty() || effects->isScreenLocked()); } bool HighlightWindowEffect::provides(Feature feature) { switch (feature) { case HighlightWindows: return true; default: return false; } } bool HighlightWindowEffect::perform(Feature feature, const QVariantList &arguments) { if (feature != HighlightWindows) { return false; } if (arguments.size() != 1) { return false; } highlightWindows(arguments.first().value>()); return true; } } // namespace diff --git a/effects/invert/invert.cpp b/effects/invert/invert.cpp index 58c32ca64..be1e1f6a3 100644 --- a/effects/invert/invert.cpp +++ b/effects/invert/invert.cpp @@ -1,151 +1,151 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2008 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "invert.h" #include #include #include #include #include #include #include #include namespace KWin { InvertEffect::InvertEffect() : m_inited(false), m_valid(true), m_shader(NULL), m_allWindows(false) { QAction* a = new QAction(this); a->setObjectName(QStringLiteral("Invert")); a->setText(i18n("Toggle Invert Effect")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::CTRL + Qt::META + Qt::Key_I); KGlobalAccel::self()->setShortcut(a, QList() << Qt::CTRL + Qt::META + Qt::Key_I); effects->registerGlobalShortcut(Qt::CTRL + Qt::META + Qt::Key_I, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleScreenInversion())); + connect(a, &QAction::triggered, this, &InvertEffect::toggleScreenInversion); QAction* b = new QAction(this); b->setObjectName(QStringLiteral("InvertWindow")); b->setText(i18n("Toggle Invert Effect on Window")); KGlobalAccel::self()->setDefaultShortcut(b, QList() << Qt::CTRL + Qt::META + Qt::Key_U); KGlobalAccel::self()->setShortcut(b, QList() << Qt::CTRL + Qt::META + Qt::Key_U); effects->registerGlobalShortcut(Qt::CTRL + Qt::META + Qt::Key_U, b); - connect(b, SIGNAL(triggered(bool)), this, SLOT(toggleWindow())); + connect(b, &QAction::triggered, this, &InvertEffect::toggleWindow); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); + connect(effects, &EffectsHandler::windowClosed, this, &InvertEffect::slotWindowClosed); } InvertEffect::~InvertEffect() { delete m_shader; } bool InvertEffect::supported() { return effects->compositingType() == OpenGL2Compositing; } bool InvertEffect::loadData() { m_inited = true; m_shader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("invert.frag")); if (!m_shader->isValid()) { qCCritical(KWINEFFECTS) << "The shader failed to load!"; return false; } return true; } void InvertEffect::drawWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { // Load if we haven't already if (m_valid && !m_inited) m_valid = loadData(); bool useShader = m_valid && (m_allWindows != m_windows.contains(w)); if (useShader) { ShaderManager *shaderManager = ShaderManager::instance(); shaderManager->pushShader(m_shader); data.shader = m_shader; } effects->drawWindow(w, mask, region, data); if (useShader) { ShaderManager::instance()->popShader(); } } void InvertEffect::paintEffectFrame(KWin::EffectFrame* frame, QRegion region, double opacity, double frameOpacity) { if (m_valid && m_allWindows) { frame->setShader(m_shader); ShaderBinder binder(m_shader); effects->paintEffectFrame(frame, region, opacity, frameOpacity); } else { effects->paintEffectFrame(frame, region, opacity, frameOpacity); } } void InvertEffect::slotWindowClosed(EffectWindow* w) { m_windows.removeOne(w); } void InvertEffect::toggleScreenInversion() { m_allWindows = !m_allWindows; effects->addRepaintFull(); } void InvertEffect::toggleWindow() { if (!effects->activeWindow()) { return; } if (!m_windows.contains(effects->activeWindow())) m_windows.append(effects->activeWindow()); else m_windows.removeOne(effects->activeWindow()); effects->activeWindow()->addRepaintFull(); } bool InvertEffect::isActive() const { return m_valid && (m_allWindows || !m_windows.isEmpty()); } bool InvertEffect::provides(Feature f) { return f == ScreenInversion; } } // namespace diff --git a/effects/invert/invert_config.cpp b/effects/invert/invert_config.cpp index 5888fb730..01bd05051 100644 --- a/effects/invert/invert_config.cpp +++ b/effects/invert/invert_config.cpp @@ -1,107 +1,107 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "invert_config.h" #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(InvertEffectConfigFactory, "invert_config.json", registerPlugin();) namespace KWin { InvertEffectConfig::InvertEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("invert")), parent, args) { QVBoxLayout* layout = new QVBoxLayout(this); // Shortcut config. The shortcut belongs to the component "kwin"! KActionCollection *actionCollection = new KActionCollection(this, QStringLiteral("kwin")); actionCollection->setComponentDisplayName(i18n("KWin")); QAction* a = actionCollection->addAction(QStringLiteral("Invert")); a->setText(i18n("Toggle Invert Effect")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::CTRL + Qt::META + Qt::Key_I); KGlobalAccel::self()->setShortcut(a, QList() << Qt::CTRL + Qt::META + Qt::Key_I); QAction* b = actionCollection->addAction(QStringLiteral("InvertWindow")); b->setText(i18n("Toggle Invert Effect on Window")); b->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(b, QList() << Qt::CTRL + Qt::META + Qt::Key_U); KGlobalAccel::self()->setShortcut(b, QList() << Qt::CTRL + Qt::META + Qt::Key_U); mShortcutEditor = new KShortcutsEditor(actionCollection, this, KShortcutsEditor::GlobalAction, KShortcutsEditor::LetterShortcutsDisallowed); - connect(mShortcutEditor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(mShortcutEditor, &KShortcutsEditor::keyChange, this, qOverload<>(&InvertEffectConfig::changed)); layout->addWidget(mShortcutEditor); load(); } InvertEffectConfig::~InvertEffectConfig() { // Undo (only) unsaved changes to global key shortcuts mShortcutEditor->undoChanges(); } void InvertEffectConfig::load() { KCModule::load(); emit changed(false); } void InvertEffectConfig::save() { KCModule::save(); mShortcutEditor->save(); // undo() will restore to this state from now on emit changed(false); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("invert")); } void InvertEffectConfig::defaults() { mShortcutEditor->allDefault(); emit changed(true); } } // namespace #include "invert_config.moc" diff --git a/effects/kscreen/kscreen.cpp b/effects/kscreen/kscreen.cpp index d377fabcf..6c3ce844a 100644 --- a/effects/kscreen/kscreen.cpp +++ b/effects/kscreen/kscreen.cpp @@ -1,192 +1,192 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2013 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ // own #include "kscreen.h" // KConfigSkeleton #include "kscreenconfig.h" /** * How this effect works: * * Effect announces that it is around through property _KDE_KWIN_KSCREEN_SUPPORT on the root window. * * KScreen watches for this property and when it wants to adjust screens, KScreen goes * through the following protocol: * 1. KScreen sets the property value to 1 * 2. Effect starts to fade out all windows * 3. When faded out the effect sets property value to 2 * 4. KScreen adjusts the screens * 5. KScreen sets property value to 3 * 6. Effect starts to fade in all windows again * 7. Effect sets back property value to 0 * * The property has type 32 bits cardinal. To test it use: * xprop -root -f _KDE_KWIN_KSCREEN_SUPPORT 32c -set _KDE_KWIN_KSCREEN_SUPPORT 1 * * The states are: * 0: normal * 1: fading out * 2: faded out * 3: fading in **/ namespace KWin { KscreenEffect::KscreenEffect() : Effect() , m_state(StateNormal) , m_atom(effects->announceSupportProperty("_KDE_KWIN_KSCREEN_SUPPORT", this)) { initConfig(); - connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), SLOT(propertyNotify(KWin::EffectWindow*,long))); + connect(effects, &EffectsHandler::propertyNotify, this, &KscreenEffect::propertyNotify); connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this] { m_atom = effects->announceSupportProperty(QByteArrayLiteral("_KDE_KWIN_KSCREEN_SUPPORT"), this); } ); reconfigure(ReconfigureAll); } KscreenEffect::~KscreenEffect() { } void KscreenEffect::reconfigure(ReconfigureFlags flags) { Q_UNUSED(flags) KscreenConfig::self()->read(); m_timeLine.setDuration( std::chrono::milliseconds(animationTime(250))); } void KscreenEffect::prePaintScreen(ScreenPrePaintData &data, int time) { if (m_state == StateFadingIn || m_state == StateFadingOut) { m_timeLine.update(std::chrono::milliseconds(time)); if (m_timeLine.done()) { switchState(); } } effects->prePaintScreen(data, time); } void KscreenEffect::postPaintScreen() { if (m_state == StateFadingIn || m_state == StateFadingOut) { effects->addRepaintFull(); } } void KscreenEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time) { if (m_state != StateNormal) { data.setTranslucent(); } effects->prePaintWindow(w, data, time); } void KscreenEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { //fade to black and fully opaque switch (m_state) { case StateFadingOut: data.setOpacity(data.opacity() + (1.0 - data.opacity()) * m_timeLine.value()); data.multiplyBrightness(1.0 - m_timeLine.value()); break; case StateFadedOut: data.multiplyOpacity(0.0); data.multiplyBrightness(0.0); break; case StateFadingIn: data.setOpacity(data.opacity() + (1.0 - data.opacity()) * (1.0 - m_timeLine.value())); data.multiplyBrightness(m_timeLine.value()); break; default: // no adjustment break; } effects->paintWindow(w, mask, region, data); } void KscreenEffect::propertyNotify(EffectWindow *window, long int atom) { if (window || atom != m_atom || m_atom == XCB_ATOM_NONE) { return; } QByteArray byteData = effects->readRootProperty(m_atom, XCB_ATOM_CARDINAL, 32); const uint32_t *data = byteData.isEmpty() ? nullptr : reinterpret_cast(byteData.data()); if (!data // Property was deleted || data[0] == 0) { // normal state - KWin should have switched to it if (m_state != StateNormal) { m_state = StateNormal; effects->addRepaintFull(); } return; } if (data[0] == 2) { // faded out state - KWin should have switched to it if (m_state != StateFadedOut) { m_state = StateFadedOut; effects->addRepaintFull(); } return; } if (data[0] == 1) { // kscreen wants KWin to fade out all windows m_state = StateFadingOut; m_timeLine.reset(); effects->addRepaintFull(); return; } if (data[0] == 3) { // kscreen wants KWin to fade in again m_state = StateFadingIn; m_timeLine.reset(); effects->addRepaintFull(); return; } qCDebug(KWINEFFECTS) << "Incorrect Property state, immediate stop: " << data[0]; m_state = StateNormal; effects->addRepaintFull(); } void KscreenEffect::switchState() { long value = -1l; if (m_state == StateFadingOut) { m_state = StateFadedOut; value = 2l; } else if (m_state == StateFadingIn) { m_state = StateNormal; value = 0l; } if (value != -1l && m_atom != XCB_ATOM_NONE) { xcb_change_property(xcbConnection(), XCB_PROP_MODE_REPLACE, x11RootWindow(), m_atom, XCB_ATOM_CARDINAL, 32, 1, &value); } } bool KscreenEffect::isActive() const { return m_state != StateNormal; } } // namespace KWin diff --git a/effects/lookingglass/lookingglass.cpp b/effects/lookingglass/lookingglass.cpp index f275cd8e5..70ea704e7 100644 --- a/effects/lookingglass/lookingglass.cpp +++ b/effects/lookingglass/lookingglass.cpp @@ -1,257 +1,257 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2007 Christian Nitschkowski This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "lookingglass.h" // KConfigSkeleton #include "lookingglassconfig.h" #include #include #include #include #include #include #include #include #include #include namespace KWin { LookingGlassEffect::LookingGlassEffect() : zoom(1.0f) , target_zoom(1.0f) , polling(false) , m_texture(NULL) , m_fbo(NULL) , m_vbo(NULL) , m_shader(NULL) , m_enabled(false) , m_valid(false) { initConfig(); QAction* a; a = KStandardAction::zoomIn(this, SLOT(zoomIn()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); effects->registerGlobalShortcut(Qt::META + Qt::Key_Equal, a); a = KStandardAction::zoomOut(this, SLOT(zoomOut()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); effects->registerGlobalShortcut(Qt::META + Qt::Key_Minus, a); a = KStandardAction::actualSize(this, SLOT(toggle()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); effects->registerGlobalShortcut(Qt::META + Qt::Key_0, a); - connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); + connect(effects, &EffectsHandler::mouseChanged, this, &LookingGlassEffect::slotMouseChanged); + reconfigure(ReconfigureAll); } LookingGlassEffect::~LookingGlassEffect() { delete m_texture; delete m_fbo; delete m_shader; delete m_vbo; } bool LookingGlassEffect::supported() { return effects->compositingType() == OpenGL2Compositing && !GLPlatform::instance()->supports(LimitedNPOT); } void LookingGlassEffect::reconfigure(ReconfigureFlags) { LookingGlassConfig::self()->read(); initialradius = LookingGlassConfig::radius(); radius = initialradius; qCDebug(KWINEFFECTS) << "Radius from config:" << radius; m_valid = loadData(); } bool LookingGlassEffect::loadData() { const QSize screenSize = effects->virtualScreenSize(); int texw = screenSize.width(); int texh = screenSize.height(); // Create texture and render target const int levels = std::log2(qMin(texw, texh)) + 1; m_texture = new GLTexture(GL_RGBA8, texw, texh, levels); m_texture->setFilter(GL_LINEAR_MIPMAP_LINEAR); m_texture->setWrapMode(GL_CLAMP_TO_EDGE); m_fbo = new GLRenderTarget(*m_texture); if (!m_fbo->valid()) { return false; } m_shader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("lookingglass.frag")); if (m_shader->isValid()) { ShaderBinder binder(m_shader); m_shader->setUniform("u_textureSize", QVector2D(screenSize.width(), screenSize.height())); } else { qCCritical(KWINEFFECTS) << "The shader failed to load!"; return false; } m_vbo = new GLVertexBuffer(GLVertexBuffer::Static); QVector verts; QVector texcoords; texcoords << screenSize.width() << 0.0; verts << screenSize.width() << 0.0; texcoords << 0.0 << 0.0; verts << 0.0 << 0.0; texcoords << 0.0 << screenSize.height(); verts << 0.0 << screenSize.height(); texcoords << 0.0 << screenSize.height(); verts << 0.0 << screenSize.height(); texcoords << screenSize.width() << screenSize.height(); verts << screenSize.width() << screenSize.height(); texcoords << screenSize.width() << 0.0; verts << screenSize.width() << 0.0; m_vbo->setData(6, 2, verts.constData(), texcoords.constData()); return true; } void LookingGlassEffect::toggle() { if (target_zoom == 1.0f) { target_zoom = 2.0f; if (!polling) { polling = true; effects->startMousePolling(); } m_enabled = true; } else { target_zoom = 1.0f; if (polling) { polling = false; effects->stopMousePolling(); } if (zoom == target_zoom) { m_enabled = false; } } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } void LookingGlassEffect::zoomIn() { target_zoom = qMin(7.0, target_zoom + 0.5); m_enabled = true; if (!polling) { polling = true; effects->startMousePolling(); } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } void LookingGlassEffect::zoomOut() { target_zoom -= 0.5; if (target_zoom < 1) { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } if (zoom == target_zoom) { m_enabled = false; } } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } void LookingGlassEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (zoom != target_zoom) { double diff = time / animationTime(500.0); if (target_zoom > zoom) zoom = qMin(zoom * qMax(1.0 + diff, 1.2), target_zoom); else zoom = qMax(zoom * qMin(1.0 - diff, 0.8), target_zoom); qCDebug(KWINEFFECTS) << "zoom is now " << zoom; radius = qBound((double)initialradius, initialradius * zoom, 3.5 * initialradius); if (zoom <= 1.0f) { m_enabled = false; } effects->addRepaint(cursorPos().x() - radius, cursorPos().y() - radius, 2 * radius, 2 * radius); } if (m_valid && m_enabled) { data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; // Start rendering to texture GLRenderTarget::pushRenderTarget(m_fbo); } effects->prePaintScreen(data, time); } void LookingGlassEffect::slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (pos != old && m_enabled) { effects->addRepaint(pos.x() - radius, pos.y() - radius, 2 * radius, 2 * radius); effects->addRepaint(old.x() - radius, old.y() - radius, 2 * radius, 2 * radius); } } void LookingGlassEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data) { // Call the next effect. effects->paintScreen(mask, region, data); if (m_valid && m_enabled) { // Disable render texture GLRenderTarget* target = GLRenderTarget::popRenderTarget(); assert(target == m_fbo); Q_UNUSED(target); m_texture->bind(); m_texture->generateMipmaps(); // Use the shader ShaderBinder binder(m_shader); m_shader->setUniform("u_zoom", (float)zoom); m_shader->setUniform("u_radius", (float)radius); m_shader->setUniform("u_cursor", QVector2D(cursorPos().x(), cursorPos().y())); m_shader->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); m_vbo->render(GL_TRIANGLES); m_texture->unbind(); } } bool LookingGlassEffect::isActive() const { return m_valid && m_enabled; } } // namespace diff --git a/effects/lookingglass/lookingglass_config.cpp b/effects/lookingglass/lookingglass_config.cpp index 7b2de554d..494083dc4 100644 --- a/effects/lookingglass/lookingglass_config.cpp +++ b/effects/lookingglass/lookingglass_config.cpp @@ -1,119 +1,119 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Christian Nitschkowski This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "lookingglass_config.h" // KConfigSkeleton #include "lookingglassconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(LookingGlassEffectConfigFactory, "lookingglass_config.json", registerPlugin();) namespace KWin { LookingGlassEffectConfigForm::LookingGlassEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } LookingGlassEffectConfig::LookingGlassEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("lookingglass")), parent, args) { m_ui = new LookingGlassEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); LookingGlassConfig::instance(KWIN_CONFIG); addConfig(LookingGlassConfig::self(), m_ui); - connect(m_ui->editor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(m_ui->editor, &KShortcutsEditor::keyChange, this, qOverload<>(&LookingGlassEffectConfig::changed)); // Shortcut config. The shortcut belongs to the component "kwin"! m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); m_actionCollection->setConfigGroup(QStringLiteral("LookingGlass")); m_actionCollection->setConfigGlobal(true); QAction* a; a = m_actionCollection->addAction(KStandardAction::ZoomIn); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); a = m_actionCollection->addAction(KStandardAction::ZoomOut); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); a = m_actionCollection->addAction(KStandardAction::ActualSize); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); m_ui->editor->addCollection(m_actionCollection); } LookingGlassEffectConfig::~LookingGlassEffectConfig() { // Undo (only) unsaved changes to global key shortcuts m_ui->editor->undoChanges(); } void LookingGlassEffectConfig::save() { qDebug() << "Saving config of LookingGlass" ; KCModule::save(); m_ui->editor->save(); // undo() will restore to this state from now on OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("lookingglass")); } void LookingGlassEffectConfig::defaults() { m_ui->editor->allDefault(); KCModule::defaults(); } } // namespace #include "lookingglass_config.moc" diff --git a/effects/magiclamp/magiclamp.cpp b/effects/magiclamp/magiclamp.cpp index e3821560d..76513b710 100644 --- a/effects/magiclamp/magiclamp.cpp +++ b/effects/magiclamp/magiclamp.cpp @@ -1,374 +1,374 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ // based on minimize animation by Rivo Laks #include "magiclamp.h" // KConfigSkeleton #include "magiclampconfig.h" namespace KWin { MagicLampEffect::MagicLampEffect() { initConfig(); reconfigure(ReconfigureAll); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowMinimized(KWin::EffectWindow*)), this, SLOT(slotWindowMinimized(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowUnminimized(KWin::EffectWindow*)), this, SLOT(slotWindowUnminimized(KWin::EffectWindow*))); + connect(effects, &EffectsHandler::windowDeleted, this, &MagicLampEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::windowMinimized, this, &MagicLampEffect::slotWindowMinimized); + connect(effects, &EffectsHandler::windowUnminimized, this, &MagicLampEffect::slotWindowUnminimized); } bool MagicLampEffect::supported() { return effects->isOpenGLCompositing() && effects->animationsSupported(); } void MagicLampEffect::reconfigure(ReconfigureFlags) { MagicLampConfig::self()->read(); // TODO: Rename animationDuration to duration so we can use // animationTime(250). const int d = MagicLampConfig::animationDuration() != 0 ? MagicLampConfig::animationDuration() : 250; m_duration = std::chrono::milliseconds(static_cast(animationTime(d))); } void MagicLampEffect::prePaintScreen(ScreenPrePaintData& data, int time) { const std::chrono::milliseconds delta(time); auto animationIt = m_animations.begin(); while (animationIt != m_animations.end()) { (*animationIt).update(delta); ++animationIt; } // We need to mark the screen windows as transformed. Otherwise the // whole screen won't be repainted, resulting in artefacts. data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; effects->prePaintScreen(data, time); } void MagicLampEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { // Schedule window for transformation if the animation is still in // progress if (m_animations.contains(w)) { // We'll transform this window data.setTransformed(); data.quads = data.quads.makeGrid(40); w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE); } effects->prePaintWindow(w, data, time); } void MagicLampEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { auto animationIt = m_animations.constFind(w); if (animationIt != m_animations.constEnd()) { // 0 = not minimized, 1 = fully minimized const qreal progress = (*animationIt).value(); QRect geo = w->geometry(); QRect icon = w->iconGeometry(); IconPosition position = Top; // If there's no icon geometry, minimize to the center of the screen if (!icon.isValid()) { QRect extG = geo; QPoint pt = cursorPos(); // focussing inside the window is no good, leads to ugly artefacts, find nearest border if (extG.contains(pt)) { const int d[2][2] = { {pt.x() - extG.x(), extG.right() - pt.x()}, {pt.y() - extG.y(), extG.bottom() - pt.y()} }; int di = d[1][0]; position = Top; if (d[0][0] < di) { di = d[0][0]; position = Left; } if (d[1][1] < di) { di = d[1][1]; position = Bottom; } if (d[0][1] < di) position = Right; switch(position) { case Top: pt.setY(extG.y()); break; case Left: pt.setX(extG.x()); break; case Bottom: pt.setY(extG.bottom()); break; case Right: pt.setX(extG.right()); break; } } else { if (pt.y() < geo.y()) position = Top; else if (pt.x() < geo.x()) position = Left; else if (pt.y() > geo.bottom()) position = Bottom; else if (pt.x() > geo.right()) position = Right; } icon = QRect(pt, QSize(0, 0)); } else { // Assumption: there is a panel containing the icon position EffectWindow* panel = NULL; foreach (EffectWindow * window, effects->stackingOrder()) { if (!window->isDock()) continue; // we have to use intersects as there seems to be a Plasma bug // the published icon geometry might be bigger than the panel if (window->geometry().intersects(icon)) { panel = window; break; } } if (panel) { // Assumption: width of horizonal panel is greater than its height and vice versa // The panel has to border one screen edge, so get it's screen area QRect panelScreen = effects->clientArea(ScreenArea, panel); if (panel->width() >= panel->height()) { // horizontal panel if (panel->y() == panelScreen.y()) position = Top; else position = Bottom; } else { // vertical panel if (panel->x() == panelScreen.x()) position = Left; else position = Right; } } else { // we did not find a panel, so it might be autohidden QRect iconScreen = effects->clientArea(ScreenArea, icon.topLeft(), effects->currentDesktop()); // as the icon geometry could be overlap a screen edge we use an intersection QRect rect = iconScreen.intersected(icon); // here we need a different assumption: icon geometry borders one screen edge // this assumption might be wrong for e.g. task applet being the only applet in panel // in this case the icon borders two screen edges // there might be a wrong animation, but not distorted if (rect.x() == iconScreen.x()) { position = Left; } else if (rect.x() + rect.width() == iconScreen.x() + iconScreen.width()) { position = Right; } else if (rect.y() == iconScreen.y()) { position = Top; } else { position = Bottom; } } } #define SANITIZE_PROGRESS if (p_progress[0] < 0)\ p_progress[0] = -p_progress[0];\ if (p_progress[1] < 0)\ p_progress[1] = -p_progress[1] #define SET_QUADS(_SET_A_, _A_, _DA_, _SET_B_, _B_, _O0_, _O1_, _O2_, _O3_) quad[0]._SET_A_((icon._A_() + icon._DA_()*(quad[0]._A_() / geo._DA_()) - (quad[0]._A_() + geo._A_()))*p_progress[_O0_] + quad[0]._A_());\ quad[1]._SET_A_((icon._A_() + icon._DA_()*(quad[1]._A_() / geo._DA_()) - (quad[1]._A_() + geo._A_()))*p_progress[_O1_] + quad[1]._A_());\ quad[2]._SET_A_((icon._A_() + icon._DA_()*(quad[2]._A_() / geo._DA_()) - (quad[2]._A_() + geo._A_()))*p_progress[_O2_] + quad[2]._A_());\ quad[3]._SET_A_((icon._A_() + icon._DA_()*(quad[3]._A_() / geo._DA_()) - (quad[3]._A_() + geo._A_()))*p_progress[_O3_] + quad[3]._A_());\ \ quad[0]._SET_B_(quad[0]._B_() + offset[_O0_]);\ quad[1]._SET_B_(quad[1]._B_() + offset[_O1_]);\ quad[2]._SET_B_(quad[2]._B_() + offset[_O2_]);\ quad[3]._SET_B_(quad[3]._B_() + offset[_O3_]) WindowQuadList newQuads; newQuads.reserve(data.quads.count()); float quadFactor; // defines how fast a quad is vertically moved: y coordinates near to window top are slowed down // it is used as quadFactor^3/windowHeight^3 // quadFactor is the y position of the quad but is changed towards becomming the window height // by that the factor becomes 1 and has no influence any more float offset[2] = {0,0}; // how far has a quad to be moved? Distance between icon and window multiplied by the progress and by the quadFactor float p_progress[2] = {0,0}; // the factor which defines how far the x values have to be changed // factor is the current moved y value diveded by the distance between icon and window WindowQuad lastQuad(WindowQuadError); lastQuad[0].setX(-1); lastQuad[0].setY(-1); lastQuad[1].setX(-1); lastQuad[1].setY(-1); lastQuad[2].setX(-1); lastQuad[2].setY(-1); if (position == Bottom) { float height_cube = float(geo.height()) * float(geo.height()) * float(geo.height()); foreach (WindowQuad quad, data.quads) { // krazy:exclude=foreach if (quad[0].y() != lastQuad[0].y() || quad[2].y() != lastQuad[2].y()) { quadFactor = quad[0].y() + (geo.height() - quad[0].y()) * progress; offset[0] = (icon.y() + quad[0].y() - geo.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube); quadFactor = quad[2].y() + (geo.height() - quad[2].y()) * progress; offset[1] = (icon.y() + quad[2].y() - geo.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube); p_progress[1] = qMin(offset[1] / (icon.y() + icon.height() - geo.y() - float(quad[2].y())), 1.0f); p_progress[0] = qMin(offset[0] / (icon.y() + icon.height() - geo.y() - float(quad[0].y())), 1.0f); } else lastQuad = quad; SANITIZE_PROGRESS; // x values are moved towards the center of the icon SET_QUADS(setX, x, width, setY, y, 0,0,1,1); newQuads.append(quad); } } else if (position == Top) { float height_cube = float(geo.height()) * float(geo.height()) * float(geo.height()); foreach (WindowQuad quad, data.quads) { // krazy:exclude=foreach if (quad[0].y() != lastQuad[0].y() || quad[2].y() != lastQuad[2].y()) { quadFactor = geo.height() - quad[0].y() + (quad[0].y()) * progress; offset[0] = (geo.y() - icon.height() + geo.height() + quad[0].y() - icon.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube); quadFactor = geo.height() - quad[2].y() + (quad[2].y()) * progress; offset[1] = (geo.y() - icon.height() + geo.height() + quad[2].y() - icon.y()) * progress * ((quadFactor * quadFactor * quadFactor) / height_cube); p_progress[0] = qMin(offset[0] / (geo.y() - icon.height() + geo.height() - icon.y() - float(geo.height() - quad[0].y())), 1.0f); p_progress[1] = qMin(offset[1] / (geo.y() - icon.height() + geo.height() - icon.y() - float(geo.height() - quad[2].y())), 1.0f); } else lastQuad = quad; offset[0] = -offset[0]; offset[1] = -offset[1]; SANITIZE_PROGRESS; // x values are moved towards the center of the icon SET_QUADS(setX, x, width, setY, y, 0,0,1,1); newQuads.append(quad); } } else if (position == Left) { float width_cube = float(geo.width()) * float(geo.width()) * float(geo.width()); foreach (WindowQuad quad, data.quads) { // krazy:exclude=foreach if (quad[0].x() != lastQuad[0].x() || quad[1].x() != lastQuad[1].x()) { quadFactor = geo.width() - quad[0].x() + (quad[0].x()) * progress; offset[0] = (geo.x() - icon.width() + geo.width() + quad[0].x() - icon.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube); quadFactor = geo.width() - quad[1].x() + (quad[1].x()) * progress; offset[1] = (geo.x() - icon.width() + geo.width() + quad[1].x() - icon.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube); p_progress[0] = qMin(offset[0] / (geo.x() - icon.width() + geo.width() - icon.x() - float(geo.width() - quad[0].x())), 1.0f); p_progress[1] = qMin(offset[1] / (geo.x() - icon.width() + geo.width() - icon.x() - float(geo.width() - quad[1].x())), 1.0f); } else lastQuad = quad; offset[0] = -offset[0]; offset[1] = -offset[1]; SANITIZE_PROGRESS; // y values are moved towards the center of the icon SET_QUADS(setY, y, height, setX, x, 0,1,1,0); newQuads.append(quad); } } else if (position == Right) { float width_cube = float(geo.width()) * float(geo.width()) * float(geo.width()); foreach (WindowQuad quad, data.quads) { // krazy:exclude=foreach if (quad[0].x() != lastQuad[0].x() || quad[1].x() != lastQuad[1].x()) { quadFactor = quad[0].x() + (geo.width() - quad[0].x()) * progress; offset[0] = (icon.x() + quad[0].x() - geo.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube); quadFactor = quad[1].x() + (geo.width() - quad[1].x()) * progress; offset[1] = (icon.x() + quad[1].x() - geo.x()) * progress * ((quadFactor * quadFactor * quadFactor) / width_cube); p_progress[0] = qMin(offset[0] / (icon.x() + icon.width() - geo.x() - float(quad[0].x())), 1.0f); p_progress[1] = qMin(offset[1] / (icon.x() + icon.width() - geo.x() - float(quad[1].x())), 1.0f); } else lastQuad = quad; SANITIZE_PROGRESS; // y values are moved towards the center of the icon SET_QUADS(setY, y, height, setX, x, 0,1,1,0); newQuads.append(quad); } } data.quads = newQuads; } // Call the next effect. effects->paintWindow(w, mask, region, data); } void MagicLampEffect::postPaintScreen() { auto animationIt = m_animations.begin(); while (animationIt != m_animations.end()) { if ((*animationIt).done()) { animationIt = m_animations.erase(animationIt); } else { ++animationIt; } } effects->addRepaintFull(); // Call the next effect. effects->postPaintScreen(); } void MagicLampEffect::slotWindowDeleted(EffectWindow* w) { m_animations.remove(w); } void MagicLampEffect::slotWindowMinimized(EffectWindow* w) { if (effects->activeFullScreenEffect()) return; TimeLine &timeLine = m_animations[w]; if (timeLine.running()) { timeLine.toggleDirection(); } else { timeLine.setDirection(TimeLine::Forward); timeLine.setDuration(m_duration); timeLine.setEasingCurve(QEasingCurve::Linear); } effects->addRepaintFull(); } void MagicLampEffect::slotWindowUnminimized(EffectWindow* w) { if (effects->activeFullScreenEffect()) return; TimeLine &timeLine = m_animations[w]; if (timeLine.running()) { timeLine.toggleDirection(); } else { timeLine.setDirection(TimeLine::Backward); timeLine.setDuration(m_duration); timeLine.setEasingCurve(QEasingCurve::Linear); } effects->addRepaintFull(); } bool MagicLampEffect::isActive() const { return !m_animations.isEmpty(); } } // namespace diff --git a/effects/magnifier/magnifier.cpp b/effects/magnifier/magnifier.cpp index 22aa7fb5d..9a22b3806 100644 --- a/effects/magnifier/magnifier.cpp +++ b/effects/magnifier/magnifier.cpp @@ -1,342 +1,342 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak Copyright (C) 2007 Christian Nitschkowski Copyright (C) 2011 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "magnifier.h" // KConfigSkeleton #include "magnifierconfig.h" #include #include #include #include #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #endif #include namespace KWin { const int FRAME_WIDTH = 5; MagnifierEffect::MagnifierEffect() : zoom(1) , target_zoom(1) , polling(false) , m_texture(0) , m_fbo(0) #ifdef KWIN_HAVE_XRENDER_COMPOSITING , m_pixmap(XCB_PIXMAP_NONE) #endif { initConfig(); QAction* a; a = KStandardAction::zoomIn(this, SLOT(zoomIn()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); effects->registerGlobalShortcut(Qt::META + Qt::Key_Equal, a); a = KStandardAction::zoomOut(this, SLOT(zoomOut()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); effects->registerGlobalShortcut(Qt::META + Qt::Key_Minus, a); a = KStandardAction::actualSize(this, SLOT(toggle()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); effects->registerGlobalShortcut(Qt::META + Qt::Key_0, a); - connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); + connect(effects, &EffectsHandler::mouseChanged, this, &MagnifierEffect::slotMouseChanged); + reconfigure(ReconfigureAll); } MagnifierEffect::~MagnifierEffect() { delete m_fbo; delete m_texture; destroyPixmap(); // Save the zoom value. MagnifierConfig::setInitialZoom(target_zoom); MagnifierConfig::self()->save(); } void MagnifierEffect::destroyPixmap() { #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() != XRenderCompositing) { return; } m_picture.reset(); if (m_pixmap != XCB_PIXMAP_NONE) { xcb_free_pixmap(xcbConnection(), m_pixmap); m_pixmap = XCB_PIXMAP_NONE; } #endif } bool MagnifierEffect::supported() { return effects->compositingType() == XRenderCompositing || (effects->isOpenGLCompositing() && GLRenderTarget::blitSupported()); } void MagnifierEffect::reconfigure(ReconfigureFlags) { MagnifierConfig::self()->read(); int width, height; width = MagnifierConfig::width(); height = MagnifierConfig::height(); magnifier_size = QSize(width, height); // Load the saved zoom value. target_zoom = MagnifierConfig::initialZoom(); if (target_zoom != zoom) toggle(); } void MagnifierEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (zoom != target_zoom) { double diff = time / animationTime(500.0); if (target_zoom > zoom) zoom = qMin(zoom * qMax(1 + diff, 1.2), target_zoom); else { zoom = qMax(zoom * qMin(1 - diff, 0.8), target_zoom); if (zoom == 1.0) { // zoom ended - delete FBO and texture delete m_fbo; delete m_texture; m_fbo = NULL; m_texture = NULL; destroyPixmap(); } } } effects->prePaintScreen(data, time); if (zoom != 1.0) data.paint |= magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH); } void MagnifierEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); // paint normal screen if (zoom != 1.0) { // get the right area from the current rendered screen const QRect area = magnifierArea(); const QPoint cursor = cursorPos(); QRect srcArea(cursor.x() - (double)area.width() / (zoom*2), cursor.y() - (double)area.height() / (zoom*2), (double)area.width() / zoom, (double)area.height() / zoom); if (effects->isOpenGLCompositing()) { m_fbo->blitFromFramebuffer(srcArea); // paint magnifier m_texture->bind(); auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); QMatrix4x4 mvp; const QSize size = effects->virtualScreenSize(); mvp.ortho(0, size.width(), size.height(), 0, 0, 65535); mvp.translate(area.x(), area.y()); s->setUniform(GLShader::ModelViewProjectionMatrix, mvp); m_texture->render(infiniteRegion(), area); ShaderManager::instance()->popShader(); m_texture->unbind(); QVector verts; GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setColor(QColor(0, 0, 0)); const QRectF areaF = area; // top frame verts << areaF.right() + FRAME_WIDTH << areaF.top() - FRAME_WIDTH; verts << areaF.left() - FRAME_WIDTH << areaF.top() - FRAME_WIDTH; verts << areaF.left() - FRAME_WIDTH << areaF.top(); verts << areaF.left() - FRAME_WIDTH << areaF.top(); verts << areaF.right() + FRAME_WIDTH << areaF.top(); verts << areaF.right() + FRAME_WIDTH << areaF.top() - FRAME_WIDTH; // left frame verts << areaF.left() << areaF.top() - FRAME_WIDTH; verts << areaF.left() - FRAME_WIDTH << areaF.top() - FRAME_WIDTH; verts << areaF.left() - FRAME_WIDTH << areaF.bottom() + FRAME_WIDTH; verts << areaF.left() - FRAME_WIDTH << areaF.bottom() + FRAME_WIDTH; verts << areaF.left() << areaF.bottom() + FRAME_WIDTH; verts << areaF.left() << areaF.top() - FRAME_WIDTH; // right frame verts << areaF.right() + FRAME_WIDTH << areaF.top() - FRAME_WIDTH; verts << areaF.right() << areaF.top() - FRAME_WIDTH; verts << areaF.right() << areaF.bottom() + FRAME_WIDTH; verts << areaF.right() << areaF.bottom() + FRAME_WIDTH; verts << areaF.right() + FRAME_WIDTH << areaF.bottom() + FRAME_WIDTH; verts << areaF.right() + FRAME_WIDTH << areaF.top() - FRAME_WIDTH; // bottom frame verts << areaF.right() + FRAME_WIDTH << areaF.bottom(); verts << areaF.left() - FRAME_WIDTH << areaF.bottom(); verts << areaF.left() - FRAME_WIDTH << areaF.bottom() + FRAME_WIDTH; verts << areaF.left() - FRAME_WIDTH << areaF.bottom() + FRAME_WIDTH; verts << areaF.right() + FRAME_WIDTH << areaF.bottom() + FRAME_WIDTH; verts << areaF.right() + FRAME_WIDTH << areaF.bottom(); vbo->setData(verts.size() / 2, 2, verts.constData(), NULL); ShaderBinder binder(ShaderTrait::UniformColor); binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); vbo->render(GL_TRIANGLES); } if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (m_pixmap == XCB_PIXMAP_NONE || m_pixmapSize != srcArea.size()) { destroyPixmap(); m_pixmap = xcb_generate_id(xcbConnection()); m_pixmapSize = srcArea.size(); xcb_create_pixmap(xcbConnection(), 32, m_pixmap, x11RootWindow(), m_pixmapSize.width(), m_pixmapSize.height()); m_picture.reset(new XRenderPicture(m_pixmap, 32)); } #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) static const xcb_render_transform_t identity = { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; static xcb_render_transform_t xform = { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; xcb_render_composite(xcbConnection(), XCB_RENDER_PICT_OP_SRC, effects->xrenderBufferPicture(), 0, *m_picture, srcArea.x(), srcArea.y(), 0, 0, 0, 0, srcArea.width(), srcArea.height()); xcb_flush(xcbConnection()); xform.matrix11 = DOUBLE_TO_FIXED(1.0/zoom); xform.matrix22 = DOUBLE_TO_FIXED(1.0/zoom); #undef DOUBLE_TO_FIXED xcb_render_set_picture_transform(xcbConnection(), *m_picture, xform); xcb_render_set_picture_filter(xcbConnection(), *m_picture, 4, const_cast("good"), 0, NULL); xcb_render_composite(xcbConnection(), XCB_RENDER_PICT_OP_SRC, *m_picture, 0, effects->xrenderBufferPicture(), 0, 0, 0, 0, area.x(), area.y(), area.width(), area.height() ); xcb_render_set_picture_filter(xcbConnection(), *m_picture, 4, const_cast("fast"), 0, NULL); xcb_render_set_picture_transform(xcbConnection(), *m_picture, identity); const xcb_rectangle_t rects[4] = { { int16_t(area.x()+FRAME_WIDTH), int16_t(area.y()), uint16_t(area.width()-FRAME_WIDTH), uint16_t(FRAME_WIDTH)}, { int16_t(area.right()-FRAME_WIDTH), int16_t(area.y()+FRAME_WIDTH), uint16_t(FRAME_WIDTH), uint16_t(area.height()-FRAME_WIDTH)}, { int16_t(area.x()), int16_t(area.bottom()-FRAME_WIDTH), uint16_t(area.width()-FRAME_WIDTH), uint16_t(FRAME_WIDTH)}, { int16_t(area.x()), int16_t(area.y()), uint16_t(FRAME_WIDTH), uint16_t(area.height()-FRAME_WIDTH)} }; xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_SRC, effects->xrenderBufferPicture(), preMultiply(QColor(0,0,0,255)), 4, rects); #endif } } } void MagnifierEffect::postPaintScreen() { if (zoom != target_zoom) { QRect framedarea = magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH); effects->addRepaint(framedarea); } effects->postPaintScreen(); } QRect MagnifierEffect::magnifierArea(QPoint pos) const { return QRect(pos.x() - magnifier_size.width() / 2, pos.y() - magnifier_size.height() / 2, magnifier_size.width(), magnifier_size.height()); } void MagnifierEffect::zoomIn() { target_zoom *= 1.2; if (!polling) { polling = true; effects->startMousePolling(); } if (effects->isOpenGLCompositing() && !m_texture) { effects->makeOpenGLContextCurrent(); m_texture = new GLTexture(GL_RGBA8, magnifier_size.width(), magnifier_size.height()); m_texture->setYInverted(false); m_fbo = new GLRenderTarget(*m_texture); } effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH)); } void MagnifierEffect::zoomOut() { target_zoom /= 1.2; if (target_zoom <= 1) { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } if (zoom == target_zoom) { effects->makeOpenGLContextCurrent(); delete m_fbo; delete m_texture; m_fbo = NULL; m_texture = NULL; destroyPixmap(); } } effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH)); } void MagnifierEffect::toggle() { if (zoom == 1.0) { if (target_zoom == 1.0) { target_zoom = 2; } if (!polling) { polling = true; effects->startMousePolling(); } if (effects->isOpenGLCompositing() && !m_texture) { effects->makeOpenGLContextCurrent(); m_texture = new GLTexture(GL_RGBA8, magnifier_size.width(), magnifier_size.height()); m_texture->setYInverted(false); m_fbo = new GLRenderTarget(*m_texture); } } else { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } } effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH)); } void MagnifierEffect::slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (pos != old && zoom != 1) // need full repaint as we might lose some change events on fast mouse movements // see Bug 187658 effects->addRepaintFull(); } bool MagnifierEffect::isActive() const { return zoom != 1.0 || zoom != target_zoom; } } // namespace diff --git a/effects/magnifier/magnifier_config.cpp b/effects/magnifier/magnifier_config.cpp index 61f4ae997..93dc8c13d 100644 --- a/effects/magnifier/magnifier_config.cpp +++ b/effects/magnifier/magnifier_config.cpp @@ -1,120 +1,120 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Christian Nitschkowski This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "magnifier_config.h" // KConfigSkeleton #include "magnifierconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(MagnifierEffectConfigFactory, "magnifier_config.json", registerPlugin();) namespace KWin { MagnifierEffectConfigForm::MagnifierEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } MagnifierEffectConfig::MagnifierEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("magnifier")), parent, args) { m_ui = new MagnifierEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); MagnifierConfig::instance(KWIN_CONFIG); addConfig(MagnifierConfig::self(), m_ui); - connect(m_ui->editor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(m_ui->editor, &KShortcutsEditor::keyChange, this, qOverload<>(&MagnifierEffectConfig::changed)); // Shortcut config. The shortcut belongs to the component "kwin"! m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); m_actionCollection->setConfigGroup(QStringLiteral("Magnifier")); m_actionCollection->setConfigGlobal(true); QAction* a; a = m_actionCollection->addAction(KStandardAction::ZoomIn); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); a = m_actionCollection->addAction(KStandardAction::ZoomOut); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); a = m_actionCollection->addAction(KStandardAction::ActualSize); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); m_ui->editor->addCollection(m_actionCollection); load(); } MagnifierEffectConfig::~MagnifierEffectConfig() { // Undo (only) unsaved changes to global key shortcuts m_ui->editor->undoChanges(); } void MagnifierEffectConfig::save() { qDebug() << "Saving config of Magnifier" ; m_ui->editor->save(); // undo() will restore to this state from now on KCModule::save(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("magnifier")); } void MagnifierEffectConfig::defaults() { m_ui->editor->allDefault(); KCModule::defaults(); } } // namespace #include "magnifier_config.moc" diff --git a/effects/mouseclick/mouseclick.cpp b/effects/mouseclick/mouseclick.cpp index e15c46b03..4cf5788a4 100644 --- a/effects/mouseclick/mouseclick.cpp +++ b/effects/mouseclick/mouseclick.cpp @@ -1,391 +1,389 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012 Filip Wieladek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "mouseclick.h" // KConfigSkeleton #include "mouseclickconfig.h" #include #include #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #include #endif #include #include #include #include namespace KWin { MouseClickEffect::MouseClickEffect() { initConfig(); m_enabled = false; QAction* a = new QAction(this); a->setObjectName(QStringLiteral("ToggleMouseClick")); a->setText(i18n("Toggle Mouse Click Effect")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Asterisk); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Asterisk); effects->registerGlobalShortcut(Qt::META + Qt::Key_Asterisk, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleEnabled())); + connect(a, &QAction::triggered, this, &MouseClickEffect::toggleEnabled); reconfigure(ReconfigureAll); m_buttons[0] = new MouseButton(i18nc("Left mouse button", "Left"), Qt::LeftButton); m_buttons[1] = new MouseButton(i18nc("Middle mouse button", "Middle"), Qt::MiddleButton); m_buttons[2] = new MouseButton(i18nc("Right mouse button", "Right"), Qt::RightButton); } MouseClickEffect::~MouseClickEffect() { if (m_enabled) effects->stopMousePolling(); qDeleteAll(m_clicks); m_clicks.clear(); for (int i = 0; i < BUTTON_COUNT; ++i) { delete m_buttons[i]; } } void MouseClickEffect::reconfigure(ReconfigureFlags) { MouseClickConfig::self()->read(); m_colors[0] = MouseClickConfig::color1(); m_colors[1] = MouseClickConfig::color2(); m_colors[2] = MouseClickConfig::color3(); m_lineWidth = MouseClickConfig::lineWidth(); m_ringLife = MouseClickConfig::ringLife(); m_ringMaxSize = MouseClickConfig::ringSize(); m_ringCount = MouseClickConfig::ringCount(); m_showText = MouseClickConfig::showText(); m_font = MouseClickConfig::font(); } void MouseClickEffect::prePaintScreen(ScreenPrePaintData& data, int time) { foreach (MouseEvent* click, m_clicks) { click->m_time += time; } for (int i = 0; i < BUTTON_COUNT; ++i) { if (m_buttons[i]->m_isPressed) { m_buttons[i]->m_time += time; } } while (m_clicks.size() > 0) { MouseEvent* first = m_clicks[0]; if (first->m_time <= m_ringLife) { break; } m_clicks.pop_front(); delete first; } effects->prePaintScreen(data, time); } void MouseClickEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); paintScreenSetup(mask, region, data); foreach (const MouseEvent* click, m_clicks) { for (int i = 0; i < m_ringCount; ++i) { float alpha = computeAlpha(click, i); float size = computeRadius(click, i); if (size > 0 && alpha > 0) { QColor color = m_colors[click->m_button]; color.setAlphaF(alpha); drawCircle(color, click->m_pos.x(), click->m_pos.y(), size); } } if (m_showText && click->m_frame) { float frameAlpha = (click->m_time * 2.0f - m_ringLife) / m_ringLife; frameAlpha = frameAlpha < 0 ? 1 : -(frameAlpha * frameAlpha) + 1; click->m_frame->render(infiniteRegion(), frameAlpha, frameAlpha); } } paintScreenFinish(mask, region, data); } void MouseClickEffect::postPaintScreen() { effects->postPaintScreen(); repaint(); } float MouseClickEffect::computeRadius(const MouseEvent* click, int ring) { float ringDistance = m_ringLife / (m_ringCount * 3); if (click->m_press) { return ((click->m_time - ringDistance * ring) / m_ringLife) * m_ringMaxSize; } return ((m_ringLife - click->m_time - ringDistance * ring) / m_ringLife) * m_ringMaxSize; } float MouseClickEffect::computeAlpha(const MouseEvent* click, int ring) { float ringDistance = m_ringLife / (m_ringCount * 3); return (m_ringLife - (float)click->m_time - ringDistance * (ring)) / m_ringLife; } void MouseClickEffect::slotMouseChanged(const QPoint& pos, const QPoint&, Qt::MouseButtons buttons, Qt::MouseButtons oldButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (buttons == oldButtons) return; MouseEvent* m = NULL; int i = BUTTON_COUNT; while (--i >= 0) { MouseButton* b = m_buttons[i]; if (isPressed(b->m_button, buttons, oldButtons)) { m = new MouseEvent(i, pos, 0, createEffectFrame(pos, b->m_labelDown), true); break; } else if (isReleased(b->m_button, buttons, oldButtons) && (!b->m_isPressed || b->m_time > m_ringLife)) { // we might miss a press, thus also check !b->m_isPressed, bug #314762 m = new MouseEvent(i, pos, 0, createEffectFrame(pos, b->m_labelUp), false); break; } b->setPressed(b->m_button & buttons); } if (m) { m_clicks.append(m); } repaint(); } EffectFrame* MouseClickEffect::createEffectFrame(const QPoint& pos, const QString& text) { if (!m_showText) { return NULL; } QPoint point(pos.x() + m_ringMaxSize, pos.y()); EffectFrame* frame = effects->effectFrame(EffectFrameStyled, false, point, Qt::AlignLeft); frame->setFont(m_font); frame->setText(text); return frame; } void MouseClickEffect::repaint() { if (m_clicks.size() > 0) { QRegion dirtyRegion; const int radius = m_ringMaxSize + m_lineWidth; foreach (MouseEvent* click, m_clicks) { dirtyRegion |= QRect(click->m_pos.x() - radius, click->m_pos.y() - radius, 2*radius, 2*radius); if (click->m_frame) { // we grant the plasma style 32px padding for stuff like shadows... dirtyRegion |= click->m_frame->geometry().adjusted(-32,-32,32,32); } } effects->addRepaint(dirtyRegion); } } bool MouseClickEffect::isReleased(Qt::MouseButtons button, Qt::MouseButtons buttons, Qt::MouseButtons oldButtons) { return !(button & buttons) && (button & oldButtons); } bool MouseClickEffect::isPressed(Qt::MouseButtons button, Qt::MouseButtons buttons, Qt::MouseButtons oldButtons) { return (button & buttons) && !(button & oldButtons); } void MouseClickEffect::toggleEnabled() { m_enabled = !m_enabled; if (m_enabled) { - connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); + connect(effects, &EffectsHandler::mouseChanged, this, &MouseClickEffect::slotMouseChanged); effects->startMousePolling(); } else { - disconnect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); + disconnect(effects, &EffectsHandler::mouseChanged, this, &MouseClickEffect::slotMouseChanged); effects->stopMousePolling(); } qDeleteAll(m_clicks); m_clicks.clear(); for (int i = 0; i < BUTTON_COUNT; ++i) { m_buttons[i]->m_time = 0; m_buttons[i]->m_isPressed = false; } } bool MouseClickEffect::isActive() const { return m_enabled && (m_clicks.size() > 0); } void MouseClickEffect::drawCircle(const QColor& color, float cx, float cy, float r) { if (effects->isOpenGLCompositing()) drawCircleGl(color, cx, cy, r); if (effects->compositingType() == XRenderCompositing) drawCircleXr(color, cx, cy, r); if (effects->compositingType() == QPainterCompositing) drawCircleQPainter(color, cx, cy, r); } void MouseClickEffect::paintScreenSetup(int mask, QRegion region, ScreenPaintData& data) { if (effects->isOpenGLCompositing()) paintScreenSetupGl(mask, region, data); } void MouseClickEffect::paintScreenFinish(int mask, QRegion region, ScreenPaintData& data) { if (effects->isOpenGLCompositing()) paintScreenFinishGl(mask, region, data); } void MouseClickEffect::drawCircleGl(const QColor& color, float cx, float cy, float r) { static const int num_segments = 80; static const float theta = 2 * 3.1415926 / float(num_segments); static const float c = cosf(theta); //precalculate the sine and cosine static const float s = sinf(theta); float t; float x = r;//we start at angle = 0 float y = 0; GLVertexBuffer* vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); vbo->setColor(color); QVector verts; verts.reserve(num_segments * 2); for (int ii = 0; ii < num_segments; ++ii) { verts << x + cx << y + cy;//output vertex //apply the rotation matrix t = x; x = c * x - s * y; y = s * t + c * y; } vbo->setData(verts.size() / 2, 2, verts.data(), NULL); vbo->render(GL_LINE_LOOP); } void MouseClickEffect::drawCircleXr(const QColor& color, float cx, float cy, float r) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (r <= m_lineWidth) return; int num_segments = r+8; float theta = 2.0 * 3.1415926 / num_segments; float cos = cosf(theta); //precalculate the sine and cosine float sin = sinf(theta); float x[2] = {r, r-m_lineWidth}; float y[2] = {0, 0}; #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) QVector strip; strip.reserve(2*num_segments+2); xcb_render_pointfix_t point; point.x = DOUBLE_TO_FIXED(x[1]+cx); point.y = DOUBLE_TO_FIXED(y[1]+cy); strip << point; for (int i = 0; i < num_segments; ++i) { //apply the rotation matrix const float h[2] = {x[0], x[1]}; x[0] = cos * x[0] - sin * y[0]; x[1] = cos * x[1] - sin * y[1]; y[0] = sin * h[0] + cos * y[0]; y[1] = sin * h[1] + cos * y[1]; point.x = DOUBLE_TO_FIXED(x[0]+cx); point.y = DOUBLE_TO_FIXED(y[0]+cy); strip << point; point.x = DOUBLE_TO_FIXED(x[1]+cx); point.y = DOUBLE_TO_FIXED(y[1]+cy); strip << point; } const float h = x[0]; x[0] = cos * x[0] - sin * y[0]; y[0] = sin * h + cos * y[0]; point.x = DOUBLE_TO_FIXED(x[0]+cx); point.y = DOUBLE_TO_FIXED(y[0]+cy); strip << point; XRenderPicture fill = xRenderFill(color); xcb_render_tri_strip(xcbConnection(), XCB_RENDER_PICT_OP_OVER, fill, effects->xrenderBufferPicture(), 0, 0, 0, strip.count(), strip.constData()); #undef DOUBLE_TO_FIXED #else Q_UNUSED(color) Q_UNUSED(cx) Q_UNUSED(cy) Q_UNUSED(r) #endif } void MouseClickEffect::drawCircleQPainter(const QColor &color, float cx, float cy, float r) { QPainter *painter = effects->scenePainter(); painter->save(); painter->setPen(color); painter->drawArc(cx - r, cy - r, r * 2, r * 2, 0, 5760); painter->restore(); } void MouseClickEffect::paintScreenSetupGl(int, QRegion, ScreenPaintData &data) { GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::UniformColor); shader->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); glLineWidth(m_lineWidth); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } void MouseClickEffect::paintScreenFinishGl(int, QRegion, ScreenPaintData&) { glDisable(GL_BLEND); ShaderManager::instance()->popShader(); } } // namespace diff --git a/effects/mouseclick/mouseclick_config.cpp b/effects/mouseclick/mouseclick_config.cpp index ba516e25b..014825f3b 100644 --- a/effects/mouseclick/mouseclick_config.cpp +++ b/effects/mouseclick/mouseclick_config.cpp @@ -1,94 +1,94 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012 Filip Wieladek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "mouseclick_config.h" // KConfigSkeleton #include "mouseclickconfig.h" #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(MouseClickEffectConfigFactory, "mouseclick_config.json", registerPlugin();) namespace KWin { MouseClickEffectConfigForm::MouseClickEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } MouseClickEffectConfig::MouseClickEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("mouseclick")), parent, args) { m_ui = new MouseClickEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); - connect(m_ui->editor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(m_ui->editor, &KShortcutsEditor::keyChange, this, qOverload<>(&MouseClickEffectConfig::changed)); // Shortcut config. The shortcut belongs to the component "kwin"! m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); QAction* a = m_actionCollection->addAction(QStringLiteral("ToggleMouseClick")); a->setText(i18n("Toggle Mouse Click Effect")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Asterisk); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Asterisk); m_ui->editor->addCollection(m_actionCollection); MouseClickConfig::instance(KWIN_CONFIG); addConfig(MouseClickConfig::self(), m_ui); load(); } MouseClickEffectConfig::~MouseClickEffectConfig() { // Undo (only) unsaved changes to global key shortcuts m_ui->editor->undoChanges(); } void MouseClickEffectConfig::save() { KCModule::save(); m_ui->editor->save(); // undo() will restore to this state from now on OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("mouseclick")); } } // namespace #include "mouseclick_config.moc" diff --git a/effects/mousemark/mousemark.cpp b/effects/mousemark/mousemark.cpp index 25ddc0f9a..e969d16bf 100644 --- a/effects/mousemark/mousemark.cpp +++ b/effects/mousemark/mousemark.cpp @@ -1,295 +1,294 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2007 Christian Nitschkowski This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "mousemark.h" // KConfigSkeleton #include "mousemarkconfig.h" #include #include #include #include #include #include #include #include #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #endif namespace KWin { #define NULL_POINT (QPoint( -1, -1 )) // null point is (0,0), which is valid :-/ MouseMarkEffect::MouseMarkEffect() { initConfig(); QAction* a = new QAction(this); a->setObjectName(QStringLiteral("ClearMouseMarks")); a->setText(i18n("Clear All Mouse Marks")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::SHIFT + Qt::META + Qt::Key_F11); KGlobalAccel::self()->setShortcut(a, QList() << Qt::SHIFT + Qt::META + Qt::Key_F11); effects->registerGlobalShortcut(Qt::SHIFT + Qt::META + Qt::Key_F11, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(clear())); + connect(a, &QAction::triggered, this, &MouseMarkEffect::clear); a = new QAction(this); a->setObjectName(QStringLiteral("ClearLastMouseMark")); a->setText(i18n("Clear Last Mouse Mark")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::SHIFT + Qt::META + Qt::Key_F12); KGlobalAccel::self()->setShortcut(a, QList() << Qt::SHIFT + Qt::META + Qt::Key_F12); effects->registerGlobalShortcut(Qt::SHIFT + Qt::META + Qt::Key_F12, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(clearLast())); + connect(a, &QAction::triggered, this, &MouseMarkEffect::clearLast); - connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); - connect(effects, SIGNAL(screenLockingChanged(bool)), SLOT(screenLockingChanged(bool))); + connect(effects, &EffectsHandler::mouseChanged, this, &MouseMarkEffect::slotMouseChanged); + connect(effects, &EffectsHandler::screenLockingChanged, this, &MouseMarkEffect::screenLockingChanged); reconfigure(ReconfigureAll); arrow_start = NULL_POINT; effects->startMousePolling(); // We require it to detect activation as well } MouseMarkEffect::~MouseMarkEffect() { effects->stopMousePolling(); } static int width_2 = 1; void MouseMarkEffect::reconfigure(ReconfigureFlags) { MouseMarkConfig::self()->read(); width = MouseMarkConfig::lineWidth(); width_2 = width / 2; color = MouseMarkConfig::color(); color.setAlphaF(1.0); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING void MouseMarkEffect::addRect(const QPoint &p1, const QPoint &p2, xcb_rectangle_t *r, xcb_render_color_t *c) { r->x = qMin(p1.x(), p2.x()) - width_2; r->y = qMin(p1.y(), p2.y()) - width_2; r->width = qAbs(p1.x()-p2.x()) + 1 + width_2; r->height = qAbs(p1.y()-p2.y()) + 1 + width_2; // fast move -> large rect, tess... interpolate a line if (r->width > 3*width/2 && r->height > 3*width/2) { const int n = sqrt(r->width*r->width + r->height*r->height) / width; xcb_rectangle_t *rects = new xcb_rectangle_t[n-1]; const int w = p1.x() < p2.x() ? r->width : -r->width; const int h = p1.y() < p2.y() ? r->height : -r->height; for (int i = 1; i < n; ++i) { rects[i-1].x = p1.x() + i*w/n; rects[i-1].y = p1.y() + i*h/n; rects[i-1].width = rects[i-1].height = width; } xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_SRC, effects->xrenderBufferPicture(), *c, n - 1, rects); delete [] rects; r->x = p1.x(); r->y = p1.y(); r->width = r->height = width; } } #endif void MouseMarkEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); // paint normal screen if (marks.isEmpty() && drawing.isEmpty()) return; if ( effects->isOpenGLCompositing()) { if (!GLPlatform::instance()->isGLES()) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); } glLineWidth(width); GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); vbo->setColor(color); ShaderBinder binder(ShaderTrait::UniformColor); binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); QVector verts; foreach (const Mark & mark, marks) { verts.clear(); verts.reserve(mark.size() * 2); foreach (const QPoint & p, mark) { verts << p.x() << p.y(); } vbo->setData(verts.size() / 2, 2, verts.data(), NULL); vbo->render(GL_LINE_STRIP); } if (!drawing.isEmpty()) { verts.clear(); verts.reserve(drawing.size() * 2); foreach (const QPoint & p, drawing) { verts << p.x() << p.y(); } vbo->setData(verts.size() / 2, 2, verts.data(), NULL); vbo->render(GL_LINE_STRIP); } glLineWidth(1.0); if (!GLPlatform::instance()->isGLES()) { glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); } } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if ( effects->compositingType() == XRenderCompositing) { xcb_render_color_t c = preMultiply(color); for (int i = 0; i < marks.count(); ++i) { const int n = marks[i].count() - 1; if (n > 0) { xcb_rectangle_t *rects = new xcb_rectangle_t[n]; for (int j = 0; j < marks[i].count()-1; ++j) { addRect(marks[i][j], marks[i][j+1], &rects[j], &c); } xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_SRC, effects->xrenderBufferPicture(), c, n, rects); delete [] rects; } } const int n = drawing.count() - 1; if (n > 0) { xcb_rectangle_t *rects = new xcb_rectangle_t[n]; for (int i = 0; i < n; ++i) addRect(drawing[i], drawing[i+1], &rects[i], &c); xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_SRC, effects->xrenderBufferPicture(), c, n, rects); delete [] rects; } } #endif if (effects->compositingType() == QPainterCompositing) { QPainter *painter = effects->scenePainter(); painter->save(); QPen pen(color); pen.setWidth(width); painter->setPen(pen); foreach (const Mark &mark, marks) { drawMark(painter, mark); } drawMark(painter, drawing); painter->restore(); } } void MouseMarkEffect::drawMark(QPainter *painter, const Mark &mark) { if (mark.count() <= 1) { return; } for (int i = 0; i < mark.count() - 1; ++i) { painter->drawLine(mark[i], mark[i+1]); } } void MouseMarkEffect::slotMouseChanged(const QPoint& pos, const QPoint&, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers) { if (modifiers == (Qt::META | Qt::SHIFT | Qt::CTRL)) { // start/finish arrow if (arrow_start != NULL_POINT) { marks.append(createArrow(arrow_start, pos)); arrow_start = NULL_POINT; effects->addRepaintFull(); return; } else arrow_start = pos; } if (arrow_start != NULL_POINT) return; // TODO the shortcuts now trigger this right before they're activated if (modifiers == (Qt::META | Qt::SHIFT)) { // activated if (drawing.isEmpty()) drawing.append(pos); if (drawing.last() == pos) return; QPoint pos2 = drawing.last(); drawing.append(pos); QRect repaint = QRect(qMin(pos.x(), pos2.x()), qMin(pos.y(), pos2.y()), qMax(pos.x(), pos2.x()), qMax(pos.y(), pos2.y())); repaint.adjust(-width, -width, width, width); effects->addRepaint(repaint); } else if (!drawing.isEmpty()) { marks.append(drawing); drawing.clear(); } } void MouseMarkEffect::clear() { drawing.clear(); marks.clear(); effects->addRepaintFull(); } void MouseMarkEffect::clearLast() { if (arrow_start != NULL_POINT) { arrow_start = NULL_POINT; } else if (!drawing.isEmpty()) { drawing.clear(); effects->addRepaintFull(); } else if (!marks.isEmpty()) { marks.pop_back(); effects->addRepaintFull(); } } MouseMarkEffect::Mark MouseMarkEffect::createArrow(QPoint arrow_start, QPoint arrow_end) { Mark ret; double angle = atan2((double)(arrow_end.y() - arrow_start.y()), (double)(arrow_end.x() - arrow_start.x())); ret += arrow_start + QPoint(50 * cos(angle + M_PI / 6), 50 * sin(angle + M_PI / 6)); // right one ret += arrow_start; ret += arrow_end; ret += arrow_start; // it's connected lines, so go back with the middle one ret += arrow_start + QPoint(50 * cos(angle - M_PI / 6), 50 * sin(angle - M_PI / 6)); // left one return ret; } void MouseMarkEffect::screenLockingChanged(bool locked) { if (!marks.isEmpty() || !drawing.isEmpty()) { effects->addRepaintFull(); } // disable mouse polling while screen is locked. if (locked) { effects->stopMousePolling(); } else { effects->startMousePolling(); } } bool MouseMarkEffect::isActive() const { return (!marks.isEmpty() || !drawing.isEmpty()) && !effects->isScreenLocked(); } } // namespace diff --git a/effects/presentwindows/presentwindows.cpp b/effects/presentwindows/presentwindows.cpp index 3a6bc6a46..cfce07281 100644 --- a/effects/presentwindows/presentwindows.cpp +++ b/effects/presentwindows/presentwindows.cpp @@ -1,2067 +1,2067 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2008 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "presentwindows.h" //KConfigSkeleton #include "presentwindowsconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { PresentWindowsEffect::PresentWindowsEffect() : m_proxy(this) , m_activated(false) , m_ignoreMinimized(false) , m_decalOpacity(0.0) , m_hasKeyboardGrab(false) , m_mode(ModeCurrentDesktop) , m_managerWindow(NULL) , m_needInitialSelection(false) , m_highlightedWindow(NULL) , m_filterFrame(NULL) , m_closeView(NULL) , m_closeWindow(NULL) , m_exposeAction(new QAction(this)) , m_exposeAllAction(new QAction(this)) , m_exposeClassAction(new QAction(this)) { initConfig(); auto announceSupportProperties = [this] { m_atomDesktop = effects->announceSupportProperty("_KDE_PRESENT_WINDOWS_DESKTOP", this); m_atomWindows = effects->announceSupportProperty("_KDE_PRESENT_WINDOWS_GROUP", this); }; announceSupportProperties(); connect(effects, &EffectsHandler::xcbConnectionChanged, this, announceSupportProperties); QAction* exposeAction = m_exposeAction; exposeAction->setObjectName(QStringLiteral("Expose")); exposeAction->setText(i18n("Toggle Present Windows (Current desktop)")); KGlobalAccel::self()->setDefaultShortcut(exposeAction, QList() << Qt::CTRL + Qt::Key_F9); KGlobalAccel::self()->setShortcut(exposeAction, QList() << Qt::CTRL + Qt::Key_F9); shortcut = KGlobalAccel::self()->shortcut(exposeAction); effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F9, exposeAction); - connect(exposeAction, SIGNAL(triggered(bool)), this, SLOT(toggleActive())); + connect(exposeAction, &QAction::triggered, this, &PresentWindowsEffect::toggleActive); QAction* exposeAllAction = m_exposeAllAction; exposeAllAction->setObjectName(QStringLiteral("ExposeAll")); exposeAllAction->setText(i18n("Toggle Present Windows (All desktops)")); KGlobalAccel::self()->setDefaultShortcut(exposeAllAction, QList() << Qt::CTRL + Qt::Key_F10 << Qt::Key_LaunchC); KGlobalAccel::self()->setShortcut(exposeAllAction, QList() << Qt::CTRL + Qt::Key_F10 << Qt::Key_LaunchC); shortcutAll = KGlobalAccel::self()->shortcut(exposeAllAction); effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F10, exposeAllAction); effects->registerTouchpadSwipeShortcut(SwipeDirection::Down, exposeAllAction); - connect(exposeAllAction, SIGNAL(triggered(bool)), this, SLOT(toggleActiveAllDesktops())); + connect(exposeAllAction, &QAction::triggered, this, &PresentWindowsEffect::toggleActiveAllDesktops); QAction* exposeClassAction = m_exposeClassAction; exposeClassAction->setObjectName(QStringLiteral("ExposeClass")); exposeClassAction->setText(i18n("Toggle Present Windows (Window class)")); KGlobalAccel::self()->setDefaultShortcut(exposeClassAction, QList() << Qt::CTRL + Qt::Key_F7); KGlobalAccel::self()->setShortcut(exposeClassAction, QList() << Qt::CTRL + Qt::Key_F7); effects->registerGlobalShortcut(Qt::CTRL + Qt::Key_F7, exposeClassAction); - connect(exposeClassAction, SIGNAL(triggered(bool)), this, SLOT(toggleActiveClass())); + connect(exposeClassAction, &QAction::triggered, this, &PresentWindowsEffect::toggleActiveClass); shortcutClass = KGlobalAccel::self()->shortcut(exposeClassAction); connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &PresentWindowsEffect::globalShortcutChanged); reconfigure(ReconfigureAll); - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), this, SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowGeometryShapeChanged(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowGeometryShapeChanged(KWin::EffectWindow*,QRect))); - connect(effects, SIGNAL(propertyNotify(KWin::EffectWindow*,long)), this, SLOT(slotPropertyNotify(KWin::EffectWindow*,long))); + connect(effects, &EffectsHandler::windowAdded, this, &PresentWindowsEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowClosed, this, &PresentWindowsEffect::slotWindowClosed); + connect(effects, &EffectsHandler::windowDeleted, this, &PresentWindowsEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::windowGeometryShapeChanged, this, &PresentWindowsEffect::slotWindowGeometryShapeChanged); + connect(effects, &EffectsHandler::propertyNotify, this, &PresentWindowsEffect::slotPropertyNotify); connect(effects, &EffectsHandler::numberScreensChanged, this, [this] { if (isActive()) reCreateGrids(); } ); } PresentWindowsEffect::~PresentWindowsEffect() { delete m_filterFrame; delete m_closeView; } void PresentWindowsEffect::reconfigure(ReconfigureFlags) { PresentWindowsConfig::self()->read(); foreach (ElectricBorder border, m_borderActivate) { effects->unreserveElectricBorder(border, this); } foreach (ElectricBorder border, m_borderActivateAll) { effects->unreserveElectricBorder(border, this); } m_borderActivate.clear(); m_borderActivateAll.clear(); foreach (int i, PresentWindowsConfig::borderActivate()) { m_borderActivate.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } foreach (int i, PresentWindowsConfig::borderActivateAll()) { m_borderActivateAll.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } foreach (int i, PresentWindowsConfig::borderActivateClass()) { m_borderActivateClass.append(ElectricBorder(i)); effects->reserveElectricBorder(ElectricBorder(i), this); } m_layoutMode = PresentWindowsConfig::layoutMode(); m_showCaptions = PresentWindowsConfig::drawWindowCaptions(); m_showIcons = PresentWindowsConfig::drawWindowIcons(); m_doNotCloseWindows = !PresentWindowsConfig::allowClosingWindows(); if (m_doNotCloseWindows) { delete m_closeView; m_closeView = nullptr; m_closeWindow = nullptr; } m_ignoreMinimized = PresentWindowsConfig::ignoreMinimized(); m_accuracy = PresentWindowsConfig::accuracy() * 20; m_fillGaps = PresentWindowsConfig::fillGaps(); m_fadeDuration = double(animationTime(150)); m_showPanel = PresentWindowsConfig::showPanel(); m_leftButtonWindow = (WindowMouseAction)PresentWindowsConfig::leftButtonWindow(); m_middleButtonWindow = (WindowMouseAction)PresentWindowsConfig::middleButtonWindow(); m_rightButtonWindow = (WindowMouseAction)PresentWindowsConfig::rightButtonWindow(); m_leftButtonDesktop = (DesktopMouseAction)PresentWindowsConfig::leftButtonDesktop(); m_middleButtonDesktop = (DesktopMouseAction)PresentWindowsConfig::middleButtonDesktop(); m_rightButtonDesktop = (DesktopMouseAction)PresentWindowsConfig::rightButtonDesktop(); // touch screen edges const QVector relevantBorders{ElectricLeft, ElectricTop, ElectricRight, ElectricBottom}; for (auto e : relevantBorders) { effects->unregisterTouchBorder(e, m_exposeAction); effects->unregisterTouchBorder(e, m_exposeAllAction); effects->unregisterTouchBorder(e, m_exposeClassAction); } auto touchEdge = [&relevantBorders] (const QList touchBorders, QAction *action) { for (int i : touchBorders) { if (!relevantBorders.contains(ElectricBorder(i))) { continue; } effects->registerTouchBorder(ElectricBorder(i), action); } }; touchEdge(PresentWindowsConfig::touchBorderActivate(), m_exposeAction); touchEdge(PresentWindowsConfig::touchBorderActivateAll(), m_exposeAllAction); touchEdge(PresentWindowsConfig::touchBorderActivateClass(), m_exposeClassAction); } void* PresentWindowsEffect::proxy() { return &m_proxy; } void PresentWindowsEffect::toggleActiveClass() { if (!m_activated) { if (!effects->activeWindow()) return; m_mode = ModeWindowClass; m_class = effects->activeWindow()->windowClass(); } setActive(!m_activated); } //----------------------------------------------------------------------------- // Screen painting void PresentWindowsEffect::prePaintScreen(ScreenPrePaintData &data, int time) { m_motionManager.calculate(time); // We need to mark the screen as having been transformed otherwise there will be no repainting if (m_activated || m_motionManager.managingWindows()) data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; if (m_activated) m_decalOpacity = qMin(1.0, m_decalOpacity + time / m_fadeDuration); else m_decalOpacity = qMax(0.0, m_decalOpacity - time / m_fadeDuration); effects->prePaintScreen(data, time); } void PresentWindowsEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data) { effects->paintScreen(mask, region, data); // Display the filter box if (!m_windowFilter.isEmpty()) m_filterFrame->render(region); } void PresentWindowsEffect::postPaintScreen() { if (m_motionManager.areWindowsMoving()) effects->addRepaintFull(); else if (!m_activated && m_motionManager.managingWindows() && !(m_closeWindow && m_closeView->isVisible())) { // We have finished moving them back, stop processing m_motionManager.unmanageAll(); DataHash::iterator i = m_windowData.begin(); while (i != m_windowData.end()) { delete i.value().textFrame; delete i.value().iconFrame; ++i; } m_windowData.clear(); foreach (EffectWindow * w, effects->stackingOrder()) { w->setData(WindowForceBlurRole, QVariant()); w->setData(WindowForceBackgroundContrastRole, QVariant()); } effects->setActiveFullScreenEffect(NULL); effects->addRepaintFull(); } else if (m_activated && m_needInitialSelection) { m_needInitialSelection = false; QMouseEvent me(QEvent::MouseMove, cursorPos(), Qt::NoButton, Qt::NoButton, Qt::NoModifier); windowInputMouseEvent(&me); } // Update windows that are changing brightness or opacity DataHash::const_iterator i; for (i = m_windowData.constBegin(); i != m_windowData.constEnd(); ++i) { if (i.value().opacity > 0.0 && i.value().opacity < 1.0) i.key()->addRepaintFull(); if (i.key()->isDesktop() && !m_motionManager.isManaging(i.key())) { if (i.value().highlight != 0.3) i.key()->addRepaintFull(); } else if (i.value().highlight > 0.0 && i.value().highlight < 1.0) i.key()->addRepaintFull(); } effects->postPaintScreen(); } //----------------------------------------------------------------------------- // Window painting void PresentWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time) { // TODO: We should also check to see if any windows are fading just in case fading takes longer // than moving the windows when the effect is deactivated. if (m_activated || m_motionManager.areWindowsMoving() || m_closeWindow) { DataHash::iterator winData = m_windowData.find(w); if (winData == m_windowData.end()) { effects->prePaintWindow(w, data, time); return; } w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE); // Display always w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); if (winData->visible) w->enablePainting(EffectWindow::PAINT_DISABLED_BY_TAB_GROUP); // Calculate window's opacity // TODO: Minimized windows or windows not on the current desktop are only 75% visible? if (winData->visible) { if (winData->deleted) winData->opacity = qMax(0.0, winData->opacity - time / m_fadeDuration); else winData->opacity = qMin(/*(w->isMinimized() || !w->isOnCurrentDesktop()) ? 0.75 :*/ 1.0, winData->opacity + time / m_fadeDuration); } else winData->opacity = qMax(0.0, winData->opacity - time / m_fadeDuration); if (winData->opacity <= 0.0) { // don't disable painting for panels if show panel is set if (!(m_showPanel && w->isDock())) w->disablePainting(EffectWindow::PAINT_DISABLED); } else if (winData->opacity != 1.0) data.setTranslucent(); const bool isInMotion = m_motionManager.isManaging(w); // Calculate window's brightness if (w == m_highlightedWindow || w == m_closeWindow || !m_activated) winData->highlight = qMin(1.0, winData->highlight + time / m_fadeDuration); else if (!isInMotion && w->isDesktop()) winData->highlight = 0.3; else winData->highlight = qMax(0.0, winData->highlight - time / m_fadeDuration); // Closed windows if (winData->deleted) { data.setTranslucent(); if (winData->opacity <= 0.0 && winData->referenced) { // it's possible that another effect has referenced the window // we have to keep the window in the list to prevent flickering winData->referenced = false; w->unrefWindow(); if (w == m_closeWindow) { m_closeWindow = NULL; } } else w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE); } // desktop windows on other desktops (Plasma activity per desktop) should not be painted if (w->isDesktop() && !w->isOnCurrentDesktop()) w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP); if (isInMotion) data.setTransformed(); // We will be moving this window } effects->prePaintWindow(w, data, time); } void PresentWindowsEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { if (m_activated || m_motionManager.areWindowsMoving()) { DataHash::const_iterator winData = m_windowData.constFind(w); if (winData == m_windowData.constEnd() || (w->isDock() && m_showPanel)) { // in case the panel should be shown just display it without any changes effects->paintWindow(w, mask, region, data); return; } mask |= PAINT_WINDOW_LANCZOS; // Apply opacity and brightness data.multiplyOpacity(winData->opacity); data.multiplyBrightness(interpolate(0.40, 1.0, winData->highlight)); if (m_motionManager.isManaging(w)) { if (w->isDesktop()) { effects->paintWindow(w, mask, region, data); } m_motionManager.apply(w, data); QRect rect = m_motionManager.transformedGeometry(w).toRect(); if (m_activated && winData->highlight > 0.0) { // scale the window (interpolated by the highlight level) to at least 105% or to cover 1/16 of the screen size - yet keep it in screen bounds QRect area = effects->clientArea(FullScreenArea, w); QSizeF effSize(w->width()*data.xScale(), w->height()*data.yScale()); const float xr = area.width()/effSize.width(); const float yr = area.height()/effSize.height(); float tScale = 0.0; if (xr < yr) { tScale = qMax(xr/4.0, yr/32.0); } else { tScale = qMax(xr/32.0, yr/4.0); } if (tScale < 1.05) { tScale = 1.05; } if (effSize.width()*tScale > area.width()) tScale = area.width() / effSize.width(); if (effSize.height()*tScale > area.height()) tScale = area.height() / effSize.height(); const qreal scale = interpolate(1.0, tScale, winData->highlight); if (scale > 1.0) { if (scale < tScale) // don't use lanczos during transition mask &= ~PAINT_WINDOW_LANCZOS; const float df = (tScale-1.0f)*0.5f; int tx = qRound(rect.width()*df); int ty = qRound(rect.height()*df); QRect tRect(rect.adjusted(-tx, -ty, tx, ty)); tx = qMax(tRect.x(), area.x()) + qMin(0, area.right()-tRect.right()); ty = qMax(tRect.y(), area.y()) + qMin(0, area.bottom()-tRect.bottom()); tx = qRound((tx-rect.x())*winData->highlight); ty = qRound((ty-rect.y())*winData->highlight); rect.translate(tx,ty); rect.setWidth(rect.width()*scale); rect.setHeight(rect.height()*scale); data *= QVector2D(scale, scale); data += QPoint(tx, ty); } } if (m_motionManager.areWindowsMoving()) { mask &= ~PAINT_WINDOW_LANCZOS; } effects->paintWindow(w, mask, region, data); if (m_showIcons) { QPoint point(rect.x() + rect.width() * 0.95, rect.y() + rect.height() * 0.95); winData->iconFrame->setPosition(point); if (effects->compositingType() == KWin::OpenGL2Compositing && data.shader) { const float a = 0.9 * data.opacity() * m_decalOpacity * 0.75; data.shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } winData->iconFrame->render(region, 0.9 * data.opacity() * m_decalOpacity, 0.75); } if (m_showCaptions) { QPoint point(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); winData->textFrame->setPosition(point); if (effects->compositingType() == KWin::OpenGL2Compositing && data.shader) { const float a = 0.9 * data.opacity() * m_decalOpacity * 0.75; data.shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a)); } winData->textFrame->render(region, 0.9 * data.opacity() * m_decalOpacity, 0.75); } } else { if (w == m_closeWindow && m_closeView && !m_closeView->isVisible()) { data.setOpacity(0); } effects->paintWindow(w, mask, region, data); } } else effects->paintWindow(w, mask, region, data); } //----------------------------------------------------------------------------- // User interaction void PresentWindowsEffect::slotWindowAdded(EffectWindow *w) { if (!m_activated) return; WindowData *winData = &m_windowData[w]; winData->visible = isVisibleWindow(w); winData->opacity = 0.0; winData->highlight = 0.0; winData->textFrame = effects->effectFrame(EffectFrameUnstyled, false); QFont font; font.setBold(true); font.setPointSize(12); winData->textFrame->setFont(font); winData->iconFrame = effects->effectFrame(EffectFrameUnstyled, false); winData->iconFrame->setAlignment(Qt::AlignRight | Qt::AlignBottom); winData->iconFrame->setIcon(w->icon()); winData->iconFrame->setIconSize(QSize(32, 32)); if (isSelectableWindow(w)) { m_motionManager.manage(w); rearrangeWindows(); } if (m_closeView && w == effects->findWindow(m_closeView->winId())) { if (m_closeWindow != w) { DataHash::iterator winDataIt = m_windowData.find(m_closeWindow); if (winDataIt != m_windowData.end()) { if (winDataIt->referenced) { m_closeWindow->unrefWindow(); } m_windowData.erase(winDataIt); } } winData->visible = true; winData->highlight = 1.0; m_closeWindow = w; w->setData(WindowForceBlurRole, QVariant(true)); w->setData(WindowForceBackgroundContrastRole, QVariant(true)); } } void PresentWindowsEffect::slotWindowClosed(EffectWindow *w) { if (m_managerWindow == w) m_managerWindow = NULL; DataHash::iterator winData = m_windowData.find(w); if (winData == m_windowData.end()) return; winData->deleted = true; if (!winData->referenced) { winData->referenced = true; w->refWindow(); } if (m_highlightedWindow == w) setHighlightedWindow(findFirstWindow()); if (m_closeWindow == w) { return; // don't rearrange, get's nulled when unref'd } rearrangeWindows(); foreach (EffectWindow *w, m_motionManager.managedWindows()) { winData = m_windowData.find(w); if (winData != m_windowData.end() && !winData->deleted) return; // found one that is not deleted? then we go on } setActive(false); //else no need to keep this open } void PresentWindowsEffect::slotWindowDeleted(EffectWindow *w) { DataHash::iterator winData = m_windowData.find(w); if (winData == m_windowData.end()) return; delete winData->textFrame; delete winData->iconFrame; m_windowData.erase(winData); m_motionManager.unmanage(w); } void PresentWindowsEffect::slotWindowGeometryShapeChanged(EffectWindow* w, const QRect& old) { Q_UNUSED(old) if (!m_activated) return; if (!m_windowData.contains(w)) return; if (w != m_closeWindow) rearrangeWindows(); } bool PresentWindowsEffect::borderActivated(ElectricBorder border) { int mode = 0; if (m_borderActivate.contains(border)) mode |= 1; else if (m_borderActivateAll.contains(border)) mode |= 2; else if (m_borderActivateClass.contains(border)) mode |= 4; if (!mode) return false; if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return true; if (mode & 1) toggleActive(); else if (mode & 2) toggleActiveAllDesktops(); else if (mode & 4) toggleActiveClass(); return true; } void PresentWindowsEffect::windowInputMouseEvent(QEvent *e) { QMouseEvent* me = dynamic_cast< QMouseEvent* >(e); if (!me) { return; } if (m_closeView) { const bool contains = m_closeView->geometry().contains(me->pos()); if (!m_closeView->isVisible() && contains) { updateCloseWindow(); } if (m_closeView->isVisible()) { const QPoint widgetPos = m_closeView->mapFromGlobal(me->pos()); // const QPointF scenePos = m_closeView->mapToScene(widgetPos); QMouseEvent event(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers()); m_closeView->windowInputMouseEvent(&event); if (contains) { // filter out return; } } } inputEventUpdate(me->pos(), me->type(), me->button()); } void PresentWindowsEffect::inputEventUpdate(const QPoint &pos, QEvent::Type type, Qt::MouseButton button) { // Which window are we hovering over? Always trigger as we don't always get move events before clicking // We cannot use m_motionManager.windowAtPoint() as the window might not be visible EffectWindowList windows = m_motionManager.managedWindows(); bool hovering = false; EffectWindow *highlightCandidate = NULL; for (int i = 0; i < windows.size(); ++i) { DataHash::const_iterator winData = m_windowData.constFind(windows.at(i)); if (winData == m_windowData.constEnd()) continue; if (m_motionManager.transformedGeometry(windows.at(i)).contains(pos) && winData->visible && !winData->deleted) { hovering = true; if (windows.at(i) && m_highlightedWindow != windows.at(i)) highlightCandidate = windows.at(i); break; } } if (!hovering) setHighlightedWindow(NULL); if (m_highlightedWindow && m_motionManager.transformedGeometry(m_highlightedWindow).contains(pos)) updateCloseWindow(); else if (m_closeView) m_closeView->hide(); if (type == QEvent::MouseButtonRelease) { if (highlightCandidate) setHighlightedWindow(highlightCandidate); if (button == Qt::LeftButton) { if (hovering) { // mouse is hovering above a window - use MouseActionsWindow mouseActionWindow(m_leftButtonWindow); } else { // mouse is hovering above desktop - use MouseActionsDesktop mouseActionDesktop(m_leftButtonDesktop); } } if (button == Qt::MidButton) { if (hovering) { // mouse is hovering above a window - use MouseActionsWindow mouseActionWindow(m_middleButtonWindow); } else { // mouse is hovering above desktop - use MouseActionsDesktop mouseActionDesktop(m_middleButtonDesktop); } } if (button == Qt::RightButton) { if (hovering) { // mouse is hovering above a window - use MouseActionsWindow mouseActionWindow(m_rightButtonWindow); } else { // mouse is hovering above desktop - use MouseActionsDesktop mouseActionDesktop(m_rightButtonDesktop); } } } else if (highlightCandidate && !m_motionManager.areWindowsMoving()) setHighlightedWindow(highlightCandidate); } bool PresentWindowsEffect::touchDown(quint32 id, const QPointF &pos, quint32 time) { Q_UNUSED(time) if (!m_activated) { return false; } // only if we don't track a touch id yet if (!m_touch.active) { m_touch.active = true; m_touch.id = id; inputEventUpdate(pos.toPoint()); } return true; } bool PresentWindowsEffect::touchMotion(quint32 id, const QPointF &pos, quint32 time) { Q_UNUSED(id) Q_UNUSED(time) if (!m_activated) { return false; } if (m_touch.active && m_touch.id == id) { // only update for the touch id we track inputEventUpdate(pos.toPoint()); } return true; } bool PresentWindowsEffect::touchUp(quint32 id, quint32 time) { Q_UNUSED(id) Q_UNUSED(time) if (!m_activated) { return false; } if (m_touch.active && m_touch.id == id) { m_touch.active = false; m_touch.id = 0; if (m_highlightedWindow) { mouseActionWindow(m_leftButtonWindow); } } return true; } void PresentWindowsEffect::mouseActionWindow(WindowMouseAction& action) { switch(action) { case WindowActivateAction: if (m_highlightedWindow) effects->activateWindow(m_highlightedWindow); setActive(false); break; case WindowExitAction: setActive(false); break; case WindowToCurrentDesktopAction: if (m_highlightedWindow) effects->windowToDesktop(m_highlightedWindow, effects->currentDesktop()); break; case WindowToAllDesktopsAction: if (m_highlightedWindow) { if (m_highlightedWindow->isOnAllDesktops()) effects->windowToDesktop(m_highlightedWindow, effects->currentDesktop()); else effects->windowToDesktop(m_highlightedWindow, NET::OnAllDesktops); } break; case WindowMinimizeAction: if (m_highlightedWindow) { if (m_highlightedWindow->isMinimized()) m_highlightedWindow->unminimize(); else m_highlightedWindow->minimize(); } break; default: break; } } void PresentWindowsEffect::mouseActionDesktop(DesktopMouseAction& action) { switch(action) { case DesktopActivateAction: if (m_highlightedWindow) effects->activateWindow(m_highlightedWindow); setActive(false); break; case DesktopExitAction: setActive(false); break; case DesktopShowDesktopAction: effects->setShowingDesktop(true); setActive(false); default: break; } } void PresentWindowsEffect::grabbedKeyboardEvent(QKeyEvent *e) { if (e->type() == QEvent::KeyPress) { // check for global shortcuts // HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155) if (m_mode == ModeCurrentDesktop && shortcut.contains(e->key() + e->modifiers())) { toggleActive(); return; } if (m_mode == ModeAllDesktops && shortcutAll.contains(e->key() + e->modifiers())) { toggleActiveAllDesktops(); return; } if (m_mode == ModeWindowClass && shortcutClass.contains(e->key() + e->modifiers())) { toggleActiveClass(); return; } switch(e->key()) { // Wrap only if not auto-repeating case Qt::Key_Left: setHighlightedWindow(relativeWindow(m_highlightedWindow, -1, 0, !e->isAutoRepeat())); break; case Qt::Key_Right: setHighlightedWindow(relativeWindow(m_highlightedWindow, 1, 0, !e->isAutoRepeat())); break; case Qt::Key_Up: setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, -1, !e->isAutoRepeat())); break; case Qt::Key_Down: setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, 1, !e->isAutoRepeat())); break; case Qt::Key_Home: setHighlightedWindow(relativeWindow(m_highlightedWindow, -1000, 0, false)); break; case Qt::Key_End: setHighlightedWindow(relativeWindow(m_highlightedWindow, 1000, 0, false)); break; case Qt::Key_PageUp: setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, -1000, false)); break; case Qt::Key_PageDown: setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, 1000, false)); break; case Qt::Key_Backspace: if (!m_windowFilter.isEmpty()) { m_windowFilter.remove(m_windowFilter.length() - 1, 1); updateFilterFrame(); rearrangeWindows(); } return; case Qt::Key_Escape: setActive(false); return; case Qt::Key_Return: case Qt::Key_Enter: if (m_highlightedWindow) effects->activateWindow(m_highlightedWindow); setActive(false); return; case Qt::Key_Tab: return; // Nothing at the moment case Qt::Key_Delete: if (!m_windowFilter.isEmpty()) { m_windowFilter.clear(); updateFilterFrame(); rearrangeWindows(); } break; case 0: return; // HACK: Workaround for Qt bug on unbound keys (#178547) default: if (!e->text().isEmpty()) { m_windowFilter.append(e->text()); updateFilterFrame(); rearrangeWindows(); return; } break; } } } //----------------------------------------------------------------------------- // Atom handling void PresentWindowsEffect::slotPropertyNotify(EffectWindow* w, long a) { if (m_atomDesktop == XCB_ATOM_NONE && m_atomWindows == XCB_ATOM_NONE) { return; } if (!w || (a != m_atomDesktop && a != m_atomWindows)) return; // Not our atom if (a == m_atomDesktop) { QByteArray byteData = w->readProperty(m_atomDesktop, m_atomDesktop, 32); if (byteData.length() < 1) { // Property was removed, end present windows setActive(false); return; } auto* data = reinterpret_cast(byteData.data()); if (!data[0]) { // Purposely ending present windows by issuing a NULL target setActive(false); return; } // present windows is active so don't do anything if (m_activated) return; int desktop = data[0]; if (desktop > effects->numberOfDesktops()) return; if (desktop == -1) toggleActiveAllDesktops(); else { m_mode = ModeSelectedDesktop; m_desktop = desktop; m_managerWindow = w; setActive(true); } } else if (a == m_atomWindows) { QByteArray byteData = w->readProperty(m_atomWindows, m_atomWindows, 32); if (byteData.length() < 1) { // Property was removed, end present windows setActive(false); return; } auto* data = reinterpret_cast(byteData.data()); if (!data[0]) { // Purposely ending present windows by issuing a NULL target setActive(false); return; } // present windows is active so don't do anything if (m_activated) return; // for security clear selected windows m_selectedWindows.clear(); int length = byteData.length() / sizeof(data[0]); for (int i = 0; i < length; i++) { EffectWindow* foundWin = effects->findWindow(data[i]); if (!foundWin) { qCDebug(KWINEFFECTS) << "Invalid window targetted for present windows. Requested:" << data[i]; continue; } m_selectedWindows.append(foundWin); } m_mode = ModeWindowGroup; m_managerWindow = w; setActive(true); } } //----------------------------------------------------------------------------- // Window rearranging void PresentWindowsEffect::rearrangeWindows() { if (!m_activated) return; effects->addRepaintFull(); // Trigger the first repaint if (m_closeView) m_closeView->hide(); // Work out which windows are on which screens EffectWindowList windowlist; QList windowlists; for (int i = 0; i < effects->numScreens(); i++) windowlists.append(EffectWindowList()); if (m_windowFilter.isEmpty()) { windowlist = m_motionManager.managedWindows(); foreach (EffectWindow * w, m_motionManager.managedWindows()) { DataHash::iterator winData = m_windowData.find(w); if (winData == m_windowData.end() || winData->deleted) continue; // don't include closed windows windowlists[w->screen()].append(w); winData->visible = true; } } else { // Can we move this filtering somewhere else? foreach (EffectWindow * w, m_motionManager.managedWindows()) { DataHash::iterator winData = m_windowData.find(w); if (winData == m_windowData.end() || winData->deleted) continue; // don't include closed windows if (w->caption().contains(m_windowFilter, Qt::CaseInsensitive) || w->windowClass().contains(m_windowFilter, Qt::CaseInsensitive) || w->windowRole().contains(m_windowFilter, Qt::CaseInsensitive)) { windowlist.append(w); windowlists[w->screen()].append(w); winData->visible = true; } else winData->visible = false; } } if (windowlist.isEmpty()) { setHighlightedWindow(NULL); return; } // We filtered out the highlighted window if (m_highlightedWindow) { DataHash::iterator winData = m_windowData.find(m_highlightedWindow); if (winData != m_windowData.end() && !winData->visible) setHighlightedWindow(findFirstWindow()); } else setHighlightedWindow(findFirstWindow()); int screens = effects->numScreens(); for (int screen = 0; screen < screens; screen++) { EffectWindowList windows; windows = windowlists[screen]; // Don't rearrange if the grid is the same size as what it was before to prevent // windows moving to a better spot if one was filtered out. if (m_layoutMode == LayoutRegularGrid && m_gridSizes[screen].columns && m_gridSizes[screen].rows && windows.size() < m_gridSizes[screen].columns * m_gridSizes[screen].rows && windows.size() > (m_gridSizes[screen].columns - 1) * m_gridSizes[screen].rows && windows.size() > m_gridSizes[screen].columns *(m_gridSizes[screen].rows - 1)) continue; // No point continuing if there is no windows to process if (!windows.count()) continue; calculateWindowTransformations(windows, screen, m_motionManager); } // Resize text frames if required QFontMetrics* metrics = NULL; // All fonts are the same foreach (EffectWindow * w, m_motionManager.managedWindows()) { DataHash::iterator winData = m_windowData.find(w); if (winData == m_windowData.end()) continue; if (!metrics) metrics = new QFontMetrics(winData->textFrame->font()); QRect geom = m_motionManager.targetGeometry(w).toRect(); QString string = metrics->elidedText(w->caption(), Qt::ElideRight, geom.width() * 0.9); if (string != winData->textFrame->text()) winData->textFrame->setText(string); } delete metrics; } void PresentWindowsEffect::calculateWindowTransformations(EffectWindowList windowlist, int screen, WindowMotionManager& motionManager, bool external) { if (m_layoutMode == LayoutRegularGrid) calculateWindowTransformationsClosest(windowlist, screen, motionManager); else if (m_layoutMode == LayoutFlexibleGrid) calculateWindowTransformationsKompose(windowlist, screen, motionManager); else calculateWindowTransformationsNatural(windowlist, screen, motionManager); // If called externally we don't need to remember this data if (external) m_windowData.clear(); } static inline int distance(QPoint &pos1, QPoint &pos2) { const int xdiff = pos1.x() - pos2.x(); const int ydiff = pos1.y() - pos2.y(); return int(sqrt(float(xdiff*xdiff + ydiff*ydiff))); } void PresentWindowsEffect::calculateWindowTransformationsClosest(EffectWindowList windowlist, int screen, WindowMotionManager& motionManager) { // This layout mode requires at least one window visible if (windowlist.count() == 0) return; QRect area = effects->clientArea(ScreenArea, screen, effects->currentDesktop()); if (m_showPanel) // reserve space for the panel area = effects->clientArea(MaximizeArea, screen, effects->currentDesktop()); int columns = int(ceil(sqrt(double(windowlist.count())))); int rows = int(ceil(windowlist.count() / double(columns))); // Remember the size for later // If we are using this layout externally we don't need to remember m_gridSizes. if (m_gridSizes.size() != 0) { m_gridSizes[screen].columns = columns; m_gridSizes[screen].rows = rows; } // Assign slots int slotWidth = area.width() / columns; int slotHeight = area.height() / rows; QVector takenSlots; takenSlots.resize(rows*columns); takenSlots.fill(0); // precalculate all slot centers QVector slotCenters; slotCenters.resize(rows*columns); for (int x = 0; x < columns; ++x) for (int y = 0; y < rows; ++y) { slotCenters[x + y*columns] = QPoint(area.x() + slotWidth * x + slotWidth / 2, area.y() + slotHeight * y + slotHeight / 2); } // Assign each window to the closest available slot EffectWindowList tmpList = windowlist; // use a QLinkedList copy instead? QPoint otherPos; while (!tmpList.isEmpty()) { EffectWindow *w = tmpList.first(); int slotCandidate = -1, slotCandidateDistance = INT_MAX; QPoint pos = w->geometry().center(); for (int i = 0; i < columns*rows; ++i) { // all slots const int dist = distance(pos, slotCenters[i]); if (dist < slotCandidateDistance) { // window is interested in this slot EffectWindow *occupier = takenSlots[i]; assert(occupier != w); if (!occupier || dist < distance((otherPos = occupier->geometry().center()), slotCenters[i])) { // either nobody lives here, or we're better - takeover the slot if it's our best slotCandidate = i; slotCandidateDistance = dist; } } } assert(slotCandidate != -1); if (takenSlots[slotCandidate]) tmpList << takenSlots[slotCandidate]; // occupier needs a new home now :p tmpList.removeAll(w); takenSlots[slotCandidate] = w; // ...and we rumble in =) } for (int slot = 0; slot < columns*rows; ++slot) { EffectWindow *w = takenSlots[slot]; if (!w) // some slots might be empty continue; // Work out where the slot is QRect target( area.x() + (slot % columns) * slotWidth, area.y() + (slot / columns) * slotHeight, slotWidth, slotHeight); target.adjust(10, 10, -10, -10); // Borders double scale; if (target.width() / double(w->width()) < target.height() / double(w->height())) { // Center vertically scale = target.width() / double(w->width()); target.moveTop(target.top() + (target.height() - int(w->height() * scale)) / 2); target.setHeight(int(w->height() * scale)); } else { // Center horizontally scale = target.height() / double(w->height()); target.moveLeft(target.left() + (target.width() - int(w->width() * scale)) / 2); target.setWidth(int(w->width() * scale)); } // Don't scale the windows too much if (scale > 2.0 || (scale > 1.0 && (w->width() > 300 || w->height() > 300))) { scale = (w->width() > 300 || w->height() > 300) ? 1.0 : 2.0; target = QRect( target.center().x() - int(w->width() * scale) / 2, target.center().y() - int(w->height() * scale) / 2, scale * w->width(), scale * w->height()); } motionManager.moveWindow(w, target); } } void PresentWindowsEffect::calculateWindowTransformationsKompose(EffectWindowList windowlist, int screen, WindowMotionManager& motionManager) { // This layout mode requires at least one window visible if (windowlist.count() == 0) return; QRect availRect = effects->clientArea(ScreenArea, screen, effects->currentDesktop()); if (m_showPanel) // reserve space for the panel availRect = effects->clientArea(MaximizeArea, screen, effects->currentDesktop()); qSort(windowlist); // The location of the windows should not depend on the stacking order // Following code is taken from Kompose 0.5.4, src/komposelayout.cpp int spacing = 10; int rows, columns; double parentRatio = availRect.width() / (double)availRect.height(); // Use more columns than rows when parent's width > parent's height if (parentRatio > 1) { columns = (int)ceil(sqrt((double)windowlist.count())); rows = (int)ceil((double)windowlist.count() / (double)columns); } else { rows = (int)ceil(sqrt((double)windowlist.count())); columns = (int)ceil((double)windowlist.count() / (double)rows); } //qCDebug(KWINEFFECTS) << "Using " << rows << " rows & " << columns << " columns for " << windowlist.count() << " clients"; // Calculate width & height int w = (availRect.width() - (columns + 1) * spacing) / columns; int h = (availRect.height() - (rows + 1) * spacing) / rows; EffectWindowList::iterator it(windowlist.begin()); QList geometryRects; QList maxRowHeights; // Process rows for (int i = 0; i < rows; ++i) { int xOffsetFromLastCol = 0; int maxHeightInRow = 0; // Process columns for (int j = 0; j < columns; ++j) { EffectWindow* window; // Check for end of List if (it == windowlist.end()) break; window = *it; // Calculate width and height of widget double ratio = aspectRatio(window); int widgetw = 100; int widgeth = 100; int usableW = w; int usableH = h; // use width of two boxes if there is no right neighbour if (window == windowlist.last() && j != columns - 1) { usableW = 2 * w; } ++it; // We need access to the neighbour in the following // expand if right neighbour has ratio < 1 if (j != columns - 1 && it != windowlist.end() && aspectRatio(*it) < 1) { int addW = w - widthForHeight(*it, h); if (addW > 0) { usableW = w + addW; } } if (ratio == -1) { widgetw = w; widgeth = h; } else { double widthByHeight = widthForHeight(window, usableH); double heightByWidth = heightForWidth(window, usableW); if ((ratio >= 1.0 && heightByWidth <= usableH) || (ratio < 1.0 && widthByHeight > usableW)) { widgetw = usableW; widgeth = (int)heightByWidth; } else if ((ratio < 1.0 && widthByHeight <= usableW) || (ratio >= 1.0 && heightByWidth > usableH)) { widgeth = usableH; widgetw = (int)widthByHeight; } // Don't upscale large-ish windows if (widgetw > window->width() && (window->width() > 300 || window->height() > 300)) { widgetw = window->width(); widgeth = window->height(); } } // Set the Widget's size int alignmentXoffset = 0; int alignmentYoffset = 0; if (i == 0 && h > widgeth) alignmentYoffset = h - widgeth; if (j == 0 && w > widgetw) alignmentXoffset = w - widgetw; QRect geom(availRect.x() + j *(w + spacing) + spacing + alignmentXoffset + xOffsetFromLastCol, availRect.y() + i *(h + spacing) + spacing + alignmentYoffset, widgetw, widgeth); geometryRects.append(geom); // Set the x offset for the next column if (alignmentXoffset == 0) xOffsetFromLastCol += widgetw - w; if (maxHeightInRow < widgeth) maxHeightInRow = widgeth; } maxRowHeights.append(maxHeightInRow); } int topOffset = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { int pos = i * columns + j; if (pos >= windowlist.count()) break; EffectWindow* window = windowlist[pos]; QRect target = geometryRects[pos]; target.setY(target.y() + topOffset); // @Marrtin: any idea what this is good for? // DataHash::iterator winData = m_windowData.find(window); // if (winData != m_windowData.end()) // winData->slot = pos; motionManager.moveWindow(window, target); //qCDebug(KWINEFFECTS) << "Window '" << window->caption() << "' gets moved to (" << // mWindowData[window].area.left() << "; " << mWindowData[window].area.right() << // "), scale: " << mWindowData[window].scale << endl; } if (maxRowHeights[i] - h > 0) topOffset += maxRowHeights[i] - h; } } void PresentWindowsEffect::calculateWindowTransformationsNatural(EffectWindowList windowlist, int screen, WindowMotionManager& motionManager) { // If windows do not overlap they scale into nothingness, fix by resetting. To reproduce // just have a single window on a Xinerama screen or have two windows that do not touch. // TODO: Work out why this happens, is most likely a bug in the manager. foreach (EffectWindow * w, windowlist) if (motionManager.transformedGeometry(w) == w->geometry()) motionManager.reset(w); if (windowlist.count() == 1) { // Just move the window to its original location to save time if (effects->clientArea(FullScreenArea, windowlist[0]).contains(windowlist[0]->geometry())) { motionManager.moveWindow(windowlist[0], windowlist[0]->geometry()); return; } } // As we are using pseudo-random movement (See "slot") we need to make sure the list // is always sorted the same way no matter which window is currently active. qSort(windowlist); QRect area = effects->clientArea(ScreenArea, screen, effects->currentDesktop()); if (m_showPanel) // reserve space for the panel area = effects->clientArea(MaximizeArea, screen, effects->currentDesktop()); QRect bounds = area; int direction = 0; QHash targets; QHash directions; foreach (EffectWindow * w, windowlist) { bounds = bounds.united(w->geometry()); targets[w] = w->geometry(); // Reuse the unused "slot" as a preferred direction attribute. This is used when the window // is on the edge of the screen to try to use as much screen real estate as possible. directions[w] = direction; direction++; if (direction == 4) direction = 0; } // Iterate over all windows, if two overlap push them apart _slightly_ as we try to // brute-force the most optimal positions over many iterations. bool overlap; do { overlap = false; foreach (EffectWindow * w, windowlist) { QRect *target_w = &targets[w]; foreach (EffectWindow * e, windowlist) { if (w == e) continue; QRect *target_e = &targets[e]; if (target_w->adjusted(-5, -5, 5, 5).intersects(target_e->adjusted(-5, -5, 5, 5))) { overlap = true; // Determine pushing direction QPoint diff(target_e->center() - target_w->center()); // Prevent dividing by zero and non-movement if (diff.x() == 0 && diff.y() == 0) diff.setX(1); // Try to keep screen aspect ratio //if (bounds.height() / bounds.width() > area.height() / area.width()) // diff.setY(diff.y() / 2); //else // diff.setX(diff.x() / 2); // Approximate a vector of between 10px and 20px in magnitude in the same direction diff *= m_accuracy / double(diff.manhattanLength()); // Move both windows apart target_w->translate(-diff); target_e->translate(diff); // Try to keep the bounding rect the same aspect as the screen so that more // screen real estate is utilised. We do this by splitting the screen into nine // equal sections, if the window center is in any of the corner sections pull the // window towards the outer corner. If it is in any of the other edge sections // alternate between each corner on that edge. We don't want to determine it // randomly as it will not produce consistant locations when using the filter. // Only move one window so we don't cause large amounts of unnecessary zooming // in some situations. We need to do this even when expanding later just in case // all windows are the same size. // (We are using an old bounding rect for this, hopefully it doesn't matter) int xSection = (target_w->x() - bounds.x()) / (bounds.width() / 3); int ySection = (target_w->y() - bounds.y()) / (bounds.height() / 3); diff = QPoint(0, 0); if (xSection != 1 || ySection != 1) { // Remove this if you want the center to pull as well if (xSection == 1) xSection = (directions[w] / 2 ? 2 : 0); if (ySection == 1) ySection = (directions[w] % 2 ? 2 : 0); } if (xSection == 0 && ySection == 0) diff = QPoint(bounds.topLeft() - target_w->center()); if (xSection == 2 && ySection == 0) diff = QPoint(bounds.topRight() - target_w->center()); if (xSection == 2 && ySection == 2) diff = QPoint(bounds.bottomRight() - target_w->center()); if (xSection == 0 && ySection == 2) diff = QPoint(bounds.bottomLeft() - target_w->center()); if (diff.x() != 0 || diff.y() != 0) { diff *= m_accuracy / double(diff.manhattanLength()); target_w->translate(diff); } // Update bounding rect bounds = bounds.united(*target_w); bounds = bounds.united(*target_e); } } } } while (overlap); // Work out scaling by getting the most top-left and most bottom-right window coords. // The 20's and 10's are so that the windows don't touch the edge of the screen. double scale; if (bounds == area) scale = 1.0; // Don't add borders to the screen else if (area.width() / double(bounds.width()) < area.height() / double(bounds.height())) scale = (area.width() - 20) / double(bounds.width()); else scale = (area.height() - 20) / double(bounds.height()); // Make bounding rect fill the screen size for later steps bounds = QRect( bounds.x() - (area.width() - 20 - bounds.width() * scale) / 2 - 10 / scale, bounds.y() - (area.height() - 20 - bounds.height() * scale) / 2 - 10 / scale, area.width() / scale, area.height() / scale ); // Move all windows back onto the screen and set their scale QHash::iterator target = targets.begin(); while (target != targets.end()) { target->setRect((target->x() - bounds.x()) * scale + area.x(), (target->y() - bounds.y()) * scale + area.y(), target->width() * scale, target->height() * scale ); ++target; } // Try to fill the gaps by enlarging windows if they have the space if (m_fillGaps) { // Don't expand onto or over the border QRegion borderRegion(area.adjusted(-200, -200, 200, 200)); borderRegion ^= area.adjusted(10 / scale, 10 / scale, -10 / scale, -10 / scale); bool moved; do { moved = false; foreach (EffectWindow * w, windowlist) { QRect oldRect; QRect *target = &targets[w]; // This may cause some slight distortion if the windows are enlarged a large amount int widthDiff = m_accuracy; int heightDiff = heightForWidth(w, target->width() + widthDiff) - target->height(); int xDiff = widthDiff / 2; // Also move a bit in the direction of the enlarge, allows the int yDiff = heightDiff / 2; // center windows to be enlarged if there is gaps on the side. // heightDiff (and yDiff) will be re-computed after each successfull enlargement attempt // so that the error introduced in the window's aspect ratio is minimized // Attempt enlarging to the top-right oldRect = *target; target->setRect(target->x() + xDiff, target->y() - yDiff - heightDiff, target->width() + widthDiff, target->height() + heightDiff ); if (isOverlappingAny(w, targets, borderRegion)) *target = oldRect; else { moved = true; heightDiff = heightForWidth(w, target->width() + widthDiff) - target->height(); yDiff = heightDiff / 2; } // Attempt enlarging to the bottom-right oldRect = *target; target->setRect( target->x() + xDiff, target->y() + yDiff, target->width() + widthDiff, target->height() + heightDiff ); if (isOverlappingAny(w, targets, borderRegion)) *target = oldRect; else { moved = true; heightDiff = heightForWidth(w, target->width() + widthDiff) - target->height(); yDiff = heightDiff / 2; } // Attempt enlarging to the bottom-left oldRect = *target; target->setRect( target->x() - xDiff - widthDiff, target->y() + yDiff, target->width() + widthDiff, target->height() + heightDiff ); if (isOverlappingAny(w, targets, borderRegion)) *target = oldRect; else { moved = true; heightDiff = heightForWidth(w, target->width() + widthDiff) - target->height(); yDiff = heightDiff / 2; } // Attempt enlarging to the top-left oldRect = *target; target->setRect( target->x() - xDiff - widthDiff, target->y() - yDiff - heightDiff, target->width() + widthDiff, target->height() + heightDiff ); if (isOverlappingAny(w, targets, borderRegion)) *target = oldRect; else moved = true; } } while (moved); // The expanding code above can actually enlarge windows over 1.0/2.0 scale, we don't like this // We can't add this to the loop above as it would cause a never-ending loop so we have to make // do with the less-than-optimal space usage with using this method. foreach (EffectWindow * w, windowlist) { QRect *target = &targets[w]; double scale = target->width() / double(w->width()); if (scale > 2.0 || (scale > 1.0 && (w->width() > 300 || w->height() > 300))) { scale = (w->width() > 300 || w->height() > 300) ? 1.0 : 2.0; target->setRect( target->center().x() - int(w->width() * scale) / 2, target->center().y() - int(w->height() * scale) / 2, w->width() * scale, w->height() * scale); } } } // Notify the motion manager of the targets foreach (EffectWindow * w, windowlist) motionManager.moveWindow(w, targets.value(w)); } bool PresentWindowsEffect::isOverlappingAny(EffectWindow *w, const QHash &targets, const QRegion &border) { QHash::const_iterator winTarget = targets.find(w); if (winTarget == targets.constEnd()) return false; if (border.intersects(*winTarget)) return true; // Is there a better way to do this? QHash::const_iterator target; for (target = targets.constBegin(); target != targets.constEnd(); ++target) { if (target == winTarget) continue; if (winTarget->adjusted(-5, -5, 5, 5).intersects(target->adjusted(-5, -5, 5, 5))) return true; } return false; } //----------------------------------------------------------------------------- // Activation void PresentWindowsEffect::setActive(bool active) { if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this) return; if (m_activated == active) return; m_activated = active; if (m_activated) { effects->setShowingDesktop(false); m_needInitialSelection = true; m_closeButtonCorner = (Qt::Corner)effects->kwinOption(KWin::CloseButtonCorner).toInt(); m_decalOpacity = 0.0; m_highlightedWindow = NULL; m_windowFilter.clear(); if (!(m_doNotCloseWindows || m_closeView)) { m_closeView = new CloseWindowView(); connect(m_closeView, &CloseWindowView::requestClose, this, &PresentWindowsEffect::closeWindow); } // Add every single window to m_windowData (Just calling [w] creates it) foreach (EffectWindow * w, effects->stackingOrder()) { DataHash::iterator winData; if ((winData = m_windowData.find(w)) != m_windowData.end()) { winData->visible = isVisibleWindow(w); continue; // Happens if we reactivate before the ending animation finishes } winData = m_windowData.insert(w, WindowData()); winData->visible = isVisibleWindow(w); winData->deleted = false; winData->referenced = false; winData->opacity = 0.0; if (w->isOnCurrentDesktop() && !w->isMinimized()) winData->opacity = 1.0; winData->highlight = 1.0; winData->textFrame = effects->effectFrame(EffectFrameUnstyled, false); QFont font; font.setBold(true); font.setPointSize(12); winData->textFrame->setFont(font); winData->iconFrame = effects->effectFrame(EffectFrameUnstyled, false); winData->iconFrame->setAlignment(Qt::AlignRight | Qt::AlignBottom); winData->iconFrame->setIcon(w->icon()); winData->iconFrame->setIconSize(QSize(32, 32)); } // Filter out special windows such as panels and taskbars foreach (EffectWindow * w, effects->stackingOrder()) { if (isSelectableWindow(w)) { m_motionManager.manage(w); } } if (m_motionManager.managedWindows().isEmpty() || ((m_motionManager.managedWindows().count() == 1) && m_motionManager.managedWindows().first()->isOnCurrentDesktop() && (m_ignoreMinimized || !m_motionManager.managedWindows().first()->isMinimized()))) { // No point triggering if there is nothing to do m_activated = false; DataHash::iterator i = m_windowData.begin(); while (i != m_windowData.end()) { delete i.value().textFrame; delete i.value().iconFrame; ++i; } m_windowData.clear(); m_motionManager.unmanageAll(); return; } // Create temporary input window to catch mouse events effects->startMouseInterception(this, Qt::PointingHandCursor); m_hasKeyboardGrab = effects->grabKeyboard(this); effects->setActiveFullScreenEffect(this); reCreateGrids(); rearrangeWindows(); setHighlightedWindow(effects->activeWindow()); foreach (EffectWindow * w, effects->stackingOrder()) { w->setData(WindowForceBlurRole, QVariant(true)); w->setData(WindowForceBackgroundContrastRole, QVariant(true)); } } else { m_needInitialSelection = false; if (m_highlightedWindow) effects->setElevatedWindow(m_highlightedWindow, false); // Fade in/out all windows EffectWindow *activeWindow = effects->activeWindow(); int desktop = effects->currentDesktop(); if (activeWindow && !activeWindow->isOnAllDesktops()) desktop = activeWindow->desktop(); foreach (EffectWindow * w, effects->stackingOrder()) { DataHash::iterator winData = m_windowData.find(w); if (winData != m_windowData.end()) winData->visible = (w->isOnDesktop(desktop) || w->isOnAllDesktops()) && !w->isMinimized() && (w->isCurrentTab() || winData->visible); } if (m_closeView) m_closeView->hide(); // Move all windows back to their original position foreach (EffectWindow * w, m_motionManager.managedWindows()) m_motionManager.moveWindow(w, w->geometry()); if (m_filterFrame) { m_filterFrame->free(); } m_windowFilter.clear(); m_selectedWindows.clear(); effects->stopMouseInterception(this); if (m_hasKeyboardGrab) effects->ungrabKeyboard(); m_hasKeyboardGrab = false; // destroy atom on manager window if (m_managerWindow) { if (m_mode == ModeSelectedDesktop && m_atomDesktop != XCB_ATOM_NONE) m_managerWindow->deleteProperty(m_atomDesktop); else if (m_mode == ModeWindowGroup && m_atomWindows != XCB_ATOM_NONE) m_managerWindow->deleteProperty(m_atomWindows); m_managerWindow = NULL; } } effects->addRepaintFull(); // Trigger the first repaint } //----------------------------------------------------------------------------- // Filter box void PresentWindowsEffect::updateFilterFrame() { QRect area = effects->clientArea(ScreenArea, effects->activeScreen(), effects->currentDesktop()); if (!m_filterFrame) { m_filterFrame = effects->effectFrame(EffectFrameStyled, false); QFont font; font.setPointSize(font.pointSize() * 2); font.setBold(true); m_filterFrame->setFont(font); } m_filterFrame->setPosition(QPoint(area.x() + area.width() / 2, area.y() + area.height() / 2)); m_filterFrame->setText(i18n("Filter:\n%1", m_windowFilter)); } //----------------------------------------------------------------------------- // Helper functions bool PresentWindowsEffect::isSelectableWindow(EffectWindow *w) { if (!w->isOnCurrentActivity()) return false; if (w->isSpecialWindow() || w->isUtility()) return false; if (w->isDeleted()) return false; if (!w->acceptsFocus()) return false; if (!w->isCurrentTab()) return false; if (w->isSkipSwitcher()) return false; if (m_closeView && w == effects->findWindow(m_closeView->winId())) return false; if (m_ignoreMinimized && w->isMinimized()) return false; switch(m_mode) { default: case ModeAllDesktops: return true; case ModeCurrentDesktop: return w->isOnCurrentDesktop(); case ModeSelectedDesktop: return w->isOnDesktop(m_desktop); case ModeWindowGroup: return m_selectedWindows.contains(w); case ModeWindowClass: return m_class == w->windowClass(); } } bool PresentWindowsEffect::isVisibleWindow(EffectWindow *w) { if (w->isDesktop() || w == m_closeWindow) return true; return isSelectableWindow(w); } void PresentWindowsEffect::setHighlightedWindow(EffectWindow *w) { if (w == m_highlightedWindow || (w != NULL && !m_motionManager.isManaging(w))) return; if (m_closeView) m_closeView->hide(); if (m_highlightedWindow) { effects->setElevatedWindow(m_highlightedWindow, false); m_highlightedWindow->addRepaintFull(); // Trigger the first repaint } m_highlightedWindow = w; if (m_highlightedWindow) { effects->setElevatedWindow(m_highlightedWindow, true); m_highlightedWindow->addRepaintFull(); // Trigger the first repaint } updateCloseWindow(); } void PresentWindowsEffect::elevateCloseWindow() { if (!m_closeView || !m_activated) return; if (EffectWindow *cw = effects->findWindow(m_closeView->winId())) effects->setElevatedWindow(cw, true); } void PresentWindowsEffect::updateCloseWindow() { if (!m_closeView || m_doNotCloseWindows) return; if (!m_activated || !m_highlightedWindow || m_highlightedWindow->isDesktop()) { m_closeView->hide(); return; } if (m_closeView->isVisible()) return; const QRectF rect(m_motionManager.targetGeometry(m_highlightedWindow)); if (2*m_closeView->width() > rect.width() && 2*m_closeView->height() > rect.height()) { // not for tiny windows (eg. with many windows) - they might become unselectable m_closeView->hide(); return; } QRect cvr(QPoint(0,0), m_closeView->size()); switch (m_closeButtonCorner) { case Qt::TopLeftCorner: default: cvr.moveTopLeft(rect.topLeft().toPoint()); break; case Qt::TopRightCorner: cvr.moveTopRight(rect.topRight().toPoint()); break; case Qt::BottomLeftCorner: cvr.moveBottomLeft(rect.bottomLeft().toPoint()); break; case Qt::BottomRightCorner: cvr.moveBottomRight(rect.bottomRight().toPoint()); break; } m_closeView->setGeometry(cvr); if (rect.contains(effects->cursorPos())) { m_closeView->show(); m_closeView->disarm(); // to wait for the next event cycle (or more if the show takes more time) // TODO: make the closeWindow a graphicsviewitem? why should there be an extra scene to be used in an exiting scene?? QTimer::singleShot(50, this, SLOT(elevateCloseWindow())); } else m_closeView->hide(); } void PresentWindowsEffect::closeWindow() { if (m_highlightedWindow) m_highlightedWindow->closeWindow(); } EffectWindow* PresentWindowsEffect::relativeWindow(EffectWindow *w, int xdiff, int ydiff, bool wrap) const { if (!w) return m_motionManager.managedWindows().first(); // TODO: Is it possible to select hidden windows? EffectWindow* next; QRect area = effects->clientArea(FullArea, 0, effects->currentDesktop()); QRect detectRect; // Detect across the width of the desktop if (xdiff != 0) { if (xdiff > 0) { // Detect right for (int i = 0; i < xdiff; i++) { QRectF wArea = m_motionManager.transformedGeometry(w); detectRect = QRect(0, wArea.y(), area.width(), wArea.height()); next = NULL; foreach (EffectWindow * e, m_motionManager.managedWindows()) { DataHash::const_iterator winData = m_windowData.find(e); if (winData == m_windowData.end() || !winData->visible) continue; QRectF eArea = m_motionManager.transformedGeometry(e); if (eArea.intersects(detectRect) && eArea.x() > wArea.x()) { if (next == NULL) next = e; else { QRectF nArea = m_motionManager.transformedGeometry(next); if (eArea.x() < nArea.x()) next = e; } } } if (next == NULL) { if (wrap) // We are at the right-most window, now get the left-most one to wrap return relativeWindow(w, -1000, 0, false); break; // No more windows to the right } w = next; } return w; } else { // Detect left for (int i = 0; i < -xdiff; i++) { QRectF wArea = m_motionManager.transformedGeometry(w); detectRect = QRect(0, wArea.y(), area.width(), wArea.height()); next = NULL; foreach (EffectWindow * e, m_motionManager.managedWindows()) { DataHash::const_iterator winData = m_windowData.find(e); if (winData == m_windowData.end() || !winData->visible) continue; QRectF eArea = m_motionManager.transformedGeometry(e); if (eArea.intersects(detectRect) && eArea.x() + eArea.width() < wArea.x() + wArea.width()) { if (next == NULL) next = e; else { QRectF nArea = m_motionManager.transformedGeometry(next); if (eArea.x() + eArea.width() > nArea.x() + nArea.width()) next = e; } } } if (next == NULL) { if (wrap) // We are at the left-most window, now get the right-most one to wrap return relativeWindow(w, 1000, 0, false); break; // No more windows to the left } w = next; } return w; } } // Detect across the height of the desktop if (ydiff != 0) { if (ydiff > 0) { // Detect down for (int i = 0; i < ydiff; i++) { QRectF wArea = m_motionManager.transformedGeometry(w); detectRect = QRect(wArea.x(), 0, wArea.width(), area.height()); next = NULL; foreach (EffectWindow * e, m_motionManager.managedWindows()) { DataHash::const_iterator winData = m_windowData.find(e); if (winData == m_windowData.end() || !winData->visible) continue; QRectF eArea = m_motionManager.transformedGeometry(e); if (eArea.intersects(detectRect) && eArea.y() > wArea.y()) { if (next == NULL) next = e; else { QRectF nArea = m_motionManager.transformedGeometry(next); if (eArea.y() < nArea.y()) next = e; } } } if (next == NULL) { if (wrap) // We are at the bottom-most window, now get the top-most one to wrap return relativeWindow(w, 0, -1000, false); break; // No more windows to the bottom } w = next; } return w; } else { // Detect up for (int i = 0; i < -ydiff; i++) { QRectF wArea = m_motionManager.transformedGeometry(w); detectRect = QRect(wArea.x(), 0, wArea.width(), area.height()); next = NULL; foreach (EffectWindow * e, m_motionManager.managedWindows()) { DataHash::const_iterator winData = m_windowData.find(e); if (winData == m_windowData.end() || !winData->visible) continue; QRectF eArea = m_motionManager.transformedGeometry(e); if (eArea.intersects(detectRect) && eArea.y() + eArea.height() < wArea.y() + wArea.height()) { if (next == NULL) next = e; else { QRectF nArea = m_motionManager.transformedGeometry(next); if (eArea.y() + eArea.height() > nArea.y() + nArea.height()) next = e; } } } if (next == NULL) { if (wrap) // We are at the top-most window, now get the bottom-most one to wrap return relativeWindow(w, 0, 1000, false); break; // No more windows to the top } w = next; } return w; } } abort(); // Should never get here } EffectWindow* PresentWindowsEffect::findFirstWindow() const { EffectWindow *topLeft = NULL; QRectF topLeftGeometry; foreach (EffectWindow * w, m_motionManager.managedWindows()) { DataHash::const_iterator winData = m_windowData.find(w); if (winData == m_windowData.end()) continue; QRectF geometry = m_motionManager.transformedGeometry(w); if (winData->visible == false) continue; // Not visible if (winData->deleted) continue; // Window has been closed if (topLeft == NULL) { topLeft = w; topLeftGeometry = geometry; } else if (geometry.x() < topLeftGeometry.x() || geometry.y() < topLeftGeometry.y()) { topLeft = w; topLeftGeometry = geometry; } } return topLeft; } void PresentWindowsEffect::globalShortcutChanged(QAction *action, const QKeySequence& seq) { if (action->objectName() == QStringLiteral("Expose")) { shortcut.clear(); shortcut.append(seq); } else if (action->objectName() == QStringLiteral("ExposeAll")) { shortcutAll.clear(); shortcutAll.append(seq); } else if (action->objectName() == QStringLiteral("ExposeClass")) { shortcutClass.clear(); shortcutClass.append(seq); } } bool PresentWindowsEffect::isActive() const { return (m_activated || m_motionManager.managingWindows()) && !effects->isScreenLocked(); } void PresentWindowsEffect::reCreateGrids() { m_gridSizes.clear(); for (int i = 0; i < effects->numScreens(); ++i) { m_gridSizes.append(GridSize()); } rearrangeWindows(); } /************************************************ * CloseWindowView ************************************************/ CloseWindowView::CloseWindowView(QObject *parent) : QObject(parent) , m_armTimer(new QElapsedTimer()) , m_window(new QQuickView()) , m_visible(false) , m_posIsValid(false) { m_window->setFlags(Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint); m_window->setColor(Qt::transparent); m_window->setSource(QUrl(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/presentwindows/main.qml")))); if (QObject *item = m_window->rootObject()->findChild(QStringLiteral("closeButton"))) { connect(item, SIGNAL(clicked()), SIGNAL(requestClose())); } m_armTimer->restart(); } void CloseWindowView::windowInputMouseEvent(QMouseEvent *e) { if (e->type() == QEvent::MouseMove) { qApp->sendEvent(m_window.data(), e); } else if (!m_armTimer->hasExpired(350)) { // 50ms until the window is elevated (seen!) and 300ms more to be "realized" by the user. return; } qApp->sendEvent(m_window.data(), e); } void CloseWindowView::disarm() { m_armTimer->restart(); } bool CloseWindowView::isVisible() const { return m_visible; } void CloseWindowView::show() { if (!m_visible && m_posIsValid) { m_window->setPosition(m_pos); m_posIsValid = false; } m_visible = true; m_window->show(); } void CloseWindowView::hide() { if (!m_posIsValid) { m_pos = m_window->position(); m_posIsValid = true; m_window->setPosition(-m_window->width(), -m_window->height()); } m_visible = false; QEvent event(QEvent::Leave); qApp->sendEvent(m_window.data(), &event); } #define DELEGATE(type, name) \ type CloseWindowView::name() const \ { \ return m_window->name(); \ } DELEGATE(int, width) DELEGATE(int, height) DELEGATE(QSize, size) DELEGATE(QRect, geometry) DELEGATE(WId, winId) #undef DELEGATE void CloseWindowView::setGeometry(const QRect &geometry) { m_posIsValid = false; m_window->setGeometry(geometry); } QPoint CloseWindowView::mapFromGlobal(const QPoint &pos) const { return m_window->mapFromGlobal(pos); } } // namespace diff --git a/effects/presentwindows/presentwindows_config.cpp b/effects/presentwindows/presentwindows_config.cpp index fe383765d..edb2af54f 100644 --- a/effects/presentwindows/presentwindows_config.cpp +++ b/effects/presentwindows/presentwindows_config.cpp @@ -1,118 +1,118 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2008 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "presentwindows_config.h" // KConfigSkeleton #include "presentwindowsconfig.h" #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(PresentWindowsEffectConfigFactory, "presentwindows_config.json", registerPlugin();) namespace KWin { PresentWindowsEffectConfigForm::PresentWindowsEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } PresentWindowsEffectConfig::PresentWindowsEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("presentwindows")), parent, args) { m_ui = new PresentWindowsEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); // Shortcut config. The shortcut belongs to the component "kwin"! m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); m_actionCollection->setConfigGroup(QStringLiteral("PresentWindows")); m_actionCollection->setConfigGlobal(true); QAction* a = m_actionCollection->addAction(QStringLiteral("ExposeAll")); a->setText(i18n("Toggle Present Windows (All desktops)")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::CTRL + Qt::Key_F10 << Qt::Key_LaunchC); KGlobalAccel::self()->setShortcut(a, QList() << Qt::CTRL + Qt::Key_F10 << Qt::Key_LaunchC); QAction* b = m_actionCollection->addAction(QStringLiteral("Expose")); b->setText(i18n("Toggle Present Windows (Current desktop)")); b->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(b, QList() << Qt::CTRL + Qt::Key_F9); KGlobalAccel::self()->setShortcut(b, QList() << Qt::CTRL + Qt::Key_F9); QAction* c = m_actionCollection->addAction(QStringLiteral("ExposeClass")); c->setText(i18n("Toggle Present Windows (Window class)")); c->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(c, QList() << Qt::CTRL + Qt::Key_F7); KGlobalAccel::self()->setShortcut(c, QList() << Qt::CTRL + Qt::Key_F7); m_ui->shortcutEditor->addCollection(m_actionCollection); - connect(m_ui->shortcutEditor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(m_ui->shortcutEditor, &KShortcutsEditor::keyChange, this, qOverload<>(&PresentWindowsEffectConfig::changed)); PresentWindowsConfig::instance(KWIN_CONFIG); addConfig(PresentWindowsConfig::self(), m_ui); load(); } PresentWindowsEffectConfig::~PresentWindowsEffectConfig() { // If save() is called undoChanges() has no effect m_ui->shortcutEditor->undoChanges(); } void PresentWindowsEffectConfig::save() { KCModule::save(); m_ui->shortcutEditor->save(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("presentwindows")); } void PresentWindowsEffectConfig::defaults() { m_ui->shortcutEditor->allDefault(); KCModule::defaults(); } } // namespace #include "presentwindows_config.moc" diff --git a/effects/resize/resize.cpp b/effects/resize/resize.cpp index e1035626d..dd0028cd0 100644 --- a/effects/resize/resize.cpp +++ b/effects/resize/resize.cpp @@ -1,177 +1,177 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "resize.h" // KConfigSkeleton #include "resizeconfig.h" #include #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include "kwinxrenderutils.h" #endif #include #include #include namespace KWin { ResizeEffect::ResizeEffect() : AnimationEffect() , m_active(false) , m_resizeWindow(0) { initConfig(); reconfigure(ReconfigureAll); - connect(effects, SIGNAL(windowStartUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowStartUserMovedResized(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowStepUserMovedResized(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowStepUserMovedResized(KWin::EffectWindow*,QRect))); - connect(effects, SIGNAL(windowFinishUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowFinishUserMovedResized(KWin::EffectWindow*))); + connect(effects, &EffectsHandler::windowStartUserMovedResized, this, &ResizeEffect::slotWindowStartUserMovedResized); + connect(effects, &EffectsHandler::windowStepUserMovedResized, this, &ResizeEffect::slotWindowStepUserMovedResized); + connect(effects, &EffectsHandler::windowFinishUserMovedResized, this, &ResizeEffect::slotWindowFinishUserMovedResized); } ResizeEffect::~ResizeEffect() { } void ResizeEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (m_active) { data.mask |= PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; } AnimationEffect::prePaintScreen(data, time); } void ResizeEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (m_active && w == m_resizeWindow) data.mask |= PAINT_WINDOW_TRANSFORMED; AnimationEffect::prePaintWindow(w, data, time); } void ResizeEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (m_active && w == m_resizeWindow) { if (m_features & TextureScale) { data += (m_currentGeometry.topLeft() - m_originalGeometry.topLeft()); data *= QVector2D(float(m_currentGeometry.width())/m_originalGeometry.width(), float(m_currentGeometry.height())/m_originalGeometry.height()); } effects->paintWindow(w, mask, region, data); if (m_features & Outline) { QRegion intersection = m_originalGeometry.intersected(m_currentGeometry); QRegion paintRegion = QRegion(m_originalGeometry).united(m_currentGeometry).subtracted(intersection); float alpha = 0.8f; QColor color = KColorScheme(QPalette::Normal, KColorScheme::Selection).background().color(); if (effects->isOpenGLCompositing()) { GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); vbo->reset(); vbo->setUseColor(true); ShaderBinder binder(ShaderTrait::UniformColor); binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, data.screenProjectionMatrix()); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); color.setAlphaF(alpha); vbo->setColor(color); QVector verts; verts.reserve(paintRegion.rects().count() * 12); foreach (const QRect & r, paintRegion.rects()) { verts << r.x() + r.width() << r.y(); verts << r.x() << r.y(); verts << r.x() << r.y() + r.height(); verts << r.x() << r.y() + r.height(); verts << r.x() + r.width() << r.y() + r.height(); verts << r.x() + r.width() << r.y(); } vbo->setData(verts.count() / 2, 2, verts.data(), NULL); vbo->render(GL_TRIANGLES); glDisable(GL_BLEND); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) { QVector rects; foreach (const QRect & r, paintRegion.rects()) { xcb_rectangle_t rect = {int16_t(r.x()), int16_t(r.y()), uint16_t(r.width()), uint16_t(r.height())}; rects << rect; } xcb_render_fill_rectangles(xcbConnection(), XCB_RENDER_PICT_OP_OVER, effects->xrenderBufferPicture(), preMultiply(color, alpha), rects.count(), rects.constData()); } #endif if (effects->compositingType() == QPainterCompositing) { QPainter *painter = effects->scenePainter(); painter->save(); color.setAlphaF(alpha); foreach (const QRect &r, paintRegion.rects()) { painter->fillRect(r, color); } painter->restore(); } } } else { AnimationEffect::paintWindow(w, mask, region, data); } } void ResizeEffect::reconfigure(ReconfigureFlags) { m_features = 0; ResizeConfig::self()->read(); if (ResizeConfig::textureScale()) m_features |= TextureScale; if (ResizeConfig::outline()) m_features |= Outline; } void ResizeEffect::slotWindowStartUserMovedResized(EffectWindow *w) { if (w->isUserResize() && !w->isUserMove()) { m_active = true; m_resizeWindow = w; m_originalGeometry = w->geometry(); m_currentGeometry = w->geometry(); w->addRepaintFull(); } } void ResizeEffect::slotWindowFinishUserMovedResized(EffectWindow *w) { if (m_active && w == m_resizeWindow) { m_active = false; m_resizeWindow = NULL; if (m_features & TextureScale) animate(w, CrossFadePrevious, 0, 150, FPx2(1.0)); effects->addRepaintFull(); } } void ResizeEffect::slotWindowStepUserMovedResized(EffectWindow *w, const QRect &geometry) { if (m_active && w == m_resizeWindow) { m_currentGeometry = geometry; effects->addRepaintFull(); } } } // namespace diff --git a/effects/screenedge/screenedgeeffect.cpp b/effects/screenedge/screenedgeeffect.cpp index 1b8dea068..481c3496a 100644 --- a/effects/screenedge/screenedgeeffect.cpp +++ b/effects/screenedge/screenedgeeffect.cpp @@ -1,361 +1,361 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2013 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "screenedgeeffect.h" // KWin #include #include #include // KDE #include // Qt #include #include #include // xcb #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #endif namespace KWin { ScreenEdgeEffect::ScreenEdgeEffect() : Effect() , m_cleanupTimer(new QTimer(this)) { - connect(effects, SIGNAL(screenEdgeApproaching(ElectricBorder,qreal,QRect)), SLOT(edgeApproaching(ElectricBorder,qreal,QRect))); + connect(effects, &EffectsHandler::screenEdgeApproaching, this, &ScreenEdgeEffect::edgeApproaching); m_cleanupTimer->setInterval(5000); m_cleanupTimer->setSingleShot(true); - connect(m_cleanupTimer, SIGNAL(timeout()), SLOT(cleanup())); + connect(m_cleanupTimer, &QTimer::timeout, this, &ScreenEdgeEffect::cleanup); connect(effects, &EffectsHandler::screenLockingChanged, this, [this] (bool locked) { if (locked) { cleanup(); } } ); } ScreenEdgeEffect::~ScreenEdgeEffect() { cleanup(); } void ScreenEdgeEffect::ensureGlowSvg() { if (!m_glow) { m_glow = new Plasma::Svg(this); m_glow->setImagePath(QStringLiteral("widgets/glowbar")); } } void ScreenEdgeEffect::cleanup() { for (QHash::iterator it = m_borders.begin(); it != m_borders.end(); ++it) { effects->addRepaint((*it)->geometry); } qDeleteAll(m_borders); m_borders.clear(); } void ScreenEdgeEffect::prePaintScreen(ScreenPrePaintData &data, int time) { effects->prePaintScreen(data, time); for (QHash::iterator it = m_borders.begin(); it != m_borders.end(); ++it) { if ((*it)->strength == 0.0) { continue; } data.paint += (*it)->geometry; } } void ScreenEdgeEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data) { effects->paintScreen(mask, region, data); for (QHash::iterator it = m_borders.begin(); it != m_borders.end(); ++it) { const qreal opacity = (*it)->strength; if (opacity == 0.0) { continue; } if (effects->isOpenGLCompositing()) { GLTexture *texture = (*it)->texture.data(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->bind(); ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::Modulate); const QVector4D constant(opacity, opacity, opacity, opacity); binder.shader()->setUniform(GLShader::ModulationConstant, constant); QMatrix4x4 mvp = data.projectionMatrix(); mvp.translate((*it)->geometry.x(), (*it)->geometry.y()); binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp); texture->render(infiniteRegion(), (*it)->geometry); texture->unbind(); glDisable(GL_BLEND); } else if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING const QRect &rect = (*it)->geometry; const QSize &size = (*it)->pictureSize; int x = rect.x(); int y = rect.y(); int width = rect.width(); int height = rect.height(); switch ((*it)->border) { case ElectricTopRight: x = rect.x() + rect.width() - size.width(); break; case ElectricBottomRight: x = rect.x() + rect.width() - size.width(); y = rect.y() + rect.height() - size.height(); break; case ElectricBottomLeft: y = rect.y() + rect.height() - size.height(); break; default: // nothing break; } xcb_render_composite(xcbConnection(), XCB_RENDER_PICT_OP_OVER, *(*it)->picture.data(), xRenderBlendPicture(opacity), effects->xrenderBufferPicture(), 0, 0, 0, 0, x, y, width, height); #endif } else if (effects->compositingType() == QPainterCompositing) { QImage tmp((*it)->image->size(), QImage::Format_ARGB32_Premultiplied); tmp.fill(Qt::transparent); QPainter p(&tmp); p.drawImage(0, 0, *(*it)->image.data()); QColor color(Qt::transparent); color.setAlphaF(opacity); p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.fillRect(QRect(QPoint(0, 0), tmp.size()), color); p.end(); QPainter *painter = effects->scenePainter(); const QRect &rect = (*it)->geometry; const QSize &size = (*it)->pictureSize; int x = rect.x(); int y = rect.y(); switch ((*it)->border) { case ElectricTopRight: x = rect.x() + rect.width() - size.width(); break; case ElectricBottomRight: x = rect.x() + rect.width() - size.width(); y = rect.y() + rect.height() - size.height(); break; case ElectricBottomLeft: y = rect.y() + rect.height() - size.height(); break; default: // nothing break; } painter->drawImage(QPoint(x, y), tmp); } } } void ScreenEdgeEffect::edgeApproaching(ElectricBorder border, qreal factor, const QRect &geometry) { QHash::iterator it = m_borders.find(border); if (it != m_borders.end()) { // need to update effects->addRepaint((*it)->geometry); (*it)->strength = factor; if ((*it)->geometry != geometry) { (*it)->geometry = geometry; effects->addRepaint((*it)->geometry); if (border == ElectricLeft || border == ElectricRight || border == ElectricTop || border == ElectricBottom) { if (effects->isOpenGLCompositing()) { (*it)->texture.reset(createEdgeGlow(border, geometry.size())); } else if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING (*it)->picture.reset(createEdgeGlow(border, geometry.size())); #endif } else if (effects->compositingType() == QPainterCompositing) { (*it)->image.reset(createEdgeGlow(border, geometry.size())); } } } if (factor == 0.0) { m_cleanupTimer->start(); } else { m_cleanupTimer->stop(); } } else if (factor != 0.0) { // need to generate new Glow Glow *glow = createGlow(border, factor, geometry); if (glow) { m_borders.insert(border, glow); effects->addRepaint(glow->geometry); } } } Glow *ScreenEdgeEffect::createGlow(ElectricBorder border, qreal factor, const QRect &geometry) { Glow *glow = new Glow(); glow->border = border; glow->strength = factor; glow->geometry = geometry; // render the glow image if (effects->isOpenGLCompositing()) { effects->makeOpenGLContextCurrent(); if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { glow->texture.reset(createCornerGlow(border)); } else { glow->texture.reset(createEdgeGlow(border, geometry.size())); } if (!glow->texture.isNull()) { glow->texture->setWrapMode(GL_CLAMP_TO_EDGE); } if (glow->texture.isNull()) { delete glow; return NULL; } } else if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { glow->pictureSize = cornerGlowSize(border); glow->picture.reset(createCornerGlow(border)); } else { glow->pictureSize = geometry.size(); glow->picture.reset(createEdgeGlow(border, geometry.size())); } if (glow->picture.isNull()) { delete glow; return NULL; } #endif } else if (effects->compositingType() == QPainterCompositing) { if (border == ElectricTopLeft || border == ElectricTopRight || border == ElectricBottomRight || border == ElectricBottomLeft) { glow->image.reset(createCornerGlow(border)); glow->pictureSize = cornerGlowSize(border); } else { glow->image.reset(createEdgeGlow(border, geometry.size())); glow->pictureSize = geometry.size(); } if (glow->image.isNull()) { delete glow; return NULL; } } return glow; } template T *ScreenEdgeEffect::createCornerGlow(ElectricBorder border) { ensureGlowSvg(); switch (border) { case ElectricTopLeft: return new T(m_glow->pixmap(QStringLiteral("bottomright")).toImage()); case ElectricTopRight: return new T(m_glow->pixmap(QStringLiteral("bottomleft")).toImage()); case ElectricBottomRight: return new T(m_glow->pixmap(QStringLiteral("topleft")).toImage()); case ElectricBottomLeft: return new T(m_glow->pixmap(QStringLiteral("topright")).toImage()); default: return NULL; } } QSize ScreenEdgeEffect::cornerGlowSize(ElectricBorder border) { ensureGlowSvg(); switch (border) { case ElectricTopLeft: return m_glow->elementSize(QStringLiteral("bottomright")); case ElectricTopRight: return m_glow->elementSize(QStringLiteral("bottomleft")); case ElectricBottomRight: return m_glow->elementSize(QStringLiteral("topleft")); case ElectricBottomLeft: return m_glow->elementSize(QStringLiteral("topright")); default: return QSize(); } } template T *ScreenEdgeEffect::createEdgeGlow(ElectricBorder border, const QSize &size) { ensureGlowSvg(); QPoint pixmapPosition(0, 0); QPixmap l, r, c; switch (border) { case ElectricTop: l = m_glow->pixmap(QStringLiteral("bottomleft")); r = m_glow->pixmap(QStringLiteral("bottomright")); c = m_glow->pixmap(QStringLiteral("bottom")); break; case ElectricBottom: l = m_glow->pixmap(QStringLiteral("topleft")); r = m_glow->pixmap(QStringLiteral("topright")); c = m_glow->pixmap(QStringLiteral("top")); pixmapPosition = QPoint(0, size.height() - c.height()); break; case ElectricLeft: l = m_glow->pixmap(QStringLiteral("topright")); r = m_glow->pixmap(QStringLiteral("bottomright")); c = m_glow->pixmap(QStringLiteral("right")); break; case ElectricRight: l = m_glow->pixmap(QStringLiteral("topleft")); r = m_glow->pixmap(QStringLiteral("bottomleft")); c = m_glow->pixmap(QStringLiteral("left")); pixmapPosition = QPoint(size.width() - c.width(), 0); break; default: return NULL; } QPixmap image(size); image.fill(Qt::transparent); QPainter p; p.begin(&image); if (border == ElectricBottom || border == ElectricTop) { p.drawPixmap(pixmapPosition, l); p.drawTiledPixmap(QRect(l.width(), pixmapPosition.y(), size.width() - l.width() - r.width(), c.height()), c); p.drawPixmap(QPoint(size.width() - r.width(), pixmapPosition.y()), r); } else { p.drawPixmap(pixmapPosition, l); p.drawTiledPixmap(QRect(pixmapPosition.x(), l.height(), c.width(), size.height() - l.height() - r.height()), c); p.drawPixmap(QPoint(pixmapPosition.x(), size.height() - r.height()), r); } p.end(); return new T(image.toImage()); } bool ScreenEdgeEffect::isActive() const { return !m_borders.isEmpty() && !effects->isScreenLocked(); } } // namespace diff --git a/effects/screenshot/screenshot.cpp b/effects/screenshot/screenshot.cpp index 3139bd85d..e35e1d84e 100644 --- a/effects/screenshot/screenshot.cpp +++ b/effects/screenshot/screenshot.cpp @@ -1,651 +1,651 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Martin Gräßlin Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "screenshot.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KWin { const static QString s_errorAlreadyTaking = QStringLiteral("org.kde.kwin.Screenshot.Error.AlreadyTaking"); const static QString s_errorAlreadyTakingMsg = QStringLiteral("A screenshot is already been taken"); const static QString s_errorFd = QStringLiteral("org.kde.kwin.Screenshot.Error.FileDescriptor"); const static QString s_errorFdMsg = QStringLiteral("No valid file descriptor"); const static QString s_errorCancelled = QStringLiteral("org.kde.kwin.Screenshot.Error.Cancelled"); const static QString s_errorCancelledMsg = QStringLiteral("Screenshot got cancelled"); const static QString s_errorInvalidArea = QStringLiteral("org.kde.kwin.Screenshot.Error.InvalidArea"); const static QString s_errorInvalidAreaMsg = QStringLiteral("Invalid area requested"); const static QString s_errorInvalidScreen = QStringLiteral("org.kde.kwin.Screenshot.Error.InvalidScreen"); const static QString s_errorInvalidScreenMsg = QStringLiteral("Invalid screen requested"); bool ScreenShotEffect::supported() { return effects->compositingType() == XRenderCompositing || (effects->isOpenGLCompositing() && GLRenderTarget::supported()); } ScreenShotEffect::ScreenShotEffect() : m_scheduledScreenshot(0) { - connect ( effects, SIGNAL(windowClosed(KWin::EffectWindow*)), SLOT(windowClosed(KWin::EffectWindow*)) ); + connect(effects, &EffectsHandler::windowClosed, this, &ScreenShotEffect::windowClosed); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Screenshot"), this, QDBusConnection::ExportScriptableContents); } ScreenShotEffect::~ScreenShotEffect() { QDBusConnection::sessionBus().unregisterObject(QStringLiteral("/Screenshot")); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING static QImage xPictureToImage(xcb_render_picture_t srcPic, const QRect &geometry, xcb_image_t **xImage) { xcb_connection_t *c = effects->xcbConnection(); xcb_pixmap_t xpix = xcb_generate_id(c); xcb_create_pixmap(c, 32, xpix, effects->x11RootWindow(), geometry.width(), geometry.height()); XRenderPicture pic(xpix, 32); xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, srcPic, XCB_RENDER_PICTURE_NONE, pic, geometry.x(), geometry.y(), 0, 0, 0, 0, geometry.width(), geometry.height()); xcb_flush(c); *xImage = xcb_image_get(c, xpix, 0, 0, geometry.width(), geometry.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP); QImage img((*xImage)->data, (*xImage)->width, (*xImage)->height, (*xImage)->stride, QImage::Format_ARGB32_Premultiplied); // TODO: byte order might need swapping xcb_free_pixmap(c, xpix); return img.copy(); } #endif void ScreenShotEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data) { m_cachedOutputGeometry = data.outputGeometry(); effects->paintScreen(mask, region, data); } void ScreenShotEffect::postPaintScreen() { effects->postPaintScreen(); if (m_scheduledScreenshot) { WindowPaintData d(m_scheduledScreenshot); double left = 0; double top = 0; double right = m_scheduledScreenshot->width(); double bottom = m_scheduledScreenshot->height(); if (m_scheduledScreenshot->hasDecoration() && m_type & INCLUDE_DECORATION) { foreach (const WindowQuad & quad, d.quads) { // we need this loop to include the decoration padding left = qMin(left, quad.left()); top = qMin(top, quad.top()); right = qMax(right, quad.right()); bottom = qMax(bottom, quad.bottom()); } } else if (m_scheduledScreenshot->hasDecoration()) { WindowQuadList newQuads; left = m_scheduledScreenshot->width(); top = m_scheduledScreenshot->height(); right = 0; bottom = 0; foreach (const WindowQuad & quad, d.quads) { if (quad.type() == WindowQuadContents) { newQuads << quad; left = qMin(left, quad.left()); top = qMin(top, quad.top()); right = qMax(right, quad.right()); bottom = qMax(bottom, quad.bottom()); } } d.quads = newQuads; } const int width = right - left; const int height = bottom - top; bool validTarget = true; QScopedPointer offscreenTexture; QScopedPointer target; if (effects->isOpenGLCompositing()) { offscreenTexture.reset(new GLTexture(GL_RGBA8, width, height)); offscreenTexture->setFilter(GL_LINEAR); offscreenTexture->setWrapMode(GL_CLAMP_TO_EDGE); target.reset(new GLRenderTarget(*offscreenTexture)); validTarget = target->valid(); } if (validTarget) { d.setXTranslation(-m_scheduledScreenshot->x() - left); d.setYTranslation(-m_scheduledScreenshot->y() - top); // render window into offscreen texture int mask = PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_TRANSLUCENT; QImage img; if (effects->isOpenGLCompositing()) { GLRenderTarget::pushRenderTarget(target.data()); glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0); QMatrix4x4 projection; projection.ortho(QRect(0, 0, offscreenTexture->width(), offscreenTexture->height())); d.setProjectionMatrix(projection); effects->drawWindow(m_scheduledScreenshot, mask, infiniteRegion(), d); // copy content from framebuffer into image img = QImage(QSize(width, height), QImage::Format_ARGB32); glReadnPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, img.byteCount(), (GLvoid*)img.bits()); GLRenderTarget::popRenderTarget(); ScreenShotEffect::convertFromGLImage(img, width, height); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING xcb_image_t *xImage = NULL; if (effects->compositingType() == XRenderCompositing) { setXRenderOffscreen(true); effects->drawWindow(m_scheduledScreenshot, mask, QRegion(0, 0, width, height), d); if (xRenderOffscreenTarget()) { img = xPictureToImage(xRenderOffscreenTarget(), QRect(0, 0, width, height), &xImage); } setXRenderOffscreen(false); } #endif if (m_type & INCLUDE_CURSOR) { grabPointerImage(img, m_scheduledScreenshot->x() + left, m_scheduledScreenshot->y() + top); } if (m_windowMode == WindowMode::Xpixmap) { const int depth = img.depth(); xcb_pixmap_t xpix = xcb_generate_id(xcbConnection()); xcb_create_pixmap(xcbConnection(), depth, xpix, x11RootWindow(), img.width(), img.height()); xcb_gcontext_t cid = xcb_generate_id(xcbConnection()); xcb_create_gc(xcbConnection(), cid, xpix, 0, NULL); xcb_put_image(xcbConnection(), XCB_IMAGE_FORMAT_Z_PIXMAP, xpix, cid, img.width(), img.height(), 0, 0, 0, depth, img.byteCount(), img.constBits()); xcb_free_gc(xcbConnection(), cid); xcb_flush(xcbConnection()); emit screenshotCreated(xpix); m_windowMode = WindowMode::NoCapture; } else if (m_windowMode == WindowMode::File) { sendReplyImage(img); } else if (m_windowMode == WindowMode::FileDescriptor) { QtConcurrent::run( [] (int fd, const QImage &img) { QFile file; if (file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle)) { QDataStream ds(&file); ds << img; file.close(); } else { close(fd); } }, m_fd, img); m_windowMode = WindowMode::NoCapture; m_fd = -1; } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (xImage) { xcb_image_destroy(xImage); } #endif } m_scheduledScreenshot = NULL; } if (!m_scheduledGeometry.isNull()) { if (!m_cachedOutputGeometry.isNull()) { // special handling for per-output geometry rendering const QRect intersection = m_scheduledGeometry.intersected(m_cachedOutputGeometry); if (intersection.isEmpty()) { // doesn't intersect, not going onto this screenshot return; } const QImage img = blitScreenshot(intersection); if (img.size() == m_scheduledGeometry.size()) { // we are done sendReplyImage(img); return; } if (m_multipleOutputsImage.isNull()) { m_multipleOutputsImage = QImage(m_scheduledGeometry.size(), QImage::Format_ARGB32); m_multipleOutputsImage.fill(Qt::transparent); } QPainter p; p.begin(&m_multipleOutputsImage); p.drawImage(intersection.topLeft() - m_scheduledGeometry.topLeft(), img); p.end(); m_multipleOutputsRendered = m_multipleOutputsRendered.united(intersection); if (m_multipleOutputsRendered.boundingRect() == m_scheduledGeometry) { sendReplyImage(m_multipleOutputsImage); } } else { const QImage img = blitScreenshot(m_scheduledGeometry); sendReplyImage(img); } } } void ScreenShotEffect::sendReplyImage(const QImage &img) { if (m_fd != -1) { QtConcurrent::run( [] (int fd, const QImage &img) { QFile file; if (file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle)) { QDataStream ds(&file); ds << img; file.close(); } else { close(fd); } }, m_fd, img); m_fd = -1; } else { QDBusConnection::sessionBus().send(m_replyMessage.createReply(saveTempImage(img))); } m_scheduledGeometry = QRect(); m_multipleOutputsImage = QImage(); m_multipleOutputsRendered = QRegion(); m_captureCursor = false; m_windowMode = WindowMode::NoCapture; } QString ScreenShotEffect::saveTempImage(const QImage &img) { if (img.isNull()) { return QString(); } QTemporaryFile temp(QDir::tempPath() + QDir::separator() + QLatin1String("kwin_screenshot_XXXXXX.png")); temp.setAutoRemove(false); if (!temp.open()) { return QString(); } img.save(&temp); temp.close(); KNotification::event(KNotification::Notification, i18nc("Notification caption that a screenshot got saved to file", "Screenshot"), i18nc("Notification with path to screenshot file", "Screenshot saved to %1", temp.fileName()), QStringLiteral("spectacle")); return temp.fileName(); } void ScreenShotEffect::screenshotWindowUnderCursor(int mask) { if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return; } m_type = (ScreenShotType)mask; const QPoint cursor = effects->cursorPos(); EffectWindowList order = effects->stackingOrder(); EffectWindowList::const_iterator it = order.constEnd(), first = order.constBegin(); while( it != first ) { m_scheduledScreenshot = *(--it); if (m_scheduledScreenshot->isOnCurrentDesktop() && !m_scheduledScreenshot->isMinimized() && !m_scheduledScreenshot->isDeleted() && m_scheduledScreenshot->geometry().contains(cursor)) break; m_scheduledScreenshot = 0; } if (m_scheduledScreenshot) { m_scheduledScreenshot->addRepaintFull(); } } void ScreenShotEffect::screenshotForWindow(qulonglong winid, int mask) { m_type = (ScreenShotType) mask; EffectWindow* w = effects->findWindow(winid); if(w && !w->isMinimized() && !w->isDeleted()) { m_windowMode = WindowMode::Xpixmap; m_scheduledScreenshot = w; m_scheduledScreenshot->addRepaintFull(); } } QString ScreenShotEffect::interactive(int mask) { if (!calledFromDBus()) { return QString(); } if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return QString(); } m_type = (ScreenShotType) mask; m_windowMode = WindowMode::File; m_replyMessage = message(); setDelayedReply(true); effects->startInteractiveWindowSelection( [this] (EffectWindow *w) { hideInfoMessage(); if (!w) { QDBusConnection::sessionBus().send(m_replyMessage.createErrorReply(s_errorCancelled, s_errorCancelledMsg)); m_windowMode = WindowMode::NoCapture; return; } else { m_scheduledScreenshot = w; m_scheduledScreenshot->addRepaintFull(); } }); showInfoMessage(InfoMessageMode::Window); return QString(); } void ScreenShotEffect::interactive(QDBusUnixFileDescriptor fd, int mask) { if (!calledFromDBus()) { return; } if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return; } m_fd = dup(fd.fileDescriptor()); if (m_fd == -1) { sendErrorReply(s_errorFd, s_errorFdMsg); return; } m_type = (ScreenShotType) mask; m_windowMode = WindowMode::FileDescriptor; effects->startInteractiveWindowSelection( [this] (EffectWindow *w) { hideInfoMessage(); if (!w) { close(m_fd); m_fd = -1; m_windowMode = WindowMode::NoCapture; return; } else { m_scheduledScreenshot = w; m_scheduledScreenshot->addRepaintFull(); } }); showInfoMessage(InfoMessageMode::Window); } void ScreenShotEffect::showInfoMessage(InfoMessageMode mode) { QString text; switch (mode) { case InfoMessageMode::Window: text = i18n("Select window to screen shot with left click or enter.\nEscape or right click to cancel."); break; case InfoMessageMode::Screen: text = i18n("Create screen shot with left click or enter.\nEscape or right click to cancel."); break; } effects->showOnScreenMessage(text, QStringLiteral("spectacle")); } void ScreenShotEffect::hideInfoMessage() { effects->hideOnScreenMessage(EffectsHandler::OnScreenMessageHideFlag::SkipsCloseAnimation); } QString ScreenShotEffect::screenshotFullscreen(bool captureCursor) { if (!calledFromDBus()) { return QString(); } if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return QString(); } m_replyMessage = message(); setDelayedReply(true); m_scheduledGeometry = effects->virtualScreenGeometry(); m_captureCursor = captureCursor; effects->addRepaintFull(); return QString(); } void ScreenShotEffect::screenshotFullscreen(QDBusUnixFileDescriptor fd, bool captureCursor) { if (!calledFromDBus()) { return; } if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return; } m_fd = dup(fd.fileDescriptor()); if (m_fd == -1) { sendErrorReply(s_errorFd, s_errorFdMsg); return; } m_captureCursor = captureCursor; showInfoMessage(InfoMessageMode::Screen); effects->startInteractivePositionSelection( [this] (const QPoint &p) { hideInfoMessage(); if (p == QPoint(-1, -1)) { // error condition close(m_fd); m_fd = -1; } else { m_scheduledGeometry = effects->virtualScreenGeometry(); effects->addRepaint(m_scheduledGeometry); } } ); } QString ScreenShotEffect::screenshotScreen(int screen, bool captureCursor) { if (!calledFromDBus()) { return QString(); } if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return QString(); } m_scheduledGeometry = effects->clientArea(FullScreenArea, screen, 0); if (m_scheduledGeometry.isNull()) { sendErrorReply(s_errorInvalidScreen, s_errorInvalidScreenMsg); return QString(); } m_captureCursor = captureCursor; m_replyMessage = message(); setDelayedReply(true); effects->addRepaint(m_scheduledGeometry); return QString(); } void ScreenShotEffect::screenshotScreen(QDBusUnixFileDescriptor fd, bool captureCursor) { if (!calledFromDBus()) { return; } if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return; } m_fd = dup(fd.fileDescriptor()); if (m_fd == -1) { sendErrorReply(s_errorFd, s_errorFdMsg); return; } m_captureCursor = captureCursor; showInfoMessage(InfoMessageMode::Screen); effects->startInteractivePositionSelection( [this] (const QPoint &p) { hideInfoMessage(); if (p == QPoint(-1, -1)) { // error condition close(m_fd); m_fd = -1; } else { m_scheduledGeometry = effects->clientArea(FullScreenArea, effects->screenNumber(p), 0); if (m_scheduledGeometry.isNull()) { close(m_fd); m_fd = -1; return; } effects->addRepaint(m_scheduledGeometry); } } ); } QString ScreenShotEffect::screenshotArea(int x, int y, int width, int height, bool captureCursor) { if (!calledFromDBus()) { return QString(); } if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); return QString(); } m_scheduledGeometry = QRect(x, y, width, height); if (m_scheduledGeometry.isNull() || m_scheduledGeometry.isEmpty()) { m_scheduledGeometry = QRect(); sendErrorReply(s_errorInvalidArea, s_errorInvalidAreaMsg); return QString(); } m_captureCursor = captureCursor; m_replyMessage = message(); setDelayedReply(true); effects->addRepaint(m_scheduledGeometry); return QString(); } QImage ScreenShotEffect::blitScreenshot(const QRect &geometry) { QImage img; if (effects->isOpenGLCompositing()) { img = QImage(geometry.size(), QImage::Format_ARGB32); if (GLRenderTarget::blitSupported() && !GLPlatform::instance()->isGLES()) { GLTexture tex(GL_RGBA8, geometry.width(), geometry.height()); GLRenderTarget target(tex); target.blitFromFramebuffer(geometry); // copy content from framebuffer into image tex.bind(); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)img.bits()); tex.unbind(); } else { glReadPixels(0, 0, img.width(), img.height(), GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)img.bits()); } ScreenShotEffect::convertFromGLImage(img, geometry.width(), geometry.height()); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) { xcb_image_t *xImage = NULL; img = xPictureToImage(effects->xrenderBufferPicture(), geometry, &xImage); if (xImage) { xcb_image_destroy(xImage); } } #endif if (m_captureCursor) { grabPointerImage(img, geometry.x(), geometry.y()); } return img; } void ScreenShotEffect::grabPointerImage(QImage& snapshot, int offsetx, int offsety) { const auto cursor = effects->cursorImage(); if (cursor.image().isNull()) return; QPainter painter(&snapshot); painter.drawImage(effects->cursorPos() - cursor.hotSpot() - QPoint(offsetx, offsety), cursor.image()); } void ScreenShotEffect::convertFromGLImage(QImage &img, int w, int h) { // from QtOpenGL/qgl.cpp // Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) // see http://qt.gitorious.org/qt/qt/blobs/master/src/opengl/qgl.cpp if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { // OpenGL gives RGBA; Qt wants ARGB uint *p = (uint*)img.bits(); uint *end = p + w * h; while (p < end) { uint a = *p << 24; *p = (*p >> 8) | a; p++; } } else { // OpenGL gives ABGR (i.e. RGBA backwards); Qt wants ARGB for (int y = 0; y < h; y++) { uint *q = (uint*)img.scanLine(y); for (int x = 0; x < w; ++x) { const uint pixel = *q; *q = ((pixel << 16) & 0xff0000) | ((pixel >> 16) & 0xff) | (pixel & 0xff00ff00); q++; } } } img = img.mirrored(); } bool ScreenShotEffect::isActive() const { return (m_scheduledScreenshot != NULL || !m_scheduledGeometry.isNull()) && !effects->isScreenLocked(); } void ScreenShotEffect::windowClosed( EffectWindow* w ) { if (w == m_scheduledScreenshot) { m_scheduledScreenshot = NULL; screenshotWindowUnderCursor(m_type); } } bool ScreenShotEffect::isTakingScreenshot() const { if (!m_scheduledGeometry.isNull()) { return true; } if (m_windowMode != WindowMode::NoCapture) { return true; } if (m_fd != -1) { return true; } return false; } } // namespace diff --git a/effects/slideback/slideback.cpp b/effects/slideback/slideback.cpp index c0b7aa946..46073645d 100644 --- a/effects/slideback/slideback.cpp +++ b/effects/slideback/slideback.cpp @@ -1,341 +1,341 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Michael Zanetti This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "slideback.h" namespace KWin { SlideBackEffect::SlideBackEffect() { m_tabboxActive = 0; m_justMapped = m_upmostWindow = NULL; - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowDeleted(KWin::EffectWindow*)), SLOT(slotWindowDeleted(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowUnminimized(KWin::EffectWindow*)), SLOT(slotWindowUnminimized(KWin::EffectWindow*))); - connect(effects, SIGNAL(tabBoxAdded(int)), SLOT(slotTabBoxAdded())); - connect(effects, SIGNAL(stackingOrderChanged()), SLOT(slotStackingOrderChanged())); - connect(effects, SIGNAL(tabBoxClosed()), SLOT(slotTabBoxClosed())); + connect(effects, &EffectsHandler::windowAdded, this, &SlideBackEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowDeleted, this, &SlideBackEffect::slotWindowDeleted); + connect(effects, &EffectsHandler::windowUnminimized, this, &SlideBackEffect::slotWindowUnminimized); + connect(effects, &EffectsHandler::tabBoxAdded, this, &SlideBackEffect::slotTabBoxAdded); + connect(effects, &EffectsHandler::stackingOrderChanged, this, &SlideBackEffect::slotStackingOrderChanged); + connect(effects, &EffectsHandler::tabBoxClosed, this, &SlideBackEffect::slotTabBoxClosed); } void SlideBackEffect::slotStackingOrderChanged() { if (effects->activeFullScreenEffect() || m_tabboxActive) { oldStackingOrder = effects->stackingOrder(); usableOldStackingOrder = usableWindows(oldStackingOrder); return; } EffectWindowList newStackingOrder = effects->stackingOrder(), usableNewStackingOrder = usableWindows(newStackingOrder); if (usableNewStackingOrder == usableOldStackingOrder || usableNewStackingOrder.isEmpty()) { oldStackingOrder = newStackingOrder; usableOldStackingOrder = usableNewStackingOrder; return; } m_upmostWindow = usableNewStackingOrder.last(); if (m_upmostWindow == m_justMapped ) // a window was added, got on top, stacking changed. Nothing impressive m_justMapped = 0; else if (!usableOldStackingOrder.isEmpty() && m_upmostWindow != usableOldStackingOrder.last()) windowRaised(m_upmostWindow); oldStackingOrder = newStackingOrder; usableOldStackingOrder = usableNewStackingOrder; } void SlideBackEffect::windowRaised(EffectWindow *w) { // Determine all windows on top of the activated one bool currentFound = false; foreach (EffectWindow * tmp, oldStackingOrder) { if (!currentFound) { if (tmp == w) { currentFound = true; } } else { if (isWindowUsable(tmp) && tmp->isOnCurrentDesktop() && w->isOnCurrentDesktop()) { // Do we have to move it? if (intersects(w, tmp->geometry())) { QRect slideRect; slideRect = getSlideDestination(getModalGroupGeometry(w), tmp->geometry()); effects->setElevatedWindow(tmp, true); elevatedList.append(tmp); motionManager.manage(tmp); motionManager.moveWindow(tmp, slideRect); destinationList.insert(tmp, slideRect); coveringWindows.append(tmp); } else { //Does it intersect with a moved (elevated) window and do we have to elevate it too? foreach (EffectWindow * elevatedWindow, elevatedList) { if (tmp->geometry().intersects(elevatedWindow->geometry())) { effects->setElevatedWindow(tmp, true); elevatedList.append(tmp); break; } } } } if (tmp->isDock() || tmp->keepAbove()) { effects->setElevatedWindow(tmp, true); elevatedList.append(tmp); } } } // If a window is minimized it could happen that the panels stay elevated without any windows sliding. // clear all elevation settings if (!motionManager.managingWindows()) { foreach (EffectWindow * tmp, elevatedList) { effects->setElevatedWindow(tmp, false); } } } QRect SlideBackEffect::getSlideDestination(const QRect &windowUnderGeometry, const QRect &windowOverGeometry) { // Determine the shortest way: int leftSlide = windowUnderGeometry.left() - windowOverGeometry.right() - 20; int rightSlide = windowUnderGeometry.right() - windowOverGeometry.left() + 20; int upSlide = windowUnderGeometry.top() - windowOverGeometry.bottom() - 20; int downSlide = windowUnderGeometry.bottom() - windowOverGeometry.top() + 20; int horizSlide = leftSlide; if (qAbs(horizSlide) > qAbs(rightSlide)) { horizSlide = rightSlide; } int vertSlide = upSlide; if (qAbs(vertSlide) > qAbs(downSlide)) { vertSlide = downSlide; } QRect slideRect = windowOverGeometry; if (qAbs(horizSlide) < qAbs(vertSlide)) { slideRect.moveLeft(slideRect.x() + horizSlide); } else { slideRect.moveTop(slideRect.y() + vertSlide); } return slideRect; } void SlideBackEffect::prePaintScreen(ScreenPrePaintData &data, int time) { if (motionManager.managingWindows()) { motionManager.calculate(time); data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS; } for (auto const &w : effects->stackingOrder()) { w->setData(WindowForceBlurRole, QVariant(true)); } effects->prePaintScreen(data, time); } void SlideBackEffect::postPaintScreen() { if (motionManager.areWindowsMoving()) { effects->addRepaintFull(); } for (auto &w : effects->stackingOrder()) { w->setData(WindowForceBlurRole, QVariant()); } effects->postPaintScreen(); } void SlideBackEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time) { if (motionManager.isManaging(w)) { data.setTransformed(); } effects->prePaintWindow(w, data, time); } void SlideBackEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { if (motionManager.isManaging(w)) { motionManager.apply(w, data); } foreach (const QRegion &r, clippedRegions) { region = region.intersected(r); } effects->paintWindow(w, mask, region, data); for (int i = clippedRegions.count() - 1; i > -1; --i) PaintClipper::pop(clippedRegions.at(i)); clippedRegions.clear(); } void SlideBackEffect::postPaintWindow(EffectWindow* w) { if (motionManager.isManaging(w)) { if (destinationList.contains(w)) { if (!motionManager.isWindowMoving(w)) { // has window reched its destination? // If we are still intersecting with the upmostWindow it is moving. slide to somewhere else // restore the stacking order of all windows not intersecting any more except panels if (coveringWindows.contains(w)) { EffectWindowList tmpList; foreach (EffectWindow * tmp, elevatedList) { QRect elevatedGeometry = tmp->geometry(); if (motionManager.isManaging(tmp)) { elevatedGeometry = motionManager.transformedGeometry(tmp).toAlignedRect(); } if (m_upmostWindow && !tmp->isDock() && !tmp->keepAbove() && m_upmostWindow->geometry().intersects(elevatedGeometry)) { QRect newDestination; newDestination = getSlideDestination(getModalGroupGeometry(m_upmostWindow), elevatedGeometry); if (!motionManager.isManaging(tmp)) { motionManager.manage(tmp); } motionManager.moveWindow(tmp, newDestination); destinationList[tmp] = newDestination; } else { if (!tmp->isDock()) { bool keepElevated = false; foreach (EffectWindow * elevatedWindow, tmpList) { if (tmp->geometry().intersects(elevatedWindow->geometry())) { keepElevated = true; } } if (!keepElevated) { effects->setElevatedWindow(tmp, false); elevatedList.removeAll(tmp); } } } tmpList.append(tmp); } } else { // Move the window back where it belongs motionManager.moveWindow(w, w->geometry()); destinationList.remove(w); } } } else { // is window back at its original position? if (!motionManager.isWindowMoving(w)) { motionManager.unmanage(w); effects->addRepaintFull(); } } if (coveringWindows.contains(w)) { // It could happen that there is no aciveWindow() here if the user clicks the close-button on an inactive window. // Just skip... the window will be removed in windowDeleted() later if (m_upmostWindow && !intersects(m_upmostWindow, motionManager.transformedGeometry(w).toAlignedRect())) { coveringWindows.removeAll(w); if (coveringWindows.isEmpty()) { // Restore correct stacking order foreach (EffectWindow * tmp, elevatedList) { effects->setElevatedWindow(tmp, false); } elevatedList.clear(); } } } } effects->postPaintWindow(w); } void SlideBackEffect::slotWindowDeleted(EffectWindow* w) { if (w == m_upmostWindow) m_upmostWindow = 0; if (w == m_justMapped) m_justMapped = 0; usableOldStackingOrder.removeAll(w); oldStackingOrder.removeAll(w); coveringWindows.removeAll(w); elevatedList.removeAll(w); if (motionManager.isManaging(w)) { motionManager.unmanage(w); } } void SlideBackEffect::slotWindowAdded(EffectWindow *w) { m_justMapped = w; } void SlideBackEffect::slotWindowUnminimized(EffectWindow* w) { // SlideBack should not be triggered on an unminimized window. For this we need to store the last unminimized window. m_justMapped = w; // the stackingOrderChanged() signal came before the window turned an effect window // usually this is no problem as the change shall not be caught anyway, but // the window may have changed its stack position, bug #353745 slotStackingOrderChanged(); } void SlideBackEffect::slotTabBoxAdded() { ++m_tabboxActive; } void SlideBackEffect::slotTabBoxClosed() { m_tabboxActive = qMax(m_tabboxActive-1, 0); } bool SlideBackEffect::isWindowUsable(EffectWindow* w) { return w && (w->isNormalWindow() || w->isDialog()) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized() && w->isCurrentTab() && w->isPaintingEnabled(); } bool SlideBackEffect::intersects(EffectWindow* windowUnder, const QRect &windowOverGeometry) { QRect windowUnderGeometry = getModalGroupGeometry(windowUnder); return windowUnderGeometry.intersects(windowOverGeometry); } EffectWindowList SlideBackEffect::usableWindows(const EffectWindowList & allWindows) { EffectWindowList retList; auto isWindowVisible = [] (const EffectWindow *window) { return window && effects->virtualScreenGeometry().intersects(window->geometry()); }; foreach (EffectWindow * tmp, allWindows) { if (isWindowUsable(tmp) && isWindowVisible(tmp)) { retList.append(tmp); } } return retList; } QRect SlideBackEffect::getModalGroupGeometry(EffectWindow *w) { QRect modalGroupGeometry = w->geometry(); if (w->isModal()) { foreach (EffectWindow * modalWindow, w->mainWindows()) { modalGroupGeometry = modalGroupGeometry.united(getModalGroupGeometry(modalWindow)); } } return modalGroupGeometry; } bool SlideBackEffect::isActive() const { return motionManager.managingWindows(); } } //Namespace diff --git a/effects/startupfeedback/startupfeedback.cpp b/effects/startupfeedback/startupfeedback.cpp index 08e1e2d7c..01f0ae23d 100644 --- a/effects/startupfeedback/startupfeedback.cpp +++ b/effects/startupfeedback/startupfeedback.cpp @@ -1,402 +1,401 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "startupfeedback.h" // Qt #include #include #include #include #include #include // KDE #include #include #include #include #include #include // KWin #include // based on StartupId in KRunner by Lubos Lunak // Copyright (C) 2001 Lubos Lunak namespace KWin { // number of key frames for bouncing animation static const int BOUNCE_FRAMES = 20; // duration between two key frames in msec static const int BOUNCE_FRAME_DURATION = 30; // duration of one bounce animation static const int BOUNCE_DURATION = BOUNCE_FRAME_DURATION * BOUNCE_FRAMES; // number of key frames for blinking animation static const int BLINKING_FRAMES = 5; // duration between two key frames in msec static const int BLINKING_FRAME_DURATION = 100; // duration of one blinking animation static const int BLINKING_DURATION = BLINKING_FRAME_DURATION * BLINKING_FRAMES; //const int color_to_pixmap[] = { 0, 1, 2, 3, 2, 1 }; static const int FRAME_TO_BOUNCE_YOFFSET[] = { -5, -1, 2, 5, 8, 10, 12, 13, 15, 15, 15, 15, 14, 12, 10, 8, 5, 2, -1, -5 }; static const QSize BOUNCE_SIZES[] = { QSize(16, 16), QSize(14, 18), QSize(12, 20), QSize(18, 14), QSize(20, 12) }; static const int FRAME_TO_BOUNCE_TEXTURE[] = { 0, 0, 0, 1, 2, 2, 1, 0, 3, 4, 4, 3, 0, 1, 2, 2, 1, 0, 0, 0 }; static const int FRAME_TO_BLINKING_COLOR[] = { 0, 1, 2, 3, 2, 1 }; static const QColor BLINKING_COLORS[] = { Qt::black, Qt::darkGray, Qt::lightGray, Qt::white, Qt::white }; static const int s_startupDefaultTimeout = 5; StartupFeedbackEffect::StartupFeedbackEffect() : m_bounceSizesRatio(1.0) , m_startupInfo(new KStartupInfo(KStartupInfo::CleanOnCantDetect, this)) , m_selection(nullptr) , m_active(false) , m_frame(0) , m_progress(0) , m_texture(0) , m_type(BouncingFeedback) , m_blinkingShader(0) , m_cursorSize(0) { for (int i = 0; i < 5; ++i) { m_bouncingTextures[i] = 0; } if (KWindowSystem::isPlatformX11()) { m_selection = new KSelectionOwner("_KDE_STARTUP_FEEDBACK", xcbConnection(), x11RootWindow(), this); m_selection->claim(true); } - connect(m_startupInfo, SIGNAL(gotNewStartup(KStartupInfoId,KStartupInfoData)), SLOT(gotNewStartup(KStartupInfoId,KStartupInfoData))); - connect(m_startupInfo, SIGNAL(gotRemoveStartup(KStartupInfoId,KStartupInfoData)), SLOT(gotRemoveStartup(KStartupInfoId,KStartupInfoData))); - connect(m_startupInfo, SIGNAL(gotStartupChange(KStartupInfoId,KStartupInfoData)), SLOT(gotStartupChange(KStartupInfoId,KStartupInfoData))); - connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); + connect(m_startupInfo, &KStartupInfo::gotNewStartup, this, &StartupFeedbackEffect::gotNewStartup); + connect(m_startupInfo, &KStartupInfo::gotRemoveStartup, this, &StartupFeedbackEffect::gotRemoveStartup); + connect(m_startupInfo, &KStartupInfo::gotStartupChange, this, &StartupFeedbackEffect::gotStartupChange); + connect(effects, &EffectsHandler::mouseChanged, this, &StartupFeedbackEffect::slotMouseChanged); reconfigure(ReconfigureAll); } StartupFeedbackEffect::~StartupFeedbackEffect() { if (m_active) { effects->stopMousePolling(); } for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; } delete m_texture; delete m_blinkingShader; } bool StartupFeedbackEffect::supported() { return effects->isOpenGLCompositing(); } void StartupFeedbackEffect::reconfigure(Effect::ReconfigureFlags flags) { Q_UNUSED(flags) KConfig conf(QStringLiteral("klaunchrc"), KConfig::NoGlobals); KConfigGroup c = conf.group("FeedbackStyle"); const bool busyCursor = c.readEntry("BusyCursor", true); c = conf.group("BusyCursorSettings"); m_startupInfo->setTimeout(c.readEntry("Timeout", s_startupDefaultTimeout)); const bool busyBlinking = c.readEntry("Blinking", false); const bool busyBouncing = c.readEntry("Bouncing", true); if (!busyCursor) m_type = NoFeedback; else if (busyBouncing) m_type = BouncingFeedback; else if (busyBlinking) { m_type = BlinkingFeedback; if (effects->compositingType() == OpenGL2Compositing) { delete m_blinkingShader; m_blinkingShader = ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("blinking-startup-fragment.glsl")); if (m_blinkingShader->isValid()) { qCDebug(KWINEFFECTS) << "Blinking Shader is valid"; } else { qCDebug(KWINEFFECTS) << "Blinking Shader is not valid"; } } } else m_type = PassiveFeedback; if (m_active) { stop(); start(m_startups[ m_currentStartup ]); } } void StartupFeedbackEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (m_active) { // need the unclipped version switch(m_type) { case BouncingFeedback: m_progress = (m_progress + time) % BOUNCE_DURATION; m_frame = qRound((qreal)m_progress / (qreal)BOUNCE_FRAME_DURATION) % BOUNCE_FRAMES; m_currentGeometry = feedbackRect(); // bounce alters geometry with m_frame data.paint = data.paint.united(m_currentGeometry); break; case BlinkingFeedback: m_progress = (m_progress + time) % BLINKING_DURATION; m_frame = qRound((qreal)m_progress / (qreal)BLINKING_FRAME_DURATION) % BLINKING_FRAMES; break; default: break; // nothing } } effects->prePaintScreen(data, time); } void StartupFeedbackEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); if (m_active) { GLTexture* texture; switch(m_type) { case BouncingFeedback: texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]]; break; case BlinkingFeedback: // fall through case PassiveFeedback: texture = m_texture; break; default: return; // safety } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); texture->bind(); if (m_type == BlinkingFeedback && m_blinkingShader && m_blinkingShader->isValid()) { const QColor& blinkingColor = BLINKING_COLORS[ FRAME_TO_BLINKING_COLOR[ m_frame ]]; ShaderManager::instance()->pushShader(m_blinkingShader); m_blinkingShader->setUniform(GLShader::Color, blinkingColor); } else { ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); } QMatrix4x4 mvp = data.projectionMatrix(); mvp.translate(m_currentGeometry.x(), m_currentGeometry.y()); ShaderManager::instance()->getBoundShader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp); texture->render(m_currentGeometry, m_currentGeometry); ShaderManager::instance()->popShader(); texture->unbind(); glDisable(GL_BLEND); } } void StartupFeedbackEffect::postPaintScreen() { if (m_active) { m_dirtyRect = m_currentGeometry; // ensure the now dirty region is cleaned on the next pass if (m_type == BlinkingFeedback || m_type == BouncingFeedback) effects->addRepaint(m_dirtyRect); // we also have to trigger a repaint } effects->postPaintScreen(); } void StartupFeedbackEffect::slotMouseChanged(const QPoint& pos, const QPoint& oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers) { Q_UNUSED(pos) Q_UNUSED(oldpos) Q_UNUSED(buttons) Q_UNUSED(oldbuttons) Q_UNUSED(modifiers) Q_UNUSED(oldmodifiers) if (m_active) { m_dirtyRect |= m_currentGeometry; m_currentGeometry = feedbackRect(); m_dirtyRect |= m_currentGeometry; effects->addRepaint(m_dirtyRect); } } void StartupFeedbackEffect::gotNewStartup(const KStartupInfoId& id, const KStartupInfoData& data) { const QString& icon = data.findIcon(); m_currentStartup = id; m_startups[ id ] = icon; start(icon); } void StartupFeedbackEffect::gotRemoveStartup(const KStartupInfoId& id, const KStartupInfoData& data) { Q_UNUSED( data ) m_startups.remove(id); if (m_startups.count() == 0) { m_currentStartup = KStartupInfoId(); // null stop(); return; } m_currentStartup = m_startups.begin().key(); start(m_startups[ m_currentStartup ]); } void StartupFeedbackEffect::gotStartupChange(const KStartupInfoId& id, const KStartupInfoData& data) { if (m_currentStartup == id) { const QString& icon = data.findIcon(); if (!icon.isEmpty() && icon != m_startups[ m_currentStartup ]) { m_startups[ id ] = icon; start(icon); } } } void StartupFeedbackEffect::start(const QString& icon) { if (m_type == NoFeedback) return; if (!m_active) effects->startMousePolling(); m_active = true; // get ratio for bouncing cursor so we don't need to manually calculate the sizes for each icon size if (m_type == BouncingFeedback) m_bounceSizesRatio = IconSize(KIconLoader::Small) / 16.0; QPixmap iconPixmap = KIconLoader::global()->loadIcon(icon, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0, true); // return null pixmap if not found if (iconPixmap.isNull()) iconPixmap = SmallIcon(QStringLiteral("system-run")); prepareTextures(iconPixmap); auto readCursorSize = []() -> int { // read details about the mouse-cursor theme define per default KConfigGroup mousecfg(effects->inputConfig(), "Mouse"); QString size = mousecfg.readEntry("cursorSize", QString()); // fetch a reasonable size for the cursor-theme image bool ok; int cursorSize = size.toInt(&ok); if (!ok) cursorSize = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize); return cursorSize; }; m_cursorSize = readCursorSize(); m_dirtyRect = m_currentGeometry = feedbackRect(); effects->addRepaint(m_dirtyRect); } void StartupFeedbackEffect::stop() { if (m_active) effects->stopMousePolling(); m_active = false; effects->makeOpenGLContextCurrent(); switch(m_type) { case BouncingFeedback: for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; m_bouncingTextures[i] = 0; } break; case BlinkingFeedback: case PassiveFeedback: delete m_texture; m_texture = 0; break; case NoFeedback: return; // don't want the full repaint default: break; // impossible } effects->addRepaintFull(); } void StartupFeedbackEffect::prepareTextures(const QPixmap& pix) { effects->makeOpenGLContextCurrent(); switch(m_type) { case BouncingFeedback: for (int i = 0; i < 5; ++i) { delete m_bouncingTextures[i]; m_bouncingTextures[i] = new GLTexture(scalePixmap(pix, BOUNCE_SIZES[i])); } break; case BlinkingFeedback: case PassiveFeedback: m_texture = new GLTexture(pix); break; default: // for safety m_active = false; break; } } QImage StartupFeedbackEffect::scalePixmap(const QPixmap& pm, const QSize& size) const { const QSize& adjustedSize = size * m_bounceSizesRatio; QImage scaled = pm.toImage().scaled(adjustedSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); if (scaled.format() != QImage::Format_ARGB32_Premultiplied && scaled.format() != QImage::Format_ARGB32) scaled = scaled.convertToFormat(QImage::Format_ARGB32); QImage result(20 * m_bounceSizesRatio, 20 * m_bounceSizesRatio, QImage::Format_ARGB32); QPainter p(&result); p.setCompositionMode(QPainter::CompositionMode_Source); p.fillRect(result.rect(), Qt::transparent); p.drawImage((20 * m_bounceSizesRatio - adjustedSize.width()) / 2, (20*m_bounceSizesRatio - adjustedSize.height()) / 2, scaled, 0, 0, adjustedSize.width(), adjustedSize.height() * m_bounceSizesRatio); return result; } QRect StartupFeedbackEffect::feedbackRect() const { int xDiff; if (m_cursorSize <= 16) xDiff = 8 + 7; else if (m_cursorSize <= 32) xDiff = 16 + 7; else if (m_cursorSize <= 48) xDiff = 24 + 7; else xDiff = 32 + 7; int yDiff = xDiff; GLTexture* texture = 0; int yOffset = 0; switch(m_type) { case BouncingFeedback: texture = m_bouncingTextures[ FRAME_TO_BOUNCE_TEXTURE[ m_frame ]]; yOffset = FRAME_TO_BOUNCE_YOFFSET[ m_frame ] * m_bounceSizesRatio; break; case BlinkingFeedback: // fall through case PassiveFeedback: texture = m_texture; break; default: // nothing break; } const QPoint cursorPos = effects->cursorPos() + QPoint(xDiff, yDiff + yOffset); QRect rect; if( texture ) rect = QRect(cursorPos, texture->size()); return rect; } bool StartupFeedbackEffect::isActive() const { return m_active; } } // namespace diff --git a/effects/thumbnailaside/thumbnailaside.cpp b/effects/thumbnailaside/thumbnailaside.cpp index 2e58ea9b7..c17269e44 100644 --- a/effects/thumbnailaside/thumbnailaside.cpp +++ b/effects/thumbnailaside/thumbnailaside.cpp @@ -1,192 +1,192 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Lubos Lunak Copyright (C) 2007 Christian Nitschkowski This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "thumbnailaside.h" // KConfigSkeleton #include "thumbnailasideconfig.h" #include #include #include namespace KWin { ThumbnailAsideEffect::ThumbnailAsideEffect() { initConfig(); QAction* a = new QAction(this); a->setObjectName(QStringLiteral("ToggleCurrentThumbnail")); a->setText(i18n("Toggle Thumbnail for Current Window")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_T); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_T); effects->registerGlobalShortcut(Qt::META + Qt::CTRL + Qt::Key_T, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleCurrentThumbnail())); + connect(a, &QAction::triggered, this, &ThumbnailAsideEffect::toggleCurrentThumbnail); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowGeometryShapeChanged(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowGeometryShapeChanged(KWin::EffectWindow*,QRect))); - connect(effects, SIGNAL(windowDamaged(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowDamaged(KWin::EffectWindow*,QRect))); - connect(effects, SIGNAL(screenLockingChanged(bool)), SLOT(repaintAll())); + connect(effects, &EffectsHandler::windowClosed, this, &ThumbnailAsideEffect::slotWindowClosed); + connect(effects, &EffectsHandler::windowGeometryShapeChanged, this, &ThumbnailAsideEffect::slotWindowGeometryShapeChanged); + connect(effects, &EffectsHandler::windowDamaged, this, &ThumbnailAsideEffect::slotWindowDamaged); + connect(effects, &EffectsHandler::screenLockingChanged, this, &ThumbnailAsideEffect::repaintAll); reconfigure(ReconfigureAll); } void ThumbnailAsideEffect::reconfigure(ReconfigureFlags) { ThumbnailAsideConfig::self()->read(); maxwidth = ThumbnailAsideConfig::maxWidth(); spacing = ThumbnailAsideConfig::spacing(); opacity = ThumbnailAsideConfig::opacity()/100.0; screen = ThumbnailAsideConfig::screen(); // Xinerama screen TODO add gui option arrange(); } void ThumbnailAsideEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { painted = QRegion(); effects->paintScreen(mask, region, data); foreach (const Data & d, windows) { if (painted.intersects(d.rect)) { WindowPaintData data(d.window); data.multiplyOpacity(opacity); QRect region; setPositionTransformations(data, region, d.window, d.rect, Qt::KeepAspectRatio); effects->drawWindow(d.window, PAINT_WINDOW_OPAQUE | PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED | PAINT_WINDOW_LANCZOS, region, data); } } } void ThumbnailAsideEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data) { effects->paintWindow(w, mask, region, data); painted |= region; } void ThumbnailAsideEffect::slotWindowDamaged(EffectWindow* w, const QRect&) { foreach (const Data & d, windows) { if (d.window == w) effects->addRepaint(d.rect); } } void ThumbnailAsideEffect::slotWindowGeometryShapeChanged(EffectWindow* w, const QRect& old) { foreach (const Data & d, windows) { if (d.window == w) { if (w->size() == old.size()) effects->addRepaint(d.rect); else arrange(); return; } } } void ThumbnailAsideEffect::slotWindowClosed(EffectWindow* w) { removeThumbnail(w); } void ThumbnailAsideEffect::toggleCurrentThumbnail() { EffectWindow* active = effects->activeWindow(); if (active == NULL) return; if (windows.contains(active)) removeThumbnail(active); else addThumbnail(active); } void ThumbnailAsideEffect::addThumbnail(EffectWindow* w) { repaintAll(); // repaint old areas Data d; d.window = w; d.index = windows.count(); windows[ w ] = d; arrange(); } void ThumbnailAsideEffect::removeThumbnail(EffectWindow* w) { if (!windows.contains(w)) return; repaintAll(); // repaint old areas int index = windows[ w ].index; windows.remove(w); for (QHash< EffectWindow*, Data >::Iterator it = windows.begin(); it != windows.end(); ++it) { Data& d = *it; if (d.index > index) --d.index; } arrange(); } void ThumbnailAsideEffect::arrange() { if (windows.size() == 0) return; int height = 0; QVector< int > pos(windows.size()); int mwidth = 0; foreach (const Data & d, windows) { height += d.window->height(); mwidth = qMax(mwidth, d.window->width()); pos[ d.index ] = d.window->height(); } QRect area = effects->clientArea(MaximizeArea, screen, effects->currentDesktop()); double scale = area.height() / double(height); scale = qMin(scale, maxwidth / double(mwidth)); // don't be wider than maxwidth pixels int add = 0; for (int i = 0; i < windows.size(); ++i) { pos[ i ] = int(pos[ i ] * scale); pos[ i ] += spacing + add; // compute offset of each item add = pos[ i ]; } for (QHash< EffectWindow*, Data >::Iterator it = windows.begin(); it != windows.end(); ++it) { Data& d = *it; int width = int(d.window->width() * scale); d.rect = QRect(area.right() - width, area.bottom() - pos[ d.index ], width, int(d.window->height() * scale)); } repaintAll(); } void ThumbnailAsideEffect::repaintAll() { foreach (const Data & d, windows) effects->addRepaint(d.rect); } bool ThumbnailAsideEffect::isActive() const { return !windows.isEmpty() && !effects->isScreenLocked(); } } // namespace diff --git a/effects/thumbnailaside/thumbnailaside_config.cpp b/effects/thumbnailaside/thumbnailaside_config.cpp index 89eccbebe..453180b1b 100644 --- a/effects/thumbnailaside/thumbnailaside_config.cpp +++ b/effects/thumbnailaside/thumbnailaside_config.cpp @@ -1,101 +1,101 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Christian Nitschkowski This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "thumbnailaside_config.h" // KConfigSkeleton #include "thumbnailasideconfig.h" #include #include #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(ThumbnailAsideEffectConfigFactory, "thumbnailaside_config.json", registerPlugin();) namespace KWin { ThumbnailAsideEffectConfigForm::ThumbnailAsideEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } ThumbnailAsideEffectConfig::ThumbnailAsideEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("thumbnailaside")), parent, args) { m_ui = new ThumbnailAsideEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); - connect(m_ui->editor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(m_ui->editor, &KShortcutsEditor::keyChange, this, qOverload<>(&ThumbnailAsideEffectConfig::changed)); ThumbnailAsideConfig::instance(KWIN_CONFIG); addConfig(ThumbnailAsideConfig::self(), this); // Shortcut config. The shortcut belongs to the component "kwin"! m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); m_actionCollection->setConfigGroup(QStringLiteral("ThumbnailAside")); m_actionCollection->setConfigGlobal(true); QAction* a = m_actionCollection->addAction(QStringLiteral("ToggleCurrentThumbnail")); a->setText(i18n("Toggle Thumbnail for Current Window")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_T); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_T); m_ui->editor->addCollection(m_actionCollection); load(); } ThumbnailAsideEffectConfig::~ThumbnailAsideEffectConfig() { // Undo (only) unsaved changes to global key shortcuts m_ui->editor->undoChanges(); } void ThumbnailAsideEffectConfig::save() { KCModule::save(); m_ui->editor->save(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("thumbnailaside")); } } // namespace #include "thumbnailaside_config.moc" diff --git a/effects/trackmouse/trackmouse.cpp b/effects/trackmouse/trackmouse.cpp index f92342104..8a2630089 100644 --- a/effects/trackmouse/trackmouse.cpp +++ b/effects/trackmouse/trackmouse.cpp @@ -1,313 +1,312 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2010 Jorge Mata Copyright (C) 2018 Vlad Zagorodniy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "trackmouse.h" // KConfigSkeleton #include "trackmouseconfig.h" #include #include #include #include #include #include #include #include #include #include namespace KWin { TrackMouseEffect::TrackMouseEffect() : m_angle(0) { initConfig(); m_texture[0] = m_texture[1] = 0; #ifdef KWIN_HAVE_XRENDER_COMPOSITING m_picture[0] = m_picture[1] = 0; if ( effects->compositingType() == XRenderCompositing) m_angleBase = 1.57079632679489661923; // Pi/2 #endif if ( effects->isOpenGLCompositing() || effects->compositingType() == QPainterCompositing) m_angleBase = 90.0; m_mousePolling = false; m_action = new QAction(this); m_action->setObjectName(QStringLiteral("TrackMouse")); m_action->setText(i18n("Track mouse")); KGlobalAccel::self()->setDefaultShortcut(m_action, QList()); KGlobalAccel::self()->setShortcut(m_action, QList()); effects->registerGlobalShortcut(QKeySequence(), m_action); - connect(m_action, SIGNAL(triggered(bool)), this, SLOT(toggle())); + connect(m_action, &QAction::triggered, this, &TrackMouseEffect::toggle); - connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); + connect(effects, &EffectsHandler::mouseChanged, this, &TrackMouseEffect::slotMouseChanged); reconfigure(ReconfigureAll); } TrackMouseEffect::~TrackMouseEffect() { if (m_mousePolling) effects->stopMousePolling(); for (int i = 0; i < 2; ++i) { delete m_texture[i]; m_texture[i] = 0; #ifdef KWIN_HAVE_XRENDER_COMPOSITING delete m_picture[i]; m_picture[i] = 0; #endif } } void TrackMouseEffect::reconfigure(ReconfigureFlags) { m_modifiers = 0; TrackMouseConfig::self()->read(); if (TrackMouseConfig::shift()) m_modifiers |= Qt::ShiftModifier; if (TrackMouseConfig::alt()) m_modifiers |= Qt::AltModifier; if (TrackMouseConfig::control()) m_modifiers |= Qt::ControlModifier; if (TrackMouseConfig::meta()) m_modifiers |= Qt::MetaModifier; if (m_modifiers) { if (!m_mousePolling) effects->startMousePolling(); m_mousePolling = true; } else if (m_mousePolling) { effects->stopMousePolling(); m_mousePolling = false; } } void TrackMouseEffect::prePaintScreen(ScreenPrePaintData& data, int time) { QTime t = QTime::currentTime(); m_angle = ((t.second() % 4) * m_angleBase) + (t.msec() / 1000.0 * m_angleBase); m_lastRect[0].moveCenter(cursorPos()); m_lastRect[1].moveCenter(cursorPos()); data.paint |= m_lastRect[0].adjusted(-1,-1,1,1); effects->prePaintScreen(data, time); } void TrackMouseEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { effects->paintScreen(mask, region, data); // paint normal screen if ( effects->isOpenGLCompositing() && m_texture[0] && m_texture[1]) { ShaderBinder binder(ShaderTrait::MapTexture); GLShader *shader(binder.shader()); if (!shader) { return; } glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); QMatrix4x4 matrix(data.projectionMatrix()); const QPointF p = m_lastRect[0].topLeft() + QPoint(m_lastRect[0].width()/2.0, m_lastRect[0].height()/2.0); const float x = p.x()*data.xScale() + data.xTranslation(); const float y = p.y()*data.yScale() + data.yTranslation(); for (int i = 0; i < 2; ++i) { matrix.translate(x, y, 0.0); matrix.rotate(i ? -2*m_angle : m_angle, 0, 0, 1.0); matrix.translate(-x, -y, 0.0); QMatrix4x4 mvp(matrix); mvp.translate(m_lastRect[i].x(), m_lastRect[i].y()); shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp); m_texture[i]->bind(); m_texture[i]->render(region, m_lastRect[i]); m_texture[i]->unbind(); } glDisable(GL_BLEND); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if ( effects->compositingType() == XRenderCompositing && m_picture[0] && m_picture[1]) { float sine = sin(m_angle); const float cosine = cos(m_angle); for (int i = 0; i < 2; ++i) { if (i) sine = -sine; const float dx = m_size[i].width()/2.0; const float dy = m_size[i].height()/2.0; const xcb_render_picture_t picture = *m_picture[i]; #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) xcb_render_transform_t xform = { DOUBLE_TO_FIXED( cosine ), DOUBLE_TO_FIXED( -sine ), DOUBLE_TO_FIXED( dx - cosine*dx + sine*dy ), DOUBLE_TO_FIXED( sine ), DOUBLE_TO_FIXED( cosine ), DOUBLE_TO_FIXED( dy - sine*dx - cosine*dy ), DOUBLE_TO_FIXED( 0.0 ), DOUBLE_TO_FIXED( 0.0 ), DOUBLE_TO_FIXED( 1.0 ) }; #undef DOUBLE_TO_FIXED xcb_render_set_picture_transform(xcbConnection(), picture, xform); xcb_render_set_picture_filter(xcbConnection(), picture, 8, "bilinear", 0, NULL); const QRect &rect = m_lastRect[i]; xcb_render_composite(xcbConnection(), XCB_RENDER_PICT_OP_OVER, picture, XCB_RENDER_PICTURE_NONE, effects->xrenderBufferPicture(), 0, 0, 0, 0, qRound((rect.x()+rect.width()/2.0)*data.xScale() - rect.width()/2.0 + data.xTranslation()), qRound((rect.y()+rect.height()/2.0)*data.yScale() - rect.height()/2.0 + data.yTranslation()), rect.width(), rect.height()); } } #endif if (effects->compositingType() == QPainterCompositing && !m_image[0].isNull() && !m_image[1].isNull()) { QPainter *painter = effects->scenePainter(); const QPointF p = m_lastRect[0].topLeft() + QPoint(m_lastRect[0].width()/2.0, m_lastRect[0].height()/2.0); for (int i = 0; i < 2; ++i) { painter->save(); painter->translate(p.x(), p.y()); painter->rotate(i ? -2*m_angle : m_angle); painter->translate(-p.x(), -p.y()); painter->drawImage(m_lastRect[i], m_image[i]); painter->restore(); } } } void TrackMouseEffect::postPaintScreen() { effects->addRepaint(m_lastRect[0].adjusted(-1,-1,1,1)); effects->postPaintScreen(); } bool TrackMouseEffect::init() { effects->makeOpenGLContextCurrent(); #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (!(m_texture[0] || m_picture[0] || !m_image[0].isNull())) { loadTexture(); if (!(m_texture[0] || m_picture[0] || !m_image[0].isNull())) return false; } #else if (!m_texture[0] || m_image[0].isNull()) { loadTexture(); if (!m_texture[0] || m_image[0].isNull()) return false; } #endif m_lastRect[0].moveCenter(cursorPos()); m_lastRect[1].moveCenter(cursorPos()); m_angle = 0; return true; } void TrackMouseEffect::toggle() { switch (m_state) { case State::ActivatedByModifiers: m_state = State::ActivatedByShortcut; break; case State::ActivatedByShortcut: m_state = State::Inactive; break; case State::Inactive: if (!init()) { return; } m_state = State::ActivatedByShortcut; break; default: Q_UNREACHABLE(); break; } effects->addRepaint(m_lastRect[0].adjusted(-1, -1, 1, 1)); } void TrackMouseEffect::slotMouseChanged(const QPoint&, const QPoint&, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers) { if (!m_mousePolling) { // we didn't ask for it but maybe someone else did... return; } switch (m_state) { case State::ActivatedByModifiers: if (modifiers == m_modifiers) { return; } m_state = State::Inactive; break; case State::ActivatedByShortcut: return; case State::Inactive: if (modifiers != m_modifiers) { return; } if (!init()) { return; } m_state = State::ActivatedByModifiers; break; default: Q_UNREACHABLE(); break; } effects->addRepaint(m_lastRect[0].adjusted(-1, -1, 1, 1)); } void TrackMouseEffect::loadTexture() { QString f[2] = {QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("tm_outer.png")), QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral("tm_inner.png"))}; if (f[0].isEmpty() || f[1].isEmpty()) return; for (int i = 0; i < 2; ++i) { if ( effects->isOpenGLCompositing()) { QImage img(f[i]); m_texture[i] = new GLTexture(img); m_lastRect[i].setSize(img.size()); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if ( effects->compositingType() == XRenderCompositing) { QImage pixmap(f[i]); m_picture[i] = new XRenderPicture(pixmap); m_size[i] = pixmap.size(); m_lastRect[i].setSize(pixmap.size()); } #endif if (effects->compositingType() == QPainterCompositing) { m_image[i] = QImage(f[i]); m_lastRect[i].setSize(m_image[i].size()); } } } bool TrackMouseEffect::isActive() const { return m_state != State::Inactive; } } // namespace diff --git a/effects/trackmouse/trackmouse_config.cpp b/effects/trackmouse/trackmouse_config.cpp index 94d321193..61b04b670 100644 --- a/effects/trackmouse/trackmouse_config.cpp +++ b/effects/trackmouse/trackmouse_config.cpp @@ -1,124 +1,124 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2010 Jorge Mata This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "trackmouse_config.h" // KConfigSkeleton #include "trackmouseconfig.h" K_PLUGIN_FACTORY_WITH_JSON(TrackMouseEffectConfigFactory, "trackmouse_config.json", registerPlugin();) namespace KWin { static const QString s_toggleTrackMouseActionName = QStringLiteral("TrackMouse"); TrackMouseEffectConfigForm::TrackMouseEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } TrackMouseEffectConfig::TrackMouseEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("trackmouse")), parent, args) { TrackMouseConfig::instance(KWIN_CONFIG); m_ui = new TrackMouseEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); addConfig(TrackMouseConfig::self(), m_ui); m_actionCollection = new KActionCollection(this, QStringLiteral("kwin")); m_actionCollection->setComponentDisplayName(i18n("KWin")); m_actionCollection->setConfigGroup(QStringLiteral("TrackMouse")); m_actionCollection->setConfigGlobal(true); QAction *a = m_actionCollection->addAction(s_toggleTrackMouseActionName); a->setText(i18n("Track mouse")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList()); KGlobalAccel::self()->setShortcut(a, QList()); - connect(m_ui->shortcut, SIGNAL(keySequenceChanged(QKeySequence)), - SLOT(shortcutChanged(QKeySequence))); + connect(m_ui->shortcut, &KKeySequenceWidget::keySequenceChanged, + this, &TrackMouseEffectConfig::shortcutChanged); load(); } TrackMouseEffectConfig::~TrackMouseEffectConfig() { } void TrackMouseEffectConfig::load() { KCModule::load(); if (QAction *a = m_actionCollection->action(s_toggleTrackMouseActionName)) { auto shortcuts = KGlobalAccel::self()->shortcut(a); if (!shortcuts.isEmpty()) { m_ui->shortcut->setKeySequence(shortcuts.first()); } } } void TrackMouseEffectConfig::save() { KCModule::save(); m_actionCollection->writeSettings(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("trackmouse")); } void TrackMouseEffectConfig::defaults() { KCModule::defaults(); m_ui->shortcut->clearKeySequence(); } void TrackMouseEffectConfig::shortcutChanged(const QKeySequence &seq) { if (QAction *a = m_actionCollection->action(QStringLiteral("TrackMouse"))) { KGlobalAccel::self()->setShortcut(a, QList() << seq, KGlobalAccel::NoAutoloading); } emit changed(true); } } // namespace #include "trackmouse_config.moc" diff --git a/effects/windowgeometry/windowgeometry.cpp b/effects/windowgeometry/windowgeometry.cpp index 8d66eadc6..cdffdaa03 100644 --- a/effects/windowgeometry/windowgeometry.cpp +++ b/effects/windowgeometry/windowgeometry.cpp @@ -1,237 +1,237 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Thomas Lübking This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "windowgeometry.h" // KConfigSkeleton #include "windowgeometryconfig.h" #include #include #include #include #include #include #include using namespace KWin; WindowGeometry::WindowGeometry() { initConfig(); iAmActivated = true; iAmActive = false; myResizeWindow = 0L; #define myResizeString "Window geometry display, %1 and %2 are the new size," \ " %3 and %4 are pixel increments - avoid reformatting or suffixes like 'px'", \ "Width: %1 (%3)\nHeight: %2 (%4)" #define myCoordString_0 "Window geometry display, %1 and %2 are the cartesian x and y coordinates" \ " - avoid reformatting or suffixes like 'px'", \ "X: %1\nY: %2" #define myCoordString_1 "Window geometry display, %1 and %2 are the cartesian x and y coordinates," \ " %3 and %4 are the resp. increments - avoid reformatting or suffixes like 'px'", \ "X: %1 (%3)\nY: %2 (%4)" reconfigure(ReconfigureAll); QAction* a = new QAction(this); a->setObjectName(QStringLiteral("WindowGeometry")); a->setText(i18n("Toggle window geometry display (effect only)")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::CTRL + Qt::SHIFT + Qt::Key_F11); KGlobalAccel::self()->setShortcut(a, QList() << Qt::CTRL + Qt::SHIFT + Qt::Key_F11); effects->registerGlobalShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_F11, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(toggle())); + connect(a, &QAction::triggered, this, &WindowGeometry::toggle); - connect(effects, SIGNAL(windowStartUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowStartUserMovedResized(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowFinishUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowFinishUserMovedResized(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowStepUserMovedResized(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowStepUserMovedResized(KWin::EffectWindow*,QRect))); + connect(effects, &EffectsHandler::windowStartUserMovedResized, this, &WindowGeometry::slotWindowStartUserMovedResized); + connect(effects, &EffectsHandler::windowFinishUserMovedResized, this, &WindowGeometry::slotWindowFinishUserMovedResized); + connect(effects, &EffectsHandler::windowStepUserMovedResized, this, &WindowGeometry::slotWindowStepUserMovedResized); } WindowGeometry::~WindowGeometry() { for (int i = 0; i < 3; ++i) delete myMeasure[i]; } void WindowGeometry::createFrames() { if (myMeasure[0]) { return; } QFont fnt; fnt.setBold(true); fnt.setPointSize(12); for (int i = 0; i < 3; ++i) { myMeasure[i] = effects->effectFrame(EffectFrameUnstyled, false); myMeasure[i]->setFont(fnt); } myMeasure[0]->setAlignment(Qt::AlignLeft | Qt::AlignTop); myMeasure[1]->setAlignment(Qt::AlignCenter); myMeasure[2]->setAlignment(Qt::AlignRight | Qt::AlignBottom); } void WindowGeometry::reconfigure(ReconfigureFlags) { WindowGeometryConfiguration::self()->read(); iHandleMoves = WindowGeometryConfiguration::move(); iHandleResizes = WindowGeometryConfiguration::resize(); } void WindowGeometry::paintScreen(int mask, QRegion region, ScreenPaintData &data) { effects->paintScreen(mask, region, data); if (iAmActivated && iAmActive) { for (int i = 0; i < 3; ++i) myMeasure[i]->render(infiniteRegion(), 1.0, .66); } } void WindowGeometry::toggle() { iAmActivated = !iAmActivated; createFrames(); } void WindowGeometry::slotWindowStartUserMovedResized(EffectWindow *w) { if (!iAmActivated) return; if (w->isUserResize() && !iHandleResizes) return; if (w->isUserMove() && !iHandleMoves) return; createFrames(); iAmActive = true; myResizeWindow = w; myOriginalGeometry = w->geometry(); myCurrentGeometry = w->geometry(); slotWindowStepUserMovedResized(w, w->geometry()); } void WindowGeometry::slotWindowFinishUserMovedResized(EffectWindow *w) { if (iAmActive && w == myResizeWindow) { iAmActive = false; myResizeWindow = 0L; w->addRepaintFull(); if (myExtraDirtyArea.isValid()) w->addLayerRepaint(myExtraDirtyArea); myExtraDirtyArea = QRect(); } } static inline QString number(int n) { QLocale locale; QString sign; if (n >= 0) { sign = locale.positiveSign(); if (sign.isEmpty()) sign = QStringLiteral("+"); } else { n = -n; sign = locale.negativeSign(); if (sign.isEmpty()) sign = QStringLiteral("-"); } return sign + QString::number(n); } void WindowGeometry::slotWindowStepUserMovedResized(EffectWindow *w, const QRect &geometry) { if (iAmActivated && iAmActive && w == myResizeWindow) { if (myExtraDirtyArea.isValid()) effects->addRepaint(myExtraDirtyArea); myExtraDirtyArea = QRect(); myCurrentGeometry = geometry; QPoint center = geometry.center(); const QRect &r = geometry; const QRect &r2 = myOriginalGeometry; const QRect screen = effects->clientArea(ScreenArea, center, w->desktop()); QRect expandedGeometry = w->expandedGeometry(); expandedGeometry = geometry.adjusted(expandedGeometry.x() - w->x(), expandedGeometry.y() - w->y(), expandedGeometry.right() - w->geometry().right(), expandedGeometry.bottom() - w->geometry().bottom()); // sufficient for moves, resizes calculated otherwise int dx = r.x() - r2.x(); int dy = r.y() - r2.y(); // upper left ---------------------- if (w->isUserResize()) myMeasure[0]->setText( i18nc(myCoordString_1, r.x(), r.y(), number(dx), number(dy) ) ); else myMeasure[0]->setText( i18nc(myCoordString_0, r.x(), r.y() ) ); QPoint pos = expandedGeometry.topLeft(); pos = QPoint(qMax(pos.x(), screen.x()), qMax(pos.y(), screen.y())); myMeasure[0]->setPosition(pos + QPoint(6,6)); // "6" is magic number because the unstyled effectframe has 5px padding // center ---------------------- if (w->isUserResize()) { // calc width for center element, otherwise the current dx/dx remains right dx = r.width() - r2.width(); dy = r.height() - r2.height(); const QSize baseInc = w->basicUnit(); if (baseInc != QSize(1,1)) { Q_ASSERT(baseInc.width() && baseInc.height()); const QSize csz = w->contentsRect().size(); myMeasure[1]->setText( i18nc(myResizeString, csz.width()/baseInc.width(), csz.height()/baseInc.height(), number(dx/baseInc.width()), number(dy/baseInc.height()) ) ); } else myMeasure[1]->setText( i18nc(myResizeString, r.width(), r.height(), number(dx), number(dy) ) ); // calc width for bottomright element, superfluous otherwise dx = r.right() - r2.right(); dy = r.bottom() - r2.bottom(); } else myMeasure[1]->setText( i18nc(myCoordString_0, number(dx), number(dy) ) ); const int cdx = myMeasure[1]->geometry().width() / 2 + 3; // "3" = 6/2 is magic number because const int cdy = myMeasure[1]->geometry().height() / 2 + 3; // the unstyled effectframe has 5px padding center = QPoint(qMax(center.x(), screen.x() + cdx), qMax(center.y(), screen.y() + cdy)); center = QPoint(qMin(center.x(), screen.right() - cdx), qMin(center.y(), screen.bottom() - cdy)); myMeasure[1]->setPosition(center); // lower right ---------------------- if (w->isUserResize()) myMeasure[2]->setText( i18nc(myCoordString_1, r.right(), r.bottom(), number(dx), number(dy) ) ); else myMeasure[2]->setText( i18nc(myCoordString_0, r.right(), r.bottom() ) ); pos = expandedGeometry.bottomRight(); pos = QPoint(qMin(pos.x(), screen.right()), qMin(pos.y(), screen.bottom())); myMeasure[2]->setPosition(pos - QPoint(6,6)); // "6" is magic number because the unstyled effectframe has 5px padding myExtraDirtyArea |= myMeasure[0]->geometry(); myExtraDirtyArea |= myMeasure[1]->geometry(); myExtraDirtyArea |= myMeasure[2]->geometry(); myExtraDirtyArea.adjust(-6,-6,6,6); if (myExtraDirtyArea.isValid()) effects->addRepaint(myExtraDirtyArea); } } bool WindowGeometry::isActive() const { return iAmActive; } diff --git a/effects/windowgeometry/windowgeometry_config.cpp b/effects/windowgeometry/windowgeometry_config.cpp index 3544e4c7f..b2b58417e 100644 --- a/effects/windowgeometry/windowgeometry_config.cpp +++ b/effects/windowgeometry/windowgeometry_config.cpp @@ -1,95 +1,95 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2010 Thomas Lübking This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "windowgeometry_config.h" // KConfigSkeleton #include "windowgeometryconfig.h" #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(WindowGeometryEffectConfigFactory, "windowgeometry_config.json", registerPlugin();) namespace KWin { WindowGeometryConfigForm::WindowGeometryConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } WindowGeometryConfig::WindowGeometryConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("windowgeometry")), parent, args) { WindowGeometryConfiguration::instance(KWIN_CONFIG); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(myUi = new WindowGeometryConfigForm(this)); // Shortcut config. The shortcut belongs to the component "kwin"! myActionCollection = new KActionCollection(this, QStringLiteral("kwin")); myActionCollection->setComponentDisplayName(i18n("KWin")); QAction* a = myActionCollection->addAction(QStringLiteral("WindowGeometry")); a->setText(i18n("Toggle KWin composited geometry display")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::CTRL + Qt::SHIFT + Qt::Key_F11); KGlobalAccel::self()->setShortcut(a, QList() << Qt::CTRL + Qt::SHIFT + Qt::Key_F11); myUi->shortcuts->addCollection(myActionCollection); - connect(myUi->shortcuts, SIGNAL(keyChange()), this, SLOT(changed())); + connect(myUi->shortcuts, &KShortcutsEditor::keyChange, this, qOverload<>(&WindowGeometryConfig::changed)); addConfig(WindowGeometryConfiguration::self(), myUi); load(); } WindowGeometryConfig::~WindowGeometryConfig() { // Undo (only) unsaved changes to global key shortcuts myUi->shortcuts->undoChanges(); } void WindowGeometryConfig::save() { KCModule::save(); myUi->shortcuts->save(); // undo() will restore to this state from now on OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("windowgeometry")); } void WindowGeometryConfig::defaults() { myUi->shortcuts->allDefault(); emit changed(true); } } //namespace #include "windowgeometry_config.moc" diff --git a/effects/wobblywindows/wobblywindows.cpp b/effects/wobblywindows/wobblywindows.cpp index cd50ae732..40f543aef 100644 --- a/effects/wobblywindows/wobblywindows.cpp +++ b/effects/wobblywindows/wobblywindows.cpp @@ -1,1228 +1,1227 @@ /***************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Cédric Borgese You can Freely distribute this program under the GNU General Public License. See the file "COPYING" for the exact licensing terms. ******************************************************************/ #include "wobblywindows.h" #include "wobblywindowsconfig.h" #include //#define COMPUTE_STATS // if you enable it and run kwin in a terminal from the session it manages, // be sure to redirect the output of kwin in a file or // you'll propably get deadlocks. //#define VERBOSE_MODE #if defined COMPUTE_STATS && !defined VERBOSE_MODE # ifdef __GNUC__ # warning "You enable COMPUTE_STATS without VERBOSE_MODE, computed stats will not be printed." # endif #endif namespace KWin { struct ParameterSet { qreal stiffness; qreal drag; qreal move_factor; qreal xTesselation; qreal yTesselation; qreal minVelocity; qreal maxVelocity; qreal stopVelocity; qreal minAcceleration; qreal maxAcceleration; qreal stopAcceleration; bool moveEffectEnabled; bool openEffectEnabled; bool closeEffectEnabled; }; static const ParameterSet set_0 = { 0.15, 0.80, 0.10, 20.0, 20.0, 0.0, 1000.0, 0.5, 0.0, 1000.0, 0.5, true, false, false }; static const ParameterSet set_1 = { 0.10, 0.85, 0.10, 20.0, 20.0, 0.0, 1000.0, 0.5, 0.0, 1000.0, 0.5, true, false, false }; static const ParameterSet set_2 = { 0.06, 0.90, 0.10, 20.0, 20.0, 0.0, 1000.0, 0.5, 0.0, 1000.0, 0.5, true, false, false }; static const ParameterSet set_3 = { 0.03, 0.92, 0.20, 20.0, 20.0, 0.0, 1000.0, 0.5, 0.0, 1000.0, 0.5, true, false, false }; static const ParameterSet set_4 = { 0.01, 0.97, 0.25, 20.0, 20.0, 0.0, 1000.0, 0.5, 0.0, 1000.0, 0.5, true, false, false }; static const ParameterSet pset[5] = { set_0, set_1, set_2, set_3, set_4 }; WobblyWindowsEffect::WobblyWindowsEffect() { initConfig(); reconfigure(ReconfigureAll); - connect(effects, SIGNAL(windowAdded(KWin::EffectWindow*)), this, SLOT(slotWindowAdded(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowClosed(KWin::EffectWindow*)), this, SLOT(slotWindowClosed(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowStartUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowStartUserMovedResized(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowStepUserMovedResized(KWin::EffectWindow*,QRect)), this, SLOT(slotWindowStepUserMovedResized(KWin::EffectWindow*,QRect))); - connect(effects, SIGNAL(windowFinishUserMovedResized(KWin::EffectWindow*)), this, SLOT(slotWindowFinishUserMovedResized(KWin::EffectWindow*))); - connect(effects, SIGNAL(windowMaximizedStateChanged(KWin::EffectWindow*,bool,bool)), this, SLOT(slotWindowMaximizeStateChanged(KWin::EffectWindow*,bool,bool))); - + connect(effects, &EffectsHandler::windowAdded, this, &WobblyWindowsEffect::slotWindowAdded); + connect(effects, &EffectsHandler::windowClosed, this, &WobblyWindowsEffect::slotWindowClosed); + connect(effects, &EffectsHandler::windowStartUserMovedResized, this, &WobblyWindowsEffect::slotWindowStartUserMovedResized); + connect(effects, &EffectsHandler::windowStepUserMovedResized, this, &WobblyWindowsEffect::slotWindowStepUserMovedResized); + connect(effects, &EffectsHandler::windowFinishUserMovedResized, this, &WobblyWindowsEffect::slotWindowFinishUserMovedResized); + connect(effects, &EffectsHandler::windowMaximizedStateChanged, this, &WobblyWindowsEffect::slotWindowMaximizeStateChanged); connect(effects, &EffectsHandler::windowDataChanged, this, &WobblyWindowsEffect::cancelWindowGrab); } WobblyWindowsEffect::~WobblyWindowsEffect() { if (!windows.empty()) { // we should be empty at this point... // emit a warning and clean the list. qCDebug(KWINEFFECTS) << "Windows list not empty. Left items : " << windows.count(); QHash< const EffectWindow*, WindowWobblyInfos >::iterator i; for (i = windows.begin(); i != windows.end(); ++i) { freeWobblyInfo(i.value()); } } } void WobblyWindowsEffect::reconfigure(ReconfigureFlags) { WobblyWindowsConfig::self()->read(); QString settingsMode = WobblyWindowsConfig::settings(); if (settingsMode != QStringLiteral("Custom")) { unsigned int wobblynessLevel = WobblyWindowsConfig::wobblynessLevel(); if (wobblynessLevel > 4) { qCDebug(KWINEFFECTS) << "Wrong value for \"WobblynessLevel\" : " << wobblynessLevel; wobblynessLevel = 4; } setParameterSet(pset[wobblynessLevel]); if (WobblyWindowsConfig::advancedMode()) { m_stiffness = WobblyWindowsConfig::stiffness() / 100.0; m_drag = WobblyWindowsConfig::drag() / 100.0; m_move_factor = WobblyWindowsConfig::moveFactor() / 100.0; } } else { // Custom method, read all values from config file. m_stiffness = WobblyWindowsConfig::stiffness() / 100.0; m_drag = WobblyWindowsConfig::drag() / 100.0; m_move_factor = WobblyWindowsConfig::moveFactor() / 100.0; m_xTesselation = WobblyWindowsConfig::xTesselation(); m_yTesselation = WobblyWindowsConfig::yTesselation(); m_minVelocity = WobblyWindowsConfig::minVelocity(); m_maxVelocity = WobblyWindowsConfig::maxVelocity(); m_stopVelocity = WobblyWindowsConfig::stopVelocity(); m_minAcceleration = WobblyWindowsConfig::minAcceleration(); m_maxAcceleration = WobblyWindowsConfig::maxAcceleration(); m_stopAcceleration = WobblyWindowsConfig::stopAcceleration(); m_moveEffectEnabled = WobblyWindowsConfig::moveEffect(); m_openEffectEnabled = WobblyWindowsConfig::openEffect(); // disable close effect by default for now as it doesn't do what I want. m_closeEffectEnabled = WobblyWindowsConfig::closeEffect(); } m_moveWobble = WobblyWindowsConfig::moveWobble(); m_resizeWobble = WobblyWindowsConfig::resizeWobble(); #if defined VERBOSE_MODE qCDebug(KWINEFFECTS) << "Parameters :\n" << "move : " << m_moveEffectEnabled << ", open : " << m_openEffectEnabled << ", close : " << m_closeEffectEnabled << "\n" "grid(" << m_stiffness << ", " << m_drag << ", " << m_move_factor << ")\n" << "velocity(" << m_minVelocity << ", " << m_maxVelocity << ", " << m_stopVelocity << ")\n" << "acceleration(" << m_minAcceleration << ", " << m_maxAcceleration << ", " << m_stopAcceleration << ")\n" << "tesselation(" << m_xTesselation << ", " << m_yTesselation << ")"; #endif } bool WobblyWindowsEffect::supported() { return effects->isOpenGLCompositing() && effects->animationsSupported(); } void WobblyWindowsEffect::setParameterSet(const ParameterSet& pset) { m_stiffness = pset.stiffness; m_drag = pset.drag; m_move_factor = pset.move_factor; m_xTesselation = pset.xTesselation; m_yTesselation = pset.yTesselation; m_minVelocity = pset.minVelocity; m_maxVelocity = pset.maxVelocity; m_stopVelocity = pset.stopVelocity; m_minAcceleration = pset.minAcceleration; m_maxAcceleration = pset.maxAcceleration; m_stopAcceleration = pset.stopAcceleration; m_moveEffectEnabled = pset.moveEffectEnabled; m_openEffectEnabled = pset.openEffectEnabled; m_closeEffectEnabled = pset.closeEffectEnabled; } void WobblyWindowsEffect::setVelocityThreshold(qreal m_minVelocity) { this->m_minVelocity = m_minVelocity; } void WobblyWindowsEffect::setMoveFactor(qreal factor) { m_move_factor = factor; } void WobblyWindowsEffect::setStiffness(qreal stiffness) { m_stiffness = stiffness; } void WobblyWindowsEffect::setDrag(qreal drag) { m_drag = drag; } void WobblyWindowsEffect::prePaintScreen(ScreenPrePaintData& data, int time) { // We need to mark the screen windows as transformed. Otherwise the whole // screen won't be repainted, resulting in artefacts. // Could we just set a subset of the screen to be repainted ? if (windows.count() != 0) { m_updateRegion = QRegion(); } effects->prePaintScreen(data, time); } const qreal maxTime = 10.0; void WobblyWindowsEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data, int time) { if (windows.contains(w)) { data.setTransformed(); data.quads = data.quads.makeRegularGrid(m_xTesselation, m_yTesselation); bool stop = false; qreal updateTime = time; while (!stop && (updateTime > maxTime)) { #if defined VERBOSE_MODE qCDebug(KWINEFFECTS) << "loop time " << updateTime << " / " << time; #endif stop = !updateWindowWobblyDatas(w, maxTime); updateTime -= maxTime; } if (!stop && updateTime > 0) { updateWindowWobblyDatas(w, updateTime); } } effects->prePaintWindow(w, data, time); } void WobblyWindowsEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data) { if (!(mask & PAINT_SCREEN_TRANSFORMED) && windows.contains(w)) { WindowWobblyInfos& wwi = windows[w]; int tx = w->geometry().x(); int ty = w->geometry().y(); double left = 0.0; double top = 0.0; double right = w->width(); double bottom = w->height(); for (int i = 0; i < data.quads.count(); ++i) { for (int j = 0; j < 4; ++j) { WindowVertex& v = data.quads[i][j]; Pair oldPos = {tx + v.x(), ty + v.y()}; Pair newPos = computeBezierPoint(wwi, oldPos); v.move(newPos.x - tx, newPos.y - ty); } left = qMin(left, data.quads[i].left()); top = qMin(top, data.quads[i].top()); right = qMax(right, data.quads[i].right()); bottom = qMax(bottom, data.quads[i].bottom()); } QRectF dirtyRect( left * data.xScale() + w->x() + data.xTranslation(), top * data.yScale() + w->y() + data.yTranslation(), (right - left + 1.0) * data.xScale(), (bottom - top + 1.0) * data.yScale()); // Expand the dirty region by 1px to fix potential round/floor issues. dirtyRect.adjust(-1.0, -1.0, 1.0, 1.0); m_updateRegion = m_updateRegion.united(dirtyRect.toRect()); } // Call the next effect. effects->paintWindow(w, mask, region, data); } void WobblyWindowsEffect::postPaintScreen() { if (!windows.isEmpty()) { effects->addRepaint(m_updateRegion); } // Call the next effect. effects->postPaintScreen(); } void WobblyWindowsEffect::slotWindowStartUserMovedResized(EffectWindow *w) { if (!m_moveEffectEnabled || w->isSpecialWindow()) return; if ((w->isUserMove() && m_moveWobble) || (w->isUserResize() && m_resizeWobble)) { startMovedResized(w); } } void WobblyWindowsEffect::slotWindowStepUserMovedResized(EffectWindow *w, const QRect &geometry) { Q_UNUSED(geometry) if (windows.contains(w)) { WindowWobblyInfos& wwi = windows[w]; QRect rect = w->geometry(); if (rect.y() != wwi.resize_original_rect.y()) wwi.can_wobble_top = true; if (rect.x() != wwi.resize_original_rect.x()) wwi.can_wobble_left = true; if (rect.right() != wwi.resize_original_rect.right()) wwi.can_wobble_right = true; if (rect.bottom() != wwi.resize_original_rect.bottom()) wwi.can_wobble_bottom = true; } } void WobblyWindowsEffect::slotWindowFinishUserMovedResized(EffectWindow *w) { if (windows.contains(w)) { WindowWobblyInfos& wwi = windows[w]; wwi.status = Free; QRect rect = w->geometry(); if (rect.y() != wwi.resize_original_rect.y()) wwi.can_wobble_top = true; if (rect.x() != wwi.resize_original_rect.x()) wwi.can_wobble_left = true; if (rect.right() != wwi.resize_original_rect.right()) wwi.can_wobble_right = true; if (rect.bottom() != wwi.resize_original_rect.bottom()) wwi.can_wobble_bottom = true; } } void WobblyWindowsEffect::slotWindowMaximizeStateChanged(EffectWindow *w, bool horizontal, bool vertical) { Q_UNUSED(horizontal) Q_UNUSED(vertical) if (w->isUserMove() || !m_moveEffectEnabled || w->isSpecialWindow()) return; if (m_moveWobble && m_resizeWobble) { stepMovedResized(w); } if (windows.contains(w)) { WindowWobblyInfos& wwi = windows[w]; QRect rect = w->geometry(); if (rect.y() != wwi.resize_original_rect.y()) wwi.can_wobble_top = true; if (rect.x() != wwi.resize_original_rect.x()) wwi.can_wobble_left = true; if (rect.right() != wwi.resize_original_rect.right()) wwi.can_wobble_right = true; if (rect.bottom() != wwi.resize_original_rect.bottom()) wwi.can_wobble_bottom = true; } } void WobblyWindowsEffect::startMovedResized(EffectWindow* w) { if (!windows.contains(w)) { WindowWobblyInfos new_wwi; initWobblyInfo(new_wwi, w->geometry()); windows[w] = new_wwi; } WindowWobblyInfos& wwi = windows[w]; wwi.status = Moving; const QRectF& rect = w->geometry(); qreal x_increment = rect.width() / (wwi.width - 1.0); qreal y_increment = rect.height() / (wwi.height - 1.0); Pair picked = {static_cast(cursorPos().x()), static_cast(cursorPos().y())}; int indx = (picked.x - rect.x()) / x_increment + 0.5; int indy = (picked.y - rect.y()) / y_increment + 0.5; int pickedPointIndex = indy * wwi.width + indx; if (pickedPointIndex < 0) { qCDebug(KWINEFFECTS) << "Picked index == " << pickedPointIndex << " with (" << cursorPos().x() << "," << cursorPos().y() << ")"; pickedPointIndex = 0; } else if (static_cast(pickedPointIndex) > wwi.count - 1) { qCDebug(KWINEFFECTS) << "Picked index == " << pickedPointIndex << " with (" << cursorPos().x() << "," << cursorPos().y() << ")"; pickedPointIndex = wwi.count - 1; } #if defined VERBOSE_MODE qCDebug(KWINEFFECTS) << "Original Picked point -- x : " << picked.x << " - y : " << picked.y; #endif wwi.constraint[pickedPointIndex] = true; if (w->isUserResize()) { // on a resize, do not allow any edges to wobble until it has been moved from // its original location wwi.can_wobble_top = wwi.can_wobble_left = wwi.can_wobble_right = wwi.can_wobble_bottom = false; wwi.resize_original_rect = w->geometry(); } else { wwi.can_wobble_top = wwi.can_wobble_left = wwi.can_wobble_right = wwi.can_wobble_bottom = true; } } void WobblyWindowsEffect::stepMovedResized(EffectWindow* w) { QRect new_geometry = w->geometry(); if (!windows.contains(w)) { WindowWobblyInfos new_wwi; initWobblyInfo(new_wwi, new_geometry); windows[w] = new_wwi; } WindowWobblyInfos& wwi = windows[w]; wwi.status = Free; QRect maximized_area = effects->clientArea(MaximizeArea, w); bool throb_direction_out = (new_geometry.top() == maximized_area.top() && new_geometry.bottom() == maximized_area.bottom()) || (new_geometry.left() == maximized_area.left() && new_geometry.right() == maximized_area.right()); qreal magnitude = throb_direction_out ? 10 : -30; // a small throb out when maximized, a larger throb inwards when restored for (unsigned int j = 0; j < wwi.height; ++j) { for (unsigned int i = 0; i < wwi.width; ++i) { Pair v = { magnitude*(i / qreal(wwi.width - 1) - 0.5), magnitude*(j / qreal(wwi.height - 1) - 0.5) }; wwi.velocity[j*wwi.width+i] = v; } } // constrain the middle of the window, so that any asymetry wont cause it to drift off-center for (unsigned int j = 1; j < wwi.height - 1; ++j) { for (unsigned int i = 1; i < wwi.width - 1; ++i) { wwi.constraint[j*wwi.width+i] = true; } } } void WobblyWindowsEffect::slotWindowAdded(EffectWindow* w) { if (m_openEffectEnabled && w->data(WindowAddedGrabRole).value() == nullptr) { if (windows.contains(w)) { // could this happen ?? WindowWobblyInfos& wwi = windows[w]; wobblyOpenInit(wwi); } else { WindowWobblyInfos new_wwi; initWobblyInfo(new_wwi, w->geometry()); wobblyOpenInit(new_wwi); windows[w] = new_wwi; } } } void WobblyWindowsEffect::slotWindowClosed(EffectWindow* w) { if (windows.contains(w)) { WindowWobblyInfos& wwi = windows[w]; if (m_closeEffectEnabled) { wobblyCloseInit(wwi, w); w->refWindow(); } else { freeWobblyInfo(wwi); windows.remove(w); if (windows.isEmpty()) effects->addRepaintFull(); } } else if (m_closeEffectEnabled && w->data(WindowClosedGrabRole).value() == nullptr) { WindowWobblyInfos new_wwi; initWobblyInfo(new_wwi, w->geometry()); wobblyCloseInit(new_wwi, w); windows[w] = new_wwi; w->refWindow(); } } void WobblyWindowsEffect::wobblyOpenInit(WindowWobblyInfos& wwi) const { Pair middle = { (wwi.origin[0].x + wwi.origin[15].x) / 2, (wwi.origin[0].y + wwi.origin[15].y) / 2 }; for (unsigned int j = 0; j < 4; ++j) { for (unsigned int i = 0; i < 4; ++i) { unsigned int idx = j * 4 + i; wwi.constraint[idx] = false; wwi.position[idx].x = (wwi.position[idx].x + 3 * middle.x) / 4; wwi.position[idx].y = (wwi.position[idx].y + 3 * middle.y) / 4; } } wwi.status = Openning; wwi.can_wobble_top = wwi.can_wobble_left = wwi.can_wobble_right = wwi.can_wobble_bottom = true; } void WobblyWindowsEffect::wobblyCloseInit(WindowWobblyInfos& wwi, EffectWindow* w) const { const QRectF& rect = w->geometry(); QPointF center = rect.center(); int x1 = (rect.x() + 3 * center.x()) / 4; int x2 = (rect.x() + rect.width() + 3 * center.x()) / 4; int y1 = (rect.y() + 3 * center.y()) / 4; int y2 = (rect.y() + rect.height() + 3 * center.y()) / 4; wwi.closeRect.setCoords(x1, y1, x2, y2); // for closing, not yet used... for (unsigned int j = 0; j < 4; ++j) { for (unsigned int i = 0; i < 4; ++i) { unsigned int idx = j * 4 + i; wwi.constraint[idx] = false; } } wwi.status = Closing; } void WobblyWindowsEffect::initWobblyInfo(WindowWobblyInfos& wwi, QRect geometry) const { wwi.count = 4 * 4; wwi.width = 4; wwi.height = 4; wwi.bezierWidth = m_xTesselation; wwi.bezierHeight = m_yTesselation; wwi.bezierCount = m_xTesselation * m_yTesselation; wwi.origin = new Pair[wwi.count]; wwi.position = new Pair[wwi.count]; wwi.velocity = new Pair[wwi.count]; wwi.acceleration = new Pair[wwi.count]; wwi.buffer = new Pair[wwi.count]; wwi.constraint = new bool[wwi.count]; wwi.bezierSurface = new Pair[wwi.bezierCount]; wwi.status = Moving; qreal x = geometry.x(), y = geometry.y(); qreal width = geometry.width(), height = geometry.height(); Pair initValue = {x, y}; static const Pair nullPair = {0.0, 0.0}; qreal x_increment = width / (wwi.width - 1.0); qreal y_increment = height / (wwi.height - 1.0); for (unsigned int j = 0; j < 4; ++j) { for (unsigned int i = 0; i < 4; ++i) { unsigned int idx = j * 4 + i; wwi.origin[idx] = initValue; wwi.position[idx] = initValue; wwi.velocity[idx] = nullPair; wwi.constraint[idx] = false; if (i != 4 - 2) { // x grid count - 2, i.e. not the last point initValue.x += x_increment; } else { initValue.x = width + x; } initValue.x = initValue.x; } initValue.x = x; initValue.x = initValue.x; if (j != 4 - 2) { // y grid count - 2, i.e. not the last point initValue.y += y_increment; } else { initValue.y = height + y; } initValue.y = initValue.y; } } void WobblyWindowsEffect::freeWobblyInfo(WindowWobblyInfos& wwi) const { delete[] wwi.origin; delete[] wwi.position; delete[] wwi.velocity; delete[] wwi.acceleration; delete[] wwi.buffer; delete[] wwi.constraint; delete[] wwi.bezierSurface; } WobblyWindowsEffect::Pair WobblyWindowsEffect::computeBezierPoint(const WindowWobblyInfos& wwi, Pair point) const { // compute the input value Pair topleft = wwi.origin[0]; Pair bottomright = wwi.origin[wwi.count-1]; qreal tx = (point.x - topleft.x) / (bottomright.x - topleft.x); qreal ty = (point.y - topleft.y) / (bottomright.y - topleft.y); // compute polynomial coeff qreal px[4]; px[0] = (1 - tx) * (1 - tx) * (1 - tx); px[1] = 3 * (1 - tx) * (1 - tx) * tx; px[2] = 3 * (1 - tx) * tx * tx; px[3] = tx * tx * tx; qreal py[4]; py[0] = (1 - ty) * (1 - ty) * (1 - ty); py[1] = 3 * (1 - ty) * (1 - ty) * ty; py[2] = 3 * (1 - ty) * ty * ty; py[3] = ty * ty * ty; Pair res = {0.0, 0.0}; for (unsigned int j = 0; j < 4; ++j) { for (unsigned int i = 0; i < 4; ++i) { // this assume the grid is 4*4 res.x += px[i] * py[j] * wwi.position[i + j * wwi.width].x; res.y += px[i] * py[j] * wwi.position[i + j * wwi.width].y; } } return res; } namespace { static inline void fixVectorBounds(WobblyWindowsEffect::Pair& vec, qreal min, qreal max) { if (fabs(vec.x) < min) { vec.x = 0.0; } else if (fabs(vec.x) > max) { if (vec.x > 0.0) { vec.x = max; } else { vec.x = -max; } } if (fabs(vec.y) < min) { vec.y = 0.0; } else if (fabs(vec.y) > max) { if (vec.y > 0.0) { vec.y = max; } else { vec.y = -max; } } } #if defined COMPUTE_STATS static inline void computeVectorBounds(WobblyWindowsEffect::Pair& vec, WobblyWindowsEffect::Pair& bound) { if (fabs(vec.x) < bound.x) { bound.x = fabs(vec.x); } else if (fabs(vec.x) > bound.y) { bound.y = fabs(vec.x); } if (fabs(vec.y) < bound.x) { bound.x = fabs(vec.y); } else if (fabs(vec.y) > bound.y) { bound.y = fabs(vec.y); } } #endif } // close the anonymous namespace bool WobblyWindowsEffect::updateWindowWobblyDatas(EffectWindow* w, qreal time) { QRectF rect = w->geometry(); WindowWobblyInfos& wwi = windows[w]; if (wwi.status == Closing) { rect = wwi.closeRect; } qreal x_length = rect.width() / (wwi.width - 1.0); qreal y_length = rect.height() / (wwi.height - 1.0); #if defined VERBOSE_MODE qCDebug(KWINEFFECTS) << "time " << time; qCDebug(KWINEFFECTS) << "increment x " << x_length << " // y" << y_length; #endif Pair origine = {rect.x(), rect.y()}; for (unsigned int j = 0; j < wwi.height; ++j) { for (unsigned int i = 0; i < wwi.width; ++i) { wwi.origin[wwi.width*j + i] = origine; if (i != wwi.width - 2) { origine.x += x_length; } else { origine.x = rect.width() + rect.x(); } } origine.x = rect.x(); if (j != wwi.height - 2) { origine.y += y_length; } else { origine.y = rect.height() + rect.y(); } } Pair neibourgs[4]; Pair acceleration; qreal acc_sum = 0.0; qreal vel_sum = 0.0; // compute acceleration, velocity and position for each point // for corners // top-left if (wwi.constraint[0]) { Pair window_pos = wwi.origin[0]; Pair current_pos = wwi.position[0]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[0] = accel; } else { Pair& pos = wwi.position[0]; neibourgs[0] = wwi.position[1]; neibourgs[1] = wwi.position[wwi.width]; acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness; acceleration.y = ((neibourgs[1].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness; acceleration.x /= 2; acceleration.y /= 2; wwi.acceleration[0] = acceleration; } // top-right if (wwi.constraint[wwi.width-1]) { Pair window_pos = wwi.origin[wwi.width-1]; Pair current_pos = wwi.position[wwi.width-1]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[wwi.width-1] = accel; } else { Pair& pos = wwi.position[wwi.width-1]; neibourgs[0] = wwi.position[wwi.width-2]; neibourgs[1] = wwi.position[2*wwi.width-1]; acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness; acceleration.y = ((neibourgs[1].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness; acceleration.x /= 2; acceleration.y /= 2; wwi.acceleration[wwi.width-1] = acceleration; } // bottom-left if (wwi.constraint[wwi.width*(wwi.height-1)]) { Pair window_pos = wwi.origin[wwi.width*(wwi.height-1)]; Pair current_pos = wwi.position[wwi.width*(wwi.height-1)]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[wwi.width*(wwi.height-1)] = accel; } else { Pair& pos = wwi.position[wwi.width*(wwi.height-1)]; neibourgs[0] = wwi.position[wwi.width*(wwi.height-1)+1]; neibourgs[1] = wwi.position[wwi.width*(wwi.height-2)]; acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness; acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness; acceleration.x /= 2; acceleration.y /= 2; wwi.acceleration[wwi.width*(wwi.height-1)] = acceleration; } // bottom-right if (wwi.constraint[wwi.count-1]) { Pair window_pos = wwi.origin[wwi.count-1]; Pair current_pos = wwi.position[wwi.count-1]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[wwi.count-1] = accel; } else { Pair& pos = wwi.position[wwi.count-1]; neibourgs[0] = wwi.position[wwi.count-2]; neibourgs[1] = wwi.position[wwi.width*(wwi.height-1)-1]; acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness; acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness; acceleration.x /= 2; acceleration.y /= 2; wwi.acceleration[wwi.count-1] = acceleration; } // for borders // top border for (unsigned int i = 1; i < wwi.width - 1; ++i) { if (wwi.constraint[i]) { Pair window_pos = wwi.origin[i]; Pair current_pos = wwi.position[i]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[i] = accel; } else { Pair& pos = wwi.position[i]; neibourgs[0] = wwi.position[i-1]; neibourgs[1] = wwi.position[i+1]; neibourgs[2] = wwi.position[i+wwi.width]; acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + ((neibourgs[1].x - pos.x) - x_length) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness; acceleration.y = ((neibourgs[2].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness + (neibourgs[1].y - pos.y) * m_stiffness; acceleration.x /= 3; acceleration.y /= 3; wwi.acceleration[i] = acceleration; } } // bottom border for (unsigned int i = wwi.width * (wwi.height - 1) + 1; i < wwi.count - 1; ++i) { if (wwi.constraint[i]) { Pair window_pos = wwi.origin[i]; Pair current_pos = wwi.position[i]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[i] = accel; } else { Pair& pos = wwi.position[i]; neibourgs[0] = wwi.position[i-1]; neibourgs[1] = wwi.position[i+1]; neibourgs[2] = wwi.position[i-wwi.width]; acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + ((neibourgs[1].x - pos.x) - x_length) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness; acceleration.y = (y_length - (pos.y - neibourgs[2].y)) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness + (neibourgs[1].y - pos.y) * m_stiffness; acceleration.x /= 3; acceleration.y /= 3; wwi.acceleration[i] = acceleration; } } // left border for (unsigned int i = wwi.width; i < wwi.width*(wwi.height - 1); i += wwi.width) { if (wwi.constraint[i]) { Pair window_pos = wwi.origin[i]; Pair current_pos = wwi.position[i]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[i] = accel; } else { Pair& pos = wwi.position[i]; neibourgs[0] = wwi.position[i+1]; neibourgs[1] = wwi.position[i-wwi.width]; neibourgs[2] = wwi.position[i+wwi.width]; acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness; acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + ((neibourgs[2].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness; acceleration.x /= 3; acceleration.y /= 3; wwi.acceleration[i] = acceleration; } } // right border for (unsigned int i = 2 * wwi.width - 1; i < wwi.count - 1; i += wwi.width) { if (wwi.constraint[i]) { Pair window_pos = wwi.origin[i]; Pair current_pos = wwi.position[i]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[i] = accel; } else { Pair& pos = wwi.position[i]; neibourgs[0] = wwi.position[i-1]; neibourgs[1] = wwi.position[i-wwi.width]; neibourgs[2] = wwi.position[i+wwi.width]; acceleration.x = (x_length - (pos.x - neibourgs[0].x)) * m_stiffness + (neibourgs[1].x - pos.x) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness; acceleration.y = (y_length - (pos.y - neibourgs[1].y)) * m_stiffness + ((neibourgs[2].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness; acceleration.x /= 3; acceleration.y /= 3; wwi.acceleration[i] = acceleration; } } // for the inner points for (unsigned int j = 1; j < wwi.height - 1; ++j) { for (unsigned int i = 1; i < wwi.width - 1; ++i) { unsigned int index = i + j * wwi.width; if (wwi.constraint[index]) { Pair window_pos = wwi.origin[index]; Pair current_pos = wwi.position[index]; Pair move = {window_pos.x - current_pos.x, window_pos.y - current_pos.y}; Pair accel = {move.x*m_stiffness, move.y*m_stiffness}; wwi.acceleration[index] = accel; } else { Pair& pos = wwi.position[index]; neibourgs[0] = wwi.position[index-1]; neibourgs[1] = wwi.position[index+1]; neibourgs[2] = wwi.position[index-wwi.width]; neibourgs[3] = wwi.position[index+wwi.width]; acceleration.x = ((neibourgs[0].x - pos.x) - x_length) * m_stiffness + (x_length - (pos.x - neibourgs[1].x)) * m_stiffness + (neibourgs[2].x - pos.x) * m_stiffness + (neibourgs[3].x - pos.x) * m_stiffness; acceleration.y = (y_length - (pos.y - neibourgs[2].y)) * m_stiffness + ((neibourgs[3].y - pos.y) - y_length) * m_stiffness + (neibourgs[0].y - pos.y) * m_stiffness + (neibourgs[1].y - pos.y) * m_stiffness; acceleration.x /= 4; acceleration.y /= 4; wwi.acceleration[index] = acceleration; } } } heightRingLinearMean(&wwi.acceleration, wwi); #if defined COMPUTE_STATS Pair accBound = {m_maxAcceleration, m_minAcceleration}; Pair velBound = {m_maxVelocity, m_minVelocity}; #endif // compute the new velocity of each vertex. for (unsigned int i = 0; i < wwi.count; ++i) { Pair acc = wwi.acceleration[i]; fixVectorBounds(acc, m_minAcceleration, m_maxAcceleration); #if defined COMPUTE_STATS computeVectorBounds(acc, accBound); #endif Pair& vel = wwi.velocity[i]; vel.x = acc.x * time + vel.x * m_drag; vel.y = acc.y * time + vel.y * m_drag; acc_sum += fabs(acc.x) + fabs(acc.y); } heightRingLinearMean(&wwi.velocity, wwi); // compute the new pos of each vertex. for (unsigned int i = 0; i < wwi.count; ++i) { Pair& pos = wwi.position[i]; Pair& vel = wwi.velocity[i]; fixVectorBounds(vel, m_minVelocity, m_maxVelocity); #if defined COMPUTE_STATS computeVectorBounds(vel, velBound); #endif pos.x += vel.x * time * m_move_factor; pos.y += vel.y * time * m_move_factor; vel_sum += fabs(vel.x) + fabs(vel.y); #if defined VERBOSE_MODE if (wwi.constraint[i]) { qCDebug(KWINEFFECTS) << "Constraint point ** vel : " << vel.x << "," << vel.y << " ** move : " << vel.x*time << "," << vel.y*time; } #endif } if (!wwi.can_wobble_top) { for (unsigned int i = 0; i < wwi.width; ++i) for (unsigned j = 0; j < wwi.width - 1; ++j) wwi.position[i+wwi.width*j].y = wwi.origin[i+wwi.width*j].y; } if (!wwi.can_wobble_bottom) { for (unsigned int i = wwi.width * (wwi.height - 1); i < wwi.count; ++i) for (unsigned j = 0; j < wwi.width - 1; ++j) wwi.position[i-wwi.width*j].y = wwi.origin[i-wwi.width*j].y; } if (!wwi.can_wobble_left) { for (unsigned int i = 0; i < wwi.count; i += wwi.width) for (unsigned j = 0; j < wwi.width - 1; ++j) wwi.position[i+j].x = wwi.origin[i+j].x; } if (!wwi.can_wobble_right) { for (unsigned int i = wwi.width - 1; i < wwi.count; i += wwi.width) for (unsigned j = 0; j < wwi.width - 1; ++j) wwi.position[i-j].x = wwi.origin[i-j].x; } #if defined VERBOSE_MODE # if defined COMPUTE_STATS qCDebug(KWINEFFECTS) << "Acceleration bounds (" << accBound.x << ", " << accBound.y << ")"; qCDebug(KWINEFFECTS) << "Velocity bounds (" << velBound.x << ", " << velBound.y << ")"; # endif qCDebug(KWINEFFECTS) << "sum_acc : " << acc_sum << " *** sum_vel :" << vel_sum; #endif if (wwi.status != Moving && acc_sum < m_stopAcceleration && vel_sum < m_stopVelocity) { if (wwi.status == Closing) { w->unrefWindow(); } freeWobblyInfo(wwi); windows.remove(w); if (windows.isEmpty()) effects->addRepaintFull(); return false; } return true; } void WobblyWindowsEffect::heightRingLinearMean(Pair** data_pointer, WindowWobblyInfos& wwi) { Pair* data = *data_pointer; Pair neibourgs[8]; // for corners // top-left { Pair& res = wwi.buffer[0]; Pair vit = data[0]; neibourgs[0] = data[1]; neibourgs[1] = data[wwi.width]; neibourgs[2] = data[wwi.width+1]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0; } // top-right { Pair& res = wwi.buffer[wwi.width-1]; Pair vit = data[wwi.width-1]; neibourgs[0] = data[wwi.width-2]; neibourgs[1] = data[2*wwi.width-1]; neibourgs[2] = data[2*wwi.width-2]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0; } // bottom-left { Pair& res = wwi.buffer[wwi.width*(wwi.height-1)]; Pair vit = data[wwi.width*(wwi.height-1)]; neibourgs[0] = data[wwi.width*(wwi.height-1)+1]; neibourgs[1] = data[wwi.width*(wwi.height-2)]; neibourgs[2] = data[wwi.width*(wwi.height-2)+1]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0; } // bottom-right { Pair& res = wwi.buffer[wwi.count-1]; Pair vit = data[wwi.count-1]; neibourgs[0] = data[wwi.count-2]; neibourgs[1] = data[wwi.width*(wwi.height-1)-1]; neibourgs[2] = data[wwi.width*(wwi.height-1)-2]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + 3.0 * vit.x) / 6.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + 3.0 * vit.y) / 6.0; } // for borders // top border for (unsigned int i = 1; i < wwi.width - 1; ++i) { Pair& res = wwi.buffer[i]; Pair vit = data[i]; neibourgs[0] = data[i-1]; neibourgs[1] = data[i+1]; neibourgs[2] = data[i+wwi.width]; neibourgs[3] = data[i+wwi.width-1]; neibourgs[4] = data[i+wwi.width+1]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0; } // bottom border for (unsigned int i = wwi.width * (wwi.height - 1) + 1; i < wwi.count - 1; ++i) { Pair& res = wwi.buffer[i]; Pair vit = data[i]; neibourgs[0] = data[i-1]; neibourgs[1] = data[i+1]; neibourgs[2] = data[i-wwi.width]; neibourgs[3] = data[i-wwi.width-1]; neibourgs[4] = data[i-wwi.width+1]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0; } // left border for (unsigned int i = wwi.width; i < wwi.width*(wwi.height - 1); i += wwi.width) { Pair& res = wwi.buffer[i]; Pair vit = data[i]; neibourgs[0] = data[i+1]; neibourgs[1] = data[i-wwi.width]; neibourgs[2] = data[i+wwi.width]; neibourgs[3] = data[i-wwi.width+1]; neibourgs[4] = data[i+wwi.width+1]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0; } // right border for (unsigned int i = 2 * wwi.width - 1; i < wwi.count - 1; i += wwi.width) { Pair& res = wwi.buffer[i]; Pair vit = data[i]; neibourgs[0] = data[i-1]; neibourgs[1] = data[i-wwi.width]; neibourgs[2] = data[i+wwi.width]; neibourgs[3] = data[i-wwi.width-1]; neibourgs[4] = data[i+wwi.width-1]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + 5.0 * vit.x) / 10.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + 5.0 * vit.y) / 10.0; } // for the inner points for (unsigned int j = 1; j < wwi.height - 1; ++j) { for (unsigned int i = 1; i < wwi.width - 1; ++i) { unsigned int index = i + j * wwi.width; Pair& res = wwi.buffer[index]; Pair& vit = data[index]; neibourgs[0] = data[index-1]; neibourgs[1] = data[index+1]; neibourgs[2] = data[index-wwi.width]; neibourgs[3] = data[index+wwi.width]; neibourgs[4] = data[index-wwi.width-1]; neibourgs[5] = data[index-wwi.width+1]; neibourgs[6] = data[index+wwi.width-1]; neibourgs[7] = data[index+wwi.width+1]; res.x = (neibourgs[0].x + neibourgs[1].x + neibourgs[2].x + neibourgs[3].x + neibourgs[4].x + neibourgs[5].x + neibourgs[6].x + neibourgs[7].x + 8.0 * vit.x) / 16.0; res.y = (neibourgs[0].y + neibourgs[1].y + neibourgs[2].y + neibourgs[3].y + neibourgs[4].y + neibourgs[5].y + neibourgs[6].y + neibourgs[7].y + 8.0 * vit.y) / 16.0; } } Pair* tmp = data; *data_pointer = wwi.buffer; wwi.buffer = tmp; } void WobblyWindowsEffect::cancelWindowGrab(KWin::EffectWindow *w, int grabRole) { if (grabRole == WindowAddedGrabRole) { if (w->data(WindowAddedGrabRole).value() != this) { auto it = windows.find(w); if (it != windows.end()) { freeWobblyInfo(it.value()); windows.erase(it); } } } else if (grabRole == WindowClosedGrabRole) { if (w->data(WindowClosedGrabRole).value() != this) { auto it = windows.find(w); if (it != windows.end()) { if (it.value().status == Closing) { w->unrefWindow(); } freeWobblyInfo(it.value()); windows.erase(it); } } } } bool WobblyWindowsEffect::isActive() const { return !windows.isEmpty(); } } // namespace KWin diff --git a/effects/wobblywindows/wobblywindows_config.cpp b/effects/wobblywindows/wobblywindows_config.cpp index d9eaf5bdf..05178b075 100644 --- a/effects/wobblywindows/wobblywindows_config.cpp +++ b/effects/wobblywindows/wobblywindows_config.cpp @@ -1,120 +1,120 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2008 Cédric Borgese Copyright (C) 2008 Lucas Murray This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "wobblywindows_config.h" // KConfigSkeleton #include "wobblywindowsconfig.h" #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(WobblyWindowsEffectConfigFactory, "wobblywindows_config.json", registerPlugin();) namespace KWin { //----------------------------------------------------------------------------- // WARNING: This is (kinda) copied from wobblywindows.cpp struct ParameterSet { int stiffness; int drag; int move_factor; }; static const ParameterSet set_0 = { 15, 80, 10 }; static const ParameterSet set_1 = { 10, 85, 10 }; static const ParameterSet set_2 = { 6, 90, 10 }; static const ParameterSet set_3 = { 3, 92, 20 }; static const ParameterSet set_4 = { 1, 97, 25 }; ParameterSet pset[5] = { set_0, set_1, set_2, set_3, set_4 }; //----------------------------------------------------------------------------- WobblyWindowsEffectConfig::WobblyWindowsEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("wobblywindows")), parent, args) { WobblyWindowsConfig::instance(KWIN_CONFIG); m_ui.setupUi(this); addConfig(WobblyWindowsConfig::self(), this); - connect(m_ui.kcfg_WobblynessLevel, SIGNAL(valueChanged(int)), this, SLOT(wobblinessChanged())); + connect(m_ui.kcfg_WobblynessLevel, &QSlider::valueChanged, this, &WobblyWindowsEffectConfig::wobblinessChanged); load(); } WobblyWindowsEffectConfig::~WobblyWindowsEffectConfig() { } void WobblyWindowsEffectConfig::save() { KCModule::save(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("wobblywindows")); } void WobblyWindowsEffectConfig::wobblinessChanged() { ParameterSet preset = pset[m_ui.kcfg_WobblynessLevel->value()]; m_ui.kcfg_Stiffness->setValue(preset.stiffness); m_ui.kcfg_Drag->setValue(preset.drag); m_ui.kcfg_MoveFactor->setValue(preset.move_factor); } } // namespace #include "wobblywindows_config.moc" diff --git a/effects/zoom/zoom.cpp b/effects/zoom/zoom.cpp index efdec578a..d239c9e4f 100644 --- a/effects/zoom/zoom.cpp +++ b/effects/zoom/zoom.cpp @@ -1,535 +1,534 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak Copyright (C) 2010 Sebastian Sauer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "zoom.h" // KConfigSkeleton #include "zoomconfig.h" #include #include #include #include #include #include #include #include #include #include #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include #include #endif namespace KWin { ZoomEffect::ZoomEffect() : Effect() , zoom(1) , target_zoom(1) , polling(false) , zoomFactor(1.25) , mouseTracking(MouseTrackingProportional) , enableFocusTracking(false) , followFocus(true) , mousePointer(MousePointerScale) , focusDelay(350) // in milliseconds , imageWidth(0) , imageHeight(0) , isMouseHidden(false) , xMove(0) , yMove(0) , moveFactor(20.0) { initConfig(); QAction* a = 0; a = KStandardAction::zoomIn(this, SLOT(zoomIn()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); effects->registerGlobalShortcut(Qt::META + Qt::Key_Equal, a); effects->registerAxisShortcut(Qt::ControlModifier | Qt::MetaModifier, PointerAxisDown, a); a = KStandardAction::zoomOut(this, SLOT(zoomOut()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); effects->registerGlobalShortcut(Qt::META + Qt::Key_Minus, a); effects->registerAxisShortcut(Qt::ControlModifier | Qt::MetaModifier, PointerAxisUp, a); a = KStandardAction::actualSize(this, SLOT(actualSize()), this); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); effects->registerGlobalShortcut(Qt::META + Qt::Key_0, a); a = new QAction(this); a->setObjectName(QStringLiteral("MoveZoomLeft")); a->setText(i18n("Move Zoomed Area to Left")); KGlobalAccel::self()->setDefaultShortcut(a, QList()); KGlobalAccel::self()->setShortcut(a, QList()); effects->registerGlobalShortcut(QKeySequence(), a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomLeft())); + connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomLeft); a = new QAction(this); a->setObjectName(QStringLiteral("MoveZoomRight")); a->setText(i18n("Move Zoomed Area to Right")); KGlobalAccel::self()->setDefaultShortcut(a, QList()); KGlobalAccel::self()->setShortcut(a, QList()); effects->registerGlobalShortcut(QKeySequence(), a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomRight())); + connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomRight); a = new QAction(this); a->setObjectName(QStringLiteral("MoveZoomUp")); a->setText(i18n("Move Zoomed Area Upwards")); KGlobalAccel::self()->setDefaultShortcut(a, QList()); KGlobalAccel::self()->setShortcut(a, QList()); effects->registerGlobalShortcut(QKeySequence(), a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomUp())); + connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomUp); a = new QAction(this); a->setObjectName(QStringLiteral("MoveZoomDown")); a->setText(i18n("Move Zoomed Area Downwards")); KGlobalAccel::self()->setDefaultShortcut(a, QList()); KGlobalAccel::self()->setShortcut(a, QList()); effects->registerGlobalShortcut(QKeySequence(), a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(moveZoomDown())); + connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomDown); // TODO: these two actions don't belong into the effect. They need to be moved into KWin core a = new QAction(this); a->setObjectName(QStringLiteral("MoveMouseToFocus")); a->setText(i18n("Move Mouse to Focus")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_F5); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_F5); effects->registerGlobalShortcut(Qt::META + Qt::Key_F5, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(moveMouseToFocus())); + connect(a, &QAction::triggered, this, &ZoomEffect::moveMouseToFocus); a = new QAction(this); a->setObjectName(QStringLiteral("MoveMouseToCenter")); a->setText(i18n("Move Mouse to Center")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_F6); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_F6); effects->registerGlobalShortcut(Qt::META + Qt::Key_F6, a); - connect(a, SIGNAL(triggered(bool)), this, SLOT(moveMouseToCenter())); + connect(a, &QAction::triggered, this, &ZoomEffect::moveMouseToCenter); timeline.setDuration(350); timeline.setFrameRange(0, 100); - connect(&timeline, SIGNAL(frameChanged(int)), this, SLOT(timelineFrameChanged(int))); - connect(effects, SIGNAL(mouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers)), - this, SLOT(slotMouseChanged(QPoint,QPoint,Qt::MouseButtons,Qt::MouseButtons,Qt::KeyboardModifiers,Qt::KeyboardModifiers))); + connect(&timeline, &QTimeLine::frameChanged, this, &ZoomEffect::timelineFrameChanged); + connect(effects, &EffectsHandler::mouseChanged, this, &ZoomEffect::slotMouseChanged); source_zoom = -1; // used to trigger initialZoom reading reconfigure(ReconfigureAll); } ZoomEffect::~ZoomEffect() { // switch off and free resources showCursor(); // Save the zoom value. ZoomConfig::setInitialZoom(target_zoom); ZoomConfig::self()->save(); } void ZoomEffect::showCursor() { if (isMouseHidden) { disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &ZoomEffect::recreateTexture); // show the previously hidden mouse-pointer again and free the loaded texture/picture. effects->showCursor(); texture.reset(); #ifdef KWIN_HAVE_XRENDER_COMPOSITING xrenderPicture.reset(); #endif isMouseHidden = false; } } void ZoomEffect::hideCursor() { if (mouseTracking == MouseTrackingProportional && mousePointer == MousePointerKeep) return; // don't replace the actual cursor by a static image for no reason. if (!isMouseHidden) { // try to load the cursor-theme into a OpenGL texture and if successful then hide the mouse-pointer recreateTexture(); bool shouldHide = false; if (effects->isOpenGLCompositing()) { shouldHide = !texture.isNull(); } else if (effects->compositingType() == XRenderCompositing) { #ifdef KWIN_HAVE_XRENDER_COMPOSITING shouldHide = !xrenderPicture.isNull(); #endif } if (shouldHide) { effects->hideCursor(); connect(effects, &EffectsHandler::cursorShapeChanged, this, &ZoomEffect::recreateTexture); isMouseHidden = true; } } } void ZoomEffect::recreateTexture() { effects->makeOpenGLContextCurrent(); const auto cursor = effects->cursorImage(); if (!cursor.image().isNull()) { imageWidth = cursor.image().width(); imageHeight = cursor.image().height(); cursorHotSpot = cursor.hotSpot(); if (effects->isOpenGLCompositing()) { texture.reset(new GLTexture(cursor.image())); texture->setWrapMode(GL_CLAMP_TO_EDGE); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (effects->compositingType() == XRenderCompositing) xrenderPicture.reset(new XRenderPicture(cursor.image())); #endif } else { qCDebug(KWINEFFECTS) << "Falling back to proportional mouse tracking!"; mouseTracking = MouseTrackingProportional; } } void ZoomEffect::reconfigure(ReconfigureFlags) { ZoomConfig::self()->read(); // On zoom-in and zoom-out change the zoom by the defined zoom-factor. zoomFactor = qMax(0.1, ZoomConfig::zoomFactor()); // Visibility of the mouse-pointer. mousePointer = MousePointerType(ZoomConfig::mousePointer()); // Track moving of the mouse. mouseTracking = MouseTrackingType(ZoomConfig::mouseTracking()); // Enable tracking of the focused location. bool _enableFocusTracking = ZoomConfig::enableFocusTracking(); if (enableFocusTracking != _enableFocusTracking) { enableFocusTracking = _enableFocusTracking; if (QDBusConnection::sessionBus().isConnected()) { if (enableFocusTracking) QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.kaccessibleapp"), QStringLiteral("/Adaptor"), QStringLiteral("org.kde.kaccessibleapp.Adaptor"), QStringLiteral("focusChanged"), this, SLOT(focusChanged(int,int,int,int,int,int))); else QDBusConnection::sessionBus().disconnect(QStringLiteral("org.kde.kaccessibleapp"), QStringLiteral("/Adaptor"), QStringLiteral("org.kde.kaccessibleapp.Adaptor"), QStringLiteral("focusChanged"), this, SLOT(focusChanged(int,int,int,int,int,int))); } } // When the focus changes, move the zoom area to the focused location. followFocus = ZoomConfig::enableFollowFocus(); // The time in milliseconds to wait before a focus-event takes away a mouse-move. focusDelay = qMax(uint(0), ZoomConfig::focusDelay()); // The factor the zoom-area will be moved on touching an edge on push-mode or using the navigation KAction's. moveFactor = qMax(0.1, ZoomConfig::moveFactor()); if (source_zoom < 0) { // Load the saved zoom value. source_zoom = 1.0; target_zoom = ZoomConfig::initialZoom(); if (target_zoom > 1.0) zoomIn(target_zoom); } else { source_zoom = 1.0; } } void ZoomEffect::prePaintScreen(ScreenPrePaintData& data, int time) { if (zoom != target_zoom) { const float zoomDist = qAbs(target_zoom - source_zoom); if (target_zoom > zoom) zoom = qMin(zoom + ((zoomDist * time) / animationTime(150*zoomFactor)), target_zoom); else zoom = qMax(zoom - ((zoomDist * time) / animationTime(150*zoomFactor)), target_zoom); } if (zoom == 1.0) { showCursor(); } else { hideCursor(); data.mask |= PAINT_SCREEN_TRANSFORMED; } effects->prePaintScreen(data, time); } void ZoomEffect::paintScreen(int mask, QRegion region, ScreenPaintData& data) { if (zoom != 1.0) { data *= QVector2D(zoom, zoom); const QSize screenSize = effects->virtualScreenSize(); // mouse-tracking allows navigation of the zoom-area using the mouse. switch(mouseTracking) { case MouseTrackingProportional: data.setXTranslation(- int(cursorPoint.x() * (zoom - 1.0))); data.setYTranslation(- int(cursorPoint.y() * (zoom - 1.0))); prevPoint = cursorPoint; break; case MouseTrackingCentred: prevPoint = cursorPoint; // fall through case MouseTrackingDisabled: data.setXTranslation(qMin(0, qMax(int(screenSize.width() - screenSize.width() * zoom), int(screenSize.width() / 2 - prevPoint.x() * zoom)))); data.setYTranslation(qMin(0, qMax(int(screenSize.height() - screenSize.height() * zoom), int(screenSize.height() / 2 - prevPoint.y() * zoom)))); break; case MouseTrackingPush: { // touching an edge of the screen moves the zoom-area in that direction. int x = cursorPoint.x() * zoom - prevPoint.x() * (zoom - 1.0); int y = cursorPoint.y() * zoom - prevPoint.y() * (zoom - 1.0); int threshold = 4; xMove = yMove = 0; if (x < threshold) xMove = (x - threshold) / zoom; else if (x + threshold > screenSize.width()) xMove = (x + threshold - screenSize.width()) / zoom; if (y < threshold) yMove = (y - threshold) / zoom; else if (y + threshold > screenSize.height()) yMove = (y + threshold - screenSize.height()) / zoom; if (xMove) prevPoint.setX(qMax(0, qMin(screenSize.width(), prevPoint.x() + xMove))); if (yMove) prevPoint.setY(qMax(0, qMin(screenSize.height(), prevPoint.y() + yMove))); data.setXTranslation(- int(prevPoint.x() * (zoom - 1.0))); data.setYTranslation(- int(prevPoint.y() * (zoom - 1.0))); break; } } // use the focusPoint if focus tracking is enabled if (enableFocusTracking && followFocus) { bool acceptFocus = true; if (mouseTracking != MouseTrackingDisabled && focusDelay > 0) { // Wait some time for the mouse before doing the switch. This serves as threshold // to prevent the focus from jumping around to much while working with the mouse. const int msecs = lastMouseEvent.msecsTo(lastFocusEvent); acceptFocus = msecs > focusDelay; } if (acceptFocus) { data.setXTranslation(- int(focusPoint.x() * (zoom - 1.0))); data.setYTranslation(- int(focusPoint.y() * (zoom - 1.0))); prevPoint = focusPoint; } } } effects->paintScreen(mask, region, data); if (zoom != 1.0 && mousePointer != MousePointerHide) { // Draw the mouse-texture at the position matching to zoomed-in image of the desktop. Hiding the // previous mouse-cursor and drawing our own fake mouse-cursor is needed to be able to scale the // mouse-cursor up and to re-position those mouse-cursor to match to the chosen zoom-level. int w = imageWidth; int h = imageHeight; if (mousePointer == MousePointerScale) { w *= zoom; h *= zoom; } const QPoint p = effects->cursorPos() - cursorHotSpot; QRect rect(p.x() * zoom + data.xTranslation(), p.y() * zoom + data.yTranslation(), w, h); if (texture) { texture->bind(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); QMatrix4x4 mvp = data.projectionMatrix(); mvp.translate(rect.x(), rect.y()); s->setUniform(GLShader::ModelViewProjectionMatrix, mvp); texture->render(region, rect); ShaderManager::instance()->popShader(); texture->unbind(); glDisable(GL_BLEND); } #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (xrenderPicture) { #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) static const xcb_render_transform_t xrenderIdentity = { DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; if (mousePointer == MousePointerScale) { xcb_render_set_picture_filter(xcbConnection(), *xrenderPicture, 4, const_cast("good"), 0, NULL); const xcb_render_transform_t xform = { DOUBLE_TO_FIXED(1.0 / zoom), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1.0 / zoom), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1) }; xcb_render_set_picture_transform(xcbConnection(), *xrenderPicture, xform); } xcb_render_composite(xcbConnection(), XCB_RENDER_PICT_OP_OVER, *xrenderPicture, XCB_RENDER_PICTURE_NONE, effects->xrenderBufferPicture(), 0, 0, 0, 0, rect.x(), rect.y(), rect.width(), rect.height()); if (mousePointer == MousePointerScale) xcb_render_set_picture_transform(xcbConnection(), *xrenderPicture, xrenderIdentity); #undef DOUBLE_TO_FIXED } #endif } } void ZoomEffect::postPaintScreen() { if (zoom != target_zoom) effects->addRepaintFull(); effects->postPaintScreen(); } void ZoomEffect::zoomIn(double to) { source_zoom = zoom; if (to < 0.0) target_zoom *= zoomFactor; else target_zoom = to; if (!polling) { polling = true; effects->startMousePolling(); } cursorPoint = effects->cursorPos(); if (mouseTracking == MouseTrackingDisabled) prevPoint = cursorPoint; effects->addRepaintFull(); } void ZoomEffect::zoomOut() { source_zoom = zoom; target_zoom /= zoomFactor; if ((zoomFactor > 1 && target_zoom < 1.01) || (zoomFactor < 1 && target_zoom > 0.99)) { target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } } if (mouseTracking == MouseTrackingDisabled) prevPoint = effects->cursorPos(); effects->addRepaintFull(); } void ZoomEffect::actualSize() { source_zoom = zoom; target_zoom = 1; if (polling) { polling = false; effects->stopMousePolling(); } effects->addRepaintFull(); } void ZoomEffect::timelineFrameChanged(int /* frame */) { const QSize screenSize = effects->virtualScreenSize(); prevPoint.setX(qMax(0, qMin(screenSize.width(), prevPoint.x() + xMove))); prevPoint.setY(qMax(0, qMin(screenSize.height(), prevPoint.y() + yMove))); cursorPoint = prevPoint; effects->addRepaintFull(); } void ZoomEffect::moveZoom(int x, int y) { if (timeline.state() == QTimeLine::Running) timeline.stop(); const QSize screenSize = effects->virtualScreenSize(); if (x < 0) xMove = - qMax(1.0, screenSize.width() / zoom / moveFactor); else if (x > 0) xMove = qMax(1.0, screenSize.width() / zoom / moveFactor); else xMove = 0; if (y < 0) yMove = - qMax(1.0, screenSize.height() / zoom / moveFactor); else if (y > 0) yMove = qMax(1.0, screenSize.height() / zoom / moveFactor); else yMove = 0; timeline.start(); } void ZoomEffect::moveZoomLeft() { moveZoom(-1, 0); } void ZoomEffect::moveZoomRight() { moveZoom(1, 0); } void ZoomEffect::moveZoomUp() { moveZoom(0, -1); } void ZoomEffect::moveZoomDown() { moveZoom(0, 1); } void ZoomEffect::moveMouseToFocus() { QCursor::setPos(focusPoint.x(), focusPoint.y()); } void ZoomEffect::moveMouseToCenter() { const QRect r = effects->virtualScreenGeometry(); QCursor::setPos(r.x() + r.width() / 2, r.y() + r.height() / 2); } void ZoomEffect::slotMouseChanged(const QPoint& pos, const QPoint& old, Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) { if (zoom == 1.0) return; cursorPoint = pos; if (pos != old) { lastMouseEvent = QTime::currentTime(); effects->addRepaintFull(); } } void ZoomEffect::focusChanged(int px, int py, int rx, int ry, int rwidth, int rheight) { if (zoom == 1.0) return; const QSize screenSize = effects->virtualScreenSize(); focusPoint = (px >= 0 && py >= 0) ? QPoint(px, py) : QPoint(rx + qMax(0, (qMin(screenSize.width(), rwidth) / 2) - 60), ry + qMax(0, (qMin(screenSize.height(), rheight) / 2) - 60)); if (enableFocusTracking) { lastFocusEvent = QTime::currentTime(); effects->addRepaintFull(); } } bool ZoomEffect::isActive() const { return zoom != 1.0 || zoom != target_zoom; } } // namespace diff --git a/effects/zoom/zoom_config.cpp b/effects/zoom/zoom_config.cpp index 9bc02e417..611ca7ccb 100644 --- a/effects/zoom/zoom_config.cpp +++ b/effects/zoom/zoom_config.cpp @@ -1,150 +1,150 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2007 Rivo Laks Copyright (C) 2010 Sebastian Sauer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "zoom_config.h" // KConfigSkeleton #include "zoomconfig.h" #include #include #include #include #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON(ZoomEffectConfigFactory, "zoom_config.json", registerPlugin();) namespace KWin { ZoomEffectConfigForm::ZoomEffectConfigForm(QWidget* parent) : QWidget(parent) { setupUi(this); } ZoomEffectConfig::ZoomEffectConfig(QWidget* parent, const QVariantList& args) : KCModule(KAboutData::pluginData(QStringLiteral("zoom")), parent, args) { ZoomConfig::instance(KWIN_CONFIG); m_ui = new ZoomEffectConfigForm(this); QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); addConfig(ZoomConfig::self(), m_ui); - connect(m_ui->editor, SIGNAL(keyChange()), this, SLOT(changed())); + connect(m_ui->editor, &KShortcutsEditor::keyChange, this, qOverload<>(&ZoomEffectConfig::changed)); // Shortcut config. The shortcut belongs to the component "kwin"! KActionCollection *actionCollection = new KActionCollection(this, QStringLiteral("kwin")); actionCollection->setComponentDisplayName(i18n("KWin")); actionCollection->setConfigGroup(QStringLiteral("Zoom")); actionCollection->setConfigGlobal(true); QAction* a; a = actionCollection->addAction(KStandardAction::ZoomIn); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Equal); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Equal); a = actionCollection->addAction(KStandardAction::ZoomOut); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_Minus); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_Minus); a = actionCollection->addAction(KStandardAction::ActualSize); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_0); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_0); a = actionCollection->addAction(QStringLiteral("MoveZoomLeft")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-previous"))); a->setText(i18n("Move Left")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Left); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Left); a = actionCollection->addAction(QStringLiteral("MoveZoomRight")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-next"))); a->setText(i18n("Move Right")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Right); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Right); a = actionCollection->addAction(QStringLiteral("MoveZoomUp")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); a->setText(i18n("Move Up")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Up); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Up); a = actionCollection->addAction(QStringLiteral("MoveZoomDown")); a->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); a->setText(i18n("Move Down")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Down); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::CTRL + Qt::Key_Down); a = actionCollection->addAction(QStringLiteral("MoveMouseToFocus")); a->setIcon(QIcon::fromTheme(QStringLiteral("view-restore"))); a->setText(i18n("Move Mouse to Focus")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_F5); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_F5); a = actionCollection->addAction(QStringLiteral("MoveMouseToCenter")); a->setIcon(QIcon::fromTheme(QStringLiteral("view-restore"))); a->setText(i18n("Move Mouse to Center")); a->setProperty("isConfigurationAction", true); KGlobalAccel::self()->setDefaultShortcut(a, QList() << Qt::META + Qt::Key_F6); KGlobalAccel::self()->setShortcut(a, QList() << Qt::META + Qt::Key_F6); m_ui->editor->addCollection(actionCollection); load(); } ZoomEffectConfig::~ZoomEffectConfig() { // Undo (only) unsaved changes to global key shortcuts m_ui->editor->undoChanges(); } void ZoomEffectConfig::save() { m_ui->editor->save(); // undo() will restore to this state from now on KCModule::save(); OrgKdeKwinEffectsInterface interface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Effects"), QDBusConnection::sessionBus()); interface.reconfigureEffect(QStringLiteral("zoom")); } } // namespace #include "zoom_config.moc"