diff --git a/autotests/wayland/CMakeLists.txt b/autotests/wayland/CMakeLists.txt index 9c6d5a453..e49315cb7 100644 --- a/autotests/wayland/CMakeLists.txt +++ b/autotests/wayland/CMakeLists.txt @@ -1,136 +1,147 @@ add_definitions(-DKWINBACKENDPATH="${CMAKE_BINARY_DIR}/backends/virtual/KWinWaylandVirtualBackend.so") add_definitions(-DKWINQPAPATH="${CMAKE_BINARY_DIR}/plugins/qpa/") ######################################################## # Test Start ######################################################## set( testStart_SRCS start_test.cpp kwin_wayland_test.cpp ) add_executable(testStart ${testStart_SRCS}) target_link_libraries( testStart kwin Qt5::Test) add_test(kwin-testStart testStart) ecm_mark_as_test(testStart) ######################################################## # Transient Window without input test ######################################################## set( testTransientNoInput_SRCS transient_no_input_test.cpp kwin_wayland_test.cpp ) add_executable(testTransientNoInput ${testTransientNoInput_SRCS}) target_link_libraries( testTransientNoInput kwin Qt5::Test) add_test(kwin-testTransientNoInput testTransientNoInput) ecm_mark_as_test(testTransientNoInput) ######################################################## # Quick Tiling test ######################################################## set( testQuickTiling_SRCS quick_tiling_test.cpp kwin_wayland_test.cpp ) add_executable(testQuickTiling ${testQuickTiling_SRCS}) target_link_libraries( testQuickTiling kwin Qt5::Test) add_test(kwin-testQuickTiling testQuickTiling) ecm_mark_as_test(testQuickTiling) ######################################################## # Move/Resize window test ######################################################## set( testMoveResize_SRCS move_resize_window_test.cpp kwin_wayland_test.cpp ) add_executable(testMoveResize ${testMoveResize_SRCS}) target_link_libraries( testMoveResize kwin Qt5::Test) add_test(kwin-testMoveResize testMoveResize) ecm_mark_as_test(testMoveResize) ######################################################## # Don't Crash For glxgears ######################################################## set( testDontCrashGlxgears_SRCS dont_crash_glxgears.cpp kwin_wayland_test.cpp ) add_executable(testDontCrashGlxgears ${testDontCrashGlxgears_SRCS}) target_link_libraries( testDontCrashGlxgears kwin Qt5::Test) add_test(kwin-testDontCrashGlxgears testDontCrashGlxgears) ecm_mark_as_test(testDontCrashGlxgears) ######################################################## # Lock screen integration test ######################################################## set( testLockScreen_SRCS lockscreen.cpp kwin_wayland_test.cpp ) add_executable(testLockScreen ${testLockScreen_SRCS}) target_link_libraries( testLockScreen kwin Qt5::Test) add_test(kwin-testLockScreen testLockScreen) ecm_mark_as_test(testLockScreen) ######################################################## # Decoration input test ######################################################## set( testDecorationInput_SRCS decoration_input_test.cpp kwin_wayland_test.cpp ) add_executable(testDecorationInput ${testDecorationInput_SRCS}) target_link_libraries( testDecorationInput kwin Qt5::Test) add_test(kwin-testDecorationInput testDecorationInput) ecm_mark_as_test(testDecorationInput) ######################################################## # Internal Window test ######################################################## set( testInternalWindow_SRCS internal_window.cpp kwin_wayland_test.cpp ) add_executable(testInternalWindow ${testInternalWindow_SRCS}) target_link_libraries( testInternalWindow kwin Qt5::Test) add_test(kwin-testInternalWindow testInternalWindow) ecm_mark_as_test(testInternalWindow) ######################################################## # Touch Input Test ######################################################## set( testTouchInput_SRCS touch_input_test.cpp kwin_wayland_test.cpp ) add_executable(testTouchInput ${testTouchInput_SRCS}) target_link_libraries( testTouchInput kwin Qt5::Test) add_test(kwin-testTouchInput testTouchInput) ecm_mark_as_test(testTouchInput) ######################################################## # Input Stacking Order Test ######################################################## set( testInputStackingOrder_SRCS input_stacking_order.cpp kwin_wayland_test.cpp ) add_executable(testInputStackingOrder ${testInputStackingOrder_SRCS}) target_link_libraries( testInputStackingOrder kwin Qt5::Test) add_test(kwin-testInputStackingOrder testInputStackingOrder) ecm_mark_as_test(testInputStackingOrder) ######################################################## # Pointer Input Test ######################################################## set( testPointerInput_SRCS pointer_input.cpp kwin_wayland_test.cpp ) add_executable(testPointerInput ${testPointerInput_SRCS}) target_link_libraries( testPointerInput kwin Qt5::Test) add_test(kwin-testPointerInput testPointerInput) ecm_mark_as_test(testPointerInput) ######################################################## # PlatformCursorTest ######################################################## set( testPlatformCursor_SRCS platformcursor.cpp kwin_wayland_test.cpp ) add_executable(testPlatformCursor ${testPlatformCursor_SRCS}) target_link_libraries( testPlatformCursor kwin Qt5::Test) add_test(kwin-testPlatformCursor testPlatformCursor) ecm_mark_as_test(testPlatformCursor) ######################################################## # Don't crash cancel animation test ######################################################## set( testDontCrashCancelAnimation_SRCS dont_crash_cancel_animation.cpp kwin_wayland_test.cpp ) add_executable(testDontCrashCancelAnimation ${testDontCrashCancelAnimation_SRCS}) target_link_libraries( testDontCrashCancelAnimation kwin Qt5::Test) add_test(kwin-testDontCrashCancelAnimation testDontCrashCancelAnimation) ecm_mark_as_test(testDontCrashCancelAnimation) ######################################################## # Transient Placement Test ######################################################## set( testTransientPlacmenet_SRCS transient_placement.cpp kwin_wayland_test.cpp ) add_executable(testTransientPlacmenet ${testTransientPlacmenet_SRCS}) target_link_libraries( testTransientPlacmenet kwin Qt5::Test) add_test(kwin-testTransientPlacmenet testTransientPlacmenet) ecm_mark_as_test(testTransientPlacmenet) ######################################################## # Dont Crash Empty Deco Test ######################################################## set( testDontCrashEmptyDeco_SRCS dont_crash_empty_deco.cpp kwin_wayland_test.cpp ) add_executable(testDontCrashEmptyDeco ${testDontCrashEmptyDeco_SRCS}) target_link_libraries( testDontCrashEmptyDeco kwin Qt5::Test) add_test(kwin-testDontCrashEmptyDeco testDontCrashEmptyDeco) ecm_mark_as_test(testDontCrashEmptyDeco) + +######################################################## +# Dont Crash Aurorae Destroy Deco +######################################################## +if (XCB_ICCCM_FOUND) + set( testDontCrashAuroraeDestroyDeco_SRCS dont_crash_aurorae_destroy_deco.cpp kwin_wayland_test.cpp ) + add_executable(testDontCrashAuroraeDestroyDeco ${testDontCrashAuroraeDestroyDeco_SRCS}) + target_link_libraries( testDontCrashAuroraeDestroyDeco kwin Qt5::Test XCB::ICCCM) + add_test(kwin-testDontCrashAuroraeDestroyDeco testDontCrashAuroraeDestroyDeco) + ecm_mark_as_test(testDontCrashAuroraeDestroyDeco) +endif() diff --git a/autotests/wayland/dont_crash_aurorae_destroy_deco.cpp b/autotests/wayland/dont_crash_aurorae_destroy_deco.cpp new file mode 100644 index 000000000..1865014e4 --- /dev/null +++ b/autotests/wayland/dont_crash_aurorae_destroy_deco.cpp @@ -0,0 +1,155 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2016 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 "kwin_wayland_test.h" +#include "abstract_backend.h" +#include "client.h" +#include "cursor.h" +#include "screenedge.h" +#include "screens.h" +#include "wayland_server.h" +#include "workspace.h" +#include "shell_client.h" +#include + +#include + +#include + +#include + +namespace KWin +{ + +static const QString s_socketName = QStringLiteral("wayland_test_kwin_dont_crash_aurorae_destroy_deco-0"); + +class DontCrashAuroraeDestroyDecoTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void init(); + void testBorderlessMaximizedWindows(); + +}; + +void DontCrashAuroraeDestroyDecoTest::initTestCase() +{ + if (!QFile::exists(QStringLiteral("/dev/dri/card0"))) { + QSKIP("Needs a dri device"); + } + qRegisterMetaType(); + qRegisterMetaType(); + QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); + QVERIFY(workspaceCreatedSpy.isValid()); + waylandServer()->backend()->setInitialWindowSize(QSize(1280, 1024)); + QMetaObject::invokeMethod(waylandServer()->backend(), "setOutputCount", Qt::DirectConnection, Q_ARG(int, 2)); + waylandServer()->init(s_socketName.toLocal8Bit()); + + KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); + config->group("org.kde.kdecoration2").writeEntry("library", "org.kde.kwin.aurorae"); + config->sync(); + kwinApp()->setConfig(config); + + // this test needs to enforce OpenGL compositing to get into the crashy condition + qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2")); + kwinApp()->start(); + QVERIFY(workspaceCreatedSpy.wait()); + QCOMPARE(screens()->count(), 2); + QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024)); + QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024)); + setenv("QT_QPA_PLATFORM", "wayland", true); + waylandServer()->initWorkspace(); +} + +void DontCrashAuroraeDestroyDecoTest::init() +{ + screens()->setCurrent(0); + Cursor::setPos(QPoint(640, 512)); +} + +void DontCrashAuroraeDestroyDecoTest::testBorderlessMaximizedWindows() +{ + // this test verifies that Aurorae doesn't crash when clicking the maximize button + // with kwin config option BorderlessMaximizedWindows + // see BUG 362772 + + // first adjust the config + KConfigGroup group = kwinApp()->config()->group("Windows"); + group.writeEntry("BorderlessMaximizedWindows", true); + group.sync(); + workspace()->slotReconfigure(); + QCOMPARE(options->borderlessMaximizedWindows(), true); + + // create an xcb window + xcb_connection_t *c = xcb_connect(nullptr, nullptr); + QVERIFY(!xcb_connection_has_error(c)); + + xcb_window_t w = xcb_generate_id(c); + xcb_create_window(c, XCB_COPY_FROM_PARENT, w, rootWindow(), 0, 0, 100, 200, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); + xcb_map_window(c, w); + xcb_flush(c); + + // we should get a client for it + QSignalSpy windowCreatedSpy(workspace(), &Workspace::clientAdded); + QVERIFY(windowCreatedSpy.isValid()); + QVERIFY(windowCreatedSpy.wait()); + Client *client = windowCreatedSpy.first().first().value(); + QVERIFY(client); + QCOMPARE(client->window(), w); + QVERIFY(client->isDecorated()); + QCOMPARE(client->maximizeMode(), MaximizeRestore); + QCOMPARE(client->noBorder(), false); + // verify that the deco is Aurorae + QCOMPARE(qstrcmp(client->decoration()->metaObject()->className(), "Aurorae::Decoration"), 0); + // find the maximize button + QQuickItem *item = client->decoration()->findChild("maximizeButton"); + QVERIFY(item); + const QPointF scenePoint = item->mapToScene(QPoint(0, 0)); + + // mark the window as ready for painting, otherwise it doesn't get input events + QMetaObject::invokeMethod(client, "setReadyForPainting"); + QVERIFY(client->readyForPainting()); + + // simulate click on maximize button + QSignalSpy maximizedStateChangedSpy(client, static_cast(&AbstractClient::clientMaximizedStateChanged)); + QVERIFY(maximizedStateChangedSpy.isValid()); + quint32 timestamp = 1; + waylandServer()->backend()->pointerMotion(client->geometry().topLeft() + scenePoint.toPoint(), timestamp++); + waylandServer()->backend()->pointerButtonPressed(BTN_LEFT, timestamp++); + waylandServer()->backend()->pointerButtonReleased(BTN_LEFT, timestamp++); + QVERIFY(maximizedStateChangedSpy.wait()); + QCOMPARE(client->maximizeMode(), MaximizeFull); + QCOMPARE(client->noBorder(), true); + + // and destroy the window again + xcb_unmap_window(c, w); + xcb_destroy_window(c, w); + xcb_flush(c); + xcb_disconnect(c); + + QSignalSpy windowClosedSpy(client, &Client::windowClosed); + QVERIFY(windowClosedSpy.isValid()); + QVERIFY(windowClosedSpy.wait()); +} + +} + +WAYLANDTEST_MAIN(KWin::DontCrashAuroraeDestroyDecoTest) +#include "dont_crash_aurorae_destroy_deco.moc" diff --git a/clients/aurorae/themes/plastik/package/contents/ui/main.qml b/clients/aurorae/themes/plastik/package/contents/ui/main.qml index 6b88dea5a..ce29dc1ae 100644 --- a/clients/aurorae/themes/plastik/package/contents/ui/main.qml +++ b/clients/aurorae/themes/plastik/package/contents/ui/main.qml @@ -1,428 +1,429 @@ /******************************************************************** Copyright (C) 2012 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 . *********************************************************************/ import QtQuick 2.0 import org.kde.kwin.decoration 0.1 import org.kde.kwin.decorations.plastik 1.0 Decoration { function readBorderSize() { switch (borderSize) { case DecorationOptions.BorderTiny: borders.setBorders(3); extendedBorders.setAllBorders(0); break; case DecorationOptions.BorderLarge: borders.setBorders(8); extendedBorders.setAllBorders(0); break; case DecorationOptions.BorderVeryLarge: borders.setBorders(12); extendedBorders.setAllBorders(0); break; case DecorationOptions.BorderHuge: borders.setBorders(18); extendedBorders.setAllBorders(0); break; case DecorationOptions.BorderVeryHuge: borders.setBorders(27); extendedBorders.setAllBorders(0); break; case DecorationOptions.BorderOversized: borders.setBorders(40); extendedBorders.setAllBorders(0); break; case DecorationOptions.BorderNoSides: borders.setBorders(4); borders.setSideBorders(1); extendedBorders.setSideBorders(3); break; case DecorationOptions.BorderNone: borders.setBorders(1); extendedBorders.setBorders(3); break; case DecorationOptions.BorderNormal: // fall through to default default: borders.setBorders(4); extendedBorders.setAllBorders(0); break; } } function readConfig() { var titleAlignLeft = decoration.readConfig("titleAlignLeft", true); var titleAlignCenter = decoration.readConfig("titleAlignCenter", false); var titleAlignRight = decoration.readConfig("titleAlignRight", false); if (titleAlignRight) { root.titleAlignment = Text.AlignRight; } else if (titleAlignCenter) { root.titleAlignment = Text.AlignHCenter; } else { if (!titleAlignLeft) { console.log("Error reading title alignment: all alignment options are false"); } root.titleAlignment = Text.AlignLeft; } root.animateButtons = decoration.readConfig("animateButtons", true); root.titleShadow = decoration.readConfig("titleShadow", true); if (decoration.animationsSupported) { root.animationDuration = 150; root.animateButtons = false; } } ColorHelper { id: colorHelper } DecorationOptions { id: options deco: decoration } property int borderSize: decorationSettings.borderSize property alias buttonSize: titleRow.captionHeight property alias titleAlignment: caption.horizontalAlignment property color titleBarColor: options.titleBarColor // set by readConfig after Component completed, ensures that buttons do not flicker property int animationDuration: 0 property bool animateButtons: true property bool titleShadow: true Behavior on titleBarColor { ColorAnimation { duration: root.animationDuration } } id: root alpha: false Rectangle { color: root.titleBarColor anchors { fill: parent } border { width: decoration.client.maximized ? 0 : 2 color: colorHelper.shade(root.titleBarColor, ColorHelper.DarkShade) } Rectangle { id: borderLeft anchors { left: parent.left top: parent.top bottom: parent.bottom leftMargin: 1 bottomMargin: 1 topMargin: 1 } visible: !decoration.client.maximized width: root.borders.left color: root.titleBarColor Rectangle { width: 1 anchors { left: parent.left top: parent.top bottom: parent.bottom } color: colorHelper.shade(root.titleBarColor, ColorHelper.LightShade, colorHelper.contrast - (decoration.client.active ? 0.4 : 0.8)) } } Rectangle { id: borderRight anchors { right: parent.right top: parent.top bottom: parent.bottom rightMargin: 1 bottomMargin: 1 topMargin: 1 } visible: !decoration.client.maximzied width: root.borders.right -1 color: root.titleBarColor Rectangle { width: 1 anchors { bottom: parent.bottom top: parent.top } x: parent.x + parent.width - 1 color: colorHelper.shade(root.titleBarColor, ColorHelper.DarkShade, colorHelper.contrast - (decoration.client.active ? 0.4 : 0.8)) } } Rectangle { id: borderBottom anchors { left: parent.right right: parent.left bottom: parent.bottom leftMargin: 1 rightMargin: 1 } height: root.borders.bottom visible: !decoration.client.maximzied color: root.titleBarColor Rectangle { height: 1 anchors { left: parent.left right: parent.right } y: parent.y + parent.height - 1 color: colorHelper.shade(root.titleBarColor, ColorHelper.DarkShade, colorHelper.contrast - (decoration.client.active ? 0.4 : 0.8)) } } Rectangle { id: top property int topMargin: 1 property real normalHeight: titleRow.normalHeight + topMargin + 1 property real maximizedHeight: titleRow.maximizedHeight + 1 height: decoration.client.maximized ? maximizedHeight : normalHeight anchors { left: parent.left right: parent.right top: parent.top topMargin: decoration.client.maximized ? 0 : top.topMargin leftMargin: decoration.client.maximized ? 0 : 2 rightMargin: decoration.client.maximized ? 0 : 2 } gradient: Gradient { id: topGradient GradientStop { position: 0.0 color: colorHelper.shade(root.titleBarColor, ColorHelper.MidlightShade, colorHelper.contrast - 0.4) } GradientStop { id: middleGradientStop position: 4.0/(decoration.client.maximized ? top.maximizedHeight : top.normalHeight) color: colorHelper.shade(root.titleBarColor, ColorHelper.MidShade, colorHelper.contrast - 0.4) } GradientStop { position: 1.0 color: root.titleBarColor } } Rectangle { height: 1 anchors { top: top.top left: top.left right: top.right } visible: !decoration.client.maximized color: colorHelper.shade(root.titleBarColor, ColorHelper.LightShade, colorHelper.contrast - (decoration.client.active ? 0.4 : 0.8)) } Item { id: titleRow property real captionHeight: caption.implicitHeight + 4 property int topMargin: 3 property int bottomMargin: 1 property real normalHeight: captionHeight + bottomMargin + topMargin property real maximizedHeight: captionHeight + bottomMargin anchors { left: parent.left right: parent.right top: parent.top topMargin: decoration.client.maximized ? 0 : titleRow.topMargin leftMargin: decoration.client.maximized ? 0 : 3 rightMargin: decoration.client.maximized ? 0 : 3 bottomMargin: titleRow.bottomMargin } ButtonGroup { id: leftButtonGroup spacing: 1 explicitSpacer: root.buttonSize menuButton: menuButtonComponent appMenuButton: appMenuButtonComponent minimizeButton: minimizeButtonComponent maximizeButton: maximizeButtonComponent keepBelowButton: keepBelowButtonComponent keepAboveButton: keepAboveButtonComponent helpButton: helpButtonComponent shadeButton: shadeButtonComponent allDesktopsButton: stickyButtonComponent closeButton: closeButtonComponent buttons: options.titleButtonsLeft anchors { top: parent.top left: parent.left } } Text { id: caption textFormat: Text.PlainText anchors { top: parent.top left: leftButtonGroup.right right: rightButtonGroup.left rightMargin: 5 leftMargin: 5 topMargin: 3 } color: options.fontColor Behavior on color { ColorAnimation { duration: root.animationDuration } } text: decoration.client.caption font: options.titleFont style: root.titleShadow ? Text.Raised : Text.Normal styleColor: colorHelper.shade(color, ColorHelper.ShadowShade) elide: Text.ElideMiddle renderType: Text.NativeRendering } ButtonGroup { id: rightButtonGroup spacing: 1 explicitSpacer: root.buttonSize menuButton: menuButtonComponent appMenuButton: appMenuButtonComponent minimizeButton: minimizeButtonComponent maximizeButton: maximizeButtonComponent keepBelowButton: keepBelowButtonComponent keepAboveButton: keepAboveButtonComponent helpButton: helpButtonComponent shadeButton: shadeButtonComponent allDesktopsButton: stickyButtonComponent closeButton: closeButtonComponent buttons: options.titleButtonsRight anchors { top: parent.top right: parent.right } } Component.onCompleted: { decoration.installTitleItem(titleRow); } } } Item { id: innerBorder anchors.fill: parent Rectangle { anchors { left: parent.left right: parent.right } height: 1 y: top.height - 1 visible: decoration.client.maximized color: colorHelper.shade(root.titleBarColor, ColorHelper.MidShade) } Rectangle { anchors { fill: parent leftMargin: root.borders.left - 1 rightMargin: root.borders.right topMargin: root.borders.top - 1 bottomMargin: root.borders.bottom } border { width: 1 color: colorHelper.shade(root.titleBarColor, ColorHelper.MidShade) } visible: !decoration.client.maximized color: root.titleBarColor } } } Component { id: maximizeButtonComponent PlastikButton { + objectName: "maximizeButton" buttonType: DecorationOptions.DecorationButtonMaximizeRestore size: root.buttonSize } } Component { id: keepBelowButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonKeepBelow size: root.buttonSize } } Component { id: keepAboveButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonKeepAbove size: root.buttonSize } } Component { id: helpButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonQuickHelp size: root.buttonSize } } Component { id: minimizeButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonMinimize size: root.buttonSize } } Component { id: shadeButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonShade size: root.buttonSize } } Component { id: stickyButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonOnAllDesktops size: root.buttonSize } } Component { id: closeButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonClose size: root.buttonSize } } Component { id: menuButtonComponent MenuButton { width: root.buttonSize height: root.buttonSize } } Component { id: appMenuButtonComponent PlastikButton { buttonType: DecorationOptions.DecorationButtonApplicationMenu size: root.buttonSize } } Component.onCompleted: { borders.setBorders(4); borders.setTitle(top.normalHeight); maximizedBorders.setTitle(top.maximizedHeight); readBorderSize(); readConfig(); } Connections { target: decoration onConfigChanged: root.readConfig() } Connections { target: decorationSettings onBorderSizeChanged: root.readBorderSize(); } } diff --git a/decorations/decoratedclient.cpp b/decorations/decoratedclient.cpp index 7e0838821..242cec1b8 100644 --- a/decorations/decoratedclient.cpp +++ b/decorations/decoratedclient.cpp @@ -1,263 +1,268 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 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 "decoratedclient.h" #include "decorationpalette.h" #include "decorationrenderer.h" #include "abstract_client.h" #include "composite.h" #include "cursor.h" #include "options.h" #include "scene.h" #include "workspace.h" #include #include #include namespace KWin { namespace Decoration { DecoratedClientImpl::DecoratedClientImpl(AbstractClient *client, KDecoration2::DecoratedClient *decoratedClient, KDecoration2::Decoration *decoration) : QObject() , DecoratedClientPrivate(decoratedClient, decoration) , m_client(client) , m_renderer(nullptr) { createRenderer(); client->setDecoratedClient(QPointer(this)); connect(client, &AbstractClient::activeChanged, this, [decoratedClient, client]() { emit decoratedClient->activeChanged(client->isActive()); } ); connect(client, &AbstractClient::geometryChanged, this, [decoratedClient, client]() { emit decoratedClient->widthChanged(client->clientSize().width()); emit decoratedClient->heightChanged(client->clientSize().height()); } ); connect(client, &AbstractClient::desktopChanged, this, [decoratedClient, client]() { emit decoratedClient->onAllDesktopsChanged(client->isOnAllDesktops()); } ); connect(client, &AbstractClient::captionChanged, this, [decoratedClient, client]() { emit decoratedClient->captionChanged(client->caption()); } ); connect(client, &AbstractClient::iconChanged, this, [decoratedClient, client]() { emit decoratedClient->iconChanged(client->icon()); } ); connect(client, &AbstractClient::shadeChanged, this, &Decoration::DecoratedClientImpl::signalShadeChange); connect(client, &AbstractClient::keepAboveChanged, decoratedClient, &KDecoration2::DecoratedClient::keepAboveChanged); connect(client, &AbstractClient::keepBelowChanged, decoratedClient, &KDecoration2::DecoratedClient::keepBelowChanged); connect(Compositor::self(), &Compositor::compositingToggled, this, [this, decoration]() { delete m_renderer; m_renderer = nullptr; createRenderer(); decoration->update(); } ); connect(client, &AbstractClient::quickTileModeChanged, decoratedClient, [this, decoratedClient]() { emit decoratedClient->adjacentScreenEdgesChanged(adjacentScreenEdges()); } ); connect(client, &AbstractClient::closeableChanged, decoratedClient, &KDecoration2::DecoratedClient::closeableChanged); connect(client, &AbstractClient::shadeableChanged, decoratedClient, &KDecoration2::DecoratedClient::shadeableChanged); connect(client, &AbstractClient::minimizeableChanged, decoratedClient, &KDecoration2::DecoratedClient::minimizeableChanged); connect(client, &AbstractClient::maximizeableChanged, decoratedClient, &KDecoration2::DecoratedClient::maximizeableChanged); connect(client, &AbstractClient::paletteChanged, decoratedClient, &KDecoration2::DecoratedClient::paletteChanged); } DecoratedClientImpl::~DecoratedClientImpl() = default; void DecoratedClientImpl::signalShadeChange() { emit decoratedClient()->shadedChanged(m_client->isShade()); } #define DELEGATE(type, name, clientName) \ type DecoratedClientImpl::name() const \ { \ return m_client->clientName(); \ } #define DELEGATE2(type, name) DELEGATE(type, name, name) DELEGATE2(QString, caption) DELEGATE2(bool, isActive) DELEGATE2(bool, isCloseable) DELEGATE(bool, isMaximizeable, isMaximizable) DELEGATE(bool, isMinimizeable, isMinimizable) DELEGATE2(bool, isModal) DELEGATE(bool, isMoveable, isMovable) DELEGATE(bool, isResizeable, isResizable) DELEGATE2(bool, isShadeable) DELEGATE2(bool, providesContextHelp) DELEGATE2(int, desktop) DELEGATE2(bool, isOnAllDesktops) DELEGATE2(QPalette, palette) DELEGATE2(QIcon, icon) #undef DELEGATE2 #undef DELEGATE #define DELEGATE(type, name, clientName) \ type DecoratedClientImpl::name() const \ { \ return m_client->clientName(); \ } DELEGATE(bool, isKeepAbove, keepAbove) DELEGATE(bool, isKeepBelow, keepBelow) DELEGATE(bool, isShaded, isShade) DELEGATE(WId, windowId, window) DELEGATE(WId, decorationId, frameId) #undef DELEGATE #define DELEGATE(name, op) \ void DecoratedClientImpl::name() \ { \ Workspace::self()->performWindowOperation(m_client, Options::op); \ } DELEGATE(requestToggleShade, ShadeOp) DELEGATE(requestToggleOnAllDesktops, OnAllDesktopsOp) DELEGATE(requestToggleKeepAbove, KeepAboveOp) DELEGATE(requestToggleKeepBelow, KeepBelowOp) #undef DELEGATE #define DELEGATE(name, clientName) \ void DecoratedClientImpl::name() \ { \ m_client->clientName(); \ } DELEGATE(requestContextHelp, showContextHelp) DELEGATE(requestMinimize, minimize) #undef DELEGATE void DecoratedClientImpl::requestClose() { QMetaObject::invokeMethod(m_client, "closeWindow", Qt::QueuedConnection); } QColor DecoratedClientImpl::color(KDecoration2::ColorGroup group, KDecoration2::ColorRole role) const { auto dp = m_client->decorationPalette(); if (dp) { return dp->color(group, role); } return QColor(); } void DecoratedClientImpl::requestShowWindowMenu() { // TODO: add rect to requestShowWindowMenu Workspace::self()->showWindowMenu(QRect(Cursor::pos(), Cursor::pos()), m_client); } void DecoratedClientImpl::requestToggleMaximization(Qt::MouseButtons buttons) { - Workspace::self()->performWindowOperation(m_client, options->operationMaxButtonClick(buttons)); + QMetaObject::invokeMethod(this, "delayedRequestToggleMaximization", Qt::QueuedConnection, Q_ARG(Options::WindowOperation, options->operationMaxButtonClick(buttons))); +} + +void DecoratedClientImpl::delayedRequestToggleMaximization(Options::WindowOperation operation) +{ + Workspace::self()->performWindowOperation(m_client, operation); } int DecoratedClientImpl::width() const { return m_client->clientSize().width(); } int DecoratedClientImpl::height() const { return m_client->clientSize().height(); } bool DecoratedClientImpl::isMaximizedVertically() const { return m_client->maximizeMode() & MaximizeVertical; } bool DecoratedClientImpl::isMaximized() const { return isMaximizedHorizontally() && isMaximizedVertically(); } bool DecoratedClientImpl::isMaximizedHorizontally() const { return m_client->maximizeMode() & MaximizeHorizontal; } Qt::Edges DecoratedClientImpl::adjacentScreenEdges() const { Qt::Edges edges; const AbstractClient::QuickTileMode mode = m_client->quickTileMode(); if (mode.testFlag(AbstractClient::QuickTileLeft)) { edges |= Qt::LeftEdge; if (!mode.testFlag(AbstractClient::QuickTileTop) && !mode.testFlag(AbstractClient::QuickTileBottom)) { // using complete side edges |= Qt::TopEdge | Qt::BottomEdge; } } if (mode.testFlag(AbstractClient::QuickTileTop)) { edges |= Qt::TopEdge; } if (mode.testFlag(AbstractClient::QuickTileRight)) { edges |= Qt::RightEdge; if (!mode.testFlag(AbstractClient::QuickTileTop) && !mode.testFlag(AbstractClient::QuickTileBottom)) { // using complete side edges |= Qt::TopEdge | Qt::BottomEdge; } } if (mode.testFlag(AbstractClient::QuickTileBottom)) { edges |= Qt::BottomEdge; } return edges; } void DecoratedClientImpl::createRenderer() { if (Compositor::self()->hasScene()) { m_renderer = Compositor::self()->scene()->createDecorationRenderer(this); } else { m_renderer = new X11Renderer(this); } } void DecoratedClientImpl::destroyRenderer() { delete m_renderer; m_renderer = nullptr; } } } diff --git a/decorations/decoratedclient.h b/decorations/decoratedclient.h index 8b8514bca..224707637 100644 --- a/decorations/decoratedclient.h +++ b/decorations/decoratedclient.h @@ -1,103 +1,107 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 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 . *********************************************************************/ #ifndef KWIN_DECORATED_CLIENT_H #define KWIN_DECORATED_CLIENT_H +#include "options.h" #include #include namespace KWin { class AbstractClient; namespace Decoration { class Renderer; class DecoratedClientImpl : public QObject, public KDecoration2::DecoratedClientPrivate { Q_OBJECT public: explicit DecoratedClientImpl(AbstractClient *client, KDecoration2::DecoratedClient *decoratedClient, KDecoration2::Decoration *decoration); virtual ~DecoratedClientImpl(); QString caption() const override; WId decorationId() const override; int desktop() const override; int height() const override; QIcon icon() const override; bool isActive() const override; bool isCloseable() const override; bool isKeepAbove() const override; bool isKeepBelow() const override; bool isMaximizeable() const override; bool isMaximized() const override; bool isMaximizedHorizontally() const override; bool isMaximizedVertically() const override; bool isMinimizeable() const override; bool isModal() const override; bool isMoveable() const override; bool isOnAllDesktops() const override; bool isResizeable() const override; bool isShadeable() const override; bool isShaded() const override; QPalette palette() const override; QColor color(KDecoration2::ColorGroup group, KDecoration2::ColorRole role) const override; bool providesContextHelp() const override; int width() const override; WId windowId() const override; Qt::Edges adjacentScreenEdges() const override; void requestClose() override; void requestContextHelp() override; void requestToggleMaximization(Qt::MouseButtons buttons) override; void requestMinimize() override; void requestShowWindowMenu() override; void requestToggleKeepAbove() override; void requestToggleKeepBelow() override; void requestToggleOnAllDesktops() override; void requestToggleShade() override; AbstractClient *client() { return m_client; } Renderer *renderer() { return m_renderer; } void destroyRenderer(); KDecoration2::DecoratedClient *decoratedClient() { return KDecoration2::DecoratedClientPrivate::client(); } void signalShadeChange(); +private Q_SLOTS: + void delayedRequestToggleMaximization(Options::WindowOperation operation); + private: void createRenderer(); AbstractClient *m_client; Renderer *m_renderer; }; } } #endif