mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2026-04-10 06:11:54 +10:00
Compare commits
No commits in common. "6a244c3c560b45f3b860ed6c0fc54d0291ab6f57" and "0cb62920a7ab0b199754c941046ae86e3a1c368d" have entirely different histories.
6a244c3c56
...
0cb62920a7
19 changed files with 6 additions and 762 deletions
|
|
@ -27,8 +27,6 @@ set shell id.
|
|||
- Added a way to detect if an icon is from the system icon theme or not.
|
||||
- Added vulkan support to screencopy.
|
||||
- Added generic WindowManager interface implementing ext-workspace.
|
||||
- Added ext-background-effect window blur support.
|
||||
- Added per-corner radius support to Region.
|
||||
|
||||
## Other Changes
|
||||
|
||||
|
|
@ -40,7 +38,6 @@ set shell id.
|
|||
- Added `QS_DISABLE_FILE_WATCHER` environment variable to disable file watching.
|
||||
- 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 `AppId` pragma and `QS_APP_ID` environment variable to allow overriding the desktop application ID.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@
|
|||
#include <qdatastream.h>
|
||||
|
||||
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
|
||||
stream << info.instanceId << info.configPath << info.shellId << info.appId << info.launchTime
|
||||
<< info.pid << info.display;
|
||||
stream << info.instanceId << info.configPath << info.shellId << info.launchTime << info.pid
|
||||
<< info.display;
|
||||
return stream;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
|
||||
stream >> info.instanceId >> info.configPath >> info.shellId >> info.appId >> info.launchTime
|
||||
>> info.pid >> info.display;
|
||||
stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime >> info.pid
|
||||
>> info.display;
|
||||
return stream;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ struct InstanceInfo {
|
|||
QString instanceId;
|
||||
QString configPath;
|
||||
QString shellId;
|
||||
QString appId;
|
||||
QDateTime launchTime;
|
||||
pid_t pid = -1;
|
||||
QString display;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
#include <qlist.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qmutex.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qobject.h>
|
||||
#include <qobjectdefs.h>
|
||||
|
|
@ -221,7 +220,6 @@ void LogManager::messageHandler(
|
|||
}
|
||||
|
||||
if (display) {
|
||||
auto locker = QMutexLocker(&self->stdoutMutex);
|
||||
LogMessage::formatMessage(
|
||||
self->stdoutStream,
|
||||
message,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include <qlatin1stringview.h>
|
||||
#include <qlogging.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qmutex.h>
|
||||
#include <qobject.h>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
|
|
@ -136,7 +135,6 @@ private:
|
|||
QHash<QLatin1StringView, CategoryFilter> allFilters;
|
||||
|
||||
QTextStream stdoutStream;
|
||||
QMutex stdoutMutex;
|
||||
LoggingThreadProxy threadProxy;
|
||||
|
||||
friend void initLogCategoryLevel(const char* name, QtMsgType defaultLevel);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#include "region.hpp"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#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::widthChanged, 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);
|
||||
}
|
||||
|
||||
|
|
@ -51,79 +45,6 @@ void PendingRegion::onItemDestroyed() { this->mItem = nullptr; }
|
|||
|
||||
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() {
|
||||
return QQmlListProperty<PendingRegion>(
|
||||
this,
|
||||
|
|
@ -169,60 +90,6 @@ QRegion PendingRegion::build() const {
|
|||
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) {
|
||||
region = childRegion->applyTo(region);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,29 +66,6 @@ class PendingRegion: public QObject {
|
|||
Q_PROPERTY(qint32 width MEMBER mWidth NOTIFY widthChanged);
|
||||
/// Defaults to 0. Does nothing if @@item is set.
|
||||
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.
|
||||
///
|
||||
|
|
@ -114,25 +91,6 @@ public:
|
|||
|
||||
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();
|
||||
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
|
@ -151,11 +109,6 @@ signals:
|
|||
void yChanged();
|
||||
void widthChanged();
|
||||
void heightChanged();
|
||||
void radiusChanged();
|
||||
void topLeftRadiusChanged();
|
||||
void topRightRadiusChanged();
|
||||
void bottomLeftRadiusChanged();
|
||||
void bottomRightRadiusChanged();
|
||||
void childrenChanged();
|
||||
|
||||
/// Triggered when the region's geometry changes.
|
||||
|
|
@ -177,25 +130,12 @@ private:
|
|||
static void
|
||||
regionsReplace(QQmlListProperty<PendingRegion>* prop, qsizetype i, PendingRegion* region);
|
||||
|
||||
enum CornerOverride : quint8 {
|
||||
TopLeft = 0b1,
|
||||
TopRight = 0b10,
|
||||
BottomLeft = 0b100,
|
||||
BottomRight = 0b1000,
|
||||
};
|
||||
|
||||
QQuickItem* mItem = nullptr;
|
||||
|
||||
qint32 mX = 0;
|
||||
qint32 mY = 0;
|
||||
qint32 mWidth = 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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -230,9 +230,7 @@ void qsCheckCrash(int argc, char** argv) {
|
|||
);
|
||||
|
||||
auto app = QApplication(argc, argv);
|
||||
auto desktopId =
|
||||
info.instance.appId.isEmpty() ? QStringLiteral("org.quickshell") : info.instance.appId;
|
||||
QApplication::setDesktopFileName(desktopId);
|
||||
QApplication::setDesktopFileName("org.quickshell");
|
||||
|
||||
auto crashDir = QsPaths::crashDir(info.instance.instanceId);
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
|||
bool useSystemStyle = false;
|
||||
QString iconTheme = qEnvironmentVariable("QS_ICON_THEME");
|
||||
QHash<QString, QString> envOverrides;
|
||||
QString appId = qEnvironmentVariable("QS_APP_ID");
|
||||
QString dataDir;
|
||||
QString stateDir;
|
||||
QString cacheDir;
|
||||
|
|
@ -105,8 +104,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
|||
auto var = envPragma.sliced(0, splitIdx).trimmed();
|
||||
auto val = envPragma.sliced(splitIdx + 1).trimmed();
|
||||
pragmas.envOverrides.insert(var, val);
|
||||
} else if (pragma.startsWith("AppId ")) {
|
||||
pragmas.appId = pragma.sliced(6).trimmed();
|
||||
} else if (pragma.startsWith("ShellId ")) {
|
||||
shellId = pragma.sliced(8).trimmed();
|
||||
} 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;
|
||||
|
||||
auto launchTime = qs::Common::LAUNCH_TIME.toSecsSinceEpoch();
|
||||
auto appId = pragmas.appId.isEmpty() ? QStringLiteral("org.quickshell") : pragmas.appId;
|
||||
|
||||
InstanceInfo::CURRENT = InstanceInfo {
|
||||
.instanceId = base36Encode(getpid()) + base36Encode(launchTime),
|
||||
.configPath = args.configPath,
|
||||
.shellId = shellId,
|
||||
.appId = appId,
|
||||
.launchTime = qs::Common::LAUNCH_TIME,
|
||||
.pid = getpid(),
|
||||
.display = getDisplayConnection(),
|
||||
|
|
@ -237,7 +231,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
|
|||
app = new QGuiApplication(qArgC, argv);
|
||||
}
|
||||
|
||||
QGuiApplication::setDesktopFileName(appId);
|
||||
QGuiApplication::setDesktopFileName("org.quickshell");
|
||||
|
||||
if (args.debugPort != -1) {
|
||||
QQmlDebuggingEnabler::enableDebugging(true);
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ function (wl_proto target name dir)
|
|||
target_include_directories(${target} INTERFACE ${PROTO_BUILD_PATH})
|
||||
target_link_libraries(${target} wl-proto-${name}-wl Qt6::WaylandClient Qt6::WaylandClientPrivate)
|
||||
qs_pch(${target} SET wayland-protocol)
|
||||
target_compile_options(wl-proto-${name}-wl PRIVATE ${wayland_CFLAGS})
|
||||
endfunction()
|
||||
|
||||
# -----
|
||||
|
|
@ -120,9 +119,6 @@ if (HYPRLAND)
|
|||
add_subdirectory(hyprland)
|
||||
endif()
|
||||
|
||||
add_subdirectory(background_effect)
|
||||
list(APPEND WAYLAND_MODULES Quickshell.Wayland._BackgroundEffect)
|
||||
|
||||
add_subdirectory(idle_inhibit)
|
||||
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,5 @@ headers = [
|
|||
"idle_inhibit/inhibitor.hpp",
|
||||
"idle_notify/monitor.hpp",
|
||||
"shortcuts_inhibit/inhibitor.hpp",
|
||||
"background_effect/qml.hpp",
|
||||
]
|
||||
-----
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue