diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 5cc6da487..174653dc6 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -1,333 +1,335 @@ add_definitions(-DKWIN_UNIT_TEST) remove_definitions(-DQT_USE_QSTRINGBUILDER) add_subdirectory(libkwineffects) add_subdirectory(libxrenderutils) add_subdirectory(integration) if (HAVE_INPUT) add_subdirectory(libinput) endif() add_subdirectory(tabbox) ######################################################## # Test ScreenPaintData ######################################################## set( testScreenPaintData_SRCS test_screen_paint_data.cpp ) add_executable(testScreenPaintData ${testScreenPaintData_SRCS}) target_link_libraries( testScreenPaintData kwineffects Qt5::Test Qt5::Widgets KF5::WindowSystem) add_test(kwin-testScreenPaintData testScreenPaintData) ecm_mark_as_test(testScreenPaintData) ######################################################## # Test WindowPaintData ######################################################## set( testWindowPaintData_SRCS test_window_paint_data.cpp ) add_executable(testWindowPaintData ${testWindowPaintData_SRCS}) target_link_libraries( testWindowPaintData kwineffects Qt5::Widgets Qt5::Test ) add_test(kwin-testWindowPaintData testWindowPaintData) ecm_mark_as_test(testWindowPaintData) ######################################################## # Test VirtualDesktopManager ######################################################## set( testVirtualDesktops_SRCS test_virtual_desktops.cpp ../virtualdesktops.cpp ) add_executable(testVirtualDesktops ${testVirtualDesktops_SRCS}) target_link_libraries( testVirtualDesktops Qt5::Test Qt5::Widgets KF5::I18n KF5::GlobalAccel KF5::ConfigCore KF5::WindowSystem ) add_test(kwin-testVirtualDesktops testVirtualDesktops) ecm_mark_as_test(testVirtualDesktops) ######################################################## # Test ClientMachine ######################################################## set( testClientMachine_SRCS test_client_machine.cpp ../client_machine.cpp ) add_executable( testClientMachine ${testClientMachine_SRCS} ) target_link_libraries( testClientMachine Qt5::Concurrent Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::XFIXES ${X11_X11_LIB} # to make jenkins happy ) add_test(kwin-testClientMachine testClientMachine) ecm_mark_as_test(testClientMachine) ######################################################## # Test XcbWrapper ######################################################## set( testXcbWrapper_SRCS test_xcb_wrapper.cpp ) add_executable( testXcbWrapper ${testXcbWrapper_SRCS} ) target_link_libraries( testXcbWrapper Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(kwin-testXcbWrapper testXcbWrapper) ecm_mark_as_test(testXcbWrapper) if (XCB_ICCCM_FOUND) add_executable( testXcbSizeHints test_xcb_size_hints.cpp ) target_link_libraries( testXcbSizeHints Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::WindowSystem XCB::XCB XCB::ICCCM ) add_test(kwin-testXcbSizeHints testXcbSizeHints) ecm_mark_as_test(testXcbSizeHints) endif() ######################################################## # Test XcbWindow ######################################################## set( testXcbWindow_SRCS test_xcb_window.cpp ) add_executable( testXcbWindow ${testXcbWindow_SRCS} ) target_link_libraries( testXcbWindow Qt5::Test Qt5::X11Extras Qt5::Widgets KF5::ConfigCore KF5::WindowSystem XCB::XCB ) add_test(kwin-testXcbWindow testXcbWindow) ecm_mark_as_test(testXcbWindow) ######################################################## # Test BuiltInEffectLoader ######################################################## set( testBuiltInEffectLoader_SRCS test_builtin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testBuiltInEffectLoader ${testBuiltInEffectLoader_SRCS}) target_link_libraries(testBuiltInEffectLoader Qt5::Concurrent Qt5::Test Qt5::X11Extras KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testBuiltInEffectLoader testBuiltInEffectLoader) ecm_mark_as_test(testBuiltInEffectLoader) ######################################################## # Test ScriptedEffectLoader ######################################################## include_directories(${KWIN_SOURCE_DIR}) set( testScriptedEffectLoader_SRCS test_scripted_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ../scripting/scriptedeffect.cpp ../scripting/scriptingutils.cpp ../scripting/scripting_logging.cpp ) add_executable( testScriptedEffectLoader ${testScriptedEffectLoader_SRCS}) target_link_libraries(testScriptedEffectLoader Qt5::Concurrent Qt5::Script Qt5::Test Qt5::X11Extras KF5::ConfigGui KF5::GlobalAccel KF5::I18n KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testScriptedEffectLoader testScriptedEffectLoader) ecm_mark_as_test(testScriptedEffectLoader) ######################################################## # Test PluginEffectLoader ######################################################## set( testPluginEffectLoader_SRCS test_plugin_effectloader.cpp mock_effectshandler.cpp ../effectloader.cpp ) add_executable( testPluginEffectLoader ${testPluginEffectLoader_SRCS}) target_link_libraries(testPluginEffectLoader Qt5::Concurrent Qt5::Test Qt5::X11Extras KF5::Package kwineffects kwin4_effect_builtins ) add_test(kwin-testPluginEffectLoader testPluginEffectLoader) ecm_mark_as_test(testPluginEffectLoader) ######################################################## # FakeEffectPlugin ######################################################## add_library(fakeeffectplugin MODULE fakeeffectplugin.cpp) set_target_properties(fakeeffectplugin PROPERTIES PREFIX "") target_link_libraries(fakeeffectplugin kwineffects) ######################################################## # FakeEffectPlugin-Version ######################################################## add_library(effectversionplugin MODULE fakeeffectplugin_version.cpp) set_target_properties(effectversionplugin PROPERTIES PREFIX "") target_link_libraries(effectversionplugin kwineffects) ######################################################## # Test Screens ######################################################## set( testScreens_SRCS test_screens.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../screens.cpp ../x11eventfilter.cpp ) kconfig_add_kcfg_files(testScreens_SRCS ../settings.kcfgc) add_executable( testScreens ${testScreens_SRCS}) target_include_directories(testScreens BEFORE PRIVATE ./) target_link_libraries(testScreens Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::ConfigGui KF5::WindowSystem ) add_test(kwin_testScreens testScreens) ecm_mark_as_test(testScreens) ######################################################## # Test XrandRScreens ######################################################## set( testXRandRScreens_SRCS test_xrandr_screens.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../screens.cpp ../plugins/platforms/x11/standalone/screens_xrandr.cpp ../xcbutils.cpp # init of extensions ../x11eventfilter.cpp ) kconfig_add_kcfg_files(testXRandRScreens_SRCS ../settings.kcfgc) add_executable( testXRandRScreens ${testXRandRScreens_SRCS} ) target_link_libraries( testXRandRScreens Qt5::Test Qt5::Gui KF5::ConfigCore KF5::ConfigGui KF5::WindowSystem XCB::XCB XCB::RANDR XCB::XFIXES XCB::SYNC XCB::COMPOSITE XCB::DAMAGE XCB::GLX XCB::SHM ) add_test(kwin-testXRandRScreens testXRandRScreens) ecm_mark_as_test(testXRandRScreens) ######################################################## # Test ScreenEdges ######################################################## set( testScreenEdges_SRCS test_screen_edges.cpp mock_abstract_client.cpp mock_client.cpp mock_screens.cpp mock_workspace.cpp ../atoms.cpp ../screens.cpp ../screenedge.cpp ../virtualdesktops.cpp ../xcbutils.cpp # init of extensions ../plugins/platforms/x11/standalone/edge.cpp ) kconfig_add_kcfg_files(testScreenEdges_SRCS ../settings.kcfgc) qt5_add_dbus_interface( testScreenEdges_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.freedesktop.ScreenSaver.xml screenlocker_interface) add_executable( testScreenEdges ${testScreenEdges_SRCS}) target_include_directories(testScreenEdges BEFORE PRIVATE ./) target_link_libraries(testScreenEdges Qt5::DBus Qt5::Test Qt5::X11Extras KF5::ConfigCore KF5::ConfigGui KF5::I18n KF5::GlobalAccel KF5::WindowSystem XCB::XCB XCB::RANDR XCB::XFIXES XCB::SYNC XCB::COMPOSITE XCB::DAMAGE XCB::GLX XCB::SHM ) add_test(kwin_testScreenEdges testScreenEdges) ecm_mark_as_test(testScreenEdges) ######################################################## # Test OnScreenNotification ######################################################## set( testOnScreenNotification_SRCS onscreennotificationtest.cpp ../onscreennotification.cpp + ../input_event_spy.cpp ) add_executable( testOnScreenNotification ${testOnScreenNotification_SRCS}) target_link_libraries(testOnScreenNotification Qt5::Test + Qt5::Widgets # QAction include Qt5::Quick KF5::ConfigCore ) add_test(kwin-testOnScreenNotification testOnScreenNotification) ecm_mark_as_test(testOnScreenNotification) diff --git a/autotests/onscreennotificationtest.cpp b/autotests/onscreennotificationtest.cpp index a4465526c..a46e8513f 100644 --- a/autotests/onscreennotificationtest.cpp +++ b/autotests/onscreennotificationtest.cpp @@ -1,123 +1,141 @@ /* * Copyright 2016 Martin Graesslin * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "onscreennotificationtest.h" #include "../onscreennotification.h" +#include "../input.h" #include #include #include #include #include QTEST_MAIN(OnScreenNotificationTest); +namespace KWin +{ + +void InputRedirection::installInputEventSpy(InputEventSpy *spy) +{ + Q_UNUSED(spy); +} + +void InputRedirection::uninstallInputEventSpy(InputEventSpy *spy) +{ + Q_UNUSED(spy); +} + +InputRedirection *InputRedirection::s_self = nullptr; + +} + using KWin::OnScreenNotification; void OnScreenNotificationTest::show() { OnScreenNotification notification; auto config = KSharedConfig::openConfig(QString(), KSharedConfig::SimpleConfig); KConfigGroup group = config->group("OnScreenNotification"); group.writeEntry(QStringLiteral("QmlPath"), QString("/does/not/exist.qml")); group.sync(); notification.setConfig(config); notification.setEngine(new QQmlEngine(¬ification)); notification.setMessage(QStringLiteral("Some text so that we see it in the test")); QSignalSpy visibleChangedSpy(¬ification, &OnScreenNotification::visibleChanged); QCOMPARE(notification.isVisible(), false); notification.setVisible(true); QCOMPARE(notification.isVisible(), true); QCOMPARE(visibleChangedSpy.count(), 1); // show again should not trigger notification.setVisible(true); QCOMPARE(visibleChangedSpy.count(), 1); // timer should not have hidden QTest::qWait(500); QCOMPARE(notification.isVisible(), true); // hide again notification.setVisible(false); QCOMPARE(notification.isVisible(), false); QCOMPARE(visibleChangedSpy.count(), 2); // now show with timer notification.setTimeout(250); notification.setVisible(true); QCOMPARE(notification.isVisible(), true); QCOMPARE(visibleChangedSpy.count(), 3); QVERIFY(visibleChangedSpy.wait()); QCOMPARE(notification.isVisible(), false); QCOMPARE(visibleChangedSpy.count(), 4); } void OnScreenNotificationTest::timeout() { OnScreenNotification notification; QSignalSpy timeoutChangedSpy(¬ification, &OnScreenNotification::timeoutChanged); QCOMPARE(notification.timeout(), 0); notification.setTimeout(1000); QCOMPARE(notification.timeout(), 1000); QCOMPARE(timeoutChangedSpy.count(), 1); notification.setTimeout(1000); QCOMPARE(timeoutChangedSpy.count(), 1); notification.setTimeout(0); QCOMPARE(notification.timeout(), 0); QCOMPARE(timeoutChangedSpy.count(), 2); } void OnScreenNotificationTest::iconName() { OnScreenNotification notification; QSignalSpy iconNameChangedSpy(¬ification, &OnScreenNotification::iconNameChanged); QVERIFY(iconNameChangedSpy.isValid()); QCOMPARE(notification.iconName(), QString()); notification.setIconName(QStringLiteral("foo")); QCOMPARE(notification.iconName(), QStringLiteral("foo")); QCOMPARE(iconNameChangedSpy.count(), 1); notification.setIconName(QStringLiteral("foo")); QCOMPARE(iconNameChangedSpy.count(), 1); notification.setIconName(QStringLiteral("bar")); QCOMPARE(notification.iconName(), QStringLiteral("bar")); QCOMPARE(iconNameChangedSpy.count(), 2); } void OnScreenNotificationTest::message() { OnScreenNotification notification; QSignalSpy messageChangedSpy(¬ification, &OnScreenNotification::messageChanged); QVERIFY(messageChangedSpy.isValid()); QCOMPARE(notification.message(), QString()); notification.setMessage(QStringLiteral("foo")); QCOMPARE(notification.message(), QStringLiteral("foo")); QCOMPARE(messageChangedSpy.count(), 1); notification.setMessage(QStringLiteral("foo")); QCOMPARE(messageChangedSpy.count(), 1); notification.setMessage(QStringLiteral("bar")); QCOMPARE(notification.message(), QStringLiteral("bar")); QCOMPARE(messageChangedSpy.count(), 2); } #include "onscreennotificationtest.moc" diff --git a/onscreennotification.cpp b/onscreennotification.cpp index 9bca93180..b32349e7a 100644 --- a/onscreennotification.cpp +++ b/onscreennotification.cpp @@ -1,170 +1,239 @@ /* * Copyright 2016 Martin Graesslin * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 "onscreennotification.h" +#include "input.h" +#include "input_event.h" +#include "input_event_spy.h" #include +#include #include #include #include #include #include #include #include #include using namespace KWin; +class KWin::OnScreenNotificationInputEventSpy : public InputEventSpy +{ +public: + explicit OnScreenNotificationInputEventSpy(OnScreenNotification *parent); + + void pointerEvent(MouseEvent *event) override; +private: + OnScreenNotification *m_parent; +}; + +OnScreenNotificationInputEventSpy::OnScreenNotificationInputEventSpy(OnScreenNotification *parent) + : m_parent(parent) +{ +} + +void OnScreenNotificationInputEventSpy::pointerEvent(MouseEvent *event) +{ + if (event->type() != QEvent::MouseMove) { + return; + } + + m_parent->setContainsPointer(m_parent->geometry().contains(event->globalPos())); +} + + OnScreenNotification::OnScreenNotification(QObject *parent) : QObject(parent) , m_timer(new QTimer(this)) { m_timer->setSingleShot(true); connect(m_timer, &QTimer::timeout, this, std::bind(&OnScreenNotification::setVisible, this, false)); connect(this, &OnScreenNotification::visibleChanged, this, [this] { if (m_visible) { show(); } else { m_timer->stop(); + m_spy.reset(); + m_containsPointer = false; } } ); } OnScreenNotification::~OnScreenNotification() { if (QQuickWindow *w = qobject_cast(m_mainItem.data())) { w->hide(); w->destroy(); } } void OnScreenNotification::setConfig(KSharedConfigPtr config) { m_config = config; } void OnScreenNotification::setEngine(QQmlEngine *engine) { m_qmlEngine = engine; } bool OnScreenNotification::isVisible() const { return m_visible; } void OnScreenNotification::setVisible(bool visible) { if (m_visible == visible) { return; } m_visible = visible; emit visibleChanged(); } QString OnScreenNotification::message() const { return m_message; } void OnScreenNotification::setMessage(const QString &message) { if (m_message == message) { return; } m_message = message; emit messageChanged(); } QString OnScreenNotification::iconName() const { return m_iconName; } void OnScreenNotification::setIconName(const QString &iconName) { if (m_iconName == iconName) { return; } m_iconName = iconName; emit iconNameChanged(); } int OnScreenNotification::timeout() const { return m_timer->interval(); } void OnScreenNotification::setTimeout(int timeout) { if (m_timer->interval() == timeout) { return; } m_timer->setInterval(timeout); emit timeoutChanged(); } void OnScreenNotification::show() { Q_ASSERT(m_visible); ensureQmlContext(); ensureQmlComponent(); + createInputSpy(); if (m_timer->interval() != 0) { m_timer->start(); } } void OnScreenNotification::ensureQmlContext() { Q_ASSERT(m_qmlEngine); if (!m_qmlContext.isNull()) { return; } m_qmlContext.reset(new QQmlContext(m_qmlEngine)); m_qmlContext->setContextProperty(QStringLiteral("osd"), this); } void OnScreenNotification::ensureQmlComponent() { Q_ASSERT(m_config); Q_ASSERT(m_qmlEngine); if (!m_qmlComponent.isNull()) { return; } m_qmlComponent.reset(new QQmlComponent(m_qmlEngine)); const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, m_config->group(QStringLiteral("OnScreenNotification")).readEntry("QmlPath", QStringLiteral(KWIN_NAME "/onscreennotification/plasma/main.qml"))); if (fileName.isEmpty()) { return; } m_qmlComponent->loadUrl(QUrl::fromLocalFile(fileName)); if (!m_qmlComponent->isError()) { m_mainItem.reset(m_qmlComponent->create(m_qmlContext.data())); } else { m_qmlComponent.reset(); } } +void OnScreenNotification::createInputSpy() +{ + Q_ASSERT(m_spy.isNull()); + if (auto w = qobject_cast(m_mainItem.data())) { + m_spy.reset(new OnScreenNotificationInputEventSpy(this)); + input()->installInputEventSpy(m_spy.data()); + if (!m_animation) { + m_animation = new QPropertyAnimation(w, "opacity", this); + m_animation->setStartValue(1.0); + m_animation->setEndValue(0.0); + m_animation->setDuration(250); + m_animation->setEasingCurve(QEasingCurve::InOutQuad); + } + } +} + +QRect OnScreenNotification::geometry() const +{ + if (QQuickWindow *w = qobject_cast(m_mainItem.data())) { + return w->geometry(); + } + return QRect(); +} + +void OnScreenNotification::setContainsPointer(bool contains) +{ + if (m_containsPointer == contains) { + return; + } + m_containsPointer = contains; + if (!m_animation) { + return; + } + m_animation->setDirection(m_containsPointer ? QAbstractAnimation::Forward : QAbstractAnimation::Backward); + m_animation->start(); +} + #include "onscreennotification.moc" diff --git a/onscreennotification.h b/onscreennotification.h index 40a67e159..0c916122b 100644 --- a/onscreennotification.h +++ b/onscreennotification.h @@ -1,82 +1,93 @@ /* * Copyright 2016 Martin Graesslin * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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_ONSCREENNOTIFICATION_H #define KWIN_ONSCREENNOTIFICATION_H #include #include +class QPropertyAnimation; class QTimer; class QQmlContext; class QQmlComponent; class QQmlEngine; namespace KWin { +class OnScreenNotificationInputEventSpy; + class OnScreenNotification : public QObject { Q_OBJECT Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged) Q_PROPERTY(QString iconName READ iconName WRITE setIconName NOTIFY iconNameChanged) Q_PROPERTY(int timeout READ timeout WRITE setTimeout NOTIFY timeoutChanged) public: explicit OnScreenNotification(QObject *parent = nullptr); ~OnScreenNotification() override; bool isVisible() const; QString message() const; QString iconName() const; int timeout() const; + QRect geometry() const; + void setVisible(bool m_visible); void setMessage(const QString &message); void setIconName(const QString &iconName); void setTimeout(int timeout); void setConfig(KSharedConfigPtr config); void setEngine(QQmlEngine *engine); + void setContainsPointer(bool contains); + Q_SIGNALS: void visibleChanged(); void messageChanged(); void iconNameChanged(); void timeoutChanged(); private: void show(); void ensureQmlContext(); void ensureQmlComponent(); + void createInputSpy(); bool m_visible = false; QString m_message; QString m_iconName; QTimer *m_timer; KSharedConfigPtr m_config; QScopedPointer m_qmlContext; QScopedPointer m_qmlComponent; QQmlEngine *m_qmlEngine = nullptr; QScopedPointer m_mainItem; + QScopedPointer m_spy; + QPropertyAnimation *m_animation = nullptr; + bool m_containsPointer = false; }; } #endif // KWIN_ONSCREENNOTIFICATION_H