diff --git a/autotests/test_builtin_effectloader.cpp b/autotests/test_builtin_effectloader.cpp --- a/autotests/test_builtin_effectloader.cpp +++ b/autotests/test_builtin_effectloader.cpp @@ -81,6 +81,7 @@ 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 @@ { QStringList expectedEffects; expectedEffects << QStringLiteral("blur") + << QStringLiteral("colorpicker") << QStringLiteral("contrast") << QStringLiteral("coverswitch") << QStringLiteral("cube") @@ -196,6 +198,8 @@ 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 @@ 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 --- a/autotests/test_plugin_effectloader.cpp +++ b/autotests/test_plugin_effectloader.cpp @@ -73,6 +73,7 @@ // 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 --- a/autotests/test_scripted_effectloader.cpp +++ b/autotests/test_scripted_effectloader.cpp @@ -90,6 +90,7 @@ // 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 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -72,6 +72,7 @@ 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.h b/effects/colorpicker/colorpicker.h new file mode 100644 --- /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/colorpicker/colorpicker.cpp b/effects/colorpicker/colorpicker.cpp new file mode 100644 --- /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/effect_builtins.h b/effects/effect_builtins.h --- a/effects/effect_builtins.h +++ b/effects/effect_builtins.h @@ -35,6 +35,7 @@ { Invalid, ///< not a valid Effect Blur, + ColorPicker, Contrast, CoverSwitch, Cube, diff --git a/effects/effect_builtins.cpp b/effects/effect_builtins.cpp --- a/effects/effect_builtins.cpp +++ b/effects/effect_builtins.cpp @@ -22,6 +22,7 @@ // 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" @@ -112,6 +113,21 @@ #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"), i18ndc("kwin_effects", "Name of a KWin Effect", "Background contrast"), i18ndc("kwin_effects", "Comment describing the KWin Effect", "Improve contrast and readability behind semi-transparent windows"),