forked from quickshell/quickshell
Compare commits
10 commits
3c7dfcb220
...
f17bc07da3
Author | SHA1 | Date | |
---|---|---|---|
f17bc07da3 | |||
4f2610dece | |||
9417d6fa57 | |||
420529362f | |||
325be8857c | |||
b289bfa504 | |||
cdaff2967f | |||
c6791cf1f2 | |||
b73eff0e47 | |||
![]() |
6a017d63d6 |
36 changed files with 1014 additions and 232 deletions
|
@ -37,6 +37,7 @@ qt_add_library(quickshell-core STATIC
|
||||||
common.cpp
|
common.cpp
|
||||||
iconprovider.cpp
|
iconprovider.cpp
|
||||||
scriptmodel.cpp
|
scriptmodel.cpp
|
||||||
|
colorquantizer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(quickshell-core
|
qt_add_qml_module(quickshell-core
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "util.hpp"
|
|
||||||
|
|
||||||
SystemClock::SystemClock(QObject* parent): QObject(parent) {
|
SystemClock::SystemClock(QObject* parent): QObject(parent) {
|
||||||
QObject::connect(&this->timer, &QTimer::timeout, this, &SystemClock::onTimeout);
|
QObject::connect(&this->timer, &QTimer::timeout, this, &SystemClock::onTimeout);
|
||||||
this->update();
|
this->update();
|
||||||
|
@ -48,19 +46,16 @@ void SystemClock::update() {
|
||||||
void SystemClock::setTime(const QDateTime& targetTime) {
|
void SystemClock::setTime(const QDateTime& targetTime) {
|
||||||
auto currentTime = QDateTime::currentDateTime();
|
auto currentTime = QDateTime::currentDateTime();
|
||||||
auto offset = currentTime.msecsTo(targetTime);
|
auto offset = currentTime.msecsTo(targetTime);
|
||||||
auto dtime = offset > -500 && offset < 500 ? targetTime : currentTime;
|
this->currentTime = offset > -500 && offset < 500 ? targetTime : currentTime;
|
||||||
auto time = dtime.time();
|
|
||||||
|
|
||||||
auto secondPrecision = this->mPrecision >= SystemClock::Seconds;
|
auto time = this->currentTime.time();
|
||||||
auto secondChanged = this->setSeconds(secondPrecision ? time.second() : 0);
|
this->currentTime.setTime(QTime(
|
||||||
|
this->mPrecision >= SystemClock::Hours ? time.hour() : 0,
|
||||||
|
this->mPrecision >= SystemClock::Minutes ? time.minute() : 0,
|
||||||
|
this->mPrecision >= SystemClock::Seconds ? time.second() : 0
|
||||||
|
));
|
||||||
|
|
||||||
auto minutePrecision = this->mPrecision >= SystemClock::Minutes;
|
emit this->dateChanged();
|
||||||
auto minuteChanged = this->setMinutes(minutePrecision ? time.minute() : 0);
|
|
||||||
|
|
||||||
auto hourPrecision = this->mPrecision >= SystemClock::Hours;
|
|
||||||
auto hourChanged = this->setHours(hourPrecision ? time.hour() : 0);
|
|
||||||
|
|
||||||
DropEmitter::call(secondChanged, minuteChanged, hourChanged);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemClock::schedule(const QDateTime& targetTime) {
|
void SystemClock::schedule(const QDateTime& targetTime) {
|
||||||
|
@ -76,11 +71,11 @@ void SystemClock::schedule(const QDateTime& targetTime) {
|
||||||
auto nextTime = offset > 0 && offset < 500 ? targetTime : currentTime;
|
auto nextTime = offset > 0 && offset < 500 ? targetTime : currentTime;
|
||||||
|
|
||||||
auto baseTimeT = nextTime.time();
|
auto baseTimeT = nextTime.time();
|
||||||
nextTime.setTime(
|
nextTime.setTime(QTime(
|
||||||
{hourPrecision ? baseTimeT.hour() : 0,
|
hourPrecision ? baseTimeT.hour() : 0,
|
||||||
minutePrecision ? baseTimeT.minute() : 0,
|
minutePrecision ? baseTimeT.minute() : 0,
|
||||||
secondPrecision ? baseTimeT.second() : 0}
|
secondPrecision ? baseTimeT.second() : 0
|
||||||
);
|
));
|
||||||
|
|
||||||
if (secondPrecision) nextTime = nextTime.addSecs(1);
|
if (secondPrecision) nextTime = nextTime.addSecs(1);
|
||||||
else if (minutePrecision) nextTime = nextTime.addSecs(60);
|
else if (minutePrecision) nextTime = nextTime.addSecs(60);
|
||||||
|
@ -91,7 +86,3 @@ void SystemClock::schedule(const QDateTime& targetTime) {
|
||||||
this->timer.start(static_cast<qint32>(delay));
|
this->timer.start(static_cast<qint32>(delay));
|
||||||
this->targetTime = nextTime;
|
this->targetTime = nextTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_MEMBER_GETSET(SystemClock, hours, setHours);
|
|
||||||
DEFINE_MEMBER_GETSET(SystemClock, minutes, setMinutes);
|
|
||||||
DEFINE_MEMBER_GETSET(SystemClock, seconds, setSeconds);
|
|
||||||
|
|
|
@ -7,9 +7,26 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "util.hpp"
|
|
||||||
|
|
||||||
///! System clock accessor.
|
///! System clock accessor.
|
||||||
|
/// SystemClock is a view into the system's clock.
|
||||||
|
/// It updates at hour, minute, or second intervals depending on @@precision.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```qml
|
||||||
|
/// SystemClock {
|
||||||
|
/// id: clock
|
||||||
|
/// precision: SystemClock.Seconds
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @@QtQuick.Text {
|
||||||
|
/// text: Qt.formatDateTime(clock.date, "hh:mm:ss - yyyy-MM-dd")
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// > [!WARNING] Clock updates will trigger within 50ms of the system clock changing,
|
||||||
|
/// > however this can be either before or after the clock changes (+-50ms). If you
|
||||||
|
/// > need a date object, use @@date instead of constructing a new one, or the time
|
||||||
|
/// > of the constructed object could be off by up to a second.
|
||||||
class SystemClock: public QObject {
|
class SystemClock: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// If the clock should update. Defaults to true.
|
/// If the clock should update. Defaults to true.
|
||||||
|
@ -18,12 +35,17 @@ class SystemClock: public QObject {
|
||||||
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged);
|
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged);
|
||||||
/// The precision the clock should measure at. Defaults to `SystemClock.Seconds`.
|
/// The precision the clock should measure at. Defaults to `SystemClock.Seconds`.
|
||||||
Q_PROPERTY(SystemClock::Enum precision READ precision WRITE setPrecision NOTIFY precisionChanged);
|
Q_PROPERTY(SystemClock::Enum precision READ precision WRITE setPrecision NOTIFY precisionChanged);
|
||||||
|
/// The current date and time.
|
||||||
|
///
|
||||||
|
/// > [!TIP] You can use @@QtQml.Qt.formatDateTime() to get the time as a string in
|
||||||
|
/// > your format of choice.
|
||||||
|
Q_PROPERTY(QDateTime date READ date NOTIFY dateChanged);
|
||||||
/// The current hour.
|
/// The current hour.
|
||||||
Q_PROPERTY(quint32 hours READ hours NOTIFY hoursChanged);
|
Q_PROPERTY(quint32 hours READ hours NOTIFY dateChanged);
|
||||||
/// The current minute, or 0 if @@precision is `SystemClock.Hours`.
|
/// The current minute, or 0 if @@precision is `SystemClock.Hours`.
|
||||||
Q_PROPERTY(quint32 minutes READ minutes NOTIFY minutesChanged);
|
Q_PROPERTY(quint32 minutes READ minutes NOTIFY dateChanged);
|
||||||
/// The current second, or 0 if @@precision is `SystemClock.Hours` or `SystemClock.Minutes`.
|
/// The current second, or 0 if @@precision is `SystemClock.Hours` or `SystemClock.Minutes`.
|
||||||
Q_PROPERTY(quint32 seconds READ seconds NOTIFY secondsChanged);
|
Q_PROPERTY(quint32 seconds READ seconds NOTIFY dateChanged);
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -43,12 +65,15 @@ public:
|
||||||
[[nodiscard]] SystemClock::Enum precision() const;
|
[[nodiscard]] SystemClock::Enum precision() const;
|
||||||
void setPrecision(SystemClock::Enum precision);
|
void setPrecision(SystemClock::Enum precision);
|
||||||
|
|
||||||
|
[[nodiscard]] QDateTime date() const { return this->currentTime; }
|
||||||
|
[[nodiscard]] quint32 hours() const { return this->currentTime.time().hour(); }
|
||||||
|
[[nodiscard]] quint32 minutes() const { return this->currentTime.time().minute(); }
|
||||||
|
[[nodiscard]] quint32 seconds() const { return this->currentTime.time().second(); }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void enabledChanged();
|
void enabledChanged();
|
||||||
void precisionChanged();
|
void precisionChanged();
|
||||||
void hoursChanged();
|
void dateChanged();
|
||||||
void minutesChanged();
|
|
||||||
void secondsChanged();
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onTimeout();
|
void onTimeout();
|
||||||
|
@ -56,17 +81,11 @@ private slots:
|
||||||
private:
|
private:
|
||||||
bool mEnabled = true;
|
bool mEnabled = true;
|
||||||
SystemClock::Enum mPrecision = SystemClock::Seconds;
|
SystemClock::Enum mPrecision = SystemClock::Seconds;
|
||||||
quint32 mHours = 0;
|
|
||||||
quint32 mMinutes = 0;
|
|
||||||
quint32 mSeconds = 0;
|
|
||||||
QTimer timer;
|
QTimer timer;
|
||||||
|
QDateTime currentTime;
|
||||||
QDateTime targetTime;
|
QDateTime targetTime;
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
void setTime(const QDateTime& targetTime);
|
void setTime(const QDateTime& targetTime);
|
||||||
void schedule(const QDateTime& targetTime);
|
void schedule(const QDateTime& targetTime);
|
||||||
|
|
||||||
DECLARE_PRIVATE_MEMBER(SystemClock, hours, setHours, mHours, hoursChanged);
|
|
||||||
DECLARE_PRIVATE_MEMBER(SystemClock, minutes, setMinutes, mMinutes, minutesChanged);
|
|
||||||
DECLARE_PRIVATE_MEMBER(SystemClock, seconds, setSeconds, mSeconds, secondsChanged);
|
|
||||||
};
|
};
|
||||||
|
|
182
src/core/colorquantizer.cpp
Normal file
182
src/core/colorquantizer.cpp
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
#include "colorquantizer.hpp"
|
||||||
|
|
||||||
|
#include <qcolor.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
#include <qthreadpool.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
ColorQuantizerOperation::ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize)
|
||||||
|
: source(source)
|
||||||
|
, maxDepth(depth)
|
||||||
|
, rescaleSize(rescaleSize) {
|
||||||
|
mColors = QList<QColor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorQuantizerOperation::run() {
|
||||||
|
quantizeImage();
|
||||||
|
|
||||||
|
emit done(mColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorQuantizerOperation::quantizeImage() {
|
||||||
|
mColors.clear();
|
||||||
|
|
||||||
|
if (source->isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage image(source->toLocalFile());
|
||||||
|
|
||||||
|
if ((image.width() > rescaleSize || image.height() > rescaleSize) && rescaleSize > 0) {
|
||||||
|
image = image.scaled(
|
||||||
|
static_cast<int>(rescaleSize),
|
||||||
|
static_cast<int>(rescaleSize),
|
||||||
|
Qt::KeepAspectRatio,
|
||||||
|
Qt::SmoothTransformation
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image.isNull()) {
|
||||||
|
qWarning() << "Failed to load image from" << source;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QColor> pixels;
|
||||||
|
for (int y = 0; y < image.height(); ++y) {
|
||||||
|
for (int x = 0; x < image.width(); ++x) {
|
||||||
|
QRgb pixel = image.pixel(x, y);
|
||||||
|
if (qAlpha(pixel) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels.append(QColor::fromRgb(pixel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDateTime startTime = QDateTime::currentDateTime();
|
||||||
|
|
||||||
|
mColors = quantization(pixels, 0);
|
||||||
|
|
||||||
|
QDateTime endTime = QDateTime::currentDateTime();
|
||||||
|
qint64 milliseconds = startTime.msecsTo(endTime);
|
||||||
|
qDebug() << "Color Quantization took: " << milliseconds << "ms";
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QColor> ColorQuantizerOperation::quantization(QList<QColor>& rgbValues, qreal depth) {
|
||||||
|
if (depth >= maxDepth || rgbValues.isEmpty()) {
|
||||||
|
if (rgbValues.isEmpty()) {
|
||||||
|
return QList<QColor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalR = 0;
|
||||||
|
int totalG = 0;
|
||||||
|
int totalB = 0;
|
||||||
|
|
||||||
|
for (const QColor& color: rgbValues) {
|
||||||
|
totalR += color.red();
|
||||||
|
totalG += color.green();
|
||||||
|
totalB += color.blue();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor avgColor(
|
||||||
|
qRound(totalR / static_cast<double>(rgbValues.size())),
|
||||||
|
qRound(totalG / static_cast<double>(rgbValues.size())),
|
||||||
|
qRound(totalB / static_cast<double>(rgbValues.size()))
|
||||||
|
);
|
||||||
|
|
||||||
|
return QList<QColor>() << avgColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString dominantChannel = findBiggestColorRange(rgbValues);
|
||||||
|
std::ranges::sort(rgbValues, [dominantChannel](const QColor& a, const QColor& b) {
|
||||||
|
if (dominantChannel == "r") return a.red() < b.red();
|
||||||
|
else if (dominantChannel == "g") return a.green() < b.green();
|
||||||
|
return a.blue() < b.blue();
|
||||||
|
});
|
||||||
|
|
||||||
|
qsizetype mid = rgbValues.size() / 2;
|
||||||
|
|
||||||
|
QList<QColor> leftHalf = rgbValues.mid(0, mid);
|
||||||
|
QList<QColor> rightHalf = rgbValues.mid(mid + 1);
|
||||||
|
|
||||||
|
QList<QColor> result;
|
||||||
|
result.append(quantization(leftHalf, depth + 1));
|
||||||
|
result.append(quantization(rightHalf, depth + 1));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ColorQuantizerOperation::findBiggestColorRange(const QList<QColor>& rgbValues) {
|
||||||
|
if (rgbValues.isEmpty()) return "r";
|
||||||
|
|
||||||
|
int rMin = 255;
|
||||||
|
int gMin = 255;
|
||||||
|
int bMin = 255;
|
||||||
|
int rMax = 0;
|
||||||
|
int gMax = 0;
|
||||||
|
int bMax = 0;
|
||||||
|
|
||||||
|
for (const QColor& color: rgbValues) {
|
||||||
|
rMin = qMin(rMin, color.red());
|
||||||
|
gMin = qMin(gMin, color.green());
|
||||||
|
bMin = qMin(bMin, color.blue());
|
||||||
|
|
||||||
|
rMax = qMax(rMax, color.red());
|
||||||
|
gMax = qMax(gMax, color.green());
|
||||||
|
bMax = qMax(bMax, color.blue());
|
||||||
|
}
|
||||||
|
|
||||||
|
int rRange = rMax - rMin;
|
||||||
|
int gRange = gMax - gMin;
|
||||||
|
int bRange = bMax - bMin;
|
||||||
|
|
||||||
|
int biggestRange = qMax(rRange, qMax(gRange, bRange));
|
||||||
|
if (biggestRange == rRange) {
|
||||||
|
return "r";
|
||||||
|
} else if (biggestRange == gRange) {
|
||||||
|
return "g";
|
||||||
|
} else {
|
||||||
|
return "b";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QColor> ColorQuantizer::colors() { return mColors; }
|
||||||
|
|
||||||
|
void ColorQuantizer::setSource(const QUrl& source) {
|
||||||
|
if (mSource != source) {
|
||||||
|
mSource = source;
|
||||||
|
emit sourceChanged();
|
||||||
|
quantizeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorQuantizer::setDepth(qreal depth) {
|
||||||
|
if (mDepth != depth) {
|
||||||
|
mDepth = depth;
|
||||||
|
emit depthChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorQuantizer::setRescaleSize(int rescaleSize) {
|
||||||
|
if (mRescaleSize != rescaleSize) {
|
||||||
|
mRescaleSize = rescaleSize;
|
||||||
|
emit rescaleSizeChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorQuantizer::operationFinished(const QList<QColor>& result) {
|
||||||
|
mColors = result;
|
||||||
|
emit colorsChanged();
|
||||||
|
isProcessing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorQuantizer::quantizeAsync() {
|
||||||
|
if (isProcessing) return;
|
||||||
|
|
||||||
|
qDebug() << "Starting color quantization asynchronously";
|
||||||
|
isProcessing = true;
|
||||||
|
auto* task = new ColorQuantizerOperation(&mSource, mDepth, mRescaleSize);
|
||||||
|
QObject::connect(task, &ColorQuantizerOperation::done, this, &ColorQuantizer::operationFinished);
|
||||||
|
QThreadPool::globalInstance()->start(task);
|
||||||
|
}
|
95
src/core/colorquantizer.hpp
Normal file
95
src/core/colorquantizer.hpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qlist.h>
|
||||||
|
#include <qmutex.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qrunnable.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
class ColorQuantizerOperation
|
||||||
|
: public QObject
|
||||||
|
, public QRunnable {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize);
|
||||||
|
|
||||||
|
void run() override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void done(QList<QColor> colors);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<QColor> mColors;
|
||||||
|
QUrl* source;
|
||||||
|
qreal maxDepth;
|
||||||
|
qreal rescaleSize;
|
||||||
|
|
||||||
|
void quantizeImage();
|
||||||
|
QList<QColor> quantization(QList<QColor>& rgbValues, qreal depth);
|
||||||
|
static QString findBiggestColorRange(const QList<QColor>& rgbValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
///! Color Quantization Utility
|
||||||
|
/// A color quantization utility used for getting prevalent colors in an image, by
|
||||||
|
/// averaging out the image's color data recursively.
|
||||||
|
///
|
||||||
|
/// #### Example
|
||||||
|
/// ```qml
|
||||||
|
/// ColorQuantizer {
|
||||||
|
/// id: colorQuantizer
|
||||||
|
/// source: Qt.resolvedUrl("./yourImage.png")
|
||||||
|
/// depth: 3 // Will produce 8 colors (2³)
|
||||||
|
/// rescaleSize: 64 // Rescale to 64x64 for faster processing
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
class ColorQuantizer: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
/// Access the colors resulting from the color quantization performed.
|
||||||
|
/// > [!NOTE] The amount of colors returned from the quantization is determined by
|
||||||
|
/// > the property depth, specifically 2ⁿ where n is the depth.
|
||||||
|
Q_PROPERTY(QList<QColor> colors READ colors NOTIFY colorsChanged);
|
||||||
|
|
||||||
|
/// Path to the image you'd like to run the color quantization on.
|
||||||
|
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged);
|
||||||
|
|
||||||
|
/// Max depth for the color quantization. Each level of depth represents another
|
||||||
|
/// binary split of the color space
|
||||||
|
Q_PROPERTY(qreal depth READ depth WRITE setDepth NOTIFY depthChanged);
|
||||||
|
|
||||||
|
/// The size to rescale the image to, when rescaleSize is 0 then no scaling will be done.
|
||||||
|
/// > [!NOTE] Results from color quantization doesn't suffer much when rescaling, it's
|
||||||
|
/// > reccommended to rescale, otherwise the quantization process will take much longer.
|
||||||
|
Q_PROPERTY(qreal rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY rescaleSizeChanged);
|
||||||
|
QML_ELEMENT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QList<QColor> colors();
|
||||||
|
[[nodiscard]] QUrl source() const { return mSource; }
|
||||||
|
void setSource(const QUrl& source);
|
||||||
|
[[nodiscard]] qreal depth() const { return mDepth; }
|
||||||
|
void setDepth(qreal depth);
|
||||||
|
[[nodiscard]] qreal rescaleSize() const { return mRescaleSize; }
|
||||||
|
void setRescaleSize(int rescaleSize);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void colorsChanged();
|
||||||
|
void sourceChanged();
|
||||||
|
void depthChanged();
|
||||||
|
void rescaleSizeChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void operationFinished(const QList<QColor>& result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isProcessing = false;
|
||||||
|
QList<QColor> mColors;
|
||||||
|
QUrl mSource;
|
||||||
|
qreal mDepth = 0;
|
||||||
|
qreal mRescaleSize = 0;
|
||||||
|
|
||||||
|
void quantizeAsync();
|
||||||
|
};
|
|
@ -213,7 +213,7 @@ QVector<QString> DesktopEntry::parseExecString(const QString& execString) {
|
||||||
|
|
||||||
currentArgument += c;
|
currentArgument += c;
|
||||||
escape = 0;
|
escape = 0;
|
||||||
} else if (c == u'"') {
|
} else if (c == u'"' || c == u'\'') {
|
||||||
parsingString = false;
|
parsingString = false;
|
||||||
} else {
|
} else {
|
||||||
currentArgument += c;
|
currentArgument += c;
|
||||||
|
@ -229,7 +229,7 @@ QVector<QString> DesktopEntry::parseExecString(const QString& execString) {
|
||||||
percent = false;
|
percent = false;
|
||||||
} else if (c == '%') {
|
} else if (c == '%') {
|
||||||
percent = true;
|
percent = true;
|
||||||
} else if (c == u'"') {
|
} else if (c == u'"' || c == u'\'') {
|
||||||
parsingString = true;
|
parsingString = true;
|
||||||
} else if (c == u' ') {
|
} else if (c == u' ') {
|
||||||
if (!currentArgument.isEmpty()) {
|
if (!currentArgument.isEmpty()) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "imageprovider.hpp"
|
#include "imageprovider.hpp"
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
#include <qimage.h>
|
#include <qimage.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
@ -7,10 +8,14 @@
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qpixmap.h>
|
#include <qpixmap.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
namespace {
|
||||||
QMap<QString, QsImageHandle*> liveImages; // NOLINT
|
QMap<QString, QsImageHandle*> liveImages; // NOLINT
|
||||||
|
quint32 handleIndex = 0; // NOLINT
|
||||||
|
} // namespace
|
||||||
|
|
||||||
void parseReq(const QString& req, QString& target, QString& param) {
|
void parseReq(const QString& req, QString& target, QString& param) {
|
||||||
auto splitIdx = req.indexOf('/');
|
auto splitIdx = req.indexOf('/');
|
||||||
|
@ -24,14 +29,9 @@ void parseReq(const QString& req, QString& target, QString& param) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent)
|
QsImageHandle::QsImageHandle(QQmlImageProviderBase::ImageType type)
|
||||||
: QObject(parent)
|
: type(type)
|
||||||
, type(type) {
|
, id(QString::number(++handleIndex)) {
|
||||||
{
|
|
||||||
auto dbg = QDebug(&this->id);
|
|
||||||
dbg.nospace() << static_cast<void*>(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
liveImages.insert(this->id, this);
|
liveImages.insert(this->id, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,3 +85,9 @@ QsPixmapProvider::requestPixmap(const QString& id, QSize* size, const QSize& req
|
||||||
return QPixmap();
|
return QPixmap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QsIndexedImageHandle::url() const {
|
||||||
|
return this->QsImageHandle::url() % '/' % QString::number(this->changeIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QsIndexedImageHandle::imageChanged() { ++this->changeIndex; }
|
||||||
|
|
|
@ -20,15 +20,13 @@ public:
|
||||||
QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override;
|
QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class QsImageHandle: public QObject {
|
class QsImageHandle {
|
||||||
Q_OBJECT;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QsImageHandle(QQmlImageProviderBase::ImageType type, QObject* parent = nullptr);
|
explicit QsImageHandle(QQmlImageProviderBase::ImageType type);
|
||||||
~QsImageHandle() override;
|
virtual ~QsImageHandle();
|
||||||
Q_DISABLE_COPY_MOVE(QsImageHandle);
|
Q_DISABLE_COPY_MOVE(QsImageHandle);
|
||||||
|
|
||||||
[[nodiscard]] QString url() const;
|
[[nodiscard]] virtual QString url() const;
|
||||||
|
|
||||||
virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize);
|
virtual QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize);
|
||||||
virtual QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize);
|
virtual QPixmap requestPixmap(const QString& id, QSize* size, const QSize& requestedSize);
|
||||||
|
@ -37,3 +35,14 @@ private:
|
||||||
QQmlImageProviderBase::ImageType type;
|
QQmlImageProviderBase::ImageType type;
|
||||||
QString id;
|
QString id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class QsIndexedImageHandle: public QsImageHandle {
|
||||||
|
public:
|
||||||
|
explicit QsIndexedImageHandle(QQmlImageProviderBase::ImageType type): QsImageHandle(type) {}
|
||||||
|
|
||||||
|
[[nodiscard]] QString url() const override;
|
||||||
|
void imageChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
quint32 changeIndex = 0;
|
||||||
|
};
|
||||||
|
|
|
@ -29,5 +29,6 @@ headers = [
|
||||||
"qsmenuanchor.hpp",
|
"qsmenuanchor.hpp",
|
||||||
"clock.hpp",
|
"clock.hpp",
|
||||||
"scriptmodel.hpp",
|
"scriptmodel.hpp",
|
||||||
|
"colorquantizer.hpp",
|
||||||
]
|
]
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -42,6 +42,24 @@ QString QuickshellScreenInfo::name() const {
|
||||||
return this->screen->name();
|
return this->screen->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QuickshellScreenInfo::model() const {
|
||||||
|
if (this->screen == nullptr) {
|
||||||
|
this->warnDangling();
|
||||||
|
return "{ NULL SCREEN }";
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->screen->model();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QuickshellScreenInfo::serialNumber() const {
|
||||||
|
if (this->screen == nullptr) {
|
||||||
|
this->warnDangling();
|
||||||
|
return "{ NULL SCREEN }";
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->screen->serialNumber();
|
||||||
|
}
|
||||||
|
|
||||||
qint32 QuickshellScreenInfo::x() const {
|
qint32 QuickshellScreenInfo::x() const {
|
||||||
if (this->screen == nullptr) {
|
if (this->screen == nullptr) {
|
||||||
this->warnDangling();
|
this->warnDangling();
|
||||||
|
|
|
@ -29,6 +29,10 @@ class QuickshellScreenInfo: public QObject {
|
||||||
///
|
///
|
||||||
/// Usually something like `DP-1`, `HDMI-1`, `eDP-1`.
|
/// Usually something like `DP-1`, `HDMI-1`, `eDP-1`.
|
||||||
Q_PROPERTY(QString name READ name CONSTANT);
|
Q_PROPERTY(QString name READ name CONSTANT);
|
||||||
|
/// The model of the screen as seen by the operating system.
|
||||||
|
Q_PROPERTY(QString model READ model CONSTANT);
|
||||||
|
/// The serial number of the screen as seen by the operating system.
|
||||||
|
Q_PROPERTY(QString serialNumber READ serialNumber CONSTANT);
|
||||||
Q_PROPERTY(qint32 x READ x NOTIFY geometryChanged);
|
Q_PROPERTY(qint32 x READ x NOTIFY geometryChanged);
|
||||||
Q_PROPERTY(qint32 y READ y NOTIFY geometryChanged);
|
Q_PROPERTY(qint32 y READ y NOTIFY geometryChanged);
|
||||||
Q_PROPERTY(qint32 width READ width NOTIFY geometryChanged);
|
Q_PROPERTY(qint32 width READ width NOTIFY geometryChanged);
|
||||||
|
@ -49,6 +53,8 @@ public:
|
||||||
bool operator==(QuickshellScreenInfo& other) const;
|
bool operator==(QuickshellScreenInfo& other) const;
|
||||||
|
|
||||||
[[nodiscard]] QString name() const;
|
[[nodiscard]] QString name() const;
|
||||||
|
[[nodiscard]] QString model() const;
|
||||||
|
[[nodiscard]] QString serialNumber() const;
|
||||||
[[nodiscard]] qint32 x() const;
|
[[nodiscard]] qint32 x() const;
|
||||||
[[nodiscard]] qint32 y() const;
|
[[nodiscard]] qint32 y() const;
|
||||||
[[nodiscard]] qint32 width() const;
|
[[nodiscard]] qint32 width() const;
|
||||||
|
|
|
@ -59,8 +59,8 @@ QString DBusMenuItem::icon() const {
|
||||||
this->iconName,
|
this->iconName,
|
||||||
this->menu->iconThemePath.value().join(':')
|
this->menu->iconThemePath.value().join(':')
|
||||||
);
|
);
|
||||||
} else if (this->image != nullptr) {
|
} else if (this->image.hasData()) {
|
||||||
return this->image->url();
|
return this->image.url();
|
||||||
} else return nullptr;
|
} else return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
|
||||||
auto originalEnabled = this->mEnabled;
|
auto originalEnabled = this->mEnabled;
|
||||||
auto originalVisible = this->visible;
|
auto originalVisible = this->visible;
|
||||||
auto originalIconName = this->iconName;
|
auto originalIconName = this->iconName;
|
||||||
auto* originalImage = this->image;
|
auto imageChanged = false;
|
||||||
auto originalIsSeparator = this->mSeparator;
|
auto originalIsSeparator = this->mSeparator;
|
||||||
auto originalButtonType = this->mButtonType;
|
auto originalButtonType = this->mButtonType;
|
||||||
auto originalToggleState = this->mCheckState;
|
auto originalToggleState = this->mCheckState;
|
||||||
|
@ -173,12 +173,16 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
|
||||||
if (iconData.canConvert<QByteArray>()) {
|
if (iconData.canConvert<QByteArray>()) {
|
||||||
auto data = iconData.value<QByteArray>();
|
auto data = iconData.value<QByteArray>();
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
this->image = nullptr;
|
imageChanged = this->image.hasData();
|
||||||
} else if (this->image == nullptr || this->image->data != data) {
|
this->image.data.clear();
|
||||||
this->image = new DBusMenuPngImage(data, this);
|
} else if (!this->image.hasData() || this->image.data != data) {
|
||||||
|
imageChanged = true;
|
||||||
|
this->image.data = data;
|
||||||
|
this->image.imageChanged();
|
||||||
}
|
}
|
||||||
} else if (removed.isEmpty() || removed.contains("icon-data")) {
|
} else if (removed.isEmpty() || removed.contains("icon-data")) {
|
||||||
this->image = nullptr;
|
imageChanged = this->image.hasData();
|
||||||
|
image.data.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto type = properties.value("type");
|
auto type = properties.value("type");
|
||||||
|
@ -239,17 +243,13 @@ void DBusMenuItem::updateProperties(const QVariantMap& properties, const QString
|
||||||
if (this->mSeparator != originalIsSeparator) emit this->isSeparatorChanged();
|
if (this->mSeparator != originalIsSeparator) emit this->isSeparatorChanged();
|
||||||
if (this->displayChildren != originalDisplayChildren) emit this->hasChildrenChanged();
|
if (this->displayChildren != originalDisplayChildren) emit this->hasChildrenChanged();
|
||||||
|
|
||||||
if (this->iconName != originalIconName || this->image != originalImage) {
|
if (this->iconName != originalIconName || imageChanged) {
|
||||||
if (this->image != originalImage) {
|
|
||||||
delete originalImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit this->iconChanged();
|
emit this->iconChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(logDbusMenu).nospace() << "Updated properties of " << this << " { label=" << this->mText
|
qCDebug(logDbusMenu).nospace() << "Updated properties of " << this << " { label=" << this->mText
|
||||||
<< ", enabled=" << this->mEnabled << ", visible=" << this->visible
|
<< ", enabled=" << this->mEnabled << ", visible=" << this->visible
|
||||||
<< ", iconName=" << this->iconName << ", iconData=" << this->image
|
<< ", iconName=" << this->iconName << ", iconData=" << &this->image
|
||||||
<< ", separator=" << this->mSeparator
|
<< ", separator=" << this->mSeparator
|
||||||
<< ", toggleType=" << this->mButtonType
|
<< ", toggleType=" << this->mButtonType
|
||||||
<< ", toggleState=" << this->mCheckState
|
<< ", toggleState=" << this->mCheckState
|
||||||
|
|
|
@ -30,7 +30,17 @@ namespace qs::dbus::dbusmenu {
|
||||||
using menu::QsMenuEntry;
|
using menu::QsMenuEntry;
|
||||||
|
|
||||||
class DBusMenu;
|
class DBusMenu;
|
||||||
class DBusMenuPngImage;
|
class DBusMenuItem;
|
||||||
|
|
||||||
|
class DBusMenuPngImage: public QsIndexedImageHandle {
|
||||||
|
public:
|
||||||
|
explicit DBusMenuPngImage(): QsIndexedImageHandle(QQuickImageProvider::Image) {}
|
||||||
|
|
||||||
|
[[nodiscard]] bool hasData() const { return !data.isEmpty(); }
|
||||||
|
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
};
|
||||||
|
|
||||||
///! Menu item shared by an external program.
|
///! Menu item shared by an external program.
|
||||||
/// Menu item shared by an external program via the
|
/// Menu item shared by an external program via the
|
||||||
|
@ -93,7 +103,7 @@ private:
|
||||||
bool visible = true;
|
bool visible = true;
|
||||||
bool mSeparator = false;
|
bool mSeparator = false;
|
||||||
QString iconName;
|
QString iconName;
|
||||||
DBusMenuPngImage* image = nullptr;
|
DBusMenuPngImage image;
|
||||||
menu::QsMenuButtonType::Enum mButtonType = menu::QsMenuButtonType::None;
|
menu::QsMenuButtonType::Enum mButtonType = menu::QsMenuButtonType::None;
|
||||||
Qt::CheckState mCheckState = Qt::Unchecked;
|
Qt::CheckState mCheckState = Qt::Unchecked;
|
||||||
bool displayChildren = false;
|
bool displayChildren = false;
|
||||||
|
@ -156,17 +166,6 @@ private:
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, DBusMenu* menu);
|
QDebug operator<<(QDebug debug, DBusMenu* menu);
|
||||||
|
|
||||||
class DBusMenuPngImage: public QsImageHandle {
|
|
||||||
public:
|
|
||||||
explicit DBusMenuPngImage(QByteArray data, DBusMenuItem* parent)
|
|
||||||
: QsImageHandle(QQuickImageProvider::Image, parent)
|
|
||||||
, data(std::move(data)) {}
|
|
||||||
|
|
||||||
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
|
||||||
|
|
||||||
QByteArray data;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DBusMenuHandle;
|
class DBusMenuHandle;
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
QDebug operator<<(QDebug debug, const DBusMenuHandle* handle);
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
|
#include <cstring>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <qcolor.h>
|
#include <qcolor.h>
|
||||||
#include <qmetatype.h>
|
#include <qmetatype.h>
|
||||||
#include <qobjectdefs.h>
|
#include <qobjectdefs.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
|
||||||
namespace qs::io::ipc {
|
namespace qs::io::ipc {
|
||||||
|
|
||||||
|
@ -14,6 +17,12 @@ const BoolIpcType BoolIpcType::INSTANCE {};
|
||||||
const DoubleIpcType DoubleIpcType::INSTANCE {};
|
const DoubleIpcType DoubleIpcType::INSTANCE {};
|
||||||
const ColorIpcType ColorIpcType::INSTANCE {};
|
const ColorIpcType ColorIpcType::INSTANCE {};
|
||||||
|
|
||||||
|
void* IpcType::copyStorage(const void* data) const {
|
||||||
|
auto* storage = this->createStorage();
|
||||||
|
memcpy(storage, data, this->size());
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
const IpcType* IpcType::ipcType(const QMetaType& metaType) {
|
const IpcType* IpcType::ipcType(const QMetaType& metaType) {
|
||||||
if (metaType.id() == QMetaType::Void) return &VoidIpcType::INSTANCE;
|
if (metaType.id() == QMetaType::Void) return &VoidIpcType::INSTANCE;
|
||||||
if (metaType.id() == QMetaType::QString) return &StringIpcType::INSTANCE;
|
if (metaType.id() == QMetaType::QString) return &StringIpcType::INSTANCE;
|
||||||
|
@ -70,12 +79,18 @@ void IpcTypeSlot::replace(void* value) {
|
||||||
this->storage = value;
|
this->storage = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IpcTypeSlot::replace(const QVariant& value) {
|
||||||
|
this->replace(this->mType->copyStorage(value.constData()));
|
||||||
|
}
|
||||||
|
|
||||||
const char* VoidIpcType::name() const { return "void"; }
|
const char* VoidIpcType::name() const { return "void"; }
|
||||||
const char* VoidIpcType::genericArgumentName() const { return "void"; }
|
const char* VoidIpcType::genericArgumentName() const { return "void"; }
|
||||||
|
qsizetype VoidIpcType::size() const { return 0; }
|
||||||
|
|
||||||
// string
|
// string
|
||||||
const char* StringIpcType::name() const { return "string"; }
|
const char* StringIpcType::name() const { return "string"; }
|
||||||
const char* StringIpcType::genericArgumentName() const { return "QString"; }
|
const char* StringIpcType::genericArgumentName() const { return "QString"; }
|
||||||
|
qsizetype StringIpcType::size() const { return sizeof(QString); }
|
||||||
void* StringIpcType::fromString(const QString& string) const { return new QString(string); }
|
void* StringIpcType::fromString(const QString& string) const { return new QString(string); }
|
||||||
QString StringIpcType::toString(void* slot) const { return *static_cast<QString*>(slot); }
|
QString StringIpcType::toString(void* slot) const { return *static_cast<QString*>(slot); }
|
||||||
void* StringIpcType::createStorage() const { return new QString(); }
|
void* StringIpcType::createStorage() const { return new QString(); }
|
||||||
|
@ -84,6 +99,7 @@ void StringIpcType::destroyStorage(void* slot) const { delete static_cast<QStrin
|
||||||
// int
|
// int
|
||||||
const char* IntIpcType::name() const { return "int"; }
|
const char* IntIpcType::name() const { return "int"; }
|
||||||
const char* IntIpcType::genericArgumentName() const { return "int"; }
|
const char* IntIpcType::genericArgumentName() const { return "int"; }
|
||||||
|
qsizetype IntIpcType::size() const { return sizeof(int); }
|
||||||
|
|
||||||
void* IntIpcType::fromString(const QString& string) const {
|
void* IntIpcType::fromString(const QString& string) const {
|
||||||
auto ok = false;
|
auto ok = false;
|
||||||
|
@ -100,6 +116,7 @@ void IntIpcType::destroyStorage(void* slot) const { delete static_cast<int*>(slo
|
||||||
// bool
|
// bool
|
||||||
const char* BoolIpcType::name() const { return "bool"; }
|
const char* BoolIpcType::name() const { return "bool"; }
|
||||||
const char* BoolIpcType::genericArgumentName() const { return "bool"; }
|
const char* BoolIpcType::genericArgumentName() const { return "bool"; }
|
||||||
|
qsizetype BoolIpcType::size() const { return sizeof(bool); }
|
||||||
|
|
||||||
void* BoolIpcType::fromString(const QString& string) const {
|
void* BoolIpcType::fromString(const QString& string) const {
|
||||||
if (string == "true") return new bool(true);
|
if (string == "true") return new bool(true);
|
||||||
|
@ -121,6 +138,7 @@ void BoolIpcType::destroyStorage(void* slot) const { delete static_cast<bool*>(s
|
||||||
// double
|
// double
|
||||||
const char* DoubleIpcType::name() const { return "real"; }
|
const char* DoubleIpcType::name() const { return "real"; }
|
||||||
const char* DoubleIpcType::genericArgumentName() const { return "double"; }
|
const char* DoubleIpcType::genericArgumentName() const { return "double"; }
|
||||||
|
qsizetype DoubleIpcType::size() const { return sizeof(double); }
|
||||||
|
|
||||||
void* DoubleIpcType::fromString(const QString& string) const {
|
void* DoubleIpcType::fromString(const QString& string) const {
|
||||||
auto ok = false;
|
auto ok = false;
|
||||||
|
@ -139,6 +157,7 @@ void DoubleIpcType::destroyStorage(void* slot) const { delete static_cast<double
|
||||||
// color
|
// color
|
||||||
const char* ColorIpcType::name() const { return "color"; }
|
const char* ColorIpcType::name() const { return "color"; }
|
||||||
const char* ColorIpcType::genericArgumentName() const { return "QColor"; }
|
const char* ColorIpcType::genericArgumentName() const { return "QColor"; }
|
||||||
|
qsizetype ColorIpcType::size() const { return sizeof(QColor); }
|
||||||
|
|
||||||
void* ColorIpcType::fromString(const QString& string) const {
|
void* ColorIpcType::fromString(const QString& string) const {
|
||||||
auto color = QColor::fromString(string);
|
auto color = QColor::fromString(string);
|
||||||
|
@ -167,6 +186,10 @@ QString WireFunctionDefinition::toString() const {
|
||||||
return "function " % this->name % '(' % paramString % "): " % this->returnType;
|
return "function " % this->name % '(' % paramString % "): " % this->returnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString WirePropertyDefinition::toString() const {
|
||||||
|
return "property " % this->name % ": " % this->type;
|
||||||
|
}
|
||||||
|
|
||||||
QString WireTargetDefinition::toString() const {
|
QString WireTargetDefinition::toString() const {
|
||||||
QString accum = "target " % this->name;
|
QString accum = "target " % this->name;
|
||||||
|
|
||||||
|
@ -174,6 +197,10 @@ QString WireTargetDefinition::toString() const {
|
||||||
accum += "\n " % func.toString();
|
accum += "\n " % func.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& prop: this->properties) {
|
||||||
|
accum += "\n " % prop.toString();
|
||||||
|
}
|
||||||
|
|
||||||
return accum;
|
return accum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qobjectdefs.h>
|
#include <qobjectdefs.h>
|
||||||
#include <qtclasshelpermacros.h>
|
#include <qtclasshelpermacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "../ipc/ipc.hpp"
|
#include "../ipc/ipc.hpp"
|
||||||
|
|
||||||
|
@ -21,10 +22,12 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] virtual const char* name() const = 0;
|
[[nodiscard]] virtual const char* name() const = 0;
|
||||||
[[nodiscard]] virtual const char* genericArgumentName() const = 0;
|
[[nodiscard]] virtual const char* genericArgumentName() const = 0;
|
||||||
|
[[nodiscard]] virtual qsizetype size() const = 0;
|
||||||
[[nodiscard]] virtual void* fromString(const QString& /*string*/) const { return nullptr; }
|
[[nodiscard]] virtual void* fromString(const QString& /*string*/) const { return nullptr; }
|
||||||
[[nodiscard]] virtual QString toString(void* /*slot*/) const { return ""; }
|
[[nodiscard]] virtual QString toString(void* /*slot*/) const { return ""; }
|
||||||
[[nodiscard]] virtual void* createStorage() const { return nullptr; }
|
[[nodiscard]] virtual void* createStorage() const { return nullptr; }
|
||||||
virtual void destroyStorage(void* /*slot*/) const {}
|
virtual void destroyStorage(void* /*slot*/) const {}
|
||||||
|
void* copyStorage(const void* data) const;
|
||||||
|
|
||||||
static const IpcType* ipcType(const QMetaType& metaType);
|
static const IpcType* ipcType(const QMetaType& metaType);
|
||||||
};
|
};
|
||||||
|
@ -43,6 +46,7 @@ public:
|
||||||
[[nodiscard]] QGenericReturnArgument asGenericReturnArgument();
|
[[nodiscard]] QGenericReturnArgument asGenericReturnArgument();
|
||||||
|
|
||||||
void replace(void* value);
|
void replace(void* value);
|
||||||
|
void replace(const QVariant& value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const IpcType* mType = nullptr;
|
const IpcType* mType = nullptr;
|
||||||
|
@ -53,6 +57,7 @@ class VoidIpcType: public IpcType {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] const char* name() const override;
|
[[nodiscard]] const char* name() const override;
|
||||||
[[nodiscard]] const char* genericArgumentName() const override;
|
[[nodiscard]] const char* genericArgumentName() const override;
|
||||||
|
[[nodiscard]] qsizetype size() const override;
|
||||||
|
|
||||||
static const VoidIpcType INSTANCE;
|
static const VoidIpcType INSTANCE;
|
||||||
};
|
};
|
||||||
|
@ -61,6 +66,7 @@ class StringIpcType: public IpcType {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] const char* name() const override;
|
[[nodiscard]] const char* name() const override;
|
||||||
[[nodiscard]] const char* genericArgumentName() const override;
|
[[nodiscard]] const char* genericArgumentName() const override;
|
||||||
|
[[nodiscard]] qsizetype size() const override;
|
||||||
[[nodiscard]] void* fromString(const QString& string) const override;
|
[[nodiscard]] void* fromString(const QString& string) const override;
|
||||||
[[nodiscard]] QString toString(void* slot) const override;
|
[[nodiscard]] QString toString(void* slot) const override;
|
||||||
[[nodiscard]] void* createStorage() const override;
|
[[nodiscard]] void* createStorage() const override;
|
||||||
|
@ -73,6 +79,7 @@ class IntIpcType: public IpcType {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] const char* name() const override;
|
[[nodiscard]] const char* name() const override;
|
||||||
[[nodiscard]] const char* genericArgumentName() const override;
|
[[nodiscard]] const char* genericArgumentName() const override;
|
||||||
|
[[nodiscard]] qsizetype size() const override;
|
||||||
[[nodiscard]] void* fromString(const QString& string) const override;
|
[[nodiscard]] void* fromString(const QString& string) const override;
|
||||||
[[nodiscard]] QString toString(void* slot) const override;
|
[[nodiscard]] QString toString(void* slot) const override;
|
||||||
[[nodiscard]] void* createStorage() const override;
|
[[nodiscard]] void* createStorage() const override;
|
||||||
|
@ -85,6 +92,7 @@ class BoolIpcType: public IpcType {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] const char* name() const override;
|
[[nodiscard]] const char* name() const override;
|
||||||
[[nodiscard]] const char* genericArgumentName() const override;
|
[[nodiscard]] const char* genericArgumentName() const override;
|
||||||
|
[[nodiscard]] qsizetype size() const override;
|
||||||
[[nodiscard]] void* fromString(const QString& string) const override;
|
[[nodiscard]] void* fromString(const QString& string) const override;
|
||||||
[[nodiscard]] QString toString(void* slot) const override;
|
[[nodiscard]] QString toString(void* slot) const override;
|
||||||
[[nodiscard]] void* createStorage() const override;
|
[[nodiscard]] void* createStorage() const override;
|
||||||
|
@ -97,6 +105,7 @@ class DoubleIpcType: public IpcType {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] const char* name() const override;
|
[[nodiscard]] const char* name() const override;
|
||||||
[[nodiscard]] const char* genericArgumentName() const override;
|
[[nodiscard]] const char* genericArgumentName() const override;
|
||||||
|
[[nodiscard]] qsizetype size() const override;
|
||||||
[[nodiscard]] void* fromString(const QString& string) const override;
|
[[nodiscard]] void* fromString(const QString& string) const override;
|
||||||
[[nodiscard]] QString toString(void* slot) const override;
|
[[nodiscard]] QString toString(void* slot) const override;
|
||||||
[[nodiscard]] void* createStorage() const override;
|
[[nodiscard]] void* createStorage() const override;
|
||||||
|
@ -109,6 +118,7 @@ class ColorIpcType: public IpcType {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] const char* name() const override;
|
[[nodiscard]] const char* name() const override;
|
||||||
[[nodiscard]] const char* genericArgumentName() const override;
|
[[nodiscard]] const char* genericArgumentName() const override;
|
||||||
|
[[nodiscard]] qsizetype size() const override;
|
||||||
[[nodiscard]] void* fromString(const QString& string) const override;
|
[[nodiscard]] void* fromString(const QString& string) const override;
|
||||||
[[nodiscard]] QString toString(void* slot) const override;
|
[[nodiscard]] QString toString(void* slot) const override;
|
||||||
[[nodiscard]] void* createStorage() const override;
|
[[nodiscard]] void* createStorage() const override;
|
||||||
|
@ -127,13 +137,23 @@ struct WireFunctionDefinition {
|
||||||
|
|
||||||
DEFINE_SIMPLE_DATASTREAM_OPS(WireFunctionDefinition, data.name, data.returnType, data.arguments);
|
DEFINE_SIMPLE_DATASTREAM_OPS(WireFunctionDefinition, data.name, data.returnType, data.arguments);
|
||||||
|
|
||||||
struct WireTargetDefinition {
|
struct WirePropertyDefinition {
|
||||||
QString name;
|
QString name;
|
||||||
QVector<WireFunctionDefinition> functions;
|
QString type;
|
||||||
|
|
||||||
[[nodiscard]] QString toString() const;
|
[[nodiscard]] QString toString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_SIMPLE_DATASTREAM_OPS(WireTargetDefinition, data.name, data.functions);
|
DEFINE_SIMPLE_DATASTREAM_OPS(WirePropertyDefinition, data.name, data.type);
|
||||||
|
|
||||||
|
struct WireTargetDefinition {
|
||||||
|
QString name;
|
||||||
|
QVector<WireFunctionDefinition> functions;
|
||||||
|
QVector<WirePropertyDefinition> properties;
|
||||||
|
|
||||||
|
[[nodiscard]] QString toString() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_SIMPLE_DATASTREAM_OPS(WireTargetDefinition, data.name, data.functions, data.properties);
|
||||||
|
|
||||||
} // namespace qs::io::ipc
|
} // namespace qs::io::ipc
|
||||||
|
|
|
@ -21,16 +21,17 @@ namespace qs::io::ipc::comm {
|
||||||
|
|
||||||
struct NoCurrentGeneration: std::monostate {};
|
struct NoCurrentGeneration: std::monostate {};
|
||||||
struct TargetNotFound: std::monostate {};
|
struct TargetNotFound: std::monostate {};
|
||||||
struct FunctionNotFound: std::monostate {};
|
struct EntryNotFound: std::monostate {};
|
||||||
|
|
||||||
using QueryResponse = std::variant<
|
using QueryResponse = std::variant<
|
||||||
std::monostate,
|
std::monostate,
|
||||||
NoCurrentGeneration,
|
NoCurrentGeneration,
|
||||||
TargetNotFound,
|
TargetNotFound,
|
||||||
FunctionNotFound,
|
EntryNotFound,
|
||||||
QVector<WireTargetDefinition>,
|
QVector<WireTargetDefinition>,
|
||||||
WireTargetDefinition,
|
WireTargetDefinition,
|
||||||
WireFunctionDefinition>;
|
WireFunctionDefinition,
|
||||||
|
WirePropertyDefinition>;
|
||||||
|
|
||||||
void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const {
|
void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const {
|
||||||
auto resp = conn->responseStream<QueryResponse>();
|
auto resp = conn->responseStream<QueryResponse>();
|
||||||
|
@ -44,16 +45,24 @@ void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const {
|
||||||
auto* handler = registry->findHandler(this->target);
|
auto* handler = registry->findHandler(this->target);
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
if (this->function.isEmpty()) {
|
if (this->name.isEmpty()) {
|
||||||
resp << handler->wireDef();
|
resp << handler->wireDef();
|
||||||
} else {
|
} else {
|
||||||
auto* func = handler->findFunction(this->function);
|
auto* func = handler->findFunction(this->name);
|
||||||
|
|
||||||
if (func) {
|
if (func) {
|
||||||
resp << func->wireDef();
|
resp << func->wireDef();
|
||||||
} else {
|
return;
|
||||||
resp << FunctionNotFound();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* prop = handler->findProperty(this->name);
|
||||||
|
|
||||||
|
if (prop) {
|
||||||
|
resp << prop->wireDef();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp << EntryNotFound();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resp << TargetNotFound();
|
resp << TargetNotFound();
|
||||||
|
@ -64,8 +73,8 @@ void QueryMetadataCommand::exec(qs::ipc::IpcServerConnection* conn) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int queryMetadata(IpcClient* client, const QString& target, const QString& function) {
|
int queryMetadata(IpcClient* client, const QString& target, const QString& name) {
|
||||||
client->sendMessage(IpcCommand(QueryMetadataCommand {.target = target, .function = function}));
|
client->sendMessage(IpcCommand(QueryMetadataCommand {.target = target, .name = name}));
|
||||||
|
|
||||||
QueryResponse slot;
|
QueryResponse slot;
|
||||||
if (!client->waitForResponse(slot)) return -1;
|
if (!client->waitForResponse(slot)) return -1;
|
||||||
|
@ -82,9 +91,11 @@ int queryMetadata(IpcClient* client, const QString& target, const QString& funct
|
||||||
qCInfo(logBare).noquote() << std::get<WireTargetDefinition>(slot).toString();
|
qCInfo(logBare).noquote() << std::get<WireTargetDefinition>(slot).toString();
|
||||||
} else if (std::holds_alternative<WireFunctionDefinition>(slot)) {
|
} else if (std::holds_alternative<WireFunctionDefinition>(slot)) {
|
||||||
qCInfo(logBare).noquote() << std::get<WireFunctionDefinition>(slot).toString();
|
qCInfo(logBare).noquote() << std::get<WireFunctionDefinition>(slot).toString();
|
||||||
|
} else if (std::holds_alternative<WirePropertyDefinition>(slot)) {
|
||||||
|
qCInfo(logBare).noquote() << std::get<WirePropertyDefinition>(slot).toString();
|
||||||
} else if (std::holds_alternative<TargetNotFound>(slot)) {
|
} else if (std::holds_alternative<TargetNotFound>(slot)) {
|
||||||
qCCritical(logBare) << "Target not found.";
|
qCCritical(logBare) << "Target not found.";
|
||||||
} else if (std::holds_alternative<FunctionNotFound>(slot)) {
|
} else if (std::holds_alternative<EntryNotFound>(slot)) {
|
||||||
qCCritical(logBare) << "Function not found.";
|
qCCritical(logBare) << "Function not found.";
|
||||||
} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
|
} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
|
||||||
qCCritical(logBare) << "Not ready to accept queries yet.";
|
qCCritical(logBare) << "Not ready to accept queries yet.";
|
||||||
|
@ -119,7 +130,7 @@ using StringCallResponse = std::variant<
|
||||||
std::monostate,
|
std::monostate,
|
||||||
NoCurrentGeneration,
|
NoCurrentGeneration,
|
||||||
TargetNotFound,
|
TargetNotFound,
|
||||||
FunctionNotFound,
|
EntryNotFound,
|
||||||
ArgParseFailed,
|
ArgParseFailed,
|
||||||
Completed>;
|
Completed>;
|
||||||
|
|
||||||
|
@ -137,7 +148,7 @@ void StringCallCommand::exec(qs::ipc::IpcServerConnection* conn) const {
|
||||||
|
|
||||||
auto* func = handler->findFunction(this->function);
|
auto* func = handler->findFunction(this->function);
|
||||||
if (!func) {
|
if (!func) {
|
||||||
resp << FunctionNotFound();
|
resp << EntryNotFound();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +234,7 @@ int callFunction(
|
||||||
qCCritical(logBare).noquote() << "Function definition:" << error.definition.toString();
|
qCCritical(logBare).noquote() << "Function definition:" << error.definition.toString();
|
||||||
} else if (std::holds_alternative<TargetNotFound>(slot)) {
|
} else if (std::holds_alternative<TargetNotFound>(slot)) {
|
||||||
qCCritical(logBare) << "Target not found.";
|
qCCritical(logBare) << "Target not found.";
|
||||||
} else if (std::holds_alternative<FunctionNotFound>(slot)) {
|
} else if (std::holds_alternative<EntryNotFound>(slot)) {
|
||||||
qCCritical(logBare) << "Function not found.";
|
qCCritical(logBare) << "Function not found.";
|
||||||
} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
|
} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
|
||||||
qCCritical(logBare) << "Not ready to accept queries yet.";
|
qCCritical(logBare) << "Not ready to accept queries yet.";
|
||||||
|
@ -233,4 +244,74 @@ int callFunction(
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PropertyValue {
|
||||||
|
QString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_SIMPLE_DATASTREAM_OPS(PropertyValue, data.value);
|
||||||
|
|
||||||
|
using StringPropReadResponse =
|
||||||
|
std::variant<std::monostate, NoCurrentGeneration, TargetNotFound, EntryNotFound, PropertyValue>;
|
||||||
|
|
||||||
|
void StringPropReadCommand::exec(qs::ipc::IpcServerConnection* conn) const {
|
||||||
|
auto resp = conn->responseStream<StringPropReadResponse>();
|
||||||
|
|
||||||
|
if (auto* generation = EngineGeneration::currentGeneration()) {
|
||||||
|
auto* registry = IpcHandlerRegistry::forGeneration(generation);
|
||||||
|
|
||||||
|
auto* handler = registry->findHandler(this->target);
|
||||||
|
if (!handler) {
|
||||||
|
resp << TargetNotFound();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* prop = handler->findProperty(this->property);
|
||||||
|
if (!prop) {
|
||||||
|
resp << EntryNotFound();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto slot = IpcTypeSlot(prop->type);
|
||||||
|
prop->read(handler, slot);
|
||||||
|
|
||||||
|
resp << PropertyValue {
|
||||||
|
.value = slot.type()->toString(slot.get()),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
conn->respond(StringCallResponse(NoCurrentGeneration()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getProperty(IpcClient* client, const QString& target, const QString& property) {
|
||||||
|
if (target.isEmpty()) {
|
||||||
|
qCCritical(logBare) << "Target required to send message.";
|
||||||
|
return -1;
|
||||||
|
} else if (property.isEmpty()) {
|
||||||
|
qCCritical(logBare) << "Property required to send message.";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->sendMessage(IpcCommand(StringPropReadCommand {.target = target, .property = property}));
|
||||||
|
|
||||||
|
StringPropReadResponse slot;
|
||||||
|
if (!client->waitForResponse(slot)) return -1;
|
||||||
|
|
||||||
|
if (std::holds_alternative<PropertyValue>(slot)) {
|
||||||
|
auto& result = std::get<PropertyValue>(slot);
|
||||||
|
QTextStream(stdout) << result.value << Qt::endl;
|
||||||
|
return 0;
|
||||||
|
} else if (std::holds_alternative<TargetNotFound>(slot)) {
|
||||||
|
qCCritical(logBare) << "Target not found.";
|
||||||
|
} else if (std::holds_alternative<EntryNotFound>(slot)) {
|
||||||
|
qCCritical(logBare) << "Property not found.";
|
||||||
|
} else if (std::holds_alternative<NoCurrentGeneration>(slot)) {
|
||||||
|
qCCritical(logBare) << "Not ready to accept queries yet.";
|
||||||
|
} else {
|
||||||
|
qCCritical(logIpc) << "Received invalid IPC response from" << client;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace qs::io::ipc::comm
|
} // namespace qs::io::ipc::comm
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qflags.h>
|
#include <qflags.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "../ipc/ipc.hpp"
|
#include "../ipc/ipc.hpp"
|
||||||
|
|
||||||
|
@ -9,12 +10,12 @@ namespace qs::io::ipc::comm {
|
||||||
|
|
||||||
struct QueryMetadataCommand {
|
struct QueryMetadataCommand {
|
||||||
QString target;
|
QString target;
|
||||||
QString function;
|
QString name;
|
||||||
|
|
||||||
void exec(qs::ipc::IpcServerConnection* conn) const;
|
void exec(qs::ipc::IpcServerConnection* conn) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_SIMPLE_DATASTREAM_OPS(QueryMetadataCommand, data.target, data.function);
|
DEFINE_SIMPLE_DATASTREAM_OPS(QueryMetadataCommand, data.target, data.name);
|
||||||
|
|
||||||
struct StringCallCommand {
|
struct StringCallCommand {
|
||||||
QString target;
|
QString target;
|
||||||
|
@ -27,7 +28,7 @@ struct StringCallCommand {
|
||||||
DEFINE_SIMPLE_DATASTREAM_OPS(StringCallCommand, data.target, data.function, data.arguments);
|
DEFINE_SIMPLE_DATASTREAM_OPS(StringCallCommand, data.target, data.function, data.arguments);
|
||||||
|
|
||||||
void handleMsg(qs::ipc::IpcServerConnection* conn);
|
void handleMsg(qs::ipc::IpcServerConnection* conn);
|
||||||
int queryMetadata(qs::ipc::IpcClient* client, const QString& target, const QString& function);
|
int queryMetadata(qs::ipc::IpcClient* client, const QString& target, const QString& name);
|
||||||
|
|
||||||
int callFunction(
|
int callFunction(
|
||||||
qs::ipc::IpcClient* client,
|
qs::ipc::IpcClient* client,
|
||||||
|
@ -36,4 +37,15 @@ int callFunction(
|
||||||
const QVector<QString>& arguments
|
const QVector<QString>& arguments
|
||||||
);
|
);
|
||||||
|
|
||||||
|
struct StringPropReadCommand {
|
||||||
|
QString target;
|
||||||
|
QString property;
|
||||||
|
|
||||||
|
void exec(qs::ipc::IpcServerConnection* conn) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_SIMPLE_DATASTREAM_OPS(StringPropReadCommand, data.target, data.property);
|
||||||
|
|
||||||
|
int getProperty(qs::ipc::IpcClient* client, const QString& target, const QString& property);
|
||||||
|
|
||||||
} // namespace qs::io::ipc::comm
|
} // namespace qs::io::ipc::comm
|
||||||
|
|
|
@ -107,6 +107,32 @@ WireFunctionDefinition IpcFunction::wireDef() const {
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IpcProperty::resolve(QString& error) {
|
||||||
|
this->type = IpcType::ipcType(this->property.metaType());
|
||||||
|
|
||||||
|
if (!this->type) {
|
||||||
|
error = QString("Type %1 cannot be used across IPC.").arg(this->property.metaType().name());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IpcProperty::read(QObject* target, IpcTypeSlot& slot) const {
|
||||||
|
slot.replace(this->property.read(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString IpcProperty::toString() const {
|
||||||
|
return QString("property ") % this->property.name() % ": " % this->type->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
WirePropertyDefinition IpcProperty::wireDef() const {
|
||||||
|
WirePropertyDefinition wire;
|
||||||
|
wire.name = this->property.name();
|
||||||
|
wire.type = this->type->name();
|
||||||
|
return wire;
|
||||||
|
}
|
||||||
|
|
||||||
IpcCallStorage::IpcCallStorage(const IpcFunction& function): returnSlot(function.returnType) {
|
IpcCallStorage::IpcCallStorage(const IpcFunction& function): returnSlot(function.returnType) {
|
||||||
for (const auto& arg: function.argumentTypes) {
|
for (const auto& arg: function.argumentTypes) {
|
||||||
this->argumentSlots.emplace_back(arg);
|
this->argumentSlots.emplace_back(arg);
|
||||||
|
@ -153,6 +179,21 @@ void IpcHandler::onPostReload() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto i = smeta.propertyCount(); i != meta->propertyCount(); i++) {
|
||||||
|
const auto& property = meta->property(i);
|
||||||
|
if (!property.isReadable() || !property.hasNotifySignal()) continue;
|
||||||
|
|
||||||
|
auto ipcProp = IpcProperty(property);
|
||||||
|
QString error;
|
||||||
|
|
||||||
|
if (!ipcProp.resolve(error)) {
|
||||||
|
qmlWarning(this).nospace().noquote()
|
||||||
|
<< "Error parsing property \"" << property.name() << "\": " << error;
|
||||||
|
} else {
|
||||||
|
this->propertyMap.insert(property.name(), ipcProp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->complete = true;
|
this->complete = true;
|
||||||
this->updateRegistration();
|
this->updateRegistration();
|
||||||
|
|
||||||
|
@ -270,6 +311,10 @@ WireTargetDefinition IpcHandler::wireDef() const {
|
||||||
wire.functions += func.wireDef();
|
wire.functions += func.wireDef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& prop: this->propertyMap.values()) {
|
||||||
|
wire.properties += prop.wireDef();
|
||||||
|
}
|
||||||
|
|
||||||
return wire;
|
return wire;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,6 +352,13 @@ IpcFunction* IpcHandler::findFunction(const QString& name) {
|
||||||
else return &*itr;
|
else return &*itr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IpcProperty* IpcHandler::findProperty(const QString& name) {
|
||||||
|
auto itr = this->propertyMap.find(name);
|
||||||
|
|
||||||
|
if (itr == this->propertyMap.end()) return nullptr;
|
||||||
|
else return &*itr;
|
||||||
|
}
|
||||||
|
|
||||||
IpcHandler* IpcHandlerRegistry::findHandler(const QString& target) {
|
IpcHandler* IpcHandlerRegistry::findHandler(const QString& target) {
|
||||||
return this->handlers.value(target);
|
return this->handlers.value(target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,14 +53,28 @@ private:
|
||||||
friend class IpcFunction;
|
friend class IpcFunction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IpcProperty {
|
||||||
|
public:
|
||||||
|
explicit IpcProperty(QMetaProperty property): property(property) {}
|
||||||
|
|
||||||
|
bool resolve(QString& error);
|
||||||
|
void read(QObject* target, IpcTypeSlot& slot) const;
|
||||||
|
|
||||||
|
[[nodiscard]] QString toString() const;
|
||||||
|
[[nodiscard]] WirePropertyDefinition wireDef() const;
|
||||||
|
|
||||||
|
QMetaProperty property;
|
||||||
|
const IpcType* type = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
class IpcHandlerRegistry;
|
class IpcHandlerRegistry;
|
||||||
|
|
||||||
///! Handler for IPC message calls.
|
///! Handler for IPC message calls.
|
||||||
/// Each IpcHandler is registered into a per-instance map by its unique @@target.
|
/// Each IpcHandler is registered into a per-instance map by its unique @@target.
|
||||||
/// Functions defined on the IpcHandler can be called by `qs msg`.
|
/// Functions and properties defined on the IpcHandler can be accessed via `qs ipc`.
|
||||||
///
|
///
|
||||||
/// #### Handler Functions
|
/// #### Handler Functions
|
||||||
/// IPC handler functions can be called by `qs msg` as long as they have at most 10
|
/// IPC handler functions can be called by `qs ipc call` as long as they have at most 10
|
||||||
/// arguments, and all argument types along with the return type are listed below.
|
/// arguments, and all argument types along with the return type are listed below.
|
||||||
///
|
///
|
||||||
/// **Argument and return types must be explicitly specified or they will not
|
/// **Argument and return types must be explicitly specified or they will not
|
||||||
|
@ -112,9 +126,9 @@ class IpcHandlerRegistry;
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// The list of registered targets can be inspected using `qs msg -s`.
|
/// The list of registered targets can be inspected using `qs ipc show`.
|
||||||
/// ```sh
|
/// ```sh
|
||||||
/// $ qs msg -s
|
/// $ qs ipc show
|
||||||
/// target rect
|
/// target rect
|
||||||
/// function setColor(color: color): void
|
/// function setColor(color: color): void
|
||||||
/// function getColor(): color
|
/// function getColor(): color
|
||||||
|
@ -124,18 +138,22 @@ class IpcHandlerRegistry;
|
||||||
/// function getRadius(): int
|
/// function getRadius(): int
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// and then invoked using `qs msg`.
|
/// and then invoked using `qs ipc call`.
|
||||||
/// ```sh
|
/// ```sh
|
||||||
/// $ qs msg rect setColor orange
|
/// $ qs ipc call rect setColor orange
|
||||||
/// $ qs msg rect setAngle 40.5
|
/// $ qs ipc call rect setAngle 40.5
|
||||||
/// $ qs msg rect setRadius 30
|
/// $ qs ipc call rect setRadius 30
|
||||||
/// $ qs msg rect getColor
|
/// $ qs ipc call rect getColor
|
||||||
/// #ffffa500
|
/// #ffffa500
|
||||||
/// $ qs msg rect getAngle
|
/// $ qs ipc call rect getAngle
|
||||||
/// 40.5
|
/// 40.5
|
||||||
/// $ qs msg rect getRadius
|
/// $ qs ipc call rect getRadius
|
||||||
/// 30
|
/// 30
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// #### Properties
|
||||||
|
/// Properties of an IpcHanlder can be read using `qs ipc prop get` as long as they are
|
||||||
|
/// of an IPC compatible type. See the table above for compatible types.
|
||||||
class IpcHandler
|
class IpcHandler
|
||||||
: public QObject
|
: public QObject
|
||||||
, public PostReloadHook {
|
, public PostReloadHook {
|
||||||
|
@ -162,12 +180,16 @@ public:
|
||||||
|
|
||||||
QString listMembers(qsizetype indent);
|
QString listMembers(qsizetype indent);
|
||||||
[[nodiscard]] IpcFunction* findFunction(const QString& name);
|
[[nodiscard]] IpcFunction* findFunction(const QString& name);
|
||||||
|
[[nodiscard]] IpcProperty* findProperty(const QString& name);
|
||||||
[[nodiscard]] WireTargetDefinition wireDef() const;
|
[[nodiscard]] WireTargetDefinition wireDef() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void enabledChanged();
|
void enabledChanged();
|
||||||
void targetChanged();
|
void targetChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
//void handleIpcPropertyChange();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateRegistration(bool destroying = false);
|
void updateRegistration(bool destroying = false);
|
||||||
|
|
||||||
|
@ -183,6 +205,7 @@ private:
|
||||||
bool complete = false;
|
bool complete = false;
|
||||||
|
|
||||||
QHash<QString, IpcFunction> functionMap;
|
QHash<QString, IpcFunction> functionMap;
|
||||||
|
QHash<QString, IpcProperty> propertyMap;
|
||||||
|
|
||||||
friend class IpcHandlerRegistry;
|
friend class IpcHandlerRegistry;
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ using IpcCommand = std::variant<
|
||||||
std::monostate,
|
std::monostate,
|
||||||
IpcKillCommand,
|
IpcKillCommand,
|
||||||
qs::io::ipc::comm::QueryMetadataCommand,
|
qs::io::ipc::comm::QueryMetadataCommand,
|
||||||
qs::io::ipc::comm::StringCallCommand>;
|
qs::io::ipc::comm::StringCallCommand,
|
||||||
|
qs::io::ipc::comm::StringPropReadCommand>;
|
||||||
|
|
||||||
} // namespace qs::ipc
|
} // namespace qs::ipc
|
||||||
|
|
|
@ -102,9 +102,10 @@ int locateConfigFile(CommandState& cmd, QString& path) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sortInstances(QVector<InstanceLockInfo>& list) {
|
void sortInstances(QVector<InstanceLockInfo>& list, bool newestFirst) {
|
||||||
std::ranges::sort(list, [](const InstanceLockInfo& a, const InstanceLockInfo& b) {
|
std::ranges::sort(list, [=](const InstanceLockInfo& a, const InstanceLockInfo& b) {
|
||||||
return a.instance.launchTime < b.instance.launchTime;
|
auto r = a.instance.launchTime < b.instance.launchTime;
|
||||||
|
return newestFirst ? !r : r;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -153,7 +154,7 @@ int selectInstance(CommandState& cmd, InstanceLockInfo* instance) {
|
||||||
path = QDir(basePath->filePath("by-path")).filePath(pathId);
|
path = QDir(basePath->filePath("by-path")).filePath(pathId);
|
||||||
|
|
||||||
auto instances = QsPaths::collectInstances(path);
|
auto instances = QsPaths::collectInstances(path);
|
||||||
sortInstances(instances);
|
sortInstances(instances, cmd.config.newest);
|
||||||
|
|
||||||
if (instances.isEmpty()) {
|
if (instances.isEmpty()) {
|
||||||
qCInfo(logBare) << "No running instances for" << configFilePath;
|
qCInfo(logBare) << "No running instances for" << configFilePath;
|
||||||
|
@ -227,7 +228,7 @@ int listInstances(CommandState& cmd) {
|
||||||
qCInfo(logBare) << "Use --all to list all instances.";
|
qCInfo(logBare) << "Use --all to list all instances.";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sortInstances(instances);
|
sortInstances(instances, cmd.config.newest);
|
||||||
|
|
||||||
if (cmd.output.json) {
|
if (cmd.output.json) {
|
||||||
auto array = QJsonArray();
|
auto array = QJsonArray();
|
||||||
|
@ -284,26 +285,23 @@ int killInstances(CommandState& cmd) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int msgInstance(CommandState& cmd) {
|
int ipcCommand(CommandState& cmd) {
|
||||||
InstanceLockInfo instance;
|
InstanceLockInfo instance;
|
||||||
auto r = selectInstance(cmd, &instance);
|
auto r = selectInstance(cmd, &instance);
|
||||||
if (r != 0) return r;
|
if (r != 0) return r;
|
||||||
|
|
||||||
return IpcClient::connect(instance.instance.instanceId, [&](IpcClient& client) {
|
return IpcClient::connect(instance.instance.instanceId, [&](IpcClient& client) {
|
||||||
if (cmd.ipc.info) {
|
if (*cmd.ipc.show || cmd.ipc.showOld) {
|
||||||
return qs::io::ipc::comm::queryMetadata(&client, *cmd.ipc.target, *cmd.ipc.function);
|
return qs::io::ipc::comm::queryMetadata(&client, *cmd.ipc.target, *cmd.ipc.name);
|
||||||
|
} else if (*cmd.ipc.getprop) {
|
||||||
|
return qs::io::ipc::comm::getProperty(&client, *cmd.ipc.target, *cmd.ipc.name);
|
||||||
} else {
|
} else {
|
||||||
QVector<QString> arguments;
|
QVector<QString> arguments;
|
||||||
for (auto& arg: cmd.ipc.arguments) {
|
for (auto& arg: cmd.ipc.arguments) {
|
||||||
arguments += *arg;
|
arguments += *arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
return qs::io::ipc::comm::callFunction(
|
return qs::io::ipc::comm::callFunction(&client, *cmd.ipc.target, *cmd.ipc.name, arguments);
|
||||||
&client,
|
|
||||||
*cmd.ipc.target,
|
|
||||||
*cmd.ipc.function,
|
|
||||||
arguments
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -422,8 +420,8 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
|
||||||
return listInstances(state);
|
return listInstances(state);
|
||||||
} else if (*state.subcommand.kill) {
|
} else if (*state.subcommand.kill) {
|
||||||
return killInstances(state);
|
return killInstances(state);
|
||||||
} else if (*state.subcommand.msg) {
|
} else if (*state.subcommand.msg || *state.ipc.ipc) {
|
||||||
return msgInstance(state);
|
return ipcCommand(state);
|
||||||
} else {
|
} else {
|
||||||
if (strcmp(qVersion(), QT_VERSION_STR) != 0) {
|
if (strcmp(qVersion(), QT_VERSION_STR) != 0) {
|
||||||
qWarning() << "\033[31mQuickshell was built against Qt" << QT_VERSION_STR
|
qWarning() << "\033[31mQuickshell was built against Qt" << QT_VERSION_STR
|
||||||
|
|
|
@ -49,6 +49,7 @@ struct CommandState {
|
||||||
QStringOption path;
|
QStringOption path;
|
||||||
QStringOption manifest;
|
QStringOption manifest;
|
||||||
QStringOption name;
|
QStringOption name;
|
||||||
|
bool newest = false;
|
||||||
} config;
|
} config;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -67,9 +68,13 @@ struct CommandState {
|
||||||
} output;
|
} output;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool info = false;
|
CLI::App* ipc = nullptr;
|
||||||
|
CLI::App* show = nullptr;
|
||||||
|
CLI::App* call = nullptr;
|
||||||
|
CLI::App* getprop = nullptr;
|
||||||
|
bool showOld = false;
|
||||||
QStringOption target;
|
QStringOption target;
|
||||||
QStringOption function;
|
QStringOption name;
|
||||||
std::vector<QStringOption> arguments;
|
std::vector<QStringOption> arguments;
|
||||||
} ipc;
|
} ipc;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ int parseCommand(int argc, char** argv, CommandState& state) {
|
||||||
.argv = argv,
|
.argv = argv,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto addConfigSelection = [&](CLI::App* cmd) {
|
auto addConfigSelection = [&](CLI::App* cmd, bool withNewestOption = false) {
|
||||||
auto* group = cmd->add_option_group("Config Selection")
|
auto* group = cmd->add_option_group("Config Selection")
|
||||||
->description("If no options in this group are specified,\n"
|
->description("If no options in this group are specified,\n"
|
||||||
"$XDG_CONFIG_HOME/quickshell/shell.qml will be used.");
|
"$XDG_CONFIG_HOME/quickshell/shell.qml will be used.");
|
||||||
|
@ -37,6 +37,11 @@ int parseCommand(int argc, char** argv, CommandState& state) {
|
||||||
"otherwise it is the name of a folder in $XDG_CONFIG_HOME/quickshell.")
|
"otherwise it is the name of a folder in $XDG_CONFIG_HOME/quickshell.")
|
||||||
->envname("QS_CONFIG_NAME");
|
->envname("QS_CONFIG_NAME");
|
||||||
|
|
||||||
|
if (withNewestOption) {
|
||||||
|
group->add_flag("-n,--newest", state.config.newest)
|
||||||
|
->description("Operate on the most recently launched instance instead of the oldest");
|
||||||
|
}
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,7 +135,7 @@ int parseCommand(int argc, char** argv, CommandState& state) {
|
||||||
->description("Rules to apply to the log being read, in the format of QT_LOGGING_RULES.");
|
->description("Rules to apply to the log being read, in the format of QT_LOGGING_RULES.");
|
||||||
|
|
||||||
auto* instance = addInstanceSelection(sub)->excludes(file);
|
auto* instance = addInstanceSelection(sub)->excludes(file);
|
||||||
addConfigSelection(sub)->excludes(instance)->excludes(file);
|
addConfigSelection(sub, true)->excludes(instance)->excludes(file);
|
||||||
addLoggingOptions(sub, false);
|
addLoggingOptions(sub, false);
|
||||||
|
|
||||||
state.subcommand.log = sub;
|
state.subcommand.log = sub;
|
||||||
|
@ -146,7 +151,7 @@ int parseCommand(int argc, char** argv, CommandState& state) {
|
||||||
|
|
||||||
sub->add_flag("-j,--json", state.output.json, "Output the list as a json.");
|
sub->add_flag("-j,--json", state.output.json, "Output the list as a json.");
|
||||||
|
|
||||||
addConfigSelection(sub)->excludes(all);
|
addConfigSelection(sub, true)->excludes(all);
|
||||||
addLoggingOptions(sub, false, true);
|
addLoggingOptions(sub, false, true);
|
||||||
|
|
||||||
state.subcommand.list = sub;
|
state.subcommand.list = sub;
|
||||||
|
@ -156,36 +161,71 @@ int parseCommand(int argc, char** argv, CommandState& state) {
|
||||||
auto* sub = cli->add_subcommand("kill", "Kill quickshell instances.");
|
auto* sub = cli->add_subcommand("kill", "Kill quickshell instances.");
|
||||||
//sub->add_flag("-a,--all", "Kill all matching instances instead of just one.");
|
//sub->add_flag("-a,--all", "Kill all matching instances instead of just one.");
|
||||||
auto* instance = addInstanceSelection(sub);
|
auto* instance = addInstanceSelection(sub);
|
||||||
addConfigSelection(sub)->excludes(instance);
|
addConfigSelection(sub, true)->excludes(instance);
|
||||||
addLoggingOptions(sub, false, true);
|
addLoggingOptions(sub, false, true);
|
||||||
|
|
||||||
state.subcommand.kill = sub;
|
state.subcommand.kill = sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
auto* sub = cli->add_subcommand("msg", "Send messages to IpcHandlers.")->require_option();
|
auto* sub = cli->add_subcommand("ipc", "Communicate with other Quickshell instances.")
|
||||||
|
->require_subcommand();
|
||||||
auto* target = sub->add_option("target", state.ipc.target, "The target to message.");
|
state.ipc.ipc = sub;
|
||||||
|
|
||||||
auto* function = sub->add_option("function", state.ipc.function)
|
|
||||||
->description("The function to call in the target.")
|
|
||||||
->needs(target);
|
|
||||||
|
|
||||||
auto* arguments = sub->add_option("arguments", state.ipc.arguments)
|
|
||||||
->description("Arguments to the called function.")
|
|
||||||
->needs(function)
|
|
||||||
->allow_extra_args();
|
|
||||||
|
|
||||||
sub->add_flag("-s,--show", state.ipc.info)
|
|
||||||
->description("Print information about a function or target if given, or all available "
|
|
||||||
"targets if not.")
|
|
||||||
->excludes(arguments);
|
|
||||||
|
|
||||||
auto* instance = addInstanceSelection(sub);
|
auto* instance = addInstanceSelection(sub);
|
||||||
addConfigSelection(sub)->excludes(instance);
|
addConfigSelection(sub, true)->excludes(instance);
|
||||||
addLoggingOptions(sub, false, true);
|
addLoggingOptions(sub, false, true);
|
||||||
|
|
||||||
sub->require_option();
|
{
|
||||||
|
auto* show = sub->add_subcommand("show", "Print information about available IPC targets.");
|
||||||
|
state.ipc.show = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* call = sub->add_subcommand("call", "Call an IpcHandler function.");
|
||||||
|
state.ipc.call = call;
|
||||||
|
|
||||||
|
call->add_option("target", state.ipc.target, "The target to message.");
|
||||||
|
|
||||||
|
call->add_option("function", state.ipc.name)
|
||||||
|
->description("The function to call in the target.");
|
||||||
|
|
||||||
|
call->add_option("arguments", state.ipc.arguments)
|
||||||
|
->description("Arguments to the called function.")
|
||||||
|
->allow_extra_args();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* prop =
|
||||||
|
sub->add_subcommand("prop", "Manipulate IpcHandler properties.")->require_subcommand();
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* get = prop->add_subcommand("get", "Read the value of a property.");
|
||||||
|
state.ipc.getprop = get;
|
||||||
|
get->add_option("target", state.ipc.target, "The target to read the property of.");
|
||||||
|
get->add_option("property", state.ipc.name)->description("The property to read.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* sub = cli->add_subcommand("msg", "[DEPRECATED] Moved to `ipc call`.")->require_option();
|
||||||
|
|
||||||
|
sub->add_option("target", state.ipc.target, "The target to message.");
|
||||||
|
|
||||||
|
sub->add_option("function", state.ipc.name)->description("The function to call in the target.");
|
||||||
|
|
||||||
|
sub->add_option("arguments", state.ipc.arguments)
|
||||||
|
->description("Arguments to the called function.")
|
||||||
|
->allow_extra_args();
|
||||||
|
|
||||||
|
sub->add_flag("-s,--show", state.ipc.showOld)
|
||||||
|
->description("Print information about a function or target if given, or all available "
|
||||||
|
"targets if not.");
|
||||||
|
|
||||||
|
auto* instance = addInstanceSelection(sub);
|
||||||
|
addConfigSelection(sub, true)->excludes(instance);
|
||||||
|
addLoggingOptions(sub, false, true);
|
||||||
|
|
||||||
state.subcommand.msg = sub;
|
state.subcommand.msg = sub;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <qdbusargument.h>
|
#include <qdbusargument.h>
|
||||||
#include <qimage.h>
|
#include <qimage.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
@ -23,14 +21,22 @@ struct DBusNotificationImage {
|
||||||
const QDBusArgument& operator>>(const QDBusArgument& argument, DBusNotificationImage& pixmap);
|
const QDBusArgument& operator>>(const QDBusArgument& argument, DBusNotificationImage& pixmap);
|
||||||
const QDBusArgument& operator<<(QDBusArgument& argument, const DBusNotificationImage& pixmap);
|
const QDBusArgument& operator<<(QDBusArgument& argument, const DBusNotificationImage& pixmap);
|
||||||
|
|
||||||
class NotificationImage: public QsImageHandle {
|
class NotificationImage: public QsIndexedImageHandle {
|
||||||
public:
|
public:
|
||||||
explicit NotificationImage(DBusNotificationImage image, QObject* parent)
|
explicit NotificationImage(): QsIndexedImageHandle(QQuickAsyncImageProvider::Image) {}
|
||||||
: QsImageHandle(QQuickAsyncImageProvider::Image, parent)
|
|
||||||
, image(std::move(image)) {}
|
[[nodiscard]] bool hasData() const { return !this->image.data.isEmpty(); }
|
||||||
|
void clear() { this->image.data.clear(); }
|
||||||
|
|
||||||
|
[[nodiscard]] DBusNotificationImage& writeImage() {
|
||||||
|
this->imageChanged();
|
||||||
|
return this->image;
|
||||||
|
}
|
||||||
|
|
||||||
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override;
|
||||||
|
|
||||||
|
private:
|
||||||
DBusNotificationImage image;
|
DBusNotificationImage image;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace qs::service::notifications
|
} // namespace qs::service::notifications
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#include "notification.hpp"
|
#include "notification.hpp"
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qdbusargument.h>
|
#include <qdbusargument.h>
|
||||||
|
@ -117,13 +116,12 @@ void Notification::updateProperties(
|
||||||
|
|
||||||
QString imagePath;
|
QString imagePath;
|
||||||
|
|
||||||
if (!imageDataName.isEmpty()) {
|
if (imageDataName.isEmpty()) {
|
||||||
|
this->mImagePixmap.clear();
|
||||||
|
} else {
|
||||||
auto value = hints.value(imageDataName).value<QDBusArgument>();
|
auto value = hints.value(imageDataName).value<QDBusArgument>();
|
||||||
DBusNotificationImage image;
|
value >> this->mImagePixmap.writeImage();
|
||||||
value >> image;
|
imagePath = this->mImagePixmap.url();
|
||||||
if (this->mImagePixmap) this->mImagePixmap->deleteLater();
|
|
||||||
this->mImagePixmap = new NotificationImage(std::move(image), this);
|
|
||||||
imagePath = this->mImagePixmap->url();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't store giant byte arrays longer than necessary
|
// don't store giant byte arrays longer than necessary
|
||||||
|
@ -131,7 +129,7 @@ void Notification::updateProperties(
|
||||||
hints.remove("image_data");
|
hints.remove("image_data");
|
||||||
hints.remove("icon_data");
|
hints.remove("icon_data");
|
||||||
|
|
||||||
if (!this->mImagePixmap) {
|
if (!this->mImagePixmap.hasData()) {
|
||||||
QString imagePathName;
|
QString imagePathName;
|
||||||
if (hints.contains("image-path")) imagePathName = "image-path";
|
if (hints.contains("image-path")) imagePathName = "image-path";
|
||||||
else if (hints.contains("image_path")) imagePathName = "image_path";
|
else if (hints.contains("image_path")) imagePathName = "image_path";
|
||||||
|
|
|
@ -12,11 +12,10 @@
|
||||||
|
|
||||||
#include "../../core/retainable.hpp"
|
#include "../../core/retainable.hpp"
|
||||||
#include "../../core/util.hpp"
|
#include "../../core/util.hpp"
|
||||||
|
#include "dbusimage.hpp"
|
||||||
|
|
||||||
namespace qs::service::notifications {
|
namespace qs::service::notifications {
|
||||||
|
|
||||||
class NotificationImage;
|
|
||||||
|
|
||||||
///! The urgency level of a Notification.
|
///! The urgency level of a Notification.
|
||||||
/// See @@Notification.urgency.
|
/// See @@Notification.urgency.
|
||||||
class NotificationUrgency: public QObject {
|
class NotificationUrgency: public QObject {
|
||||||
|
@ -187,7 +186,7 @@ private:
|
||||||
quint32 mId;
|
quint32 mId;
|
||||||
NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed;
|
NotificationCloseReason::Enum mCloseReason = NotificationCloseReason::Dismissed;
|
||||||
bool mLastGeneration = false;
|
bool mLastGeneration = false;
|
||||||
NotificationImage* mImagePixmap = nullptr;
|
NotificationImage mImagePixmap;
|
||||||
QList<NotificationAction*> mActions;
|
QList<NotificationAction*> mActions;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
|
@ -282,7 +282,7 @@ void StatusNotifierItem::onGetAllFailed() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayImageHandle::TrayImageHandle(StatusNotifierItem* item)
|
TrayImageHandle::TrayImageHandle(StatusNotifierItem* item)
|
||||||
: QsImageHandle(QQmlImageProviderBase::Pixmap, item)
|
: QsImageHandle(QQmlImageProviderBase::Pixmap)
|
||||||
, item(item) {}
|
, item(item) {}
|
||||||
|
|
||||||
QPixmap
|
QPixmap
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
This protocol exposes hyprland-specific wl_surface properties.
|
This protocol exposes hyprland-specific wl_surface properties.
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<interface name="hyprland_surface_manager_v1" version="1">
|
<interface name="hyprland_surface_manager_v1" version="2">
|
||||||
<description summary="manager for hyprland surface objects">
|
<description summary="manager for hyprland surface objects">
|
||||||
This interface allows a client to create hyprland surface objects.
|
This interface allows a client to create hyprland surface objects.
|
||||||
</description>
|
</description>
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
</enum>
|
</enum>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
||||||
<interface name="hyprland_surface_v1" version="1">
|
<interface name="hyprland_surface_v1" version="2">
|
||||||
<description summary="hyprland-specific wl_surface properties">
|
<description summary="hyprland-specific wl_surface properties">
|
||||||
This interface allows access to hyprland-specific properties of a wl_surface.
|
This interface allows access to hyprland-specific properties of a wl_surface.
|
||||||
|
|
||||||
|
@ -96,5 +96,31 @@
|
||||||
<entry name="no_surface" value="0" summary="wl_surface was destroyed"/>
|
<entry name="no_surface" value="0" summary="wl_surface was destroyed"/>
|
||||||
<entry name="out_of_range" value="1" summary="given opacity was not in the range 0.0 - 1.0 (inclusive)"/>
|
<entry name="out_of_range" value="1" summary="given opacity was not in the range 0.0 - 1.0 (inclusive)"/>
|
||||||
</enum>
|
</enum>
|
||||||
|
|
||||||
|
<request name="set_visible_region" since="2">
|
||||||
|
<description summary="set the visible region of the surface">
|
||||||
|
This request sets the region of the surface that contains visible content.
|
||||||
|
Visible content refers to content that has an alpha value greater than zero.
|
||||||
|
|
||||||
|
The visible region is an optimization hint for the compositor that lets it
|
||||||
|
avoid drawing parts of the surface that are not visible. Setting a visible region
|
||||||
|
that does not contain all content in the surface may result in missing content
|
||||||
|
not being drawn.
|
||||||
|
|
||||||
|
The visible region is specified in buffer-local coordinates.
|
||||||
|
|
||||||
|
The compositor ignores the parts of the visible region that fall outside of the surface.
|
||||||
|
When all parts of the region fall outside of the buffer geometry, the compositor may
|
||||||
|
avoid rendering the surface entirely.
|
||||||
|
|
||||||
|
The initial value for the visible region is empty. Setting the
|
||||||
|
visible region has copy semantics, and the wl_region object can be destroyed immediately.
|
||||||
|
A NULL wl_region causes the visible region to be set to empty.
|
||||||
|
|
||||||
|
Does not take effect until wl_surface.commit is called.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<arg name="region" type="object" interface="wl_region" allow-null="true"/>
|
||||||
|
</request>
|
||||||
</interface>
|
</interface>
|
||||||
</protocol>
|
</protocol>
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
|
|
||||||
namespace qs::hyprland::surface::impl {
|
namespace qs::hyprland::surface::impl {
|
||||||
|
|
||||||
HyprlandSurfaceManager::HyprlandSurfaceManager(): QWaylandClientExtensionTemplate(1) {
|
HyprlandSurfaceManager::HyprlandSurfaceManager(): QWaylandClientExtensionTemplate(2) {
|
||||||
this->initialize();
|
this->initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprlandSurface*
|
HyprlandSurface*
|
||||||
HyprlandSurfaceManager::createHyprlandExtension(QtWaylandClient::QWaylandWindow* surface) {
|
HyprlandSurfaceManager::createHyprlandExtension(QtWaylandClient::QWaylandWindow* surface) {
|
||||||
return new HyprlandSurface(this->get_hyprland_surface(surface->surface()));
|
return new HyprlandSurface(this->get_hyprland_surface(surface->surface()), surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprlandSurfaceManager* HyprlandSurfaceManager::instance() {
|
HyprlandSurfaceManager* HyprlandSurfaceManager::instance() {
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
#include "qml.hpp"
|
#include "qml.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <private/qhighdpiscaling_p.h>
|
||||||
#include <private/qwaylandwindow_p.h>
|
#include <private/qwaylandwindow_p.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlinfo.h>
|
#include <qqmlinfo.h>
|
||||||
|
#include <qregion.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
#include <qvariant.h>
|
||||||
#include <qwindow.h>
|
#include <qwindow.h>
|
||||||
|
|
||||||
|
#include "../../../core/region.hpp"
|
||||||
#include "../../../window/proxywindow.hpp"
|
#include "../../../window/proxywindow.hpp"
|
||||||
#include "../../../window/windowinterface.hpp"
|
#include "../../../window/windowinterface.hpp"
|
||||||
#include "../../util.hpp"
|
|
||||||
#include "manager.hpp"
|
#include "manager.hpp"
|
||||||
#include "surface.hpp"
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
@ -40,6 +43,15 @@ HyprlandWindow::HyprlandWindow(ProxyWindowBase* window): QObject(nullptr), proxy
|
||||||
&HyprlandWindow::onWindowConnected
|
&HyprlandWindow::onWindowConnected
|
||||||
);
|
);
|
||||||
|
|
||||||
|
QObject::connect(window, &ProxyWindowBase::polished, this, &HyprlandWindow::onWindowPolished);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
window,
|
||||||
|
&ProxyWindowBase::devicePixelRatioChanged,
|
||||||
|
this,
|
||||||
|
&HyprlandWindow::updateVisibleMask
|
||||||
|
);
|
||||||
|
|
||||||
QObject::connect(window, &QObject::destroyed, this, &HyprlandWindow::onProxyWindowDestroyed);
|
QObject::connect(window, &QObject::destroyed, this, &HyprlandWindow::onProxyWindowDestroyed);
|
||||||
|
|
||||||
if (window->backingWindow()) {
|
if (window->backingWindow()) {
|
||||||
|
@ -60,14 +72,76 @@ void HyprlandWindow::setOpacity(qreal opacity) {
|
||||||
|
|
||||||
this->mOpacity = opacity;
|
this->mOpacity = opacity;
|
||||||
|
|
||||||
if (this->surface) {
|
if (this->surface && this->proxyWindow) {
|
||||||
this->surface->setOpacity(opacity);
|
this->pendingPolish.opacity = true;
|
||||||
qs::wayland::util::scheduleCommit(this->proxyWindow);
|
this->proxyWindow->schedulePolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit this->opacityChanged();
|
emit this->opacityChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PendingRegion* HyprlandWindow::visibleMask() const { return this->mVisibleMask; }
|
||||||
|
|
||||||
|
void HyprlandWindow::setVisibleMask(PendingRegion* mask) {
|
||||||
|
if (mask == this->mVisibleMask) return;
|
||||||
|
|
||||||
|
if (this->mVisibleMask) {
|
||||||
|
QObject::disconnect(this->mVisibleMask, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mVisibleMask = mask;
|
||||||
|
|
||||||
|
if (mask) {
|
||||||
|
QObject::connect(mask, &QObject::destroyed, this, &HyprlandWindow::onVisibleMaskDestroyed);
|
||||||
|
QObject::connect(mask, &PendingRegion::changed, this, &HyprlandWindow::updateVisibleMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->updateVisibleMask();
|
||||||
|
emit this->visibleMaskChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandWindow::onVisibleMaskDestroyed() {
|
||||||
|
this->mVisibleMask = nullptr;
|
||||||
|
this->updateVisibleMask();
|
||||||
|
emit this->visibleMaskChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandWindow::updateVisibleMask() {
|
||||||
|
if (!this->surface || !this->proxyWindow) return;
|
||||||
|
|
||||||
|
this->pendingPolish.visibleMask = true;
|
||||||
|
this->proxyWindow->schedulePolish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HyprlandWindow::onWindowPolished() {
|
||||||
|
if (!this->surface) return;
|
||||||
|
|
||||||
|
if (this->pendingPolish.opacity) {
|
||||||
|
this->surface->setOpacity(this->mOpacity);
|
||||||
|
this->pendingPolish.opacity = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->pendingPolish.visibleMask) {
|
||||||
|
QRegion mask;
|
||||||
|
if (this->mVisibleMask != nullptr) {
|
||||||
|
mask =
|
||||||
|
this->mVisibleMask->applyTo(QRect(0, 0, this->mWindow->width(), this->mWindow->height()));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dpr = this->proxyWindow->devicePixelRatio();
|
||||||
|
if (dpr != 1.0) {
|
||||||
|
mask = QHighDpi::scale(mask, dpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask.isEmpty() && this->mVisibleMask) {
|
||||||
|
mask = QRect(-1, -1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->surface->setVisibleRegion(mask);
|
||||||
|
this->pendingPolish.visibleMask = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void HyprlandWindow::onWindowConnected() {
|
void HyprlandWindow::onWindowConnected() {
|
||||||
this->mWindow = this->proxyWindow->backingWindow();
|
this->mWindow = this->proxyWindow->backingWindow();
|
||||||
// disconnected by destructor
|
// disconnected by destructor
|
||||||
|
@ -86,11 +160,24 @@ void HyprlandWindow::onWindowVisibleChanged() {
|
||||||
if (!this->mWindow->handle()) {
|
if (!this->mWindow->handle()) {
|
||||||
this->mWindow->create();
|
this->mWindow->create();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this->mWaylandWindow = dynamic_cast<QWaylandWindow*>(this->mWindow->handle());
|
auto* window = dynamic_cast<QWaylandWindow*>(this->mWindow->handle());
|
||||||
|
if (window == this->mWaylandWindow) return;
|
||||||
|
|
||||||
if (this->mWaylandWindow) {
|
if (this->mWaylandWindow) {
|
||||||
// disconnected by destructor
|
QObject::disconnect(this->mWaylandWindow, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mWaylandWindow = window;
|
||||||
|
if (!window) return;
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->mWaylandWindow,
|
||||||
|
&QObject::destroyed,
|
||||||
|
this,
|
||||||
|
&HyprlandWindow::onWaylandWindowDestroyed
|
||||||
|
);
|
||||||
|
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
this->mWaylandWindow,
|
this->mWaylandWindow,
|
||||||
|
@ -109,10 +196,10 @@ void HyprlandWindow::onWindowVisibleChanged() {
|
||||||
if (this->mWaylandWindow->surface()) {
|
if (this->mWaylandWindow->surface()) {
|
||||||
this->onWaylandSurfaceCreated();
|
this->onWaylandSurfaceCreated();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HyprlandWindow::onWaylandWindowDestroyed() { this->mWaylandWindow = nullptr; }
|
||||||
|
|
||||||
void HyprlandWindow::onWaylandSurfaceCreated() {
|
void HyprlandWindow::onWaylandSurfaceCreated() {
|
||||||
auto* manager = impl::HyprlandSurfaceManager::instance();
|
auto* manager = impl::HyprlandSurfaceManager::instance();
|
||||||
|
|
||||||
|
@ -122,12 +209,26 @@ void HyprlandWindow::onWaylandSurfaceCreated() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto v = this->mWaylandWindow->property("hyprland_window_ext");
|
||||||
|
if (v.canConvert<HyprlandWindow*>()) {
|
||||||
|
auto* windowExt = v.value<HyprlandWindow*>();
|
||||||
|
if (windowExt != this && windowExt->surface) {
|
||||||
|
this->surface.swap(windowExt->surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->surface) {
|
||||||
auto* ext = manager->createHyprlandExtension(this->mWaylandWindow);
|
auto* ext = manager->createHyprlandExtension(this->mWaylandWindow);
|
||||||
this->surface = std::unique_ptr<impl::HyprlandSurface>(ext);
|
this->surface = std::unique_ptr<impl::HyprlandSurface>(ext);
|
||||||
|
}
|
||||||
|
|
||||||
if (this->mOpacity != 1.0) {
|
this->mWaylandWindow->setProperty("hyprland_window_ext", QVariant::fromValue(this));
|
||||||
this->surface->setOpacity(this->mOpacity);
|
|
||||||
qs::wayland::util::scheduleCommit(this->proxyWindow);
|
this->pendingPolish.opacity = this->mOpacity != 1.0;
|
||||||
|
this->pendingPolish.visibleMask = this->mVisibleMask;
|
||||||
|
|
||||||
|
if (this->pendingPolish.opacity || this->pendingPolish.visibleMask) {
|
||||||
|
this->proxyWindow->schedulePolish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +245,9 @@ void HyprlandWindow::onProxyWindowDestroyed() {
|
||||||
// Deleting it when the proxy window is deleted will cause a full opacity frame between the destruction of the
|
// Deleting it when the proxy window is deleted will cause a full opacity frame between the destruction of the
|
||||||
// hyprland_surface_v1 and wl_surface objects.
|
// hyprland_surface_v1 and wl_surface objects.
|
||||||
|
|
||||||
if (this->surface == nullptr) {
|
|
||||||
this->proxyWindow = nullptr;
|
this->proxyWindow = nullptr;
|
||||||
|
|
||||||
|
if (this->surface == nullptr) {
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
#include <qwindow.h>
|
#include <qwindow.h>
|
||||||
|
|
||||||
|
#include "../../../core/region.hpp"
|
||||||
#include "../../../window/proxywindow.hpp"
|
#include "../../../window/proxywindow.hpp"
|
||||||
#include "surface.hpp"
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
@ -31,11 +32,18 @@ namespace qs::hyprland::surface {
|
||||||
/// [hyprland-surface-v1]: https://github.com/hyprwm/hyprland-protocols/blob/main/protocols/hyprland-surface-v1.xml
|
/// [hyprland-surface-v1]: https://github.com/hyprwm/hyprland-protocols/blob/main/protocols/hyprland-surface-v1.xml
|
||||||
class HyprlandWindow: public QObject {
|
class HyprlandWindow: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
// clang-format off
|
||||||
/// A multiplier for the window's overall opacity, ranging from 1.0 to 0.0. Overall opacity includes the opacity of
|
/// A multiplier for the window's overall opacity, ranging from 1.0 to 0.0. Overall opacity includes the opacity of
|
||||||
/// both the window content *and* visual effects such as blur that apply to it.
|
/// both the window content *and* visual effects such as blur that apply to it.
|
||||||
///
|
///
|
||||||
/// Default: 1.0
|
/// Default: 1.0
|
||||||
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged);
|
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged);
|
||||||
|
/// A hint to the compositor that only certain regions of the surface should be rendered.
|
||||||
|
/// This can be used to avoid rendering large empty regions of a window which can increase
|
||||||
|
/// performance, especially if the window is blurred. The mask should include all pixels
|
||||||
|
/// of the window that do not have an alpha value of 0.
|
||||||
|
Q_PROPERTY(PendingRegion* visibleMask READ visibleMask WRITE setVisibleMask NOTIFY visibleMaskChanged);
|
||||||
|
// clang-format on
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
QML_UNCREATABLE("HyprlandWindow can only be used as an attached object.");
|
QML_UNCREATABLE("HyprlandWindow can only be used as an attached object.");
|
||||||
QML_ATTACHED(HyprlandWindow);
|
QML_ATTACHED(HyprlandWindow);
|
||||||
|
@ -48,17 +56,25 @@ public:
|
||||||
[[nodiscard]] qreal opacity() const;
|
[[nodiscard]] qreal opacity() const;
|
||||||
void setOpacity(qreal opacity);
|
void setOpacity(qreal opacity);
|
||||||
|
|
||||||
|
[[nodiscard]] PendingRegion* visibleMask() const;
|
||||||
|
virtual void setVisibleMask(PendingRegion* mask);
|
||||||
|
|
||||||
static HyprlandWindow* qmlAttachedProperties(QObject* object);
|
static HyprlandWindow* qmlAttachedProperties(QObject* object);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void opacityChanged();
|
void opacityChanged();
|
||||||
|
void visibleMaskChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onWindowConnected();
|
void onWindowConnected();
|
||||||
void onWindowVisibleChanged();
|
void onWindowVisibleChanged();
|
||||||
|
void onWaylandWindowDestroyed();
|
||||||
void onWaylandSurfaceCreated();
|
void onWaylandSurfaceCreated();
|
||||||
void onWaylandSurfaceDestroyed();
|
void onWaylandSurfaceDestroyed();
|
||||||
void onProxyWindowDestroyed();
|
void onProxyWindowDestroyed();
|
||||||
|
void onVisibleMaskDestroyed();
|
||||||
|
void onWindowPolished();
|
||||||
|
void updateVisibleMask();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void disconnectWaylandWindow();
|
void disconnectWaylandWindow();
|
||||||
|
@ -67,7 +83,13 @@ private:
|
||||||
QWindow* mWindow = nullptr;
|
QWindow* mWindow = nullptr;
|
||||||
QtWaylandClient::QWaylandWindow* mWaylandWindow = nullptr;
|
QtWaylandClient::QWaylandWindow* mWaylandWindow = nullptr;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool opacity : 1 = false;
|
||||||
|
bool visibleMask : 1 = false;
|
||||||
|
} pendingPolish;
|
||||||
|
|
||||||
qreal mOpacity = 1.0;
|
qreal mOpacity = 1.0;
|
||||||
|
PendingRegion* mVisibleMask = nullptr;
|
||||||
std::unique_ptr<impl::HyprlandSurface> surface;
|
std::unique_ptr<impl::HyprlandSurface> surface;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,53 @@
|
||||||
#include "surface.hpp"
|
#include "surface.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <private/qwaylanddisplay_p.h>
|
||||||
|
#include <private/qwaylandintegration_p.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qregion.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
#include <qwayland-hyprland-surface-v1.h>
|
#include <qwayland-hyprland-surface-v1.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
#include <wayland-hyprland-surface-v1-client-protocol.h>
|
#include <wayland-hyprland-surface-v1-client-protocol.h>
|
||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
|
|
||||||
namespace qs::hyprland::surface::impl {
|
namespace qs::hyprland::surface::impl {
|
||||||
|
|
||||||
HyprlandSurface::HyprlandSurface(::hyprland_surface_v1* surface)
|
HyprlandSurface::HyprlandSurface(
|
||||||
: QtWayland::hyprland_surface_v1(surface) {}
|
::hyprland_surface_v1* surface,
|
||||||
|
QtWaylandClient::QWaylandWindow* backer
|
||||||
|
)
|
||||||
|
: QtWayland::hyprland_surface_v1(surface)
|
||||||
|
, backer(backer)
|
||||||
|
, backerSurface(backer->surface()) {}
|
||||||
|
|
||||||
HyprlandSurface::~HyprlandSurface() { this->destroy(); }
|
HyprlandSurface::~HyprlandSurface() { this->destroy(); }
|
||||||
|
|
||||||
|
bool HyprlandSurface::surfaceEq(wl_surface* surface) const {
|
||||||
|
return surface == this->backerSurface;
|
||||||
|
}
|
||||||
|
|
||||||
void HyprlandSurface::setOpacity(qreal opacity) {
|
void HyprlandSurface::setOpacity(qreal opacity) {
|
||||||
this->set_opacity(wl_fixed_from_double(opacity));
|
this->set_opacity(wl_fixed_from_double(opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HyprlandSurface::setVisibleRegion(const QRegion& region) {
|
||||||
|
if (this->version() < HYPRLAND_SURFACE_V1_SET_VISIBLE_REGION_SINCE_VERSION) {
|
||||||
|
qWarning() << "Cannot set hyprland surface visible region: compositor does not support "
|
||||||
|
"hyprland_surface_v1.set_visible_region";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region.isEmpty()) {
|
||||||
|
this->set_visible_region(nullptr);
|
||||||
|
} else {
|
||||||
|
static const auto* waylandIntegration = QtWaylandClient::QWaylandIntegration::instance();
|
||||||
|
auto* display = waylandIntegration->display();
|
||||||
|
|
||||||
|
auto* wlRegion = display->createRegion(region);
|
||||||
|
this->set_visible_region(wlRegion);
|
||||||
|
wl_region_destroy(wlRegion); // NOLINT(misc-include-cleaner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace qs::hyprland::surface::impl
|
} // namespace qs::hyprland::surface::impl
|
||||||
|
|
|
@ -1,21 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <private/qwaylandwindow_p.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
#include <qregion.h>
|
||||||
#include <qtclasshelpermacros.h>
|
#include <qtclasshelpermacros.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
#include <qwayland-hyprland-surface-v1.h>
|
#include <qwayland-hyprland-surface-v1.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
#include <wayland-hyprland-surface-v1-client-protocol.h>
|
#include <wayland-hyprland-surface-v1-client-protocol.h>
|
||||||
|
|
||||||
namespace qs::hyprland::surface::impl {
|
namespace qs::hyprland::surface::impl {
|
||||||
|
|
||||||
class HyprlandSurface: public QtWayland::hyprland_surface_v1 {
|
class HyprlandSurface: public QtWayland::hyprland_surface_v1 {
|
||||||
public:
|
public:
|
||||||
explicit HyprlandSurface(::hyprland_surface_v1* surface);
|
explicit HyprlandSurface(::hyprland_surface_v1* surface, QtWaylandClient::QWaylandWindow* backer);
|
||||||
~HyprlandSurface() override;
|
~HyprlandSurface() override;
|
||||||
Q_DISABLE_COPY_MOVE(HyprlandSurface);
|
Q_DISABLE_COPY_MOVE(HyprlandSurface);
|
||||||
|
|
||||||
|
[[nodiscard]] bool surfaceEq(wl_surface* surface) const;
|
||||||
|
|
||||||
void setOpacity(qreal opacity);
|
void setOpacity(qreal opacity);
|
||||||
|
void setVisibleRegion(const QRegion& region);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QtWaylandClient::QWaylandWindow* backer;
|
||||||
|
wl_surface* backerSurface = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace qs::hyprland::surface::impl
|
} // namespace qs::hyprland::surface::impl
|
||||||
|
|
|
@ -90,8 +90,8 @@ void WlrLayershell::setHeight(qint32 height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WlrLayershell::setScreen(QuickshellScreenInfo* screen) {
|
void WlrLayershell::setScreen(QuickshellScreenInfo* screen) {
|
||||||
this->ProxyWindowBase::setScreen(screen);
|
|
||||||
this->ext->setUseWindowScreen(screen != nullptr);
|
this->ext->setUseWindowScreen(screen != nullptr);
|
||||||
|
this->ProxyWindowBase::setScreen(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTBEGIN
|
// NOLINTBEGIN
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <private/qquickwindow_p.h>
|
#include <private/qquickwindow_p.h>
|
||||||
#include <qcoreevent.h>
|
#include <qcoreevent.h>
|
||||||
#include <qevent.h>
|
#include <qevent.h>
|
||||||
|
#include <qguiapplication.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlcontext.h>
|
#include <qqmlcontext.h>
|
||||||
|
@ -200,9 +201,6 @@ void ProxyWindowBase::completeWindow() {
|
||||||
if (this->mScreen != nullptr && this->window->screen() != this->mScreen) {
|
if (this->mScreen != nullptr && this->window->screen() != this->mScreen) {
|
||||||
if (this->window->isVisible()) this->window->setVisible(false);
|
if (this->window->isVisible()) this->window->setVisible(false);
|
||||||
this->window->setScreen(this->mScreen);
|
this->window->setScreen(this->mScreen);
|
||||||
} else if (this->mScreen == nullptr) {
|
|
||||||
this->mScreen = this->window->screen();
|
|
||||||
QObject::connect(this->mScreen, &QObject::destroyed, this, &ProxyWindowBase::onScreenDestroyed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->setWidth(this->mWidth);
|
this->setWidth(this->mWidth);
|
||||||
|
@ -327,39 +325,39 @@ void ProxyWindowBase::setHeight(qint32 height) {
|
||||||
|
|
||||||
void ProxyWindowBase::setScreen(QuickshellScreenInfo* screen) {
|
void ProxyWindowBase::setScreen(QuickshellScreenInfo* screen) {
|
||||||
auto* qscreen = screen == nullptr ? nullptr : screen->screen;
|
auto* qscreen = screen == nullptr ? nullptr : screen->screen;
|
||||||
if (qscreen == this->mScreen) return;
|
auto newMScreen = this->mScreen != qscreen;
|
||||||
|
|
||||||
if (this->mScreen != nullptr) {
|
if (this->mScreen && newMScreen) {
|
||||||
QObject::disconnect(this->mScreen, nullptr, this, nullptr);
|
QObject::disconnect(this->mScreen, nullptr, this, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->qscreen() != qscreen) {
|
||||||
|
this->mScreen = qscreen;
|
||||||
if (this->window == nullptr) {
|
if (this->window == nullptr) {
|
||||||
emit this->screenChanged();
|
emit this->screenChanged();
|
||||||
} else {
|
} else if (qscreen) {
|
||||||
auto reshow = this->isVisibleDirect();
|
auto reshow = this->isVisibleDirect();
|
||||||
if (reshow) this->setVisibleDirect(false);
|
if (reshow) this->setVisibleDirect(false);
|
||||||
if (this->window != nullptr) this->window->setScreen(qscreen);
|
if (this->window != nullptr) this->window->setScreen(qscreen);
|
||||||
if (reshow) this->setVisibleDirect(true);
|
if (reshow) this->setVisibleDirect(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (qscreen) this->mScreen = qscreen;
|
if (qscreen && newMScreen) {
|
||||||
else this->mScreen = this->window->screen();
|
|
||||||
|
|
||||||
QObject::connect(this->mScreen, &QObject::destroyed, this, &ProxyWindowBase::onScreenDestroyed);
|
QObject::connect(this->mScreen, &QObject::destroyed, this, &ProxyWindowBase::onScreenDestroyed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::onScreenDestroyed() { this->mScreen = nullptr; }
|
void ProxyWindowBase::onScreenDestroyed() { this->mScreen = nullptr; }
|
||||||
|
|
||||||
|
QScreen* ProxyWindowBase::qscreen() const {
|
||||||
|
if (this->window) return this->window->screen();
|
||||||
|
if (this->mScreen) return this->mScreen;
|
||||||
|
return QGuiApplication::primaryScreen();
|
||||||
|
}
|
||||||
|
|
||||||
QuickshellScreenInfo* ProxyWindowBase::screen() const {
|
QuickshellScreenInfo* ProxyWindowBase::screen() const {
|
||||||
QScreen* qscreen = nullptr;
|
return QuickshellTracked::instance()->screenInfo(this->qscreen());
|
||||||
|
|
||||||
if (this->window == nullptr) {
|
|
||||||
if (this->mScreen != nullptr) qscreen = this->mScreen;
|
|
||||||
} else {
|
|
||||||
qscreen = this->window->screen();
|
|
||||||
}
|
|
||||||
|
|
||||||
return QuickshellTracked::instance()->screenInfo(qscreen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QColor ProxyWindowBase::color() const { return this->mColor; }
|
QColor ProxyWindowBase::color() const { return this->mColor; }
|
||||||
|
|
|
@ -100,7 +100,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] qreal devicePixelRatio() const;
|
[[nodiscard]] qreal devicePixelRatio() const;
|
||||||
|
|
||||||
[[nodiscard]] virtual QuickshellScreenInfo* screen() const;
|
[[nodiscard]] QScreen* qscreen() const;
|
||||||
|
[[nodiscard]] QuickshellScreenInfo* screen() const;
|
||||||
virtual void setScreen(QuickshellScreenInfo* screen);
|
virtual void setScreen(QuickshellScreenInfo* screen);
|
||||||
|
|
||||||
[[nodiscard]] QColor color() const;
|
[[nodiscard]] QColor color() const;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue