Compare commits

..

1 commit

Author SHA1 Message Date
outfoxxed
b7005e09e7
docs: ask users not to submit v1 crash reports 2026-03-17 03:57:04 -07:00
41 changed files with 132 additions and 903 deletions

View file

@ -1,17 +1,17 @@
name: Crash Report (v1) name: Crash Report (v1)
description: Quickshell has crashed (old) description: Quickshell has crashed (old)
labels: ["unactionable"] labels: ["bug", "crash"]
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 submit this report. preparing for a new release. Please do not report crashes from 0.2.1 or before for now.
- type: checkboxes - type: checkboxes
id: donotcheck id: donotcheck
attributes: attributes:
label: Read the text above. Do not submit the report. label: Do not check this box
options: options:
- label: Yes I want this report to be deleted. - label: Do not check this box
required: true required: true

View file

@ -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 `qs log <path-to-log> -r '*=true'`. You can preview the log if you'd like using `quickshell read-log <path-to-log>`.
validations: validations:
required: true required: true
- type: textarea - type: textarea

View file

@ -87,9 +87,8 @@ include(cmake/util.cmake)
add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension) add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension)
# pipewire defines these, breaking PCH # pipewire defines this, 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)

View file

@ -27,8 +27,6 @@ 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
@ -40,7 +38,6 @@ 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
@ -63,7 +60,6 @@ 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

View file

@ -129,13 +129,12 @@ 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, 6> { auto prefixes = std::array<std::string_view, 5> {
"QS_", "QS_",
"QT_", "QT_",
"QML_", "QML_",
"QML2_", "QML2_",
"QSG_", "QSG_",
"XDG_CURRENT_DESKTOP=",
}; };
for (const auto& prefix: prefixes) { for (const auto& prefix: prefixes) {

View file

@ -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.appId << info.launchTime stream << info.instanceId << info.configPath << info.shellId << info.launchTime << info.pid
<< info.pid << info.display; << 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.appId >> info.launchTime stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime >> info.pid
>> info.pid >> info.display; >> info.display;
return stream; return stream;
} }

View file

@ -9,7 +9,6 @@ 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;

View file

@ -14,7 +14,6 @@
#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>
@ -221,7 +220,6 @@ void LogManager::messageHandler(
} }
if (display) { if (display) {
auto locker = QMutexLocker(&self->stdoutMutex);
LogMessage::formatMessage( LogMessage::formatMessage(
self->stdoutStream, self->stdoutStream,
message, message,

View file

@ -10,7 +10,6 @@
#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>
@ -136,7 +135,6 @@ 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);

View file

@ -18,6 +18,7 @@
#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"
@ -90,8 +91,10 @@ 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 = ProxyWindowBase::forObject(parentWindow)) { } else if (auto* proxy = qobject_cast<ProxyWindowBase*>(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;

View file

@ -9,18 +9,6 @@ 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(); });
@ -28,10 +16,6 @@ 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();
} }

View file

@ -18,14 +18,12 @@ 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();

View file

@ -11,6 +11,7 @@
#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 {
@ -39,8 +40,10 @@ void PopupAnchor::setWindowInternal(QObject* window) {
} }
if (window) { if (window) {
if (auto* proxy = ProxyWindowBase::forObject(window)) { if (auto* proxy = qobject_cast<ProxyWindowBase*>(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.";

View file

@ -1,5 +1,4 @@
#include "region.hpp" #include "region.hpp"
#include <algorithm>
#include <cmath> #include <cmath>
#include <qobject.h> #include <qobject.h>
@ -19,11 +18,6 @@ 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);
} }
@ -51,79 +45,6 @@ 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,
@ -169,60 +90,6 @@ 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);
} }

View file

@ -66,29 +66,6 @@ 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.
/// ///
@ -114,25 +91,6 @@ 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;
@ -151,11 +109,6 @@ 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.
@ -177,25 +130,12 @@ 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;
}; };

View file

@ -145,7 +145,10 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
QString overrideText; QString overrideText;
bool isOverridden = false; bool isOverridden = false;
auto& pragmaEngine = *QmlScanner::preprocEngine(); auto pragmaEngine = QJSEngine();
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});
@ -367,13 +370,3 @@ 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;
}

View file

@ -4,7 +4,6 @@
#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>
@ -43,6 +42,4 @@ 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();
}; };

View file

@ -5,11 +5,9 @@
#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>
@ -60,12 +58,6 @@ 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);
@ -87,9 +79,13 @@ 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);
} }
@ -135,6 +131,7 @@ 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>();
@ -158,21 +155,6 @@ 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() {
@ -221,8 +203,6 @@ 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.";
} }

View file

@ -25,7 +25,6 @@
#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"
@ -231,17 +230,12 @@ void qsCheckCrash(int argc, char** argv) {
); );
auto app = QApplication(argc, argv); auto app = QApplication(argc, argv);
auto desktopId = QApplication::setDesktopFileName("org.quickshell");
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);

View file

@ -76,7 +76,6 @@ 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;
@ -105,8 +104,6 @@ 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 ")) {
@ -131,13 +128,10 @@ 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(),
@ -237,7 +231,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
app = new QGuiApplication(qArgC, argv); app = new QGuiApplication(qArgC, argv);
} }
QGuiApplication::setDesktopFileName(appId); QGuiApplication::setDesktopFileName("org.quickshell");
if (args.debugPort != -1) { if (args.debugPort != -1) {
QQmlDebuggingEnabler::enableDebugging(true); QQmlDebuggingEnabler::enableDebugging(true);

View file

@ -68,7 +68,6 @@ 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()
# ----- # -----
@ -84,7 +83,10 @@ 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 "LINKER:--export-dynamic-symbol=wl_proxy_get_listener") target_link_options(quickshell PRIVATE
"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)
@ -120,9 +122,6 @@ 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)

View file

@ -1,24 +0,0 @@
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)

View file

@ -1,38 +0,0 @@
#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

View file

@ -1,37 +0,0 @@
#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

View file

@ -1,246 +0,0 @@
#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

View file

@ -1,80 +0,0 @@
#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

View file

@ -1,37 +0,0 @@
#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

View file

@ -1,18 +0,0 @@
#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

View file

@ -1,62 +0,0 @@
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
}
}

View file

@ -10,6 +10,7 @@
#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>

View file

@ -9,6 +9,7 @@
#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"
@ -37,51 +38,8 @@ 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();
} }
@ -117,18 +75,59 @@ 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* obj: this->windowObjects) { for (auto* proxy: this->trackedProxies) {
auto* proxy = ProxyWindowBase::forObject(obj); if (proxy->backingWindow() != nullptr) {
if (proxy && proxy->backingWindow()) {
this->grab->addWindow(proxy->backingWindow()); this->grab->addWindow(proxy->backingWindow());
} }
} }
this->grab->completeTransaction(); this->grab->completeTransaction();
} }
void HyprlandFocusGrab::onObjectDestroyed(QObject* object) { void HyprlandFocusGrab::syncWindows() {
this->windowObjects.removeOne(object); auto newProxy = QList<ProxyWindowBase*>();
emit this->windowsChanged(); for (auto* windowObject: this->windowObjects) {
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

View file

@ -96,13 +96,15 @@ 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;
}; };

View file

@ -14,6 +14,7 @@
#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"
@ -22,7 +23,13 @@ using QtWaylandClient::QWaylandWindow;
namespace qs::hyprland::surface { namespace qs::hyprland::surface {
HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) { HyprlandWindow* HyprlandWindow::qmlAttachedProperties(QObject* object) {
auto* proxyWindow = ProxyWindowBase::forObject(object); auto* proxyWindow = qobject_cast<ProxyWindowBase*>(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);

View file

@ -6,6 +6,7 @@
#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 {
@ -24,13 +25,27 @@ 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 = ProxyWindowBase::forObject(window); auto* proxyWindow = qobject_cast<ProxyWindowBase*>(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 = ProxyWindowBase::forObject(window); auto* proxyWindow = qobject_cast<ProxyWindowBase*>(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) {

View file

@ -33,9 +33,8 @@ class WaylandPlugin: public QsEnginePlugin {
return isWayland; return isWayland;
} }
void preinit() override { installWlProxySafeDeref(); }
void init() override { void init() override {
installWlProxySafeDeref();
installPlatformMenuHook(); installPlatformMenuHook();
installPopupPositioner(); installPopupPositioner();
} }

View file

@ -8,6 +8,5 @@ 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",
] ]
----- -----

View file

@ -9,6 +9,7 @@
#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 {
@ -47,7 +48,14 @@ ShortcutInhibitor::~ShortcutInhibitor() {
void ShortcutInhibitor::onBoundWindowChanged() { void ShortcutInhibitor::onBoundWindowChanged() {
auto* window = this->bBoundWindow.value(); auto* window = this->bBoundWindow.value();
auto* proxyWindow = ProxyWindowBase::forObject(window); auto* proxyWindow = qobject_cast<ProxyWindowBase*>(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) {

View file

@ -9,6 +9,7 @@
#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"
@ -72,7 +73,13 @@ void Toplevel::fullscreenOn(QuickshellScreenInfo* screen) {
} }
void Toplevel::setRectangle(QObject* window, QRect rect) { void Toplevel::setRectangle(QObject* window, QRect rect) {
auto* proxyWindow = ProxyWindowBase::forObject(window); auto* proxyWindow = qobject_cast<ProxyWindowBase*>(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) {

View file

@ -1,3 +1,4 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <qlogging.h> #include <qlogging.h>
#include <qloggingcategory.h> #include <qloggingcategory.h>

View file

@ -57,12 +57,6 @@ 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();

View file

@ -66,8 +66,6 @@ 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;