Commit b93e0b21 authored by Julius Künzel's avatar Julius Künzel 💬
Browse files

Fix color picker on wayland

Fixes #1417

On wayland the features are limited by the portal. Preview and dragging
a rectangle for avarage color does not work.
parent 25edf681
Pipeline #271609 passed with stage
in 7 minutes and 41 seconds
......@@ -5,9 +5,19 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "colorpickerwidget.h"
#include "core.h"
#include "mainwindow.h"
#include <KLocalizedString>
#include <QApplication>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusMetaType>
#include <QDBusObjectPath>
#include <QDBusPendingCall>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
#include <QDebug>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QScreen>
......@@ -21,6 +31,27 @@ SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include <fixx11h.h>
QDBusArgument &operator<<(QDBusArgument &arg, const QColor &color)
arg << color.redF() << color.greenF() << color.blueF();
return arg;
const QDBusArgument &operator>>(const QDBusArgument &arg, QColor &color)
double red, green, blue;
arg >> red >> green >> blue;
return arg;
MyFrame::MyFrame(QWidget *parent)
: QFrame(parent)
......@@ -50,19 +81,36 @@ ColorPickerWidget::ColorPickerWidget(QWidget *parent)
auto *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
// Check wether grabWindow() works. On some systems like with Wayland it does.
// We fallback to the Freedesktop portal with DBus which has less features than
// our custom implementation (eg. preview and avarage color are missing)
QPoint p(pCore->window()->geometry().center());
foreach (QScreen *screen, QGuiApplication::screens()) {
QRect screenRect = screen->geometry();
if (screenRect.contains(p)) {
QPixmap pm = screen->grabWindow(pCore->window()->winId(), p.x(), p.y(), 1, 1);
qDebug() << "got pixmap that is not null";
m_useDBus = pm.isNull();
auto *button = new QToolButton(this);
button->setToolTip(i18n("Pick a color on the screen."));
button->setWhatsThis(xi18nc("@info:whatsthis", "Pick a color on the screen. By pressing the mouse button and then moving your mouse you can select a "
"section of the screen from which to get an average color."));
connect(button, &QAbstractButton::clicked, this, &ColorPickerWidget::slotSetupEventFilter);
if (!m_useDBus) {
button->setWhatsThis(xi18nc("@info:whatsthis", "Pick a color on the screen. By pressing the mouse button and then moving your mouse you can select a "
"section of the screen from which to get an average color."));
connect(button, &QAbstractButton::clicked, this, &ColorPickerWidget::slotSetupEventFilter);
} else {
connect(button, &QAbstractButton::clicked, this, &ColorPickerWidget::grabColorDBus);
m_grabRectFrame = new MyFrame();
......@@ -266,3 +314,37 @@ QColor ColorPickerWidget::grabColor(const QPoint &p, bool destroyImage)
void ColorPickerWidget::grabColorDBus()
QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), QLatin1String("/org/freedesktop/portal/desktop"),
QLatin1String("org.freedesktop.portal.Screenshot"), QLatin1String("PickColor"));
message << QLatin1String("x11:") << QVariantMap{};
QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall);
connect(watcher, &QDBusPendingCallWatcher::finished, [this](QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QDBusObjectPath> reply = *watcher;
if (reply.isError()) {
qWarning() << "Couldn't get reply";
qWarning() << "Error: " << reply.error().message();
} else {
qDebug() << reply.value() << reply.value().path() << reply.error();
QDBusConnection::sessionBus().connect(QString(), reply.value().path(), QLatin1String("org.freedesktop.portal.Request"), QLatin1String("Response"),
this, SLOT(gotColorResponse(uint, QVariantMap)));
void ColorPickerWidget::gotColorResponse(uint response, const QVariantMap &results)
if (!response) {
if (results.contains(QLatin1String("color"))) {
const QColor color = qdbus_cast<QColor>(results.value(QLatin1String("color")));
qDebug() << "picked" << color;
m_mouseColor = color;
emit colorPicked(m_mouseColor);
} else {
qWarning() << "Failed to take screenshot" << response << results;
......@@ -61,6 +61,7 @@ private:
QColor grabColor(const QPoint &p, bool destroyImage = true);
bool m_filterActive{false};
bool m_useDBus{true};
QRect m_grabRect;
QPoint m_clickPoint;
QFrame *m_grabRectFrame;
......@@ -78,6 +79,11 @@ private slots:
/** @brief Calculates the average color for the pixels in the rect m_grabRect and emits colorPicked. */
void slotGetAverageColor();
/** @brief Send a DBus message to the Freedesktop portal to request a color picker operation */
void grabColorDBus();
/** @brief To be called by the DBus connection when a response comes in */
void gotColorResponse(uint response, const QVariantMap &results);
/** @brief Signal fired when a new color has been picked */
void colorPicked(const QColor &);
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment