mirror of
https://git.outfoxxed.me/quickshell/quickshell.git
synced 2026-04-10 06:11:54 +10:00
Compare commits
14 commits
5eb6f51f4a
...
8e8f27a22a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e8f27a22a | ||
|
|
c3c3e2ca25 | ||
|
|
2cf57f43d5 | ||
|
|
a99519c3ad | ||
|
|
158db16b93 | ||
|
|
e7cd1e9982 | ||
|
|
afbc5ffd4e | ||
|
|
dacfa9de82 | ||
|
|
4429c03837 | ||
|
|
395a1301a8 | ||
|
|
1e4d804e7f | ||
|
|
191085a882 | ||
|
|
8fd0de4580 | ||
|
|
7a427ce197 |
25 changed files with 546 additions and 43 deletions
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
|
|
@ -50,6 +50,7 @@ jobs:
|
|||
wayland-protocols \
|
||||
wayland \
|
||||
libdrm \
|
||||
vulkan-headers \
|
||||
libxcb \
|
||||
libpipewire \
|
||||
cli11 \
|
||||
|
|
|
|||
1
BUILD.md
1
BUILD.md
|
|
@ -146,6 +146,7 @@ To disable: `-DSCREENCOPY=OFF`
|
|||
Dependencies:
|
||||
- `libdrm`
|
||||
- `libgbm`
|
||||
- `vulkan-headers` (build-time)
|
||||
|
||||
Specific protocols can also be disabled:
|
||||
- `DSCREENCOPY_ICC=OFF` - Disable screencopy via [ext-image-copy-capture-v1]
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ set shell id.
|
|||
- Added support for grabbing focus from popup windows.
|
||||
- Added support for IPC signal listeners.
|
||||
- Added Quickshell version checking and version gated preprocessing.
|
||||
- Added a way to detect if an icon is from the system icon theme or not.
|
||||
- Added vulkan support to screencopy.
|
||||
|
||||
## Other Changes
|
||||
|
||||
|
|
@ -35,6 +37,7 @@ set shell id.
|
|||
|
||||
- Fixed volume control breaking with pipewire pro audio mode.
|
||||
- Fixed volume control breaking with bluez streams and potentially others.
|
||||
- Fixed volume control breaking for devices without route definitions.
|
||||
- Fixed escape sequence handling in desktop entries.
|
||||
- Fixed volumes not initializing if a pipewire device was already loaded before its node.
|
||||
- Fixed hyprland active toplevel not resetting after window closes.
|
||||
|
|
@ -43,7 +46,10 @@ set shell id.
|
|||
- Fixed asynchronous loaders not working after reload.
|
||||
- Fixed asynchronous loaders not working before window creation.
|
||||
- Fixed memory leak in IPC handlers.
|
||||
- Fixed ClippingRectangle related crashes.
|
||||
- Fixed crashes when monitors are unplugged.
|
||||
|
||||
## Packaging Changes
|
||||
|
||||
`glib` and `polkit` have been added as dependencies when compiling with polkit agent support.
|
||||
`vulkan-headers` has been added as a build-time dependency for screencopy (Vulkan backend support).
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
xorg,
|
||||
libdrm,
|
||||
libgbm ? null,
|
||||
vulkan-headers,
|
||||
pipewire,
|
||||
pam,
|
||||
polkit,
|
||||
|
|
@ -77,7 +78,7 @@
|
|||
++ lib.optional withJemalloc jemalloc
|
||||
++ lib.optional (withWayland && lib.strings.compareVersions qt6.qtbase.version "6.10.0" == -1) qt6.qtwayland
|
||||
++ lib.optionals withWayland [ wayland wayland-protocols ]
|
||||
++ lib.optionals (withWayland && libgbm != null) [ libdrm libgbm ]
|
||||
++ lib.optionals (withWayland && libgbm != null) [ libdrm libgbm vulkan-headers ]
|
||||
++ lib.optional withX11 xorg.libxcb
|
||||
++ lib.optional withPam pam
|
||||
++ lib.optional withPipewire pipewire
|
||||
|
|
|
|||
|
|
@ -314,6 +314,8 @@ QString QuickshellGlobal::iconPath(const QString& icon, const QString& fallback)
|
|||
return IconImageProvider::requestString(icon, "", fallback);
|
||||
}
|
||||
|
||||
bool QuickshellGlobal::hasThemeIcon(const QString& icon) { return QIcon::hasThemeIcon(icon); }
|
||||
|
||||
bool QuickshellGlobal::hasVersion(qint32 major, qint32 minor, const QStringList& features) {
|
||||
return qs::scan::env::PreprocEnv::hasVersion(major, minor, features);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,8 @@ public:
|
|||
/// Setting the `fallback` parameter of `iconPath` will attempt to load the fallback
|
||||
/// icon if the requested one could not be loaded.
|
||||
Q_INVOKABLE static QString iconPath(const QString& icon, const QString& fallback);
|
||||
/// Check if specified icon has an available icon in your icon theme
|
||||
Q_INVOKABLE static bool hasThemeIcon(const QString& icon);
|
||||
/// Equivalent to `${Quickshell.configDir}/${path}`
|
||||
Q_INVOKABLE [[nodiscard]] QString shellPath(const QString& path) const;
|
||||
/// > [!WARNING] Deprecated: Renamed to @@shellPath() for clarity.
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
|
|||
auto stream = QTextStream(&file);
|
||||
auto imports = QVector<QString>();
|
||||
|
||||
bool inHeader = false;
|
||||
bool inHeader = true;
|
||||
auto ifScopes = QVector<bool>();
|
||||
bool sourceMasked = false;
|
||||
int lineNum = 0;
|
||||
|
|
@ -177,7 +177,7 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
|
|||
} else if (!internal && line == "//@ pragma Internal") {
|
||||
internal = true;
|
||||
} else if (line.contains('{')) {
|
||||
inHeader = true;
|
||||
inHeader = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "scanenv.hpp"
|
||||
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qtenvironmentvariables.h>
|
||||
|
||||
#include "build.hpp"
|
||||
|
||||
|
|
@ -19,4 +20,12 @@ bool PreprocEnv::hasVersion(int major, int minor, const QStringList& features) {
|
|||
return QS_VERSION_MAJOR == major && QS_VERSION_MINOR == minor;
|
||||
}
|
||||
|
||||
QString PreprocEnv::env(const QString& variable) {
|
||||
return qEnvironmentVariable(variable.toStdString().c_str());
|
||||
}
|
||||
|
||||
bool PreprocEnv::isEnvSet(const QString& variable) {
|
||||
return qEnvironmentVariableIsSet(variable.toStdString().c_str());
|
||||
}
|
||||
|
||||
} // namespace qs::scan::env
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ class PreprocEnv: public QObject {
|
|||
public:
|
||||
Q_INVOKABLE static bool
|
||||
hasVersion(int major, int minor, const QStringList& features = QStringList());
|
||||
|
||||
Q_INVOKABLE static QString env(const QString& variable);
|
||||
Q_INVOKABLE static bool isEnvSet(const QString& variable);
|
||||
};
|
||||
|
||||
} // namespace qs::scan::env
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ IpcServerConnection::IpcServerConnection(QLocalSocket* socket, IpcServer* server
|
|||
|
||||
void IpcServerConnection::onDisconnected() {
|
||||
qCInfo(logIpc) << "IPC connection disconnected" << this;
|
||||
delete this;
|
||||
this->deleteLater();
|
||||
}
|
||||
|
||||
void IpcServerConnection::onReadyRead() {
|
||||
|
|
@ -88,7 +88,7 @@ void IpcServerConnection::onReadyRead() {
|
|||
|
||||
// async connections reparent
|
||||
if (dynamic_cast<IpcServer*>(this->parent()) != nullptr) {
|
||||
delete this;
|
||||
this->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -141,6 +141,10 @@ bool PwDevice::tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps
|
|||
return true;
|
||||
}
|
||||
|
||||
bool PwDevice::hasRouteDevice(qint32 routeDevice) const {
|
||||
return this->routeDeviceIndexes.contains(routeDevice);
|
||||
}
|
||||
|
||||
void PwDevice::polled() {
|
||||
// It is far more likely that the list content has not come in yet than it having no entries,
|
||||
// and there isn't a way to check in the case that there *aren't* actually any entries.
|
||||
|
|
|
|||
|
|
@ -12,13 +12,15 @@
|
|||
#include <spa/pod/builder.h>
|
||||
|
||||
#include "core.hpp"
|
||||
#include "node.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace qs::service::pipewire {
|
||||
|
||||
class PwDevice;
|
||||
|
||||
// Forward declare to avoid circular dependency with node.hpp
|
||||
struct PwVolumeProps;
|
||||
|
||||
class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE> {
|
||||
Q_OBJECT;
|
||||
|
||||
|
|
@ -33,6 +35,7 @@ public:
|
|||
[[nodiscard]] bool waitingForDevice() const;
|
||||
|
||||
[[nodiscard]] bool tryLoadVolumeProps(qint32 routeDevice, PwVolumeProps& volumeProps);
|
||||
[[nodiscard]] bool hasRouteDevice(qint32 routeDevice) const;
|
||||
|
||||
signals:
|
||||
void deviceReady();
|
||||
|
|
|
|||
|
|
@ -429,6 +429,10 @@ void PwNodeBoundAudio::setMuted(bool muted) {
|
|||
}
|
||||
|
||||
float PwNodeBoundAudio::averageVolume() const {
|
||||
if (this->mVolumes.isEmpty()) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float total = 0;
|
||||
|
||||
for (auto volume: this->mVolumes) {
|
||||
|
|
@ -572,9 +576,8 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
|
|||
const auto* muteProp = spa_pod_find_prop(param, nullptr, SPA_PROP_mute);
|
||||
const auto* volumeStepProp = spa_pod_find_prop(param, nullptr, SPA_PROP_volumeStep);
|
||||
|
||||
if (volumesProp) {
|
||||
const auto* volumes = reinterpret_cast<const spa_pod_array*>(&volumesProp->value);
|
||||
const auto* channels = reinterpret_cast<const spa_pod_array*>(&channelsProp->value);
|
||||
|
||||
spa_pod* iter = nullptr;
|
||||
SPA_POD_ARRAY_FOREACH(volumes, iter) {
|
||||
// Cubing behavior found in MPD source, and appears to corrospond to everyone else's measurements correctly.
|
||||
|
|
@ -582,12 +585,19 @@ PwVolumeProps PwVolumeProps::parseSpaPod(const spa_pod* param) {
|
|||
auto visual = std::cbrt(linear);
|
||||
props.volumes.push_back(visual);
|
||||
}
|
||||
}
|
||||
|
||||
if (channelsProp) {
|
||||
const auto* channels = reinterpret_cast<const spa_pod_array*>(&channelsProp->value);
|
||||
spa_pod* iter = nullptr;
|
||||
SPA_POD_ARRAY_FOREACH(channels, iter) {
|
||||
props.channels.push_back(*reinterpret_cast<PwAudioChannel::Enum*>(iter));
|
||||
}
|
||||
}
|
||||
|
||||
if (muteProp) {
|
||||
spa_pod_get_bool(&muteProp->value, &props.mute);
|
||||
}
|
||||
|
||||
if (volumeStepProp) {
|
||||
spa_pod_get_float(&volumeStepProp->value, &props.volumeStep);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <spa/pod/pod.h>
|
||||
|
||||
#include "core.hpp"
|
||||
#include "device.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace qs::service::pipewire {
|
||||
|
|
@ -249,7 +250,9 @@ public:
|
|||
bool proAudio = false;
|
||||
|
||||
[[nodiscard]] bool shouldUseDevice() const {
|
||||
return this->device && !this->proAudio && this->routeDevice != -1;
|
||||
if (!this->device || this->proAudio || this->routeDevice == -1) return false;
|
||||
// Only use device control if the device actually has route indexes for this routeDevice
|
||||
return this->device->hasRouteDevice(this->routeDevice);
|
||||
}
|
||||
|
||||
signals:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(dmabuf-deps REQUIRED IMPORTED_TARGET libdrm gbm egl)
|
||||
|
||||
find_package(VulkanHeaders REQUIRED)
|
||||
|
||||
qt_add_library(quickshell-wayland-buffer STATIC
|
||||
manager.cpp
|
||||
dmabuf.cpp
|
||||
|
|
@ -10,9 +12,10 @@ qt_add_library(quickshell-wayland-buffer STATIC
|
|||
wl_proto(wlp-linux-dmabuf linux-dmabuf-v1 "${WAYLAND_PROTOCOLS}/stable/linux-dmabuf")
|
||||
|
||||
target_link_libraries(quickshell-wayland-buffer PRIVATE
|
||||
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
|
||||
Qt::Quick Qt::QuickPrivate Qt::GuiPrivate Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
|
||||
PkgConfig::dmabuf-deps
|
||||
wlp-linux-dmabuf
|
||||
Vulkan::Headers
|
||||
)
|
||||
|
||||
qs_pch(quickshell-wayland-buffer SET large)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
#include <fcntl.h>
|
||||
#include <gbm.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <private/qquickwindow_p.h>
|
||||
#include <private/qrhivulkan_p.h>
|
||||
#include <qcontainerfwd.h>
|
||||
#include <qdebug.h>
|
||||
#include <qlist.h>
|
||||
|
|
@ -24,12 +26,17 @@
|
|||
#include <qpair.h>
|
||||
#include <qquickwindow.h>
|
||||
#include <qscopedpointer.h>
|
||||
#include <qsgrendererinterface.h>
|
||||
#include <qsgtexture_platform.h>
|
||||
#include <qtypes.h>
|
||||
#include <qvulkanfunctions.h>
|
||||
#include <qvulkaninstance.h>
|
||||
#include <qwayland-linux-dmabuf-v1.h>
|
||||
#include <qwaylandclientextension.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <wayland-linux-dmabuf-v1-client-protocol.h>
|
||||
#include <wayland-util.h>
|
||||
|
|
@ -48,6 +55,36 @@ QS_LOGGING_CATEGORY(logDmabuf, "quickshell.wayland.buffer.dmabuf", QtWarningMsg)
|
|||
|
||||
LinuxDmabufManager* MANAGER = nullptr; // NOLINT
|
||||
|
||||
VkFormat drmFormatToVkFormat(uint32_t drmFormat) {
|
||||
// NOLINTBEGIN(bugprone-branch-clone): XRGB/ARGB intentionally map to the same VK format
|
||||
switch (drmFormat) {
|
||||
case DRM_FORMAT_ARGB8888: return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
case DRM_FORMAT_XRGB8888: return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
case DRM_FORMAT_ABGR8888: return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case DRM_FORMAT_XBGR8888: return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case DRM_FORMAT_ARGB2101010: return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
|
||||
case DRM_FORMAT_XRGB2101010: return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
|
||||
case DRM_FORMAT_ABGR2101010: return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
case DRM_FORMAT_XBGR2101010: return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
case DRM_FORMAT_ABGR16161616F: return VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
case DRM_FORMAT_RGB565: return VK_FORMAT_R5G6B5_UNORM_PACK16;
|
||||
case DRM_FORMAT_BGR565: return VK_FORMAT_B5G6R5_UNORM_PACK16;
|
||||
default: return VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
// NOLINTEND(bugprone-branch-clone)
|
||||
}
|
||||
|
||||
bool drmFormatHasAlpha(uint32_t drmFormat) {
|
||||
switch (drmFormat) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_ARGB2101010:
|
||||
case DRM_FORMAT_ABGR2101010:
|
||||
case DRM_FORMAT_ABGR16161616F: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QDebug& operator<<(QDebug& debug, const FourCCStr& fourcc) {
|
||||
|
|
@ -81,25 +118,27 @@ GbmDeviceHandle::~GbmDeviceHandle() {
|
|||
}
|
||||
}
|
||||
|
||||
// This will definitely backfire later
|
||||
// Prefer ARGB over XRGB: XRGB has undefined alpha bytes which cause
|
||||
// transparency artifacts on Vulkan (notably Intel GPUs) since Vulkan
|
||||
// doesn't auto-fill alpha=1.0 for X formats like EGL does.
|
||||
void LinuxDmabufFormatSelection::ensureSorted() {
|
||||
if (this->sorted) return;
|
||||
auto beginIter = this->formats.begin();
|
||||
|
||||
auto xrgbIter = std::ranges::find_if(this->formats, [](const auto& format) {
|
||||
return format.first == DRM_FORMAT_XRGB8888;
|
||||
});
|
||||
|
||||
if (xrgbIter != this->formats.end()) {
|
||||
std::swap(*beginIter, *xrgbIter);
|
||||
++beginIter;
|
||||
}
|
||||
|
||||
auto argbIter = std::ranges::find_if(this->formats, [](const auto& format) {
|
||||
return format.first == DRM_FORMAT_ARGB8888;
|
||||
});
|
||||
|
||||
if (argbIter != this->formats.end()) std::swap(*beginIter, *argbIter);
|
||||
if (argbIter != this->formats.end()) {
|
||||
std::swap(*beginIter, *argbIter);
|
||||
++beginIter;
|
||||
}
|
||||
|
||||
auto xrgbIter = std::ranges::find_if(this->formats, [](const auto& format) {
|
||||
return format.first == DRM_FORMAT_XRGB8888;
|
||||
});
|
||||
|
||||
if (xrgbIter != this->formats.end()) std::swap(*beginIter, *xrgbIter);
|
||||
|
||||
this->sorted = true;
|
||||
}
|
||||
|
|
@ -532,6 +571,15 @@ bool WlDmaBuffer::isCompatible(const WlBufferRequest& request) const {
|
|||
}
|
||||
|
||||
WlBufferQSGTexture* WlDmaBuffer::createQsgTexture(QQuickWindow* window) const {
|
||||
auto* ri = window->rendererInterface();
|
||||
if (ri && ri->graphicsApi() == QSGRendererInterface::Vulkan) {
|
||||
return this->createQsgTextureVulkan(window);
|
||||
}
|
||||
|
||||
return this->createQsgTextureGl(window);
|
||||
}
|
||||
|
||||
WlBufferQSGTexture* WlDmaBuffer::createQsgTextureGl(QQuickWindow* window) const {
|
||||
static auto* glEGLImageTargetTexture2DOES = []() {
|
||||
auto* fn = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(
|
||||
eglGetProcAddress("glEGLImageTargetTexture2DOES")
|
||||
|
|
@ -662,6 +710,313 @@ WlBufferQSGTexture* WlDmaBuffer::createQsgTexture(QQuickWindow* window) const {
|
|||
return tex;
|
||||
}
|
||||
|
||||
WlBufferQSGTexture* WlDmaBuffer::createQsgTextureVulkan(QQuickWindow* window) const {
|
||||
auto* ri = window->rendererInterface();
|
||||
auto* vkInst = window->vulkanInstance();
|
||||
|
||||
if (!vkInst) {
|
||||
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: no QVulkanInstance.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* vkDevicePtr =
|
||||
static_cast<VkDevice*>(ri->getResource(window, QSGRendererInterface::DeviceResource));
|
||||
auto* vkPhysDevicePtr = static_cast<VkPhysicalDevice*>(
|
||||
ri->getResource(window, QSGRendererInterface::PhysicalDeviceResource)
|
||||
);
|
||||
|
||||
if (!vkDevicePtr || !vkPhysDevicePtr) {
|
||||
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: could not get Vulkan device.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VkDevice device = *vkDevicePtr;
|
||||
VkPhysicalDevice physDevice = *vkPhysDevicePtr;
|
||||
|
||||
auto* devFuncs = vkInst->deviceFunctions(device);
|
||||
auto* instFuncs = vkInst->functions();
|
||||
|
||||
if (!devFuncs || !instFuncs) {
|
||||
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: "
|
||||
"could not get Vulkan functions.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto getMemoryFdPropertiesKHR = reinterpret_cast<PFN_vkGetMemoryFdPropertiesKHR>(
|
||||
instFuncs->vkGetDeviceProcAddr(device, "vkGetMemoryFdPropertiesKHR")
|
||||
);
|
||||
|
||||
if (!getMemoryFdPropertiesKHR) {
|
||||
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: "
|
||||
"vkGetMemoryFdPropertiesKHR not available. "
|
||||
"Missing VK_KHR_external_memory_fd extension.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const VkFormat vkFormat = drmFormatToVkFormat(this->format);
|
||||
if (vkFormat == VK_FORMAT_UNDEFINED) {
|
||||
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: unsupported DRM format"
|
||||
<< FourCCStr(this->format);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (this->planeCount > 4) {
|
||||
qCWarning(logDmabuf) << "Failed to create Vulkan QSG texture: too many planes"
|
||||
<< this->planeCount;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::array<VkSubresourceLayout, 4> planeLayouts = {};
|
||||
for (int i = 0; i < this->planeCount; ++i) {
|
||||
planeLayouts[i].offset = this->planes[i].offset; // NOLINT
|
||||
planeLayouts[i].rowPitch = this->planes[i].stride; // NOLINT
|
||||
planeLayouts[i].size = 0;
|
||||
planeLayouts[i].arrayPitch = 0;
|
||||
planeLayouts[i].depthPitch = 0;
|
||||
}
|
||||
|
||||
const bool useModifier = this->modifier != DRM_FORMAT_MOD_INVALID;
|
||||
|
||||
VkExternalMemoryImageCreateInfo externalInfo = {};
|
||||
externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO;
|
||||
externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
|
||||
VkImageDrmFormatModifierExplicitCreateInfoEXT modifierInfo = {};
|
||||
modifierInfo.sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT;
|
||||
modifierInfo.drmFormatModifier = this->modifier;
|
||||
modifierInfo.drmFormatModifierPlaneCount = static_cast<uint32_t>(this->planeCount);
|
||||
modifierInfo.pPlaneLayouts = planeLayouts.data();
|
||||
|
||||
if (useModifier) {
|
||||
externalInfo.pNext = &modifierInfo;
|
||||
}
|
||||
|
||||
VkImageCreateInfo imageInfo = {};
|
||||
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
imageInfo.pNext = &externalInfo;
|
||||
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||
imageInfo.format = vkFormat;
|
||||
imageInfo.extent = {.width = this->width, .height = this->height, .depth = 1};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = 1;
|
||||
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
imageInfo.tiling = useModifier ? VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT : VK_IMAGE_TILING_LINEAR;
|
||||
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkResult result = devFuncs->vkCreateImage(device, &imageInfo, nullptr, &image);
|
||||
if (result != VK_SUCCESS) {
|
||||
qCWarning(logDmabuf) << "Failed to create VkImage for DMA-BUF import, result:" << result;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
|
||||
// dup() is required because vkAllocateMemory with VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
|
||||
// takes ownership of the fd on succcess. Without dup, WlDmaBuffer would double-close.
|
||||
const int dupFd =
|
||||
dup(this->planes[0].fd); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
if (dupFd < 0) {
|
||||
qCWarning(logDmabuf) << "Failed to dup() fd for DMA-BUF import";
|
||||
goto cleanup_fail; // NOLINT
|
||||
}
|
||||
|
||||
{
|
||||
VkMemoryRequirements memReqs = {};
|
||||
devFuncs->vkGetImageMemoryRequirements(device, image, &memReqs);
|
||||
|
||||
VkMemoryFdPropertiesKHR fdProps = {};
|
||||
fdProps.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR;
|
||||
|
||||
result = getMemoryFdPropertiesKHR(
|
||||
device,
|
||||
VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
|
||||
dupFd,
|
||||
&fdProps
|
||||
);
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
close(dupFd);
|
||||
qCWarning(logDmabuf) << "vkGetMemoryFdPropertiesKHR failed, result:" << result;
|
||||
goto cleanup_fail; // NOLINT
|
||||
}
|
||||
|
||||
const uint32_t memTypeBits = memReqs.memoryTypeBits & fdProps.memoryTypeBits;
|
||||
|
||||
VkPhysicalDeviceMemoryProperties memProps = {};
|
||||
instFuncs->vkGetPhysicalDeviceMemoryProperties(physDevice, &memProps);
|
||||
|
||||
uint32_t memTypeIndex = UINT32_MAX;
|
||||
for (uint32_t j = 0; j < memProps.memoryTypeCount; ++j) {
|
||||
if (memTypeBits & (1u << j)) {
|
||||
memTypeIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (memTypeIndex == UINT32_MAX) {
|
||||
close(dupFd);
|
||||
qCWarning(logDmabuf) << "No compatible memory type for DMA-BUF import";
|
||||
goto cleanup_fail; // NOLINT
|
||||
}
|
||||
|
||||
VkImportMemoryFdInfoKHR importInfo = {};
|
||||
importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR;
|
||||
importInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT;
|
||||
importInfo.fd = dupFd;
|
||||
|
||||
VkMemoryDedicatedAllocateInfo dedicatedInfo = {};
|
||||
dedicatedInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
|
||||
dedicatedInfo.image = image;
|
||||
dedicatedInfo.pNext = &importInfo;
|
||||
|
||||
VkMemoryAllocateInfo allocInfo = {};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
allocInfo.pNext = &dedicatedInfo;
|
||||
allocInfo.allocationSize = memReqs.size;
|
||||
allocInfo.memoryTypeIndex = memTypeIndex;
|
||||
|
||||
result = devFuncs->vkAllocateMemory(device, &allocInfo, nullptr, &memory);
|
||||
if (result != VK_SUCCESS) {
|
||||
close(dupFd);
|
||||
qCWarning(logDmabuf) << "vkAllocateMemory failed, result:" << result;
|
||||
goto cleanup_fail; // NOLINT
|
||||
}
|
||||
|
||||
result = devFuncs->vkBindImageMemory(device, image, memory, 0);
|
||||
if (result != VK_SUCCESS) {
|
||||
qCWarning(logDmabuf) << "vkBindImageMemory failed, result:" << result;
|
||||
goto cleanup_fail; // NOLINT
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// acquire the DMA-BUF from the foreign (compositor) queue and transition
|
||||
// to shader-read layout. oldLayout must be GENERAL (not UNDEFINED) to
|
||||
// preserve the DMA-BUF contents written by the external producer. Hopefully.
|
||||
window->beginExternalCommands();
|
||||
|
||||
auto* cmdBufPtr = static_cast<VkCommandBuffer*>(
|
||||
ri->getResource(window, QSGRendererInterface::CommandListResource)
|
||||
);
|
||||
|
||||
if (cmdBufPtr && *cmdBufPtr) {
|
||||
VkCommandBuffer cmdBuf = *cmdBufPtr;
|
||||
|
||||
// find the graphics queue family index for the ownrship transfer.
|
||||
uint32_t graphicsQueueFamily = 0;
|
||||
uint32_t queueFamilyCount = 0;
|
||||
instFuncs->vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, nullptr);
|
||||
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
||||
instFuncs->vkGetPhysicalDeviceQueueFamilyProperties(
|
||||
physDevice,
|
||||
&queueFamilyCount,
|
||||
queueFamilies.data()
|
||||
);
|
||||
for (uint32_t i = 0; i < queueFamilyCount; ++i) {
|
||||
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
graphicsQueueFamily = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VkImageMemoryBarrier barrier = {};
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT;
|
||||
barrier.dstQueueFamilyIndex = graphicsQueueFamily;
|
||||
barrier.image = image;
|
||||
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
barrier.subresourceRange.baseMipLevel = 0;
|
||||
barrier.subresourceRange.levelCount = 1;
|
||||
barrier.subresourceRange.baseArrayLayer = 0;
|
||||
barrier.subresourceRange.layerCount = 1;
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
|
||||
devFuncs->vkCmdPipelineBarrier(
|
||||
cmdBuf,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
1,
|
||||
&barrier
|
||||
);
|
||||
}
|
||||
|
||||
window->endExternalCommands();
|
||||
|
||||
auto* qsgTexture = QQuickWindowPrivate::get(window)->createTextureFromNativeTexture(
|
||||
reinterpret_cast<quint64>(image),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
static_cast<uint>(vkFormat),
|
||||
QSize(static_cast<int>(this->width), static_cast<int>(this->height)),
|
||||
{}
|
||||
);
|
||||
|
||||
// For opaque DRM formats (XRGB, XBGR, etc.), the alpha bytes are underfined.
|
||||
// EGL silently forces alpha=1.0 for these, but Vulkan doesn't. Replace Qt's
|
||||
// default identity-swizzle VkImageView with one that maps alpha to ONE.
|
||||
if (!drmFormatHasAlpha(this->format)) {
|
||||
auto* vkTexture = static_cast<QVkTexture*>(qsgTexture->rhiTexture()); // NOLINT
|
||||
|
||||
devFuncs->vkDestroyImageView(device, vkTexture->imageView, nullptr);
|
||||
|
||||
VkImageViewCreateInfo viewInfo = {};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = image;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = vkFormat;
|
||||
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
viewInfo.components.a = VK_COMPONENT_SWIZZLE_ONE;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
result = devFuncs->vkCreateImageView(device, &viewInfo, nullptr, &vkTexture->imageView);
|
||||
if (result != VK_SUCCESS) {
|
||||
qCWarning(logDmabuf) << "Failed to create alpha-swizzled VkImageView, result:" << result;
|
||||
}
|
||||
}
|
||||
|
||||
auto* tex = new WlDmaBufferVulkanQSGTexture(devFuncs, device, image, memory, qsgTexture);
|
||||
qCDebug(logDmabuf) << "Created WlDmaBufferVulkanQSGTexture" << tex << "from" << this;
|
||||
return tex;
|
||||
}
|
||||
|
||||
cleanup_fail:
|
||||
if (image != VK_NULL_HANDLE) {
|
||||
devFuncs->vkDestroyImage(device, image, nullptr);
|
||||
}
|
||||
if (memory != VK_NULL_HANDLE) {
|
||||
devFuncs->vkFreeMemory(device, memory, nullptr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WlDmaBufferVulkanQSGTexture::~WlDmaBufferVulkanQSGTexture() {
|
||||
delete this->qsgTexture;
|
||||
|
||||
if (this->image != VK_NULL_HANDLE) {
|
||||
this->devFuncs->vkDestroyImage(this->device, this->image, nullptr);
|
||||
}
|
||||
|
||||
if (this->memory != VK_NULL_HANDLE) {
|
||||
this->devFuncs->vkFreeMemory(this->device, this->memory, nullptr);
|
||||
}
|
||||
|
||||
qCDebug(logDmabuf) << "WlDmaBufferVulkanQSGTexture" << this << "destroyed.";
|
||||
}
|
||||
|
||||
WlDmaBufferQSGTexture::~WlDmaBufferQSGTexture() {
|
||||
auto* context = QOpenGLContext::currentContext();
|
||||
auto* display = context->nativeInterface<QNativeInterface::QEGLContext>()->display();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
|
@ -12,9 +13,11 @@
|
|||
#include <qsize.h>
|
||||
#include <qtclasshelpermacros.h>
|
||||
#include <qtypes.h>
|
||||
#include <qvulkanfunctions.h>
|
||||
#include <qwayland-linux-dmabuf-v1.h>
|
||||
#include <qwaylandclientextension.h>
|
||||
#include <sys/types.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <wayland-linux-dmabuf-v1-client-protocol.h>
|
||||
#include <wayland-util.h>
|
||||
#include <xf86drm.h>
|
||||
|
|
@ -114,6 +117,36 @@ private:
|
|||
friend class WlDmaBuffer;
|
||||
};
|
||||
|
||||
class WlDmaBufferVulkanQSGTexture: public WlBufferQSGTexture {
|
||||
public:
|
||||
~WlDmaBufferVulkanQSGTexture() override;
|
||||
Q_DISABLE_COPY_MOVE(WlDmaBufferVulkanQSGTexture);
|
||||
|
||||
[[nodiscard]] QSGTexture* texture() const override { return this->qsgTexture; }
|
||||
|
||||
private:
|
||||
WlDmaBufferVulkanQSGTexture(
|
||||
QVulkanDeviceFunctions* devFuncs,
|
||||
VkDevice device,
|
||||
VkImage image,
|
||||
VkDeviceMemory memory,
|
||||
QSGTexture* qsgTexture
|
||||
)
|
||||
: devFuncs(devFuncs)
|
||||
, device(device)
|
||||
, image(image)
|
||||
, memory(memory)
|
||||
, qsgTexture(qsgTexture) {}
|
||||
|
||||
QVulkanDeviceFunctions* devFuncs = nullptr;
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
QSGTexture* qsgTexture = nullptr;
|
||||
|
||||
friend class WlDmaBuffer;
|
||||
};
|
||||
|
||||
class WlDmaBuffer: public WlBuffer {
|
||||
public:
|
||||
~WlDmaBuffer() override;
|
||||
|
|
@ -151,6 +184,9 @@ private:
|
|||
|
||||
friend class LinuxDmabufManager;
|
||||
friend QDebug& operator<<(QDebug& debug, const WlDmaBuffer* buffer);
|
||||
|
||||
[[nodiscard]] WlBufferQSGTexture* createQsgTextureGl(QQuickWindow* window) const;
|
||||
[[nodiscard]] WlBufferQSGTexture* createQsgTextureVulkan(QQuickWindow* window) const;
|
||||
};
|
||||
|
||||
QDebug& operator<<(QDebug& debug, const WlDmaBuffer* buffer);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <qqmlcomponent.h>
|
||||
#include <qqmlengine.h>
|
||||
#include <qqmllist.h>
|
||||
#include <qquickgraphicsconfiguration.h>
|
||||
#include <qquickitem.h>
|
||||
#include <qquickwindow.h>
|
||||
#include <qscreen.h>
|
||||
|
|
@ -216,6 +217,15 @@ void WlSessionLockSurface::onReload(QObject* oldInstance) {
|
|||
|
||||
if (this->window == nullptr) {
|
||||
this->window = new QQuickWindow();
|
||||
|
||||
// needed for vulkan dmabuf import, qt ignores these if not applicable
|
||||
auto graphicsConfig = this->window->graphicsConfiguration();
|
||||
graphicsConfig.setDeviceExtensions({
|
||||
"VK_KHR_external_memory_fd",
|
||||
"VK_EXT_external_memory_dma_buf",
|
||||
"VK_EXT_image_drm_format_modifier",
|
||||
});
|
||||
this->window->setGraphicsConfiguration(graphicsConfig);
|
||||
}
|
||||
|
||||
this->mContentItem->setParentItem(this->window->contentItem());
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ QSWaylandSessionLockSurface::QSWaylandSessionLockSurface(QtWaylandClient::QWayla
|
|||
wl_output* output = nullptr; // NOLINT (include)
|
||||
auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(qwindow->screen()->handle());
|
||||
|
||||
if (waylandScreen != nullptr) {
|
||||
if (waylandScreen != nullptr && !waylandScreen->isPlaceholder() && waylandScreen->output()) {
|
||||
output = waylandScreen->output();
|
||||
} else {
|
||||
qFatal() << "Session lock screen does not corrospond to a real screen. Force closing window";
|
||||
|
|
|
|||
|
|
@ -143,11 +143,11 @@ LayerSurface::LayerSurface(LayerShellIntegration* shell, QtWaylandClient::QWayla
|
|||
auto* waylandScreen =
|
||||
dynamic_cast<QtWaylandClient::QWaylandScreen*>(qwindow->screen()->handle());
|
||||
|
||||
if (waylandScreen != nullptr) {
|
||||
if (waylandScreen != nullptr && !waylandScreen->isPlaceholder() && waylandScreen->output()) {
|
||||
output = waylandScreen->output();
|
||||
} else {
|
||||
qWarning()
|
||||
<< "Layershell screen does not corrospond to a real screen. Letting the compositor pick.";
|
||||
<< "Layershell screen does not correspond to a real screen. Letting the compositor pick.";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
|
||||
///! Rectangle capable of clipping content inside its border.
|
||||
|
|
@ -72,6 +74,12 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
ShaderEffectSource {
|
||||
id: shaderSource
|
||||
hideSource: true
|
||||
sourceItem: contentItemContainer
|
||||
}
|
||||
|
||||
ShaderEffect {
|
||||
id: shader
|
||||
anchors.fill: root
|
||||
|
|
@ -79,10 +87,6 @@ Item {
|
|||
property Rectangle rect: rectangle
|
||||
property color backgroundColor: "white"
|
||||
property color borderColor: root.border.color
|
||||
|
||||
property ShaderEffectSource content: ShaderEffectSource {
|
||||
hideSource: true
|
||||
sourceItem: contentItemContainer
|
||||
}
|
||||
property ShaderEffectSource content: shaderSource
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <qqmlengine.h>
|
||||
#include <qqmlinfo.h>
|
||||
#include <qqmllist.h>
|
||||
#include <qquickgraphicsconfiguration.h>
|
||||
#include <qquickitem.h>
|
||||
#include <qquickwindow.h>
|
||||
#include <qregion.h>
|
||||
|
|
@ -147,6 +148,15 @@ void ProxyWindowBase::ensureQWindow() {
|
|||
this->window = nullptr; // createQQuickWindow may indirectly reference this->window
|
||||
this->window = this->createQQuickWindow();
|
||||
this->window->setFormat(format);
|
||||
|
||||
// needed for vulkan dmabuf import, qt ignores these if not applicable
|
||||
auto graphicsConfig = this->window->graphicsConfiguration();
|
||||
graphicsConfig.setDeviceExtensions({
|
||||
"VK_KHR_external_memory_fd",
|
||||
"VK_EXT_external_memory_dma_buf",
|
||||
"VK_EXT_image_drm_format_modifier",
|
||||
});
|
||||
this->window->setGraphicsConfiguration(graphicsConfig);
|
||||
}
|
||||
|
||||
void ProxyWindowBase::createWindow() {
|
||||
|
|
@ -213,6 +223,7 @@ void ProxyWindowBase::completeWindow() {
|
|||
this->trySetHeight(this->implicitHeight());
|
||||
this->setColor(this->mColor);
|
||||
this->updateMask();
|
||||
QQuickWindowPrivate::get(this->window)->updatesEnabled = this->mUpdatesEnabled;
|
||||
|
||||
// notify initial / post-connection geometry
|
||||
emit this->xChanged();
|
||||
|
|
@ -288,12 +299,18 @@ void ProxyWindowBase::setVisibleDirect(bool visible) {
|
|||
this->bBackerVisibility = false;
|
||||
this->deleteWindow();
|
||||
}
|
||||
} else if (this->window != nullptr) {
|
||||
} else {
|
||||
if (visible && this->window == nullptr) {
|
||||
this->createWindow();
|
||||
}
|
||||
|
||||
if (this->window != nullptr) {
|
||||
if (visible) this->polishItems();
|
||||
this->window->setVisible(visible);
|
||||
this->bBackerVisibility = visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyWindowBase::schedulePolish() {
|
||||
if (this->isVisibleDirect()) {
|
||||
|
|
@ -463,6 +480,19 @@ void ProxyWindowBase::setSurfaceFormat(QsSurfaceFormat format) {
|
|||
emit this->surfaceFormatChanged();
|
||||
}
|
||||
|
||||
bool ProxyWindowBase::updatesEnabled() const { return this->mUpdatesEnabled; }
|
||||
|
||||
void ProxyWindowBase::setUpdatesEnabled(bool updatesEnabled) {
|
||||
if (updatesEnabled == this->mUpdatesEnabled) return;
|
||||
this->mUpdatesEnabled = updatesEnabled;
|
||||
|
||||
if (this->window != nullptr) {
|
||||
QQuickWindowPrivate::get(this->window)->updatesEnabled = updatesEnabled;
|
||||
}
|
||||
|
||||
emit this->updatesEnabledChanged();
|
||||
}
|
||||
|
||||
qreal ProxyWindowBase::devicePixelRatio() const {
|
||||
if (this->window != nullptr) return this->window->devicePixelRatio();
|
||||
if (this->mScreen != nullptr) return this->mScreen->devicePixelRatio();
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ class ProxyWindowBase: public Reloadable {
|
|||
Q_PROPERTY(QObject* windowTransform READ windowTransform NOTIFY windowTransformChanged);
|
||||
Q_PROPERTY(bool backingWindowVisible READ isVisibleDirect NOTIFY backerVisibilityChanged);
|
||||
Q_PROPERTY(QsSurfaceFormat surfaceFormat READ surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged);
|
||||
Q_PROPERTY(bool updatesEnabled READ updatesEnabled WRITE setUpdatesEnabled NOTIFY updatesEnabledChanged);
|
||||
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
|
||||
// clang-format on
|
||||
Q_CLASSINFO("DefaultProperty", "data");
|
||||
|
|
@ -140,6 +141,9 @@ public:
|
|||
[[nodiscard]] QsSurfaceFormat surfaceFormat() const { return this->qsSurfaceFormat; }
|
||||
void setSurfaceFormat(QsSurfaceFormat format);
|
||||
|
||||
[[nodiscard]] bool updatesEnabled() const;
|
||||
void setUpdatesEnabled(bool updatesEnabled);
|
||||
|
||||
[[nodiscard]] QObject* windowTransform() const { return nullptr; } // NOLINT
|
||||
|
||||
[[nodiscard]] QQmlListProperty<QObject> data();
|
||||
|
|
@ -163,6 +167,7 @@ signals:
|
|||
void colorChanged();
|
||||
void maskChanged();
|
||||
void surfaceFormatChanged();
|
||||
void updatesEnabledChanged();
|
||||
void polished();
|
||||
|
||||
protected slots:
|
||||
|
|
@ -187,6 +192,7 @@ protected:
|
|||
ProxyWindowContentItem* mContentItem = nullptr;
|
||||
bool reloadComplete = false;
|
||||
bool ranLints = false;
|
||||
bool mUpdatesEnabled = true;
|
||||
QsSurfaceFormat qsSurfaceFormat;
|
||||
QSurfaceFormat mSurfaceFormat;
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,9 @@ void WindowInterface::setMask(PendingRegion* mask) const { this->proxyWindow()->
|
|||
QsSurfaceFormat WindowInterface::surfaceFormat() const { return this->proxyWindow()->surfaceFormat(); };
|
||||
void WindowInterface::setSurfaceFormat(QsSurfaceFormat format) const { this->proxyWindow()->setSurfaceFormat(format); };
|
||||
|
||||
bool WindowInterface::updatesEnabled() const { return this->proxyWindow()->updatesEnabled(); };
|
||||
void WindowInterface::setUpdatesEnabled(bool updatesEnabled) const { this->proxyWindow()->setUpdatesEnabled(updatesEnabled); };
|
||||
|
||||
QQmlListProperty<QObject> WindowInterface::data() const { return this->proxyWindow()->data(); };
|
||||
// clang-format on
|
||||
|
||||
|
|
@ -148,6 +151,7 @@ void WindowInterface::connectSignals() const {
|
|||
QObject::connect(window, &ProxyWindowBase::colorChanged, this, &WindowInterface::colorChanged);
|
||||
QObject::connect(window, &ProxyWindowBase::maskChanged, this, &WindowInterface::maskChanged);
|
||||
QObject::connect(window, &ProxyWindowBase::surfaceFormatChanged, this, &WindowInterface::surfaceFormatChanged);
|
||||
QObject::connect(window, &ProxyWindowBase::updatesEnabledChanged, this, &WindowInterface::updatesEnabledChanged);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,12 @@ class WindowInterface: public Reloadable {
|
|||
///
|
||||
/// > [!NOTE] The surface format cannot be changed after the window is created.
|
||||
Q_PROPERTY(QsSurfaceFormat surfaceFormat READ surfaceFormat WRITE setSurfaceFormat NOTIFY surfaceFormatChanged);
|
||||
/// If the window should receive render updates. Defaults to true.
|
||||
///
|
||||
/// When set to false, the window will not re-render in response to animations
|
||||
/// or other visual updates from other windows. This is useful for static windows
|
||||
/// such as wallpapers that do not need to update frequently, saving GPU cycles.
|
||||
Q_PROPERTY(bool updatesEnabled READ updatesEnabled WRITE setUpdatesEnabled NOTIFY updatesEnabledChanged);
|
||||
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
|
||||
// clang-format on
|
||||
Q_CLASSINFO("DefaultProperty", "data");
|
||||
|
|
@ -231,6 +237,9 @@ public:
|
|||
[[nodiscard]] QsSurfaceFormat surfaceFormat() const;
|
||||
void setSurfaceFormat(QsSurfaceFormat format) const;
|
||||
|
||||
[[nodiscard]] bool updatesEnabled() const;
|
||||
void setUpdatesEnabled(bool updatesEnabled) const;
|
||||
|
||||
[[nodiscard]] QQmlListProperty<QObject> data() const;
|
||||
|
||||
static QsWindowAttached* qmlAttachedProperties(QObject* object);
|
||||
|
|
@ -258,6 +267,7 @@ signals:
|
|||
void colorChanged();
|
||||
void maskChanged();
|
||||
void surfaceFormatChanged();
|
||||
void updatesEnabledChanged();
|
||||
|
||||
protected:
|
||||
void connectSignals() const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue