diff --git a/autotests/integration/effects/CMakeLists.txt b/autotests/integration/effects/CMakeLists.txt index d1d638352..294acb01f 100644 --- a/autotests/integration/effects/CMakeLists.txt +++ b/autotests/integration/effects/CMakeLists.txt @@ -1,5 +1,6 @@ if (XCB_ICCCM_FOUND) integrationTest(NAME testTranslucency SRCS translucency_test.cpp LIBS XCB::ICCCM) integrationTest(NAME testSlidingPopups SRCS slidingpopups_test.cpp LIBS XCB::ICCCM) endif() integrationTest(NAME testFade SRCS fade_test.cpp) +integrationTest(WAYLAND_ONLY NAME testEffectWindowGeometry SRCS windowgeometry_test.cpp) diff --git a/autotests/integration/effects/windowgeometry_test.cpp b/autotests/integration/effects/windowgeometry_test.cpp new file mode 100644 index 000000000..18e5d3cde --- /dev/null +++ b/autotests/integration/effects/windowgeometry_test.cpp @@ -0,0 +1,99 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 "kwin_wayland_test.h" +#include "composite.h" +#include "effects.h" +#include "effectloader.h" +#include "cursor.h" +#include "platform.h" +#include "shell_client.h" +#include "wayland_server.h" +#include "workspace.h" +#include "effect_builtins.h" + +#include + +#include +#include +#include + +using namespace KWin; +using namespace KWayland::Client; +static const QString s_socketName = QStringLiteral("wayland_test_effects_windowgeometry-0"); + +class WindowGeometryTest : public QObject +{ +Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + + void testStartup(); +}; + +void WindowGeometryTest::initTestCase() +{ + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); + QVERIFY(workspaceCreatedSpy.isValid()); + kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); + QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); + + // disable all effects - we don't want to have it interact with the rendering + auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); + KConfigGroup plugins(config, QStringLiteral("Plugins")); + ScriptedEffectLoader loader; + const auto builtinNames = BuiltInEffects::availableEffectNames() << loader.listOfKnownEffects(); + for (QString name : builtinNames) { + plugins.writeEntry(name + QStringLiteral("Enabled"), false); + } + plugins.writeEntry(BuiltInEffects::nameForEffect(BuiltInEffect::WindowGeometry) + QStringLiteral("Enabled"), true); + + config->sync(); + kwinApp()->setConfig(config); + + qputenv("KWIN_EFFECTS_FORCE_ANIMATIONS", "1"); + kwinApp()->start(); + QVERIFY(workspaceCreatedSpy.wait()); + QVERIFY(KWin::Compositor::self()); +} + +void WindowGeometryTest::init() +{ + QVERIFY(Test::setupWaylandConnection()); +} + +void WindowGeometryTest::cleanup() +{ + Test::destroyWaylandConnection(); +} + +void WindowGeometryTest::testStartup() +{ + // just a test to load the effect to verify it doesn't crash + EffectsHandlerImpl *e = static_cast(effects); + QVERIFY(e->isEffectLoaded(BuiltInEffects::nameForEffect(BuiltInEffect::WindowGeometry))); +} + +WAYLANDTEST_MAIN(WindowGeometryTest) +#include "windowgeometry_test.moc" diff --git a/effects/windowgeometry/windowgeometry.cpp b/effects/windowgeometry/windowgeometry.cpp index d9c623fe8..8d66eadc6 100644 --- a/effects/windowgeometry/windowgeometry.cpp +++ b/effects/windowgeometry/windowgeometry.cpp @@ -1,228 +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); - 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); - 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(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))); } 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.h b/effects/windowgeometry/windowgeometry.h index 276eaf1d2..6c05550bb 100644 --- a/effects/windowgeometry/windowgeometry.h +++ b/effects/windowgeometry/windowgeometry.h @@ -1,72 +1,73 @@ /******************************************************************** 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 . *********************************************************************/ #ifndef WINDOWGEOMETRY_H #define WINDOWGEOMETRY_H #include namespace KWin { class WindowGeometry : public Effect { Q_OBJECT Q_PROPERTY(bool handlesMoves READ isHandlesMoves) Q_PROPERTY(bool handlesResizes READ isHandlesResizes) public: WindowGeometry(); ~WindowGeometry(); inline bool provides(Effect::Feature ef) { return ef == Effect::GeometryTip; } void reconfigure(ReconfigureFlags); void paintScreen(int mask, QRegion region, ScreenPaintData &data); virtual bool isActive() const; int requestedEffectChainPosition() const override { return 90; } // for properties bool isHandlesMoves() const { return iHandleMoves; } bool isHandlesResizes() const { return iHandleResizes; } private Q_SLOTS: void toggle(); void slotWindowStartUserMovedResized(KWin::EffectWindow *w); void slotWindowFinishUserMovedResized(KWin::EffectWindow *w); void slotWindowStepUserMovedResized(KWin::EffectWindow *w, const QRect &geometry); private: + void createFrames(); EffectWindow *myResizeWindow; - EffectFrame *myMeasure[3]; + EffectFrame *myMeasure[3] = {nullptr, nullptr, nullptr}; QRect myOriginalGeometry, myCurrentGeometry; QRect myExtraDirtyArea; bool iAmActive, iAmActivated, iHandleMoves, iHandleResizes; QString myCoordString[2], myResizeString; }; } // namespace #endif