mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2026-04-10 06:11:54 +10:00
Compare commits
15 commits
b7005e09e7
...
b83c39c8af
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b83c39c8af | ||
|
|
ee1100eb98 | ||
|
|
9bf752ac33 | ||
|
|
313f4e47f6 | ||
|
|
6ef86dd5aa | ||
|
|
308f1e249b | ||
|
|
08058326f0 | ||
|
|
6a244c3c56 | ||
|
|
d745184823 | ||
|
|
77c04a9447 | ||
|
|
eb6eaf59c7 | ||
|
|
7511545ee2 | ||
|
|
0cb62920a7 | ||
|
|
3520c85d77 | ||
|
|
3cf65af49f |
41 changed files with 903 additions and 132 deletions
8
.github/ISSUE_TEMPLATE/crash.yml
vendored
8
.github/ISSUE_TEMPLATE/crash.yml
vendored
|
|
@ -1,17 +1,17 @@
|
||||||
name: Crash Report (v1)
|
name: Crash Report (v1)
|
||||||
description: Quickshell has crashed (old)
|
description: Quickshell has crashed (old)
|
||||||
labels: ["bug", "crash"]
|
labels: ["unactionable"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
Thank you for taking the time to click the report button.
|
Thank you for taking the time to click the report button.
|
||||||
At this point most of the worst issues in 0.2.1 and before have been fixed and we are
|
At this point most of the worst issues in 0.2.1 and before have been fixed and we are
|
||||||
preparing for a new release. Please do not report crashes from 0.2.1 or before for now.
|
preparing for a new release. Please do not submit this report.
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: donotcheck
|
id: donotcheck
|
||||||
attributes:
|
attributes:
|
||||||
label: Do not check this box
|
label: Read the text above. Do not submit the report.
|
||||||
options:
|
options:
|
||||||
- label: Do not check this box
|
- label: Yes I want this report to be deleted.
|
||||||
required: true
|
required: true
|
||||||
|
|
|
||||||
2
.github/ISSUE_TEMPLATE/crash2.yml
vendored
2
.github/ISSUE_TEMPLATE/crash2.yml
vendored
|
|
@ -23,7 +23,7 @@ body:
|
||||||
description: |
|
description: |
|
||||||
Attach `log.qslog.log` here. If it is too big to upload, compress it.
|
Attach `log.qslog.log` here. If it is too big to upload, compress it.
|
||||||
|
|
||||||
You can preview the log if you'd like using `quickshell read-log <path-to-log>`.
|
You can preview the log if you'd like using `qs log <path-to-log> -r '*=true'`.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,9 @@ include(cmake/util.cmake)
|
||||||
|
|
||||||
add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension)
|
add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension)
|
||||||
|
|
||||||
# pipewire defines this, breaking PCH
|
# pipewire defines these, breaking PCH
|
||||||
add_compile_definitions(_REENTRANT)
|
add_compile_definitions(_REENTRANT)
|
||||||
|
add_compile_options(-fno-strict-overflow)
|
||||||
|
|
||||||
if (FRAME_POINTERS)
|
if (FRAME_POINTERS)
|
||||||
add_compile_options(-fno-omit-frame-pointer)
|
add_compile_options(-fno-omit-frame-pointer)
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ set shell id.
|
||||||
- Added a way to detect if an icon is from the system icon theme or not.
|
- Added a way to detect if an icon is from the system icon theme or not.
|
||||||
- Added vulkan support to screencopy.
|
- Added vulkan support to screencopy.
|
||||||
- Added generic WindowManager interface implementing ext-workspace.
|
- Added generic WindowManager interface implementing ext-workspace.
|
||||||
|
- Added ext-background-effect window blur support.
|
||||||
|
- Added per-corner radius support to Region.
|
||||||
|
|
||||||
## Other Changes
|
## Other Changes
|
||||||
|
|
||||||
|
|
@ -38,6 +40,7 @@ set shell id.
|
||||||
- Added `QS_DISABLE_FILE_WATCHER` environment variable to disable file watching.
|
- Added `QS_DISABLE_FILE_WATCHER` environment variable to disable file watching.
|
||||||
- Added `QS_DISABLE_CRASH_HANDLER` environment variable to disable crash handling.
|
- Added `QS_DISABLE_CRASH_HANDLER` environment variable to disable crash handling.
|
||||||
- Added `QS_CRASHREPORT_URL` environment variable to allow overriding the crash reporter link.
|
- Added `QS_CRASHREPORT_URL` environment variable to allow overriding the crash reporter link.
|
||||||
|
- Added `AppId` pragma and `QS_APP_ID` environment variable to allow overriding the desktop application ID.
|
||||||
|
|
||||||
## Bug Fixes
|
## Bug Fixes
|
||||||
|
|
||||||
|
|
@ -60,6 +63,7 @@ set shell id.
|
||||||
- Desktop action order is now preserved.
|
- Desktop action order is now preserved.
|
||||||
- Fixed partial socket reads in greetd and hyprland on slow machines.
|
- Fixed partial socket reads in greetd and hyprland on slow machines.
|
||||||
- Worked around Qt bug causing crashes when plugging and unplugging monitors.
|
- Worked around Qt bug causing crashes when plugging and unplugging monitors.
|
||||||
|
- Fixed HyprlandFocusGrab crashing if windows were destroyed after being passed to it.
|
||||||
|
|
||||||
## Packaging Changes
|
## Packaging Changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -129,12 +129,13 @@ QString envInfo() {
|
||||||
auto stream = QTextStream(&info);
|
auto stream = QTextStream(&info);
|
||||||
|
|
||||||
for (auto** envp = environ; *envp != nullptr; ++envp) { // NOLINT
|
for (auto** envp = environ; *envp != nullptr; ++envp) { // NOLINT
|
||||||
auto prefixes = std::array<std::string_view, 5> {
|
auto prefixes = std::array<std::string_view, 6> {
|
||||||
"QS_",
|
"QS_",
|
||||||
"QT_",
|
"QT_",
|
||||||
"QML_",
|
"QML_",
|
||||||
"QML2_",
|
"QML2_",
|
||||||
"QSG_",
|
"QSG_",
|
||||||
|
"XDG_CURRENT_DESKTOP=",
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& prefix: prefixes) {
|
for (const auto& prefix: prefixes) {
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@
|
||||||
#include <qdatastream.h>
|
#include <qdatastream.h>
|
||||||
|
|
||||||
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
|
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
|
||||||
stream << info.instanceId << info.configPath << info.shellId << info.launchTime << info.pid
|
stream << info.instanceId << info.configPath << info.shellId << info.appId << info.launchTime
|
||||||
<< info.display;
|
<< info.pid << info.display;
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
|
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
|
||||||
stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime >> info.pid
|
stream >> info.instanceId >> info.configPath >> info.shellId >> info.appId >> info.launchTime
|
||||||
>> info.display;
|
>> info.pid >> info.display;
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ struct InstanceInfo {
|
||||||
QString instanceId;
|
QString instanceId;
|
||||||
QString configPath;
|
QString configPath;
|
||||||
QString shellId;
|
QString shellId;
|
||||||
|
QString appId;
|
||||||
QDateTime launchTime;
|
QDateTime launchTime;
|
||||||
pid_t pid = -1;
|
pid_t pid = -1;
|
||||||
QString display;
|
QString display;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
|
#include <qmutex.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qobjectdefs.h>
|
#include <qobjectdefs.h>
|
||||||
|
|
@ -220,6 +221,7 @@ void LogManager::messageHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (display) {
|
if (display) {
|
||||||
|
auto locker = QMutexLocker(&self->stdoutMutex);
|
||||||
LogMessage::formatMessage(
|
LogMessage::formatMessage(
|
||||||
self->stdoutStream,
|
self->stdoutStream,
|
||||||
message,
|
message,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include <qlatin1stringview.h>
|
#include <qlatin1stringview.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
|
#include <qmutex.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
|
@ -135,6 +136,7 @@ private:
|
||||||
QHash<QLatin1StringView, CategoryFilter> allFilters;
|
QHash<QLatin1StringView, CategoryFilter> allFilters;
|
||||||
|
|
||||||
QTextStream stdoutStream;
|
QTextStream stdoutStream;
|
||||||
|
QMutex stdoutMutex;
|
||||||
LoggingThreadProxy threadProxy;
|
LoggingThreadProxy threadProxy;
|
||||||
|
|
||||||
friend void initLogCategoryLevel(const char* name, QtMsgType defaultLevel);
|
friend void initLogCategoryLevel(const char* name, QtMsgType defaultLevel);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
#include <qwindow.h>
|
#include <qwindow.h>
|
||||||
|
|
||||||
#include "../window/proxywindow.hpp"
|
#include "../window/proxywindow.hpp"
|
||||||
#include "../window/windowinterface.hpp"
|
|
||||||
#include "iconprovider.hpp"
|
#include "iconprovider.hpp"
|
||||||
#include "model.hpp"
|
#include "model.hpp"
|
||||||
#include "platformmenu_p.hpp"
|
#include "platformmenu_p.hpp"
|
||||||
|
|
@ -91,10 +90,8 @@ bool PlatformMenuEntry::display(QObject* parentWindow, int relativeX, int relati
|
||||||
} else if (parentWindow == nullptr) {
|
} else if (parentWindow == nullptr) {
|
||||||
qCritical() << "Cannot display PlatformMenuEntry with null parent window.";
|
qCritical() << "Cannot display PlatformMenuEntry with null parent window.";
|
||||||
return false;
|
return false;
|
||||||
} else if (auto* proxy = qobject_cast<ProxyWindowBase*>(parentWindow)) {
|
} else if (auto* proxy = ProxyWindowBase::forObject(parentWindow)) {
|
||||||
window = proxy->backingWindow();
|
window = proxy->backingWindow();
|
||||||
} else if (auto* interface = qobject_cast<WindowInterface*>(parentWindow)) {
|
|
||||||
window = interface->proxyWindow()->backingWindow();
|
|
||||||
} else {
|
} else {
|
||||||
qCritical() << "PlatformMenuEntry.display() must be called with a window.";
|
qCritical() << "PlatformMenuEntry.display() must be called with a window.";
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,18 @@ static QVector<QsEnginePlugin*> plugins; // NOLINT
|
||||||
|
|
||||||
void QsEnginePlugin::registerPlugin(QsEnginePlugin& plugin) { plugins.push_back(&plugin); }
|
void QsEnginePlugin::registerPlugin(QsEnginePlugin& plugin) { plugins.push_back(&plugin); }
|
||||||
|
|
||||||
|
void QsEnginePlugin::preinitPluginsOnly() {
|
||||||
|
plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); });
|
||||||
|
|
||||||
|
std::ranges::sort(plugins, [](QsEnginePlugin* a, QsEnginePlugin* b) {
|
||||||
|
return b->dependencies().contains(a->name());
|
||||||
|
});
|
||||||
|
|
||||||
|
for (QsEnginePlugin* plugin: plugins) {
|
||||||
|
plugin->preinit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QsEnginePlugin::initPlugins() {
|
void QsEnginePlugin::initPlugins() {
|
||||||
plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); });
|
plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); });
|
||||||
|
|
||||||
|
|
@ -16,6 +28,10 @@ void QsEnginePlugin::initPlugins() {
|
||||||
return b->dependencies().contains(a->name());
|
return b->dependencies().contains(a->name());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (QsEnginePlugin* plugin: plugins) {
|
||||||
|
plugin->preinit();
|
||||||
|
}
|
||||||
|
|
||||||
for (QsEnginePlugin* plugin: plugins) {
|
for (QsEnginePlugin* plugin: plugins) {
|
||||||
plugin->init();
|
plugin->init();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,14 @@ public:
|
||||||
virtual QString name() { return QString(); }
|
virtual QString name() { return QString(); }
|
||||||
virtual QList<QString> dependencies() { return {}; }
|
virtual QList<QString> dependencies() { return {}; }
|
||||||
virtual bool applies() { return true; }
|
virtual bool applies() { return true; }
|
||||||
|
virtual void preinit() {}
|
||||||
virtual void init() {}
|
virtual void init() {}
|
||||||
virtual void registerTypes() {}
|
virtual void registerTypes() {}
|
||||||
virtual void constructGeneration(EngineGeneration& /*unused*/) {} // NOLINT
|
virtual void constructGeneration(EngineGeneration& /*unused*/) {} // NOLINT
|
||||||
virtual void onReload() {}
|
virtual void onReload() {}
|
||||||
|
|
||||||
static void registerPlugin(QsEnginePlugin& plugin);
|
static void registerPlugin(QsEnginePlugin& plugin);
|
||||||
|
static void preinitPluginsOnly();
|
||||||
static void initPlugins();
|
static void initPlugins();
|
||||||
static void runConstructGeneration(EngineGeneration& generation);
|
static void runConstructGeneration(EngineGeneration& generation);
|
||||||
static void runOnReload();
|
static void runOnReload();
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
#include <qwindow.h>
|
#include <qwindow.h>
|
||||||
|
|
||||||
#include "../window/proxywindow.hpp"
|
#include "../window/proxywindow.hpp"
|
||||||
#include "../window/windowinterface.hpp"
|
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
|
|
||||||
bool PopupAnchorState::operator==(const PopupAnchorState& other) const {
|
bool PopupAnchorState::operator==(const PopupAnchorState& other) const {
|
||||||
|
|
@ -40,10 +39,8 @@ void PopupAnchor::setWindowInternal(QObject* window) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window) {
|
if (window) {
|
||||||
if (auto* proxy = qobject_cast<ProxyWindowBase*>(window)) {
|
if (auto* proxy = ProxyWindowBase::forObject(window)) {
|
||||||
this->bProxyWindow = proxy;
|
this->bProxyWindow = proxy;
|
||||||
} else if (auto* interface = qobject_cast<WindowInterface*>(window)) {
|
|
||||||
this->bProxyWindow = interface->proxyWindow();
|
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Tried to set popup anchor window to" << window
|
qWarning() << "Tried to set popup anchor window to" << window
|
||||||
<< "which is not a quickshell window.";
|
<< "which is not a quickshell window.";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "region.hpp"
|
#include "region.hpp"
|
||||||
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
|
@ -18,6 +19,11 @@ PendingRegion::PendingRegion(QObject* parent): QObject(parent) {
|
||||||
QObject::connect(this, &PendingRegion::yChanged, this, &PendingRegion::changed);
|
QObject::connect(this, &PendingRegion::yChanged, this, &PendingRegion::changed);
|
||||||
QObject::connect(this, &PendingRegion::widthChanged, this, &PendingRegion::changed);
|
QObject::connect(this, &PendingRegion::widthChanged, this, &PendingRegion::changed);
|
||||||
QObject::connect(this, &PendingRegion::heightChanged, this, &PendingRegion::changed);
|
QObject::connect(this, &PendingRegion::heightChanged, this, &PendingRegion::changed);
|
||||||
|
QObject::connect(this, &PendingRegion::radiusChanged, this, &PendingRegion::changed);
|
||||||
|
QObject::connect(this, &PendingRegion::topLeftRadiusChanged, this, &PendingRegion::changed);
|
||||||
|
QObject::connect(this, &PendingRegion::topRightRadiusChanged, this, &PendingRegion::changed);
|
||||||
|
QObject::connect(this, &PendingRegion::bottomLeftRadiusChanged, this, &PendingRegion::changed);
|
||||||
|
QObject::connect(this, &PendingRegion::bottomRightRadiusChanged, this, &PendingRegion::changed);
|
||||||
QObject::connect(this, &PendingRegion::childrenChanged, this, &PendingRegion::changed);
|
QObject::connect(this, &PendingRegion::childrenChanged, this, &PendingRegion::changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,6 +51,79 @@ void PendingRegion::onItemDestroyed() { this->mItem = nullptr; }
|
||||||
|
|
||||||
void PendingRegion::onChildDestroyed() { this->mRegions.removeAll(this->sender()); }
|
void PendingRegion::onChildDestroyed() { this->mRegions.removeAll(this->sender()); }
|
||||||
|
|
||||||
|
qint32 PendingRegion::radius() const { return this->mRadius; }
|
||||||
|
|
||||||
|
void PendingRegion::setRadius(qint32 radius) {
|
||||||
|
if (radius == this->mRadius) return;
|
||||||
|
this->mRadius = radius;
|
||||||
|
emit this->radiusChanged();
|
||||||
|
|
||||||
|
if (!(this->mCornerOverrides & TopLeft)) emit this->topLeftRadiusChanged();
|
||||||
|
if (!(this->mCornerOverrides & TopRight)) emit this->topRightRadiusChanged();
|
||||||
|
if (!(this->mCornerOverrides & BottomLeft)) emit this->bottomLeftRadiusChanged();
|
||||||
|
if (!(this->mCornerOverrides & BottomRight)) emit this->bottomRightRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 PendingRegion::topLeftRadius() const {
|
||||||
|
return (this->mCornerOverrides & TopLeft) ? this->mTopLeftRadius : this->mRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::setTopLeftRadius(qint32 radius) {
|
||||||
|
this->mTopLeftRadius = radius;
|
||||||
|
this->mCornerOverrides |= TopLeft;
|
||||||
|
emit this->topLeftRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::resetTopLeftRadius() {
|
||||||
|
this->mCornerOverrides &= ~TopLeft;
|
||||||
|
emit this->topLeftRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 PendingRegion::topRightRadius() const {
|
||||||
|
return (this->mCornerOverrides & TopRight) ? this->mTopRightRadius : this->mRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::setTopRightRadius(qint32 radius) {
|
||||||
|
this->mTopRightRadius = radius;
|
||||||
|
this->mCornerOverrides |= TopRight;
|
||||||
|
emit this->topRightRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::resetTopRightRadius() {
|
||||||
|
this->mCornerOverrides &= ~TopRight;
|
||||||
|
emit this->topRightRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 PendingRegion::bottomLeftRadius() const {
|
||||||
|
return (this->mCornerOverrides & BottomLeft) ? this->mBottomLeftRadius : this->mRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::setBottomLeftRadius(qint32 radius) {
|
||||||
|
this->mBottomLeftRadius = radius;
|
||||||
|
this->mCornerOverrides |= BottomLeft;
|
||||||
|
emit this->bottomLeftRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::resetBottomLeftRadius() {
|
||||||
|
this->mCornerOverrides &= ~BottomLeft;
|
||||||
|
emit this->bottomLeftRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 PendingRegion::bottomRightRadius() const {
|
||||||
|
return (this->mCornerOverrides & BottomRight) ? this->mBottomRightRadius : this->mRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::setBottomRightRadius(qint32 radius) {
|
||||||
|
this->mBottomRightRadius = radius;
|
||||||
|
this->mCornerOverrides |= BottomRight;
|
||||||
|
emit this->bottomRightRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PendingRegion::resetBottomRightRadius() {
|
||||||
|
this->mCornerOverrides &= ~BottomRight;
|
||||||
|
emit this->bottomRightRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
QQmlListProperty<PendingRegion> PendingRegion::regions() {
|
QQmlListProperty<PendingRegion> PendingRegion::regions() {
|
||||||
return QQmlListProperty<PendingRegion>(
|
return QQmlListProperty<PendingRegion>(
|
||||||
this,
|
this,
|
||||||
|
|
@ -90,6 +169,60 @@ QRegion PendingRegion::build() const {
|
||||||
region = QRegion(this->mX, this->mY, this->mWidth, this->mHeight, type);
|
region = QRegion(this->mX, this->mY, this->mWidth, this->mHeight, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->mShape == RegionShape::Rect && !region.isEmpty()) {
|
||||||
|
auto tl = std::max(this->topLeftRadius(), 0);
|
||||||
|
auto tr = std::max(this->topRightRadius(), 0);
|
||||||
|
auto bl = std::max(this->bottomLeftRadius(), 0);
|
||||||
|
auto br = std::max(this->bottomRightRadius(), 0);
|
||||||
|
|
||||||
|
if (tl > 0 || tr > 0 || bl > 0 || br > 0) {
|
||||||
|
auto rect = region.boundingRect();
|
||||||
|
auto x = rect.x();
|
||||||
|
auto y = rect.y();
|
||||||
|
auto w = rect.width();
|
||||||
|
auto h = rect.height();
|
||||||
|
|
||||||
|
// Normalize so adjacent corners don't exceed their shared edge.
|
||||||
|
// Each corner is scaled by the tightest constraint of its two edges.
|
||||||
|
auto topScale = tl + tr > w ? static_cast<double>(w) / (tl + tr) : 1.0;
|
||||||
|
auto bottomScale = bl + br > w ? static_cast<double>(w) / (bl + br) : 1.0;
|
||||||
|
auto leftScale = tl + bl > h ? static_cast<double>(h) / (tl + bl) : 1.0;
|
||||||
|
auto rightScale = tr + br > h ? static_cast<double>(h) / (tr + br) : 1.0;
|
||||||
|
|
||||||
|
tl = static_cast<qint32>(tl * std::min(topScale, leftScale));
|
||||||
|
tr = static_cast<qint32>(tr * std::min(topScale, rightScale));
|
||||||
|
bl = static_cast<qint32>(bl * std::min(bottomScale, leftScale));
|
||||||
|
br = static_cast<qint32>(br * std::min(bottomScale, rightScale));
|
||||||
|
|
||||||
|
// Unlock each corner: subtract (cornerBox - quarterEllipse) from the
|
||||||
|
// full rect. Each corner only modifies pixels inside its own box,
|
||||||
|
// so no diagonal overlap is possible.
|
||||||
|
if (tl > 0) {
|
||||||
|
auto box = QRegion(x, y, tl, tl);
|
||||||
|
auto ellipse = QRegion(x, y, tl * 2, tl * 2, QRegion::Ellipse);
|
||||||
|
region -= box - (ellipse & box);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tr > 0) {
|
||||||
|
auto box = QRegion(x + w - tr, y, tr, tr);
|
||||||
|
auto ellipse = QRegion(x + w - tr * 2, y, tr * 2, tr * 2, QRegion::Ellipse);
|
||||||
|
region -= box - (ellipse & box);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bl > 0) {
|
||||||
|
auto box = QRegion(x, y + h - bl, bl, bl);
|
||||||
|
auto ellipse = QRegion(x, y + h - bl * 2, bl * 2, bl * 2, QRegion::Ellipse);
|
||||||
|
region -= box - (ellipse & box);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (br > 0) {
|
||||||
|
auto box = QRegion(x + w - br, y + h - br, br, br);
|
||||||
|
auto ellipse = QRegion(x + w - br * 2, y + h - br * 2, br * 2, br * 2, QRegion::Ellipse);
|
||||||
|
region -= box - (ellipse & box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& childRegion: this->mRegions) {
|
for (const auto& childRegion: this->mRegions) {
|
||||||
region = childRegion->applyTo(region);
|
region = childRegion->applyTo(region);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,29 @@ class PendingRegion: public QObject {
|
||||||
Q_PROPERTY(qint32 width MEMBER mWidth NOTIFY widthChanged);
|
Q_PROPERTY(qint32 width MEMBER mWidth NOTIFY widthChanged);
|
||||||
/// Defaults to 0. Does nothing if @@item is set.
|
/// Defaults to 0. Does nothing if @@item is set.
|
||||||
Q_PROPERTY(qint32 height MEMBER mHeight NOTIFY heightChanged);
|
Q_PROPERTY(qint32 height MEMBER mHeight NOTIFY heightChanged);
|
||||||
|
// clang-format off
|
||||||
|
/// Corner radius for rounded rectangles. Only applies when @@shape is `Rect`. Defaults to 0.
|
||||||
|
///
|
||||||
|
/// Acts as the default for @@topLeftRadius, @@topRightRadius, @@bottomLeftRadius,
|
||||||
|
/// and @@bottomRightRadius.
|
||||||
|
Q_PROPERTY(qint32 radius READ radius WRITE setRadius NOTIFY radiusChanged);
|
||||||
|
/// Top-left corner radius. Only applies when @@shape is `Rect`.
|
||||||
|
///
|
||||||
|
/// Defaults to @@radius, and may be reset by assigning `undefined`.
|
||||||
|
Q_PROPERTY(qint32 topLeftRadius READ topLeftRadius WRITE setTopLeftRadius RESET resetTopLeftRadius NOTIFY topLeftRadiusChanged);
|
||||||
|
/// Top-right corner radius. Only applies when @@shape is `Rect`.
|
||||||
|
///
|
||||||
|
/// Defaults to @@radius, and may be reset by assigning `undefined`.
|
||||||
|
Q_PROPERTY(qint32 topRightRadius READ topRightRadius WRITE setTopRightRadius RESET resetTopRightRadius NOTIFY topRightRadiusChanged);
|
||||||
|
/// Bottom-left corner radius. Only applies when @@shape is `Rect`.
|
||||||
|
///
|
||||||
|
/// Defaults to @@radius, and may be reset by assigning `undefined`.
|
||||||
|
Q_PROPERTY(qint32 bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius RESET resetBottomLeftRadius NOTIFY bottomLeftRadiusChanged);
|
||||||
|
/// Bottom-right corner radius. Only applies when @@shape is `Rect`.
|
||||||
|
///
|
||||||
|
/// Defaults to @@radius, and may be reset by assigning `undefined`.
|
||||||
|
Q_PROPERTY(qint32 bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius RESET resetBottomRightRadius NOTIFY bottomRightRadiusChanged);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
/// Regions to apply on top of this region.
|
/// Regions to apply on top of this region.
|
||||||
///
|
///
|
||||||
|
|
@ -91,6 +114,25 @@ public:
|
||||||
|
|
||||||
void setItem(QQuickItem* item);
|
void setItem(QQuickItem* item);
|
||||||
|
|
||||||
|
[[nodiscard]] qint32 radius() const;
|
||||||
|
void setRadius(qint32 radius);
|
||||||
|
|
||||||
|
[[nodiscard]] qint32 topLeftRadius() const;
|
||||||
|
void setTopLeftRadius(qint32 radius);
|
||||||
|
void resetTopLeftRadius();
|
||||||
|
|
||||||
|
[[nodiscard]] qint32 topRightRadius() const;
|
||||||
|
void setTopRightRadius(qint32 radius);
|
||||||
|
void resetTopRightRadius();
|
||||||
|
|
||||||
|
[[nodiscard]] qint32 bottomLeftRadius() const;
|
||||||
|
void setBottomLeftRadius(qint32 radius);
|
||||||
|
void resetBottomLeftRadius();
|
||||||
|
|
||||||
|
[[nodiscard]] qint32 bottomRightRadius() const;
|
||||||
|
void setBottomRightRadius(qint32 radius);
|
||||||
|
void resetBottomRightRadius();
|
||||||
|
|
||||||
QQmlListProperty<PendingRegion> regions();
|
QQmlListProperty<PendingRegion> regions();
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const;
|
[[nodiscard]] bool empty() const;
|
||||||
|
|
@ -109,6 +151,11 @@ signals:
|
||||||
void yChanged();
|
void yChanged();
|
||||||
void widthChanged();
|
void widthChanged();
|
||||||
void heightChanged();
|
void heightChanged();
|
||||||
|
void radiusChanged();
|
||||||
|
void topLeftRadiusChanged();
|
||||||
|
void topRightRadiusChanged();
|
||||||
|
void bottomLeftRadiusChanged();
|
||||||
|
void bottomRightRadiusChanged();
|
||||||
void childrenChanged();
|
void childrenChanged();
|
||||||
|
|
||||||
/// Triggered when the region's geometry changes.
|
/// Triggered when the region's geometry changes.
|
||||||
|
|
@ -130,12 +177,25 @@ private:
|
||||||
static void
|
static void
|
||||||
regionsReplace(QQmlListProperty<PendingRegion>* prop, qsizetype i, PendingRegion* region);
|
regionsReplace(QQmlListProperty<PendingRegion>* prop, qsizetype i, PendingRegion* region);
|
||||||
|
|
||||||
|
enum CornerOverride : quint8 {
|
||||||
|
TopLeft = 0b1,
|
||||||
|
TopRight = 0b10,
|
||||||
|
BottomLeft = 0b100,
|
||||||
|
BottomRight = 0b1000,
|
||||||
|
};
|
||||||
|
|
||||||
QQuickItem* mItem = nullptr;
|
QQuickItem* mItem = nullptr;
|
||||||
|
|
||||||
qint32 mX = 0;
|
qint32 mX = 0;
|
||||||
qint32 mY = 0;
|
qint32 mY = 0;
|
||||||
qint32 mWidth = 0;
|
qint32 mWidth = 0;
|
||||||
qint32 mHeight = 0;
|
qint32 mHeight = 0;
|
||||||
|
qint32 mRadius = 0;
|
||||||
|
qint32 mTopLeftRadius = 0;
|
||||||
|
qint32 mTopRightRadius = 0;
|
||||||
|
qint32 mBottomLeftRadius = 0;
|
||||||
|
qint32 mBottomRightRadius = 0;
|
||||||
|
quint8 mCornerOverrides = 0;
|
||||||
|
|
||||||
QList<PendingRegion*> mRegions;
|
QList<PendingRegion*> mRegions;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -145,10 +145,7 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
|
||||||
QString overrideText;
|
QString overrideText;
|
||||||
bool isOverridden = false;
|
bool isOverridden = false;
|
||||||
|
|
||||||
auto pragmaEngine = QJSEngine();
|
auto& pragmaEngine = *QmlScanner::preprocEngine();
|
||||||
pragmaEngine.globalObject().setPrototype(
|
|
||||||
pragmaEngine.newQObject(new qs::scan::env::PreprocEnv())
|
|
||||||
);
|
|
||||||
|
|
||||||
auto postError = [&, this](QString error) {
|
auto postError = [&, this](QString error) {
|
||||||
this->scanErrors.append({.file = path, .message = std::move(error), .line = lineNum});
|
this->scanErrors.append({.file = path, .message = std::move(error), .line = lineNum});
|
||||||
|
|
@ -370,3 +367,13 @@ QPair<QString, QString> QmlScanner::jsonToQml(const QJsonValue& value, int inden
|
||||||
return qMakePair(QStringLiteral("var"), "null");
|
return qMakePair(QStringLiteral("var"), "null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJSEngine* QmlScanner::preprocEngine() {
|
||||||
|
static auto* engine = [] {
|
||||||
|
auto* engine = new QJSEngine();
|
||||||
|
engine->globalObject().setPrototype(engine->newQObject(new qs::scan::env::PreprocEnv()));
|
||||||
|
return engine;
|
||||||
|
}();
|
||||||
|
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qhash.h>
|
#include <qhash.h>
|
||||||
|
#include <qjsengine.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
#include <qvector.h>
|
#include <qvector.h>
|
||||||
|
|
||||||
|
|
@ -42,4 +43,6 @@ private:
|
||||||
bool scanQmlFile(const QString& path, bool& singleton, bool& internal);
|
bool scanQmlFile(const QString& path, bool& singleton, bool& internal);
|
||||||
bool scanQmlJson(const QString& path);
|
bool scanQmlJson(const QString& path);
|
||||||
[[nodiscard]] static QPair<QString, QString> jsonToQml(const QJsonValue& value, int indent = 0);
|
[[nodiscard]] static QPair<QString, QString> jsonToQml(const QJsonValue& value, int indent = 0);
|
||||||
|
|
||||||
|
static QJSEngine* preprocEngine();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
#include <cpptrace/basic.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include <cpptrace/forward.hpp>
|
#include <cpptrace/forward.hpp>
|
||||||
|
#include <cpptrace/utils.hpp>
|
||||||
#include <qdatastream.h>
|
#include <qdatastream.h>
|
||||||
#include <qfile.h>
|
#include <qfile.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
|
|
@ -58,6 +60,12 @@ void signalHandler(
|
||||||
siginfo_t* /*info*/, // NOLINT (misc-include-cleaner)
|
siginfo_t* /*info*/, // NOLINT (misc-include-cleaner)
|
||||||
void* /*context*/
|
void* /*context*/
|
||||||
) {
|
) {
|
||||||
|
// NOLINTBEGIN (misc-include-cleaner)
|
||||||
|
sigset_t set;
|
||||||
|
sigfillset(&set);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &set, nullptr);
|
||||||
|
// NOLINTEND
|
||||||
|
|
||||||
if (CrashInfo::INSTANCE.traceFd != -1) {
|
if (CrashInfo::INSTANCE.traceFd != -1) {
|
||||||
auto traceBuffer = std::array<cpptrace::frame_ptr, 1024>();
|
auto traceBuffer = std::array<cpptrace::frame_ptr, 1024>();
|
||||||
auto frameCount = cpptrace::safe_generate_raw_trace(traceBuffer.data(), traceBuffer.size(), 1);
|
auto frameCount = cpptrace::safe_generate_raw_trace(traceBuffer.data(), traceBuffer.size(), 1);
|
||||||
|
|
@ -79,13 +87,9 @@ void signalHandler(
|
||||||
fail:;
|
fail:;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: coredump fork and crash reporter remain as zombies, fix
|
||||||
auto coredumpPid = fork();
|
auto coredumpPid = fork();
|
||||||
if (coredumpPid == 0) {
|
if (coredumpPid == 0) {
|
||||||
// NOLINTBEGIN (misc-include-cleaner)
|
|
||||||
sigset_t set;
|
|
||||||
sigfillset(&set);
|
|
||||||
sigprocmask(SIG_UNBLOCK, &set, nullptr);
|
|
||||||
// NOLINTEND
|
|
||||||
raise(sig);
|
raise(sig);
|
||||||
_exit(-1);
|
_exit(-1);
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +135,6 @@ void signalHandler(
|
||||||
perror("Failed to fork and launch crash reporter.\n");
|
perror("Failed to fork and launch crash reporter.\n");
|
||||||
_exit(-1);
|
_exit(-1);
|
||||||
} else if (pid == 0) {
|
} else if (pid == 0) {
|
||||||
|
|
||||||
// dup to remove CLOEXEC
|
// dup to remove CLOEXEC
|
||||||
auto dumpFdStr = std::array<char, 48>();
|
auto dumpFdStr = std::array<char, 48>();
|
||||||
auto logFdStr = std::array<char, 48>();
|
auto logFdStr = std::array<char, 48>();
|
||||||
|
|
@ -155,6 +158,21 @@ void signalHandler(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleCppTerminate() {
|
||||||
|
if (auto ptr = std::current_exception()) {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ptr);
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
qFatal().nospace() << "Terminate called with C++ exception ("
|
||||||
|
<< cpptrace::demangle(typeid(e).name()).data() << "): " << e.what();
|
||||||
|
} catch (...) {
|
||||||
|
qFatal() << "Terminate called with non exception object";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qFatal() << "Terminate called without active C++ exception";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void CrashHandler::init() {
|
void CrashHandler::init() {
|
||||||
|
|
@ -203,6 +221,8 @@ void CrashHandler::init() {
|
||||||
|
|
||||||
// NOLINTEND (misc-include-cleaner)
|
// NOLINTEND (misc-include-cleaner)
|
||||||
|
|
||||||
|
std::set_terminate(&handleCppTerminate);
|
||||||
|
|
||||||
qCInfo(logCrashHandler) << "Crash handler initialized.";
|
qCInfo(logCrashHandler) << "Crash handler initialized.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
#include "../core/logging.hpp"
|
#include "../core/logging.hpp"
|
||||||
#include "../core/logging_p.hpp"
|
#include "../core/logging_p.hpp"
|
||||||
#include "../core/paths.hpp"
|
#include "../core/paths.hpp"
|
||||||
|
#include "../core/plugin.hpp"
|
||||||
#include "../core/ringbuf.hpp"
|
#include "../core/ringbuf.hpp"
|
||||||
#include "interface.hpp"
|
#include "interface.hpp"
|
||||||
|
|
||||||
|
|
@ -230,12 +231,17 @@ void qsCheckCrash(int argc, char** argv) {
|
||||||
);
|
);
|
||||||
|
|
||||||
auto app = QApplication(argc, argv);
|
auto app = QApplication(argc, argv);
|
||||||
QApplication::setDesktopFileName("org.quickshell");
|
auto desktopId =
|
||||||
|
info.instance.appId.isEmpty() ? QStringLiteral("org.quickshell") : info.instance.appId;
|
||||||
|
QApplication::setDesktopFileName(desktopId);
|
||||||
|
|
||||||
auto crashDir = QsPaths::crashDir(info.instance.instanceId);
|
auto crashDir = QsPaths::crashDir(info.instance.instanceId);
|
||||||
|
|
||||||
qCInfo(logCrashReporter) << "Starting crash reporter...";
|
qCInfo(logCrashReporter) << "Starting crash reporter...";
|
||||||
|
|
||||||
|
// Required platform compatibility hooks
|
||||||
|
QsEnginePlugin::preinitPluginsOnly();
|
||||||
|
|
||||||
recordCrashInfo(crashDir, info.instance);
|
recordCrashInfo(crashDir, info.instance);
|
||||||
|
|
||||||
auto gui = CrashReporterGui(crashDir.path(), crashProc);
|
auto gui = CrashReporterGui(crashDir.path(), crashProc);
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||||
bool useSystemStyle = false;
|
bool useSystemStyle = false;
|
||||||
QString iconTheme = qEnvironmentVariable("QS_ICON_THEME");
|
QString iconTheme = qEnvironmentVariable("QS_ICON_THEME");
|
||||||
QHash<QString, QString> envOverrides;
|
QHash<QString, QString> envOverrides;
|
||||||
|
QString appId = qEnvironmentVariable("QS_APP_ID");
|
||||||
QString dataDir;
|
QString dataDir;
|
||||||
QString stateDir;
|
QString stateDir;
|
||||||
QString cacheDir;
|
QString cacheDir;
|
||||||
|
|
@ -104,6 +105,8 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||||
auto var = envPragma.sliced(0, splitIdx).trimmed();
|
auto var = envPragma.sliced(0, splitIdx).trimmed();
|
||||||
auto val = envPragma.sliced(splitIdx + 1).trimmed();
|
auto val = envPragma.sliced(splitIdx + 1).trimmed();
|
||||||
pragmas.envOverrides.insert(var, val);
|
pragmas.envOverrides.insert(var, val);
|
||||||
|
} else if (pragma.startsWith("AppId ")) {
|
||||||
|
pragmas.appId = pragma.sliced(6).trimmed();
|
||||||
} else if (pragma.startsWith("ShellId ")) {
|
} else if (pragma.startsWith("ShellId ")) {
|
||||||
shellId = pragma.sliced(8).trimmed();
|
shellId = pragma.sliced(8).trimmed();
|
||||||
} else if (pragma.startsWith("DataDir ")) {
|
} else if (pragma.startsWith("DataDir ")) {
|
||||||
|
|
@ -128,10 +131,13 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||||
qInfo() << "Shell ID:" << shellId << "Path ID" << pathId;
|
qInfo() << "Shell ID:" << shellId << "Path ID" << pathId;
|
||||||
|
|
||||||
auto launchTime = qs::Common::LAUNCH_TIME.toSecsSinceEpoch();
|
auto launchTime = qs::Common::LAUNCH_TIME.toSecsSinceEpoch();
|
||||||
|
auto appId = pragmas.appId.isEmpty() ? QStringLiteral("org.quickshell") : pragmas.appId;
|
||||||
|
|
||||||
InstanceInfo::CURRENT = InstanceInfo {
|
InstanceInfo::CURRENT = InstanceInfo {
|
||||||
.instanceId = base36Encode(getpid()) + base36Encode(launchTime),
|
.instanceId = base36Encode(getpid()) + base36Encode(launchTime),
|
||||||
.configPath = args.configPath,
|
.configPath = args.configPath,
|
||||||
.shellId = shellId,
|
.shellId = shellId,
|
||||||
|
.appId = appId,
|
||||||
.launchTime = qs::Common::LAUNCH_TIME,
|
.launchTime = qs::Common::LAUNCH_TIME,
|
||||||
.pid = getpid(),
|
.pid = getpid(),
|
||||||
.display = getDisplayConnection(),
|
.display = getDisplayConnection(),
|
||||||
|
|
@ -231,7 +237,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
||||||
app = new QGuiApplication(qArgC, argv);
|
app = new QGuiApplication(qArgC, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
QGuiApplication::setDesktopFileName("org.quickshell");
|
QGuiApplication::setDesktopFileName(appId);
|
||||||
|
|
||||||
if (args.debugPort != -1) {
|
if (args.debugPort != -1) {
|
||||||
QQmlDebuggingEnabler::enableDebugging(true);
|
QQmlDebuggingEnabler::enableDebugging(true);
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ function (wl_proto target name dir)
|
||||||
target_include_directories(${target} INTERFACE ${PROTO_BUILD_PATH})
|
target_include_directories(${target} INTERFACE ${PROTO_BUILD_PATH})
|
||||||
target_link_libraries(${target} wl-proto-${name}-wl Qt6::WaylandClient Qt6::WaylandClientPrivate)
|
target_link_libraries(${target} wl-proto-${name}-wl Qt6::WaylandClient Qt6::WaylandClientPrivate)
|
||||||
qs_pch(${target} SET wayland-protocol)
|
qs_pch(${target} SET wayland-protocol)
|
||||||
|
target_compile_options(wl-proto-${name}-wl PRIVATE ${wayland_CFLAGS})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# -----
|
# -----
|
||||||
|
|
@ -83,10 +84,7 @@ qt_add_library(quickshell-wayland STATIC
|
||||||
|
|
||||||
# required for wl_proxy_safe_deref
|
# required for wl_proxy_safe_deref
|
||||||
target_link_libraries(quickshell-wayland PRIVATE ${CMAKE_DL_LIBS})
|
target_link_libraries(quickshell-wayland PRIVATE ${CMAKE_DL_LIBS})
|
||||||
target_link_options(quickshell PRIVATE
|
target_link_options(quickshell PRIVATE "LINKER:--export-dynamic-symbol=wl_proxy_get_listener")
|
||||||
"LINKER:--export-dynamic-symbol=wl_proxy_get_listener"
|
|
||||||
"LINKER:--require-defined=wl_proxy_get_listener"
|
|
||||||
)
|
|
||||||
|
|
||||||
# required to make sure the constructor is linked
|
# required to make sure the constructor is linked
|
||||||
add_library(quickshell-wayland-init OBJECT init.cpp)
|
add_library(quickshell-wayland-init OBJECT init.cpp)
|
||||||
|
|
@ -122,6 +120,9 @@ if (HYPRLAND)
|
||||||
add_subdirectory(hyprland)
|
add_subdirectory(hyprland)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(background_effect)
|
||||||
|
list(APPEND WAYLAND_MODULES Quickshell.Wayland._BackgroundEffect)
|
||||||
|
|
||||||
add_subdirectory(idle_inhibit)
|
add_subdirectory(idle_inhibit)
|
||||||
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
|
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
|
||||||
|
|
||||||
|
|
|
||||||
24
src/wayland/background_effect/CMakeLists.txt
Normal file
24
src/wayland/background_effect/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
qt_add_library(quickshell-wayland-background-effect STATIC
|
||||||
|
manager.cpp
|
||||||
|
surface.cpp
|
||||||
|
qml.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(quickshell-wayland-background-effect
|
||||||
|
URI Quickshell.Wayland._BackgroundEffect
|
||||||
|
VERSION 0.1
|
||||||
|
DEPENDENCIES QtQml
|
||||||
|
)
|
||||||
|
|
||||||
|
install_qml_module(quickshell-wayland-background-effect)
|
||||||
|
|
||||||
|
wl_proto(wlp-background-effect ext-background-effect-v1 "${WAYLAND_PROTOCOLS}/staging/ext-background-effect")
|
||||||
|
|
||||||
|
target_link_libraries(quickshell-wayland-background-effect PRIVATE
|
||||||
|
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
|
||||||
|
wlp-background-effect
|
||||||
|
)
|
||||||
|
|
||||||
|
qs_module_pch(quickshell-wayland-background-effect)
|
||||||
|
|
||||||
|
target_link_libraries(quickshell PRIVATE quickshell-wayland-background-effectplugin)
|
||||||
38
src/wayland/background_effect/manager.cpp
Normal file
38
src/wayland/background_effect/manager.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "manager.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <private/qwaylandwindow_p.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qwayland-ext-background-effect-v1.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
|
||||||
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
namespace qs::wayland::background_effect::impl {
|
||||||
|
|
||||||
|
BackgroundEffectManager::BackgroundEffectManager(): QWaylandClientExtensionTemplate(1) {
|
||||||
|
this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundEffectSurface*
|
||||||
|
BackgroundEffectManager::createEffectSurface(QtWaylandClient::QWaylandWindow* window) {
|
||||||
|
return new BackgroundEffectSurface(this->get_background_effect(window->surface()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackgroundEffectManager::blurAvailable() const {
|
||||||
|
return this->isActive() && this->mBlurAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffectManager::ext_background_effect_manager_v1_capabilities(uint32_t flags) {
|
||||||
|
auto available = static_cast<bool>(flags & capability_blur);
|
||||||
|
if (available == this->mBlurAvailable) return;
|
||||||
|
this->mBlurAvailable = available;
|
||||||
|
emit this->blurAvailableChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundEffectManager* BackgroundEffectManager::instance() {
|
||||||
|
static auto* instance = new BackgroundEffectManager(); // NOLINT
|
||||||
|
return instance->isInitialized() ? instance : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::wayland::background_effect::impl
|
||||||
37
src/wayland/background_effect/manager.hpp
Normal file
37
src/wayland/background_effect/manager.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <private/qwaylandwindow_p.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qwayland-ext-background-effect-v1.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
|
||||||
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
namespace qs::wayland::background_effect::impl {
|
||||||
|
|
||||||
|
class BackgroundEffectManager
|
||||||
|
: public QWaylandClientExtensionTemplate<BackgroundEffectManager>
|
||||||
|
, public QtWayland::ext_background_effect_manager_v1 {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BackgroundEffectManager();
|
||||||
|
|
||||||
|
BackgroundEffectSurface* createEffectSurface(QtWaylandClient::QWaylandWindow* window);
|
||||||
|
|
||||||
|
[[nodiscard]] bool blurAvailable() const;
|
||||||
|
|
||||||
|
static BackgroundEffectManager* instance();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void blurAvailableChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ext_background_effect_manager_v1_capabilities(uint32_t flags) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mBlurAvailable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::wayland::background_effect::impl
|
||||||
246
src/wayland/background_effect/qml.cpp
Normal file
246
src/wayland/background_effect/qml.cpp
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
#include "qml.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <private/qhighdpiscaling_p.h>
|
||||||
|
#include <private/qwaylandwindow_p.h>
|
||||||
|
#include <qcoreevent.h>
|
||||||
|
#include <qevent.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qnumeric.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qregion.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
#include <qwindow.h>
|
||||||
|
|
||||||
|
#include "../../core/region.hpp"
|
||||||
|
#include "../../window/proxywindow.hpp"
|
||||||
|
#include "../../window/windowinterface.hpp"
|
||||||
|
#include "manager.hpp"
|
||||||
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
using QtWaylandClient::QWaylandWindow;
|
||||||
|
|
||||||
|
namespace qs::wayland::background_effect {
|
||||||
|
|
||||||
|
BackgroundEffect* BackgroundEffect::qmlAttachedProperties(QObject* object) {
|
||||||
|
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(object);
|
||||||
|
|
||||||
|
if (!proxyWindow) {
|
||||||
|
if (auto* iface = qobject_cast<WindowInterface*>(object)) {
|
||||||
|
proxyWindow = iface->proxyWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!proxyWindow) return nullptr;
|
||||||
|
return new BackgroundEffect(proxyWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundEffect::BackgroundEffect(ProxyWindowBase* window): QObject(nullptr), proxyWindow(window) {
|
||||||
|
QObject::connect(
|
||||||
|
window,
|
||||||
|
&ProxyWindowBase::windowConnected,
|
||||||
|
this,
|
||||||
|
&BackgroundEffect::onWindowConnected
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(window, &ProxyWindowBase::polished, this, &BackgroundEffect::onWindowPolished);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
window,
|
||||||
|
&ProxyWindowBase::devicePixelRatioChanged,
|
||||||
|
this,
|
||||||
|
&BackgroundEffect::updateBlurRegion
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(window, &QObject::destroyed, this, &BackgroundEffect::onProxyWindowDestroyed);
|
||||||
|
|
||||||
|
if (window->backingWindow()) {
|
||||||
|
this->onWindowConnected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingRegion* BackgroundEffect::blurRegion() const { return this->mBlurRegion; }
|
||||||
|
|
||||||
|
void BackgroundEffect::setBlurRegion(PendingRegion* region) {
|
||||||
|
if (region == this->mBlurRegion) return;
|
||||||
|
|
||||||
|
if (this->mBlurRegion) {
|
||||||
|
QObject::disconnect(this->mBlurRegion, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mBlurRegion = region;
|
||||||
|
|
||||||
|
if (region) {
|
||||||
|
QObject::connect(region, &QObject::destroyed, this, &BackgroundEffect::onBlurRegionDestroyed);
|
||||||
|
QObject::connect(region, &PendingRegion::changed, this, &BackgroundEffect::updateBlurRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->updateBlurRegion();
|
||||||
|
emit this->blurRegionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::onBlurRegionDestroyed() {
|
||||||
|
this->mBlurRegion = nullptr;
|
||||||
|
this->updateBlurRegion();
|
||||||
|
emit this->blurRegionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::updateBlurRegion() {
|
||||||
|
if (!this->surface || !this->proxyWindow) return;
|
||||||
|
|
||||||
|
this->pendingBlurRegion = true;
|
||||||
|
this->proxyWindow->schedulePolish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::onWindowPolished() {
|
||||||
|
if (!this->surface || !this->pendingBlurRegion) return;
|
||||||
|
if (!this->mWaylandWindow || !this->mWaylandWindow->surface()) {
|
||||||
|
this->pendingBlurRegion = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegion region;
|
||||||
|
if (this->mBlurRegion) {
|
||||||
|
region =
|
||||||
|
this->mBlurRegion->applyTo(QRect(0, 0, this->mWindow->width(), this->mWindow->height()));
|
||||||
|
|
||||||
|
auto scale = QHighDpiScaling::factor(this->mWindow);
|
||||||
|
if (!qFuzzyCompare(scale, 1.0)) {
|
||||||
|
region = QHighDpi::scale(region, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto margins = this->mWaylandWindow->clientSideMargins();
|
||||||
|
region.translate(margins.left(), margins.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
this->surface->setBlurRegion(region);
|
||||||
|
this->pendingBlurRegion = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BackgroundEffect::eventFilter(QObject* object, QEvent* event) {
|
||||||
|
if (event->type() == QEvent::PlatformSurface) {
|
||||||
|
auto* surfaceEvent = dynamic_cast<QPlatformSurfaceEvent*>(event);
|
||||||
|
if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
|
||||||
|
this->surface = nullptr;
|
||||||
|
this->pendingBlurRegion = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->QObject::eventFilter(object, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::onWindowConnected() {
|
||||||
|
this->mWindow = this->proxyWindow->backingWindow();
|
||||||
|
this->mWindow->installEventFilter(this);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->mWindow,
|
||||||
|
&QWindow::visibleChanged,
|
||||||
|
this,
|
||||||
|
&BackgroundEffect::onWindowVisibleChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
this->onWindowVisibleChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::onWindowVisibleChanged() {
|
||||||
|
if (this->mWindow->isVisible()) {
|
||||||
|
if (!this->mWindow->handle()) {
|
||||||
|
this->mWindow->create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* window = dynamic_cast<QWaylandWindow*>(this->mWindow->handle());
|
||||||
|
if (window == this->mWaylandWindow) return;
|
||||||
|
|
||||||
|
if (this->mWaylandWindow) {
|
||||||
|
QObject::disconnect(this->mWaylandWindow, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mWaylandWindow = window;
|
||||||
|
if (!window) return;
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->mWaylandWindow,
|
||||||
|
&QObject::destroyed,
|
||||||
|
this,
|
||||||
|
&BackgroundEffect::onWaylandWindowDestroyed
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->mWaylandWindow,
|
||||||
|
&QWaylandWindow::surfaceCreated,
|
||||||
|
this,
|
||||||
|
&BackgroundEffect::onWaylandSurfaceCreated
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->mWaylandWindow,
|
||||||
|
&QWaylandWindow::surfaceDestroyed,
|
||||||
|
this,
|
||||||
|
&BackgroundEffect::onWaylandSurfaceDestroyed
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this->mWaylandWindow->surface()) {
|
||||||
|
this->onWaylandSurfaceCreated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::onWaylandWindowDestroyed() { this->mWaylandWindow = nullptr; }
|
||||||
|
|
||||||
|
void BackgroundEffect::onWaylandSurfaceCreated() {
|
||||||
|
auto* manager = impl::BackgroundEffectManager::instance();
|
||||||
|
|
||||||
|
if (!manager) {
|
||||||
|
qWarning() << "Cannot enable background effect as ext-background-effect-v1 is not supported "
|
||||||
|
"by the current compositor.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steal protocol surface from previous BackgroundEffect to avoid duplicate-attachment on reload.
|
||||||
|
auto v = this->mWaylandWindow->property("qs_background_effect");
|
||||||
|
if (v.canConvert<BackgroundEffect*>()) {
|
||||||
|
auto* prev = v.value<BackgroundEffect*>();
|
||||||
|
if (prev != this && prev->surface) {
|
||||||
|
this->surface.swap(prev->surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->surface) {
|
||||||
|
this->surface = std::unique_ptr<impl::BackgroundEffectSurface>(
|
||||||
|
manager->createEffectSurface(this->mWaylandWindow)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mWaylandWindow->setProperty("qs_background_effect", QVariant::fromValue(this));
|
||||||
|
|
||||||
|
this->pendingBlurRegion = this->mBlurRegion != nullptr;
|
||||||
|
if (this->pendingBlurRegion) {
|
||||||
|
this->proxyWindow->schedulePolish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::onWaylandSurfaceDestroyed() {
|
||||||
|
this->surface = nullptr;
|
||||||
|
this->pendingBlurRegion = false;
|
||||||
|
|
||||||
|
if (!this->proxyWindow) {
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffect::onProxyWindowDestroyed() {
|
||||||
|
// Don't delete the BackgroundEffect, and therefore the impl::BackgroundEffectSurface
|
||||||
|
// until the wl_surface is destroyed. Deleting it when the proxy window is deleted would
|
||||||
|
// cause a frame without blur between the destruction of the ext_background_effect_surface_v1
|
||||||
|
// and wl_surface objects.
|
||||||
|
|
||||||
|
this->proxyWindow = nullptr;
|
||||||
|
|
||||||
|
if (this->surface == nullptr) {
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::wayland::background_effect
|
||||||
80
src/wayland/background_effect/qml.hpp
Normal file
80
src/wayland/background_effect/qml.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <private/qwaylandwindow_p.h>
|
||||||
|
#include <qcoreevent.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qwindow.h>
|
||||||
|
|
||||||
|
#include "../../core/region.hpp"
|
||||||
|
#include "../../window/proxywindow.hpp"
|
||||||
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
namespace qs::wayland::background_effect {
|
||||||
|
|
||||||
|
///! Background blur effect for Wayland surfaces.
|
||||||
|
/// Applies background blur behind a @@Quickshell.QsWindow or subclass,
|
||||||
|
/// as an attached object, using the [ext-background-effect-v1] Wayland protocol.
|
||||||
|
///
|
||||||
|
/// > [!NOTE] Using a background effect requires the compositor support the
|
||||||
|
/// > [ext-background-effect-v1] protocol.
|
||||||
|
///
|
||||||
|
/// [ext-background-effect-v1]: https://wayland.app/protocols/ext-background-effect-v1
|
||||||
|
///
|
||||||
|
/// #### Example
|
||||||
|
/// ```qml
|
||||||
|
/// @@Quickshell.PanelWindow {
|
||||||
|
/// id: root
|
||||||
|
/// color: "#80000000"
|
||||||
|
///
|
||||||
|
/// BackgroundEffect.blurRegion: Region { item: root.contentItem }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
class BackgroundEffect: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
// clang-format off
|
||||||
|
/// Region to blur behind the surface. Set to null to remove blur.
|
||||||
|
Q_PROPERTY(PendingRegion* blurRegion READ blurRegion WRITE setBlurRegion NOTIFY blurRegionChanged);
|
||||||
|
// clang-format on
|
||||||
|
QML_ELEMENT;
|
||||||
|
QML_UNCREATABLE("BackgroundEffect can only be used as an attached object.");
|
||||||
|
QML_ATTACHED(BackgroundEffect);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BackgroundEffect(ProxyWindowBase* window);
|
||||||
|
|
||||||
|
[[nodiscard]] PendingRegion* blurRegion() const;
|
||||||
|
void setBlurRegion(PendingRegion* region);
|
||||||
|
|
||||||
|
static BackgroundEffect* qmlAttachedProperties(QObject* object);
|
||||||
|
|
||||||
|
bool eventFilter(QObject* object, QEvent* event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void blurRegionChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onWindowConnected();
|
||||||
|
void onWindowVisibleChanged();
|
||||||
|
void onWaylandWindowDestroyed();
|
||||||
|
void onWaylandSurfaceCreated();
|
||||||
|
void onWaylandSurfaceDestroyed();
|
||||||
|
void onProxyWindowDestroyed();
|
||||||
|
void onBlurRegionDestroyed();
|
||||||
|
void onWindowPolished();
|
||||||
|
void updateBlurRegion();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ProxyWindowBase* proxyWindow = nullptr;
|
||||||
|
QWindow* mWindow = nullptr;
|
||||||
|
QtWaylandClient::QWaylandWindow* mWaylandWindow = nullptr;
|
||||||
|
|
||||||
|
bool pendingBlurRegion = false;
|
||||||
|
PendingRegion* mBlurRegion = nullptr;
|
||||||
|
std::unique_ptr<impl::BackgroundEffectSurface> surface;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::wayland::background_effect
|
||||||
37
src/wayland/background_effect/surface.cpp
Normal file
37
src/wayland/background_effect/surface.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
#include <private/qwaylanddisplay_p.h>
|
||||||
|
#include <private/qwaylandintegration_p.h>
|
||||||
|
#include <qregion.h>
|
||||||
|
#include <qwayland-ext-background-effect-v1.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
|
||||||
|
namespace qs::wayland::background_effect::impl {
|
||||||
|
|
||||||
|
BackgroundEffectSurface::BackgroundEffectSurface(
|
||||||
|
::ext_background_effect_surface_v1* surface // NOLINT(misc-include-cleaner)
|
||||||
|
)
|
||||||
|
: QtWayland::ext_background_effect_surface_v1(surface) {}
|
||||||
|
|
||||||
|
BackgroundEffectSurface::~BackgroundEffectSurface() {
|
||||||
|
if (!this->isInitialized()) return;
|
||||||
|
this->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackgroundEffectSurface::setBlurRegion(const QRegion& region) {
|
||||||
|
if (!this->isInitialized()) return;
|
||||||
|
|
||||||
|
if (region.isEmpty()) {
|
||||||
|
this->set_blur_region(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto* waylandIntegration = QtWaylandClient::QWaylandIntegration::instance();
|
||||||
|
auto* display = waylandIntegration->display();
|
||||||
|
|
||||||
|
auto* wlRegion = display->createRegion(region);
|
||||||
|
this->set_blur_region(wlRegion);
|
||||||
|
wl_region_destroy(wlRegion); // NOLINT(misc-include-cleaner)
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::wayland::background_effect::impl
|
||||||
18
src/wayland/background_effect/surface.hpp
Normal file
18
src/wayland/background_effect/surface.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qregion.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
|
#include <qwayland-ext-background-effect-v1.h>
|
||||||
|
|
||||||
|
namespace qs::wayland::background_effect::impl {
|
||||||
|
|
||||||
|
class BackgroundEffectSurface: public QtWayland::ext_background_effect_surface_v1 {
|
||||||
|
public:
|
||||||
|
explicit BackgroundEffectSurface(::ext_background_effect_surface_v1* surface);
|
||||||
|
~BackgroundEffectSurface() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(BackgroundEffectSurface);
|
||||||
|
|
||||||
|
void setBlurRegion(const QRegion& region);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::wayland::background_effect::impl
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
|
||||||
|
FloatingWindow {
|
||||||
|
id: root
|
||||||
|
color: "transparent"
|
||||||
|
contentItem.palette.windowText: "white"
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: enableBox
|
||||||
|
checked: true
|
||||||
|
text: "Enable Blur"
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Hide->Show"
|
||||||
|
onClicked: {
|
||||||
|
root.visible = false
|
||||||
|
showTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: showTimer
|
||||||
|
interval: 200
|
||||||
|
onTriggered: root.visible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: radiusSlider
|
||||||
|
from: 0
|
||||||
|
to: 1000
|
||||||
|
value: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
component EdgeSlider: Slider {
|
||||||
|
from: -1
|
||||||
|
to: 1000
|
||||||
|
value: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeSlider { id: topLeftSlider }
|
||||||
|
EdgeSlider { id: topRightSlider }
|
||||||
|
EdgeSlider { id: bottomLeftSlider }
|
||||||
|
EdgeSlider { id: bottomRightSlider }
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundEffect.blurRegion: Region {
|
||||||
|
item: enableBox.checked ? root.contentItem : null
|
||||||
|
radius: radiusSlider.value == -1 ? undefined : radiusSlider.value
|
||||||
|
topLeftRadius: topLeftSlider.value == -1 ? undefined : topLeftSlider.value
|
||||||
|
topRightRadius: topRightSlider.value == -1 ? undefined : topRightSlider.value
|
||||||
|
bottomLeftRadius: bottomLeftSlider.value == -1 ? undefined : bottomLeftSlider.value
|
||||||
|
bottomRightRadius: bottomRightSlider.value == -1 ? undefined : bottomRightSlider.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
#include <EGL/egl.h>
|
#include <EGL/egl.h>
|
||||||
#include <EGL/eglext.h>
|
#include <EGL/eglext.h>
|
||||||
#include <GL/gl.h>
|
#include <GL/gl.h>
|
||||||
#include <GLES3/gl32.h>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <gbm.h>
|
#include <gbm.h>
|
||||||
#include <libdrm/drm_fourcc.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
#include <qwindow.h>
|
#include <qwindow.h>
|
||||||
|
|
||||||
#include "../../../window/proxywindow.hpp"
|
#include "../../../window/proxywindow.hpp"
|
||||||
#include "../../../window/windowinterface.hpp"
|
|
||||||
#include "grab.hpp"
|
#include "grab.hpp"
|
||||||
#include "manager.hpp"
|
#include "manager.hpp"
|
||||||
|
|
||||||
|
|
@ -38,8 +37,51 @@ QObjectList HyprlandFocusGrab::windows() const { return this->windowObjects; }
|
||||||
|
|
||||||
void HyprlandFocusGrab::setWindows(QObjectList windows) {
|
void HyprlandFocusGrab::setWindows(QObjectList windows) {
|
||||||
if (windows == this->windowObjects) return;
|
if (windows == this->windowObjects) return;
|
||||||
|
if (this->grab) this->grab->startTransaction();
|
||||||
|
|
||||||
|
for (auto* obj: this->windowObjects) {
|
||||||
|
if (windows.contains(obj)) continue;
|
||||||
|
QObject::disconnect(obj, nullptr, this, nullptr);
|
||||||
|
|
||||||
|
auto* proxy = ProxyWindowBase::forObject(obj);
|
||||||
|
if (!proxy) continue;
|
||||||
|
|
||||||
|
QObject::disconnect(proxy, nullptr, this, nullptr);
|
||||||
|
|
||||||
|
if (this->grab && proxy->backingWindow()) {
|
||||||
|
this->grab->removeWindow(proxy->backingWindow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = windows.begin(); it != windows.end();) {
|
||||||
|
auto* proxy = ProxyWindowBase::forObject(*it);
|
||||||
|
if (!proxy) {
|
||||||
|
it = windows.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->windowObjects.contains(*it)) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(*it, &QObject::destroyed, this, &HyprlandFocusGrab::onObjectDestroyed);
|
||||||
|
QObject::connect(
|
||||||
|
proxy,
|
||||||
|
&ProxyWindowBase::windowConnected,
|
||||||
|
this,
|
||||||
|
&HyprlandFocusGrab::onProxyConnected
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this->grab && proxy->backingWindow()) {
|
||||||
|
this->grab->addWindow(proxy->backingWindow());
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->grab) this->grab->completeTransaction();
|
||||||
this->windowObjects = std::move(windows);
|
this->windowObjects = std::move(windows);
|
||||||
this->syncWindows();
|
|
||||||
emit this->windowsChanged();
|
emit this->windowsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,59 +117,18 @@ void HyprlandFocusGrab::tryActivate() {
|
||||||
QObject::connect(this->grab, &FocusGrab::cleared, this, &HyprlandFocusGrab::onGrabCleared);
|
QObject::connect(this->grab, &FocusGrab::cleared, this, &HyprlandFocusGrab::onGrabCleared);
|
||||||
|
|
||||||
this->grab->startTransaction();
|
this->grab->startTransaction();
|
||||||
for (auto* proxy: this->trackedProxies) {
|
for (auto* obj: this->windowObjects) {
|
||||||
if (proxy->backingWindow() != nullptr) {
|
auto* proxy = ProxyWindowBase::forObject(obj);
|
||||||
|
if (proxy && proxy->backingWindow()) {
|
||||||
this->grab->addWindow(proxy->backingWindow());
|
this->grab->addWindow(proxy->backingWindow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->grab->completeTransaction();
|
this->grab->completeTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprlandFocusGrab::syncWindows() {
|
void HyprlandFocusGrab::onObjectDestroyed(QObject* object) {
|
||||||
auto newProxy = QList<ProxyWindowBase*>();
|
this->windowObjects.removeOne(object);
|
||||||
for (auto* windowObject: this->windowObjects) {
|
emit this->windowsChanged();
|
||||||
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(windowObject);
|
|
||||||
|
|
||||||
if (proxyWindow == nullptr) {
|
|
||||||
if (auto* iface = qobject_cast<WindowInterface*>(windowObject)) {
|
|
||||||
proxyWindow = iface->proxyWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proxyWindow != nullptr) {
|
|
||||||
newProxy.push_back(proxyWindow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->grab) this->grab->startTransaction();
|
|
||||||
|
|
||||||
for (auto* oldWindow: this->trackedProxies) {
|
|
||||||
if (!newProxy.contains(oldWindow)) {
|
|
||||||
QObject::disconnect(oldWindow, nullptr, this, nullptr);
|
|
||||||
|
|
||||||
if (this->grab != nullptr && oldWindow->backingWindow() != nullptr) {
|
|
||||||
this->grab->removeWindow(oldWindow->backingWindow());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto* newProxy: newProxy) {
|
|
||||||
if (!this->trackedProxies.contains(newProxy)) {
|
|
||||||
QObject::connect(
|
|
||||||
newProxy,
|
|
||||||
&ProxyWindowBase::windowConnected,
|
|
||||||
this,
|
|
||||||
&HyprlandFocusGrab::onProxyConnected
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this->grab != nullptr && newProxy->backingWindow() != nullptr) {
|
|
||||||
this->grab->addWindow(newProxy->backingWindow());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->trackedProxies = newProxy;
|
|
||||||
if (this->grab) this->grab->completeTransaction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace qs::hyprland
|
} // namespace qs::hyprland
|
||||||
|
|
|
||||||
|
|
@ -96,15 +96,13 @@ private slots:
|
||||||
void onGrabActivated();
|
void onGrabActivated();
|
||||||
void onGrabCleared();
|
void onGrabCleared();
|
||||||
void onProxyConnected();
|
void onProxyConnected();
|
||||||
|
void onObjectDestroyed(QObject* object);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void tryActivate();
|
void tryActivate();
|
||||||
void syncWindows();
|
|
||||||
|
|
||||||
bool targetActive = false;
|
bool targetActive = false;
|
||||||
QObjectList windowObjects;
|
QObjectList windowObjects;
|
||||||
QList<ProxyWindowBase*> trackedProxies;
|
|
||||||
QList<QWindow*> trackedWindows;
|
|
||||||
|
|
||||||
focus_grab::FocusGrab* grab = nullptr;
|
focus_grab::FocusGrab* grab = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
#include "../../../core/region.hpp"
|
#include "../../../core/region.hpp"
|
||||||
#include "../../../window/proxywindow.hpp"
|
#include "../../../window/proxywindow.hpp"
|
||||||
#include "../../../window/windowinterface.hpp"
|
|
||||||
#include "manager.hpp"
|
#include "manager.hpp"
|
||||||
#include "surface.hpp"
|
#include "surface.hpp"
|
||||||
|
|
||||||
|
|
@ -23,13 +22,7 @@ using QtWaylandClient::QWaylandWindow;
|
||||||
namespace qs::hyprland::surface {
|
namespace qs::hyprland::surface {
|
||||||
|
|
||||||
HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) {
|
HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) {
|
||||||
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(object);
|
auto* proxyWindow = ProxyWindowBase::forObject(object);
|
||||||
|
|
||||||
if (!proxyWindow) {
|
|
||||||
if (auto* iface = qobject_cast<WindowInterface*>(object)) {
|
|
||||||
proxyWindow = iface->proxyWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!proxyWindow) return nullptr;
|
if (!proxyWindow) return nullptr;
|
||||||
return new HyprlandWindow(proxyWindow);
|
return new HyprlandWindow(proxyWindow);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
#include "../../window/proxywindow.hpp"
|
#include "../../window/proxywindow.hpp"
|
||||||
#include "../../window/windowinterface.hpp"
|
|
||||||
#include "proto.hpp"
|
#include "proto.hpp"
|
||||||
|
|
||||||
namespace qs::wayland::idle_inhibit {
|
namespace qs::wayland::idle_inhibit {
|
||||||
|
|
@ -25,27 +24,13 @@ QObject* IdleInhibitor::window() const { return this->bWindowObject; }
|
||||||
void IdleInhibitor::setWindow(QObject* window) {
|
void IdleInhibitor::setWindow(QObject* window) {
|
||||||
if (window == this->bWindowObject) return;
|
if (window == this->bWindowObject) return;
|
||||||
|
|
||||||
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window);
|
auto* proxyWindow = ProxyWindowBase::forObject(window);
|
||||||
|
|
||||||
if (proxyWindow == nullptr) {
|
|
||||||
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
|
|
||||||
proxyWindow = iface->proxyWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->bWindowObject = proxyWindow ? window : nullptr;
|
this->bWindowObject = proxyWindow ? window : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdleInhibitor::boundWindowChanged() {
|
void IdleInhibitor::boundWindowChanged() {
|
||||||
auto* window = this->bBoundWindow.value();
|
auto* window = this->bBoundWindow.value();
|
||||||
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window);
|
auto* proxyWindow = ProxyWindowBase::forObject(window);
|
||||||
|
|
||||||
if (proxyWindow == nullptr) {
|
|
||||||
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
|
|
||||||
proxyWindow = iface->proxyWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proxyWindow == this->proxyWindow) return;
|
if (proxyWindow == this->proxyWindow) return;
|
||||||
|
|
||||||
if (this->mWaylandWindow) {
|
if (this->mWaylandWindow) {
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,9 @@ class WaylandPlugin: public QsEnginePlugin {
|
||||||
return isWayland;
|
return isWayland;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void preinit() override { installWlProxySafeDeref(); }
|
||||||
|
|
||||||
void init() override {
|
void init() override {
|
||||||
installWlProxySafeDeref();
|
|
||||||
installPlatformMenuHook();
|
installPlatformMenuHook();
|
||||||
installPopupPositioner();
|
installPopupPositioner();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,6 @@ headers = [
|
||||||
"idle_inhibit/inhibitor.hpp",
|
"idle_inhibit/inhibitor.hpp",
|
||||||
"idle_notify/monitor.hpp",
|
"idle_notify/monitor.hpp",
|
||||||
"shortcuts_inhibit/inhibitor.hpp",
|
"shortcuts_inhibit/inhibitor.hpp",
|
||||||
|
"background_effect/qml.hpp",
|
||||||
]
|
]
|
||||||
-----
|
-----
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
#include <qwindow.h>
|
#include <qwindow.h>
|
||||||
|
|
||||||
#include "../../window/proxywindow.hpp"
|
#include "../../window/proxywindow.hpp"
|
||||||
#include "../../window/windowinterface.hpp"
|
|
||||||
#include "proto.hpp"
|
#include "proto.hpp"
|
||||||
|
|
||||||
namespace qs::wayland::shortcuts_inhibit {
|
namespace qs::wayland::shortcuts_inhibit {
|
||||||
|
|
@ -48,14 +47,7 @@ ShortcutInhibitor::~ShortcutInhibitor() {
|
||||||
|
|
||||||
void ShortcutInhibitor::onBoundWindowChanged() {
|
void ShortcutInhibitor::onBoundWindowChanged() {
|
||||||
auto* window = this->bBoundWindow.value();
|
auto* window = this->bBoundWindow.value();
|
||||||
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window);
|
auto* proxyWindow = ProxyWindowBase::forObject(window);
|
||||||
|
|
||||||
if (!proxyWindow) {
|
|
||||||
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
|
|
||||||
proxyWindow = iface->proxyWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proxyWindow == this->proxyWindow) return;
|
if (proxyWindow == this->proxyWindow) return;
|
||||||
|
|
||||||
if (this->proxyWindow) {
|
if (this->proxyWindow) {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
#include "../../core/qmlscreen.hpp"
|
#include "../../core/qmlscreen.hpp"
|
||||||
#include "../../core/util.hpp"
|
#include "../../core/util.hpp"
|
||||||
#include "../../window/proxywindow.hpp"
|
#include "../../window/proxywindow.hpp"
|
||||||
#include "../../window/windowinterface.hpp"
|
|
||||||
#include "../output_tracking.hpp"
|
#include "../output_tracking.hpp"
|
||||||
#include "handle.hpp"
|
#include "handle.hpp"
|
||||||
#include "manager.hpp"
|
#include "manager.hpp"
|
||||||
|
|
@ -73,13 +72,7 @@ void Toplevel::fullscreenOn(QuickshellScreenInfo* screen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toplevel::setRectangle(QObject* window, QRect rect) {
|
void Toplevel::setRectangle(QObject* window, QRect rect) {
|
||||||
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window);
|
auto* proxyWindow = ProxyWindowBase::forObject(window);
|
||||||
|
|
||||||
if (proxyWindow == nullptr) {
|
|
||||||
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
|
|
||||||
proxyWindow = iface->proxyWindow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proxyWindow != this->rectWindow) {
|
if (proxyWindow != this->rectWindow) {
|
||||||
if (this->rectWindow != nullptr) {
|
if (this->rectWindow != nullptr) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,12 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent)
|
||||||
|
|
||||||
ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); }
|
ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); }
|
||||||
|
|
||||||
|
ProxyWindowBase* ProxyWindowBase::forObject(QObject* obj) {
|
||||||
|
if (auto* proxy = qobject_cast<ProxyWindowBase*>(obj)) return proxy;
|
||||||
|
if (auto* iface = qobject_cast<WindowInterface*>(obj)) return iface->proxyWindow();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::onReload(QObject* oldInstance) {
|
void ProxyWindowBase::onReload(QObject* oldInstance) {
|
||||||
if (this->mVisible) this->window = this->retrieveWindow(oldInstance);
|
if (this->mVisible) this->window = this->retrieveWindow(oldInstance);
|
||||||
auto wasVisible = this->window != nullptr && this->window->isVisible();
|
auto wasVisible = this->window != nullptr && this->window->isVisible();
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ public:
|
||||||
explicit ProxyWindowBase(QObject* parent = nullptr);
|
explicit ProxyWindowBase(QObject* parent = nullptr);
|
||||||
~ProxyWindowBase() override;
|
~ProxyWindowBase() override;
|
||||||
|
|
||||||
|
static ProxyWindowBase* forObject(QObject* obj);
|
||||||
|
|
||||||
ProxyWindowBase(ProxyWindowBase&) = delete;
|
ProxyWindowBase(ProxyWindowBase&) = delete;
|
||||||
ProxyWindowBase(ProxyWindowBase&&) = delete;
|
ProxyWindowBase(ProxyWindowBase&&) = delete;
|
||||||
void operator=(ProxyWindowBase&) = delete;
|
void operator=(ProxyWindowBase&) = delete;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue