forked from quickshell/quickshell
hyprland/surface: add visibleMask
This commit is contained in:
parent
cdaff2967f
commit
b289bfa504
src/wayland/hyprland/surface
|
@ -34,7 +34,7 @@
|
|||
This protocol exposes hyprland-specific wl_surface properties.
|
||||
</description>
|
||||
|
||||
<interface name="hyprland_surface_manager_v1" version="1">
|
||||
<interface name="hyprland_surface_manager_v1" version="2">
|
||||
<description summary="manager for hyprland surface objects">
|
||||
This interface allows a client to create hyprland surface objects.
|
||||
</description>
|
||||
|
@ -63,7 +63,7 @@
|
|||
</enum>
|
||||
</interface>
|
||||
|
||||
<interface name="hyprland_surface_v1" version="1">
|
||||
<interface name="hyprland_surface_v1" version="2">
|
||||
<description summary="hyprland-specific wl_surface properties">
|
||||
This interface allows access to hyprland-specific properties of a wl_surface.
|
||||
|
||||
|
@ -96,5 +96,31 @@
|
|||
<entry name="no_surface" value="0" summary="wl_surface was destroyed"/>
|
||||
<entry name="out_of_range" value="1" summary="given opacity was not in the range 0.0 - 1.0 (inclusive)"/>
|
||||
</enum>
|
||||
|
||||
<request name="set_visible_region" since="2">
|
||||
<description summary="set the visible region of the surface">
|
||||
This request sets the region of the surface that contains visible content.
|
||||
Visible content refers to content that has an alpha value greater than zero.
|
||||
|
||||
The visible region is an optimization hint for the compositor that lets it
|
||||
avoid drawing parts of the surface that are not visible. Setting a visible region
|
||||
that does not contain all content in the surface may result in missing content
|
||||
not being drawn.
|
||||
|
||||
The visible region is specified in buffer-local coordinates.
|
||||
|
||||
The compositor ignores the parts of the visible region that fall outside of the surface.
|
||||
When all parts of the region fall outside of the buffer geometry, the compositor may
|
||||
avoid rendering the surface entirely.
|
||||
|
||||
The initial value for the visible region is empty. Setting the
|
||||
visible region has copy semantics, and the wl_region object can be destroyed immediately.
|
||||
A NULL wl_region causes the visible region to be set to empty.
|
||||
|
||||
Does not take effect until wl_surface.commit is called.
|
||||
</description>
|
||||
|
||||
<arg name="region" type="object" interface="wl_region" allow-null="true"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
|
||||
namespace qs::hyprland::surface::impl {
|
||||
|
||||
HyprlandSurfaceManager::HyprlandSurfaceManager(): QWaylandClientExtensionTemplate(1) {
|
||||
HyprlandSurfaceManager::HyprlandSurfaceManager(): QWaylandClientExtensionTemplate(2) {
|
||||
this->initialize();
|
||||
}
|
||||
|
||||
HyprlandSurface*
|
||||
HyprlandSurfaceManager::createHyprlandExtension(QtWaylandClient::QWaylandWindow* surface) {
|
||||
return new HyprlandSurface(this->get_hyprland_surface(surface->surface()));
|
||||
return new HyprlandSurface(this->get_hyprland_surface(surface->surface()), surface);
|
||||
}
|
||||
|
||||
HyprlandSurfaceManager* HyprlandSurfaceManager::instance() {
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
#include "qml.hpp"
|
||||
#include <memory>
|
||||
|
||||
#include <private/qhighdpiscaling_p.h>
|
||||
#include <private/qwaylandwindow_p.h>
|
||||
#include <qlogging.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlinfo.h>
|
||||
#include <qregion.h>
|
||||
#include <qtmetamacros.h>
|
||||
#include <qtypes.h>
|
||||
#include <qvariant.h>
|
||||
#include <qwindow.h>
|
||||
|
||||
#include "../../../core/region.hpp"
|
||||
#include "../../../window/proxywindow.hpp"
|
||||
#include "../../../window/windowinterface.hpp"
|
||||
#include "../../util.hpp"
|
||||
#include "manager.hpp"
|
||||
#include "surface.hpp"
|
||||
|
||||
|
@ -40,6 +43,15 @@ HyprlandWindow::HyprlandWindow(ProxyWindowBase* window): QObject(nullptr), proxy
|
|||
&HyprlandWindow::onWindowConnected
|
||||
);
|
||||
|
||||
QObject::connect(window, &ProxyWindowBase::polished, this, &HyprlandWindow::onWindowPolished);
|
||||
|
||||
QObject::connect(
|
||||
window,
|
||||
&ProxyWindowBase::devicePixelRatioChanged,
|
||||
this,
|
||||
&HyprlandWindow::updateVisibleMask
|
||||
);
|
||||
|
||||
QObject::connect(window, &QObject::destroyed, this, &HyprlandWindow::onProxyWindowDestroyed);
|
||||
|
||||
if (window->backingWindow()) {
|
||||
|
@ -60,14 +72,76 @@ void HyprlandWindow::setOpacity(qreal opacity) {
|
|||
|
||||
this->mOpacity = opacity;
|
||||
|
||||
if (this->surface) {
|
||||
this->surface->setOpacity(opacity);
|
||||
qs::wayland::util::scheduleCommit(this->proxyWindow);
|
||||
if (this->surface && this->proxyWindow) {
|
||||
this->pendingPolish.opacity = true;
|
||||
this->proxyWindow->schedulePolish();
|
||||
}
|
||||
|
||||
emit this->opacityChanged();
|
||||
}
|
||||
|
||||
PendingRegion* HyprlandWindow::visibleMask() const { return this->mVisibleMask; }
|
||||
|
||||
void HyprlandWindow::setVisibleMask(PendingRegion* mask) {
|
||||
if (mask == this->mVisibleMask) return;
|
||||
|
||||
if (this->mVisibleMask) {
|
||||
QObject::disconnect(this->mVisibleMask, nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
this->mVisibleMask = mask;
|
||||
|
||||
if (mask) {
|
||||
QObject::connect(mask, &QObject::destroyed, this, &HyprlandWindow::onVisibleMaskDestroyed);
|
||||
QObject::connect(mask, &PendingRegion::changed, this, &HyprlandWindow::updateVisibleMask);
|
||||
}
|
||||
|
||||
this->updateVisibleMask();
|
||||
emit this->visibleMaskChanged();
|
||||
}
|
||||
|
||||
void HyprlandWindow::onVisibleMaskDestroyed() {
|
||||
this->mVisibleMask = nullptr;
|
||||
this->updateVisibleMask();
|
||||
emit this->visibleMaskChanged();
|
||||
}
|
||||
|
||||
void HyprlandWindow::updateVisibleMask() {
|
||||
if (!this->surface || !this->proxyWindow) return;
|
||||
|
||||
this->pendingPolish.visibleMask = true;
|
||||
this->proxyWindow->schedulePolish();
|
||||
}
|
||||
|
||||
void HyprlandWindow::onWindowPolished() {
|
||||
if (!this->surface) return;
|
||||
|
||||
if (this->pendingPolish.opacity) {
|
||||
this->surface->setOpacity(this->mOpacity);
|
||||
this->pendingPolish.opacity = false;
|
||||
}
|
||||
|
||||
if (this->pendingPolish.visibleMask) {
|
||||
QRegion mask;
|
||||
if (this->mVisibleMask != nullptr) {
|
||||
mask =
|
||||
this->mVisibleMask->applyTo(QRect(0, 0, this->mWindow->width(), this->mWindow->height()));
|
||||
}
|
||||
|
||||
auto dpr = this->proxyWindow->devicePixelRatio();
|
||||
if (dpr != 1.0) {
|
||||
mask = QHighDpi::scale(mask, dpr);
|
||||
}
|
||||
|
||||
if (mask.isEmpty() && this->mVisibleMask) {
|
||||
mask = QRect(-1, -1, 1, 1);
|
||||
}
|
||||
|
||||
this->surface->setVisibleRegion(mask);
|
||||
this->pendingPolish.visibleMask = false;
|
||||
}
|
||||
}
|
||||
|
||||
void HyprlandWindow::onWindowConnected() {
|
||||
this->mWindow = this->proxyWindow->backingWindow();
|
||||
// disconnected by destructor
|
||||
|
@ -86,11 +160,24 @@ void HyprlandWindow::onWindowVisibleChanged() {
|
|||
if (!this->mWindow->handle()) {
|
||||
this->mWindow->create();
|
||||
}
|
||||
}
|
||||
|
||||
this->mWaylandWindow = dynamic_cast<QWaylandWindow*>(this->mWindow->handle());
|
||||
auto* window = dynamic_cast<QWaylandWindow*>(this->mWindow->handle());
|
||||
if (window == this->mWaylandWindow) return;
|
||||
|
||||
if (this->mWaylandWindow) {
|
||||
// disconnected by destructor
|
||||
QObject::disconnect(this->mWaylandWindow, nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
this->mWaylandWindow = window;
|
||||
if (!window) return;
|
||||
|
||||
QObject::connect(
|
||||
this->mWaylandWindow,
|
||||
&QObject::destroyed,
|
||||
this,
|
||||
&HyprlandWindow::onWaylandWindowDestroyed
|
||||
);
|
||||
|
||||
QObject::connect(
|
||||
this->mWaylandWindow,
|
||||
|
@ -109,10 +196,10 @@ void HyprlandWindow::onWindowVisibleChanged() {
|
|||
if (this->mWaylandWindow->surface()) {
|
||||
this->onWaylandSurfaceCreated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HyprlandWindow::onWaylandWindowDestroyed() { this->mWaylandWindow = nullptr; }
|
||||
|
||||
void HyprlandWindow::onWaylandSurfaceCreated() {
|
||||
auto* manager = impl::HyprlandSurfaceManager::instance();
|
||||
|
||||
|
@ -122,12 +209,26 @@ void HyprlandWindow::onWaylandSurfaceCreated() {
|
|||
return;
|
||||
}
|
||||
|
||||
auto v = this->mWaylandWindow->property("hyprland_window_ext");
|
||||
if (v.canConvert<HyprlandWindow*>()) {
|
||||
auto* windowExt = v.value<HyprlandWindow*>();
|
||||
if (windowExt != this && windowExt->surface) {
|
||||
this->surface.swap(windowExt->surface);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->surface) {
|
||||
auto* ext = manager->createHyprlandExtension(this->mWaylandWindow);
|
||||
this->surface = std::unique_ptr<impl::HyprlandSurface>(ext);
|
||||
}
|
||||
|
||||
if (this->mOpacity != 1.0) {
|
||||
this->surface->setOpacity(this->mOpacity);
|
||||
qs::wayland::util::scheduleCommit(this->proxyWindow);
|
||||
this->mWaylandWindow->setProperty("hyprland_window_ext", QVariant::fromValue(this));
|
||||
|
||||
this->pendingPolish.opacity = this->mOpacity != 1.0;
|
||||
this->pendingPolish.visibleMask = this->mVisibleMask;
|
||||
|
||||
if (this->pendingPolish.opacity || this->pendingPolish.visibleMask) {
|
||||
this->proxyWindow->schedulePolish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,8 +245,9 @@ void HyprlandWindow::onProxyWindowDestroyed() {
|
|||
// Deleting it when the proxy window is deleted will cause a full opacity frame between the destruction of the
|
||||
// hyprland_surface_v1 and wl_surface objects.
|
||||
|
||||
if (this->surface == nullptr) {
|
||||
this->proxyWindow = nullptr;
|
||||
|
||||
if (this->surface == nullptr) {
|
||||
this->deleteLater();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <qtypes.h>
|
||||
#include <qwindow.h>
|
||||
|
||||
#include "../../../core/region.hpp"
|
||||
#include "../../../window/proxywindow.hpp"
|
||||
#include "surface.hpp"
|
||||
|
||||
|
@ -31,11 +32,18 @@ namespace qs::hyprland::surface {
|
|||
/// [hyprland-surface-v1]: https://github.com/hyprwm/hyprland-protocols/blob/main/protocols/hyprland-surface-v1.xml
|
||||
class HyprlandWindow: public QObject {
|
||||
Q_OBJECT;
|
||||
// clang-format off
|
||||
/// A multiplier for the window's overall opacity, ranging from 1.0 to 0.0. Overall opacity includes the opacity of
|
||||
/// both the window content *and* visual effects such as blur that apply to it.
|
||||
///
|
||||
/// Default: 1.0
|
||||
Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged);
|
||||
/// A hint to the compositor that only certain regions of the surface should be rendered.
|
||||
/// This can be used to avoid rendering large empty regions of a window which can increase
|
||||
/// performance, especially if the window is blurred. The mask should include all pixels
|
||||
/// of the window that do not have an alpha value of 0.
|
||||
Q_PROPERTY(PendingRegion* visibleMask READ visibleMask WRITE setVisibleMask NOTIFY visibleMaskChanged);
|
||||
// clang-format on
|
||||
QML_ELEMENT;
|
||||
QML_UNCREATABLE("HyprlandWindow can only be used as an attached object.");
|
||||
QML_ATTACHED(HyprlandWindow);
|
||||
|
@ -48,17 +56,25 @@ public:
|
|||
[[nodiscard]] qreal opacity() const;
|
||||
void setOpacity(qreal opacity);
|
||||
|
||||
[[nodiscard]] PendingRegion* visibleMask() const;
|
||||
virtual void setVisibleMask(PendingRegion* mask);
|
||||
|
||||
static HyprlandWindow* qmlAttachedProperties(QObject* object);
|
||||
|
||||
signals:
|
||||
void opacityChanged();
|
||||
void visibleMaskChanged();
|
||||
|
||||
private slots:
|
||||
void onWindowConnected();
|
||||
void onWindowVisibleChanged();
|
||||
void onWaylandWindowDestroyed();
|
||||
void onWaylandSurfaceCreated();
|
||||
void onWaylandSurfaceDestroyed();
|
||||
void onProxyWindowDestroyed();
|
||||
void onVisibleMaskDestroyed();
|
||||
void onWindowPolished();
|
||||
void updateVisibleMask();
|
||||
|
||||
private:
|
||||
void disconnectWaylandWindow();
|
||||
|
@ -67,7 +83,13 @@ private:
|
|||
QWindow* mWindow = nullptr;
|
||||
QtWaylandClient::QWaylandWindow* mWaylandWindow = nullptr;
|
||||
|
||||
struct {
|
||||
bool opacity : 1 = false;
|
||||
bool visibleMask : 1 = false;
|
||||
} pendingPolish;
|
||||
|
||||
qreal mOpacity = 1.0;
|
||||
PendingRegion* mVisibleMask = nullptr;
|
||||
std::unique_ptr<impl::HyprlandSurface> surface;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,19 +1,53 @@
|
|||
#include "surface.hpp"
|
||||
#include <cmath>
|
||||
|
||||
#include <private/qwaylanddisplay_p.h>
|
||||
#include <private/qwaylandintegration_p.h>
|
||||
#include <qlogging.h>
|
||||
#include <qregion.h>
|
||||
#include <qtypes.h>
|
||||
#include <qwayland-hyprland-surface-v1.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <wayland-hyprland-surface-v1-client-protocol.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
namespace qs::hyprland::surface::impl {
|
||||
|
||||
HyprlandSurface::HyprlandSurface(::hyprland_surface_v1* surface)
|
||||
: QtWayland::hyprland_surface_v1(surface) {}
|
||||
HyprlandSurface::HyprlandSurface(
|
||||
::hyprland_surface_v1* surface,
|
||||
QtWaylandClient::QWaylandWindow* backer
|
||||
)
|
||||
: QtWayland::hyprland_surface_v1(surface)
|
||||
, backer(backer)
|
||||
, backerSurface(backer->surface()) {}
|
||||
|
||||
HyprlandSurface::~HyprlandSurface() { this->destroy(); }
|
||||
|
||||
bool HyprlandSurface::surfaceEq(wl_surface* surface) const {
|
||||
return surface == this->backerSurface;
|
||||
}
|
||||
|
||||
void HyprlandSurface::setOpacity(qreal opacity) {
|
||||
this->set_opacity(wl_fixed_from_double(opacity));
|
||||
}
|
||||
|
||||
void HyprlandSurface::setVisibleRegion(const QRegion& region) {
|
||||
if (this->version() < HYPRLAND_SURFACE_V1_SET_VISIBLE_REGION_SINCE_VERSION) {
|
||||
qWarning() << "Cannot set hyprland surface visible region: compositor does not support "
|
||||
"hyprland_surface_v1.set_visible_region";
|
||||
return;
|
||||
}
|
||||
|
||||
if (region.isEmpty()) {
|
||||
this->set_visible_region(nullptr);
|
||||
} else {
|
||||
static const auto* waylandIntegration = QtWaylandClient::QWaylandIntegration::instance();
|
||||
auto* display = waylandIntegration->display();
|
||||
|
||||
auto* wlRegion = display->createRegion(region);
|
||||
this->set_visible_region(wlRegion);
|
||||
wl_region_destroy(wlRegion); // NOLINT(misc-include-cleaner)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace qs::hyprland::surface::impl
|
||||
|
|
|
@ -1,21 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <private/qwaylandwindow_p.h>
|
||||
#include <qobject.h>
|
||||
#include <qregion.h>
|
||||
#include <qtclasshelpermacros.h>
|
||||
#include <qtmetamacros.h>
|
||||
#include <qtypes.h>
|
||||
#include <qwayland-hyprland-surface-v1.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <wayland-hyprland-surface-v1-client-protocol.h>
|
||||
|
||||
namespace qs::hyprland::surface::impl {
|
||||
|
||||
class HyprlandSurface: public QtWayland::hyprland_surface_v1 {
|
||||
public:
|
||||
explicit HyprlandSurface(::hyprland_surface_v1* surface);
|
||||
explicit HyprlandSurface(::hyprland_surface_v1* surface, QtWaylandClient::QWaylandWindow* backer);
|
||||
~HyprlandSurface() override;
|
||||
Q_DISABLE_COPY_MOVE(HyprlandSurface);
|
||||
|
||||
[[nodiscard]] bool surfaceEq(wl_surface* surface) const;
|
||||
|
||||
void setOpacity(qreal opacity);
|
||||
void setVisibleRegion(const QRegion& region);
|
||||
|
||||
private:
|
||||
QtWaylandClient::QWaylandWindow* backer;
|
||||
wl_surface* backerSurface = nullptr;
|
||||
};
|
||||
|
||||
} // namespace qs::hyprland::surface::impl
|
||||
|
|
Loading…
Reference in a new issue