From 2cc55e40779e355739665d229212291cc222e309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 24 Nov 2016 08:07:51 +0100 Subject: [PATCH] [effects] Add a colorpicker effect Summary: The effect exports itself to DBus as object "/ColorPicker" and provides an own interface "org.kde.kwin.ColorPicker". It has one exported method to DBus "pick" which returns a QColor. When invoked an interactive position picking selection is started. If it ends the effect reads the color value at the picked position from the OpenGL color buffer. This implements T4568. Reviewers: #kwin, #plasma_on_wayland, broulik Subscribers: plasma-devel, kwin Tags: #plasma_on_wayland, #kwin Differential Revision: https://phabricator.kde.org/D3480 --- autotests/test_builtin_effectloader.cpp | 6 + autotests/test_plugin_effectloader.cpp | 1 + autotests/test_scripted_effectloader.cpp | 1 + effects/CMakeLists.txt | 1 + effects/colorpicker/colorpicker.cpp | 143 +++++++++++++++++++++++ effects/colorpicker/colorpicker.h | 66 +++++++++++ effects/effect_builtins.cpp | 16 +++ effects/effect_builtins.h | 1 + 8 files changed, 235 insertions(+) create mode 100644 effects/colorpicker/colorpicker.cpp create mode 100644 effects/colorpicker/colorpicker.h diff --git a/autotests/test_builtin_effectloader.cpp b/autotests/test_builtin_effectloader.cpp index 0f3dab7898..6dd3313056 100644 --- a/autotests/test_builtin_effectloader.cpp +++ b/autotests/test_builtin_effectloader.cpp @@ -81,6 +81,7 @@ void TestBuiltInEffectLoader::testHasEffect_data() QTest::newRow("blur") << QStringLiteral("blur") << true; QTest::newRow("with kwin4_effect_ prefix") << QStringLiteral("kwin4_effect_blur") << false; QTest::newRow("case sensitive") << QStringLiteral("BlUR") << true; + QTest::newRow("Colorpicker") << QStringLiteral("colorpicker") << true; QTest::newRow("Contrast") << QStringLiteral("contrast") << true; QTest::newRow("CoverSwitch") << QStringLiteral("coverswitch") << true; QTest::newRow("Cube") << QStringLiteral("cube") << true; @@ -136,6 +137,7 @@ void TestBuiltInEffectLoader::testKnownEffects() { QStringList expectedEffects; expectedEffects << QStringLiteral("blur") + << QStringLiteral("colorpicker") << QStringLiteral("contrast") << QStringLiteral("coverswitch") << QStringLiteral("cube") @@ -196,6 +198,8 @@ void TestBuiltInEffectLoader::testSupported_data() QTest::newRow("blur") << QStringLiteral("blur") << false << xc << true; // fails for GL as it does proper tests on what's supported and doesn't just check whether it's GL QTest::newRow("blur-GL") << QStringLiteral("blur") << false << oc << true; + QTest::newRow("Colorpicker") << QStringLiteral("colorpicker") << false << xc << true; + QTest::newRow("Colorpicker-GL") << QStringLiteral("colorpicker") << true << oc << true; QTest::newRow("Contrast") << QStringLiteral("contrast") << false << xc << true; // fails for GL as it does proper tests on what's supported and doesn't just check whether it's GL QTest::newRow("Contrast-GL") << QStringLiteral("contrast") << false << oc << true; @@ -285,6 +289,8 @@ void TestBuiltInEffectLoader::testLoadEffect_data() QTest::newRow("blur") << QStringLiteral("blur") << false << xc; // fails for GL as it does proper tests on what's supported and doesn't just check whether it's GL QTest::newRow("blur-GL") << QStringLiteral("blur") << false << oc; + QTest::newRow("Colorpicker") << QStringLiteral("colorpicker") << false << xc; + QTest::newRow("Colorpicker-GL") << QStringLiteral("colorpicker") << true << oc; QTest::newRow("Contrast") << QStringLiteral("contrast") << false << xc; // fails for GL as it does proper tests on what's supported and doesn't just check whether it's GL QTest::newRow("Contrast-GL") << QStringLiteral("contrast") << false << oc; diff --git a/autotests/test_plugin_effectloader.cpp b/autotests/test_plugin_effectloader.cpp index f010847f2d..ddea2cafff 100644 --- a/autotests/test_plugin_effectloader.cpp +++ b/autotests/test_plugin_effectloader.cpp @@ -73,6 +73,7 @@ void TestPluginEffectLoader::testHasEffect_data() // all the built-in effects should fail QTest::newRow("blur") << QStringLiteral("blur") << false; + QTest::newRow("ColorPicker") << QStringLiteral("colorpicker") << false; QTest::newRow("Contrast") << QStringLiteral("contrast") << false; QTest::newRow("CoverSwitch") << QStringLiteral("coverswitch") << false; QTest::newRow("Cube") << QStringLiteral("cube") << false; diff --git a/autotests/test_scripted_effectloader.cpp b/autotests/test_scripted_effectloader.cpp index f0e2049a03..84b1e9051b 100644 --- a/autotests/test_scripted_effectloader.cpp +++ b/autotests/test_scripted_effectloader.cpp @@ -90,6 +90,7 @@ void TestScriptedEffectLoader::testHasEffect_data() // all the built-in effects should fail QTest::newRow("blur") << QStringLiteral("blur") << false; + QTest::newRow("Colorpicker") << QStringLiteral("colorpicker") << false; QTest::newRow("Contrast") << QStringLiteral("contrast") << false; QTest::newRow("CoverSwitch") << QStringLiteral("coverswitch") << false; QTest::newRow("Cube") << QStringLiteral("cube") << false; diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt index 9142a5738b..74a0b1be07 100644 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -72,6 +72,7 @@ set( kwin4_effect_builtins_sources effect_builtins.cpp blur/blur.cpp blur/blurshader.cpp + colorpicker/colorpicker.cpp cube/cube.cpp cube/cube_proxy.cpp cube/cubeslide.cpp diff --git a/effects/colorpicker/colorpicker.cpp b/effects/colorpicker/colorpicker.cpp new file mode 100644 index 0000000000..200bfc8a60 --- /dev/null +++ b/effects/colorpicker/colorpicker.cpp @@ -0,0 +1,143 @@ +/******************************************************************** + 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 "colorpicker.h" +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QColor) + +QDBusArgument &operator<< (QDBusArgument &argument, const QColor &color) +{ + argument.beginStructure(); + argument << color.rgba(); + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QColor &color) +{ + argument.beginStructure(); + QRgb rgba; + argument >> rgba; + argument.endStructure(); + color = QColor::fromRgba(rgba); + return argument; +} + +namespace KWin +{ + +bool ColorPickerEffect::supported() +{ + return effects->isOpenGLCompositing(); +} + +ColorPickerEffect::ColorPickerEffect() + : m_scheduledPosition(QPoint(-1, -1)) +{ + qDBusRegisterMetaType(); + QDBusConnection::sessionBus().registerObject(QStringLiteral("/ColorPicker"), this, QDBusConnection::ExportScriptableContents); +} + +ColorPickerEffect::~ColorPickerEffect() = default; + +void ColorPickerEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data) +{ + m_cachedOutputGeometry = data.outputGeometry(); + effects->paintScreen(mask, region, data); + + if (m_infoFrame) { + m_infoFrame->render(region); + } +} + +void ColorPickerEffect::postPaintScreen() +{ + effects->postPaintScreen(); + + if (m_scheduledPosition != QPoint(-1, -1) && (m_cachedOutputGeometry.isEmpty() || m_cachedOutputGeometry.contains(m_scheduledPosition))) { + uint8_t data[3]; + const QRect geo = GLRenderTarget::virtualScreenGeometry(); + glReadnPixels(m_scheduledPosition.x() - geo.x(), geo.height() - geo.y() - m_scheduledPosition.y(), 1, 1, GL_RGB, GL_UNSIGNED_BYTE, 3, data); + QDBusConnection::sessionBus().send(m_replyMessage.createReply(QColor(data[0], data[1], data[2]))); + m_picking = false; + m_scheduledPosition = QPoint(-1, -1); + } +} + +QColor ColorPickerEffect::pick() +{ + if (!calledFromDBus()) { + return QColor(); + } + if (m_picking) { + sendErrorReply(QDBusError::Failed, "Color picking is already in progress"); + return QColor(); + } + m_picking = true; + m_replyMessage = message(); + setDelayedReply(true); + showInfoMessage(); + effects->startInteractivePositionSelection( + [this] (const QPoint &p) { + hideInfoMessage(); + if (p == QPoint(-1, -1)) { + // error condition + QDBusConnection::sessionBus().send(m_replyMessage.createErrorReply(QStringLiteral("org.kde.kwin.ColorPicker.Error.Cancelled"), "Color picking got cancelled")); + m_picking = false; + } else { + m_scheduledPosition = p; + effects->addRepaintFull(); + } + } + ); + return QColor(); +} + +void ColorPickerEffect::showInfoMessage() +{ + if (!m_infoFrame.isNull()) { + return; + } + // TODO: turn the info message into a system wide service which performs hiding on mouse over + m_infoFrame.reset(effects->effectFrame(EffectFrameStyled, false)); + QFont font; + font.setBold(true); + m_infoFrame->setFont(font); + QRect area = effects->clientArea(ScreenArea, effects->activeScreen(), effects->currentDesktop()); + m_infoFrame->setPosition(QPoint(area.x() + area.width() / 2, area.y() + area.height() / 3)); + m_infoFrame->setText(i18n("Select a position for color picking with left click or enter.\nEscape or right click to cancel.")); + effects->addRepaintFull(); +} + +void ColorPickerEffect::hideInfoMessage() +{ + m_infoFrame.reset(); +} + +bool ColorPickerEffect::isActive() const +{ + return m_picking && ((m_scheduledPosition != QPoint(-1, -1)) || !m_infoFrame.isNull()) && !effects->isScreenLocked(); +} + +} // namespace diff --git a/effects/colorpicker/colorpicker.h b/effects/colorpicker/colorpicker.h new file mode 100644 index 0000000000..eab9410d8a --- /dev/null +++ b/effects/colorpicker/colorpicker.h @@ -0,0 +1,66 @@ +/******************************************************************** + 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 . +*********************************************************************/ +#ifndef KWIN_COLORPICKER_H +#define KWIN_COLORPICKER_H + +#include +#include +#include +#include +#include +#include + +namespace KWin +{ + +class ColorPickerEffect : public Effect, protected QDBusContext +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.ColorPicker") +public: + ColorPickerEffect(); + virtual ~ColorPickerEffect(); + void paintScreen(int mask, QRegion region, ScreenPaintData &data) override; + void postPaintScreen() override; + bool isActive() const override; + + int requestedEffectChainPosition() const override { + return 50; + } + + static bool supported(); + +public Q_SLOTS: + Q_SCRIPTABLE QColor pick(); + +private: + void showInfoMessage(); + void hideInfoMessage(); + + QDBusMessage m_replyMessage; + QRect m_cachedOutputGeometry; + QPoint m_scheduledPosition; + bool m_picking = false; + QScopedPointer m_infoFrame; +}; + +} // namespace + +#endif diff --git a/effects/effect_builtins.cpp b/effects/effect_builtins.cpp index ea16a6e220..2783cf586b 100644 --- a/effects/effect_builtins.cpp +++ b/effects/effect_builtins.cpp @@ -22,6 +22,7 @@ along with this program. If not, see . // common effects #include "backgroundcontrast/contrast.h" #include "blur/blur.h" +#include "colorpicker/colorpicker.h" #include "kscreen/kscreen.h" #include "presentwindows/presentwindows.h" #include "screenedge/screenedgeeffect.h" @@ -110,6 +111,21 @@ static const QVector s_effectData = { &BlurEffect::supported, &BlurEffect::enabledByDefault #endif +EFFECT_FALLBACK + }, { + QStringLiteral("colorpicker"), + i18ndc("kwin_effects", "Name of a KWin Effect", "Color Picker"), + i18ndc("kwin_effects", "Comment describing the KWin Effect", "Supports picking a color"), + QStringLiteral("Accessibility"), + QString(), + QUrl(), + true, + true, +#ifdef EFFECT_BUILTINS + &createHelper, + &ColorPickerEffect::supported, + nullptr +#endif EFFECT_FALLBACK }, { QStringLiteral("contrast"), diff --git a/effects/effect_builtins.h b/effects/effect_builtins.h index 7ccb768eac..77e18c6055 100644 --- a/effects/effect_builtins.h +++ b/effects/effect_builtins.h @@ -35,6 +35,7 @@ enum class BuiltInEffect { Invalid, ///< not a valid Effect Blur, + ColorPicker, Contrast, CoverSwitch, Cube,