forked from quickshell/quickshell
		
	wayland/layershell: refactor layer shell surface integration
In addition to the much needed cleanup: - The bridge/extension type is now directly tied to the QWindow instead of the WlrLayershell object, and is much smaller. - Layer requests are now comitted via polish instead of for each change individually.
This commit is contained in:
		
							parent
							
								
									6a8284dae3
								
							
						
					
					
						commit
						e0cff677a5
					
				
					 13 changed files with 366 additions and 495 deletions
				
			
		| 
						 | 
				
			
			@ -85,7 +85,6 @@ add_library(quickshell-wayland-init OBJECT init.cpp)
 | 
			
		|||
set(WAYLAND_MODULES)
 | 
			
		||||
 | 
			
		||||
if (WAYLAND_WLR_LAYERSHELL)
 | 
			
		||||
	target_sources(quickshell-wayland PRIVATE wlr_layershell.cpp)
 | 
			
		||||
	add_subdirectory(wlr_layershell)
 | 
			
		||||
	target_compile_definitions(quickshell-wayland PRIVATE QS_WAYLAND_WLR_LAYERSHELL)
 | 
			
		||||
	target_compile_definitions(quickshell-wayland-init PRIVATE QS_WAYLAND_WLR_LAYERSHELL)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@
 | 
			
		|||
#include "../core/plugin.hpp"
 | 
			
		||||
 | 
			
		||||
#ifdef QS_WAYLAND_WLR_LAYERSHELL
 | 
			
		||||
#include "wlr_layershell.hpp"
 | 
			
		||||
#include "wlr_layershell/wlr_layershell.hpp"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void installPlatformMenuHook(); // NOLINT(misc-use-internal-linkage)
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,12 @@ class WaylandPlugin: public QsEnginePlugin {
 | 
			
		|||
 | 
			
		||||
	void registerTypes() override {
 | 
			
		||||
#ifdef QS_WAYLAND_WLR_LAYERSHELL
 | 
			
		||||
		qmlRegisterType<WaylandPanelInterface>("Quickshell._WaylandOverlay", 1, 0, "PanelWindow");
 | 
			
		||||
		qmlRegisterType<qs::wayland::layershell::WaylandPanelInterface>(
 | 
			
		||||
		    "Quickshell._WaylandOverlay",
 | 
			
		||||
		    1,
 | 
			
		||||
		    0,
 | 
			
		||||
		    "PanelWindow"
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		// If any types are defined inside a module using QML_ELEMENT then all QML_ELEMENT types
 | 
			
		||||
		// will not be registered. This can be worked around with a module import which makes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
name = "Quickshell.Wayland"
 | 
			
		||||
description = "Wayland specific Quickshell types"
 | 
			
		||||
headers = [
 | 
			
		||||
	"wlr_layershell/window.hpp",
 | 
			
		||||
	"wlr_layershell.hpp",
 | 
			
		||||
	"wlr_layershell/wlr_layershell.hpp",
 | 
			
		||||
	"session_lock.hpp",
 | 
			
		||||
	"toplevel_management/qml.hpp",
 | 
			
		||||
	"screencopy/view.hpp",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
qt_add_library(quickshell-wayland-layershell STATIC
 | 
			
		||||
	wlr_layershell.cpp
 | 
			
		||||
	shell_integration.cpp
 | 
			
		||||
	surface.cpp
 | 
			
		||||
	window.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
qt_add_qml_module(quickshell-wayland-layershell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,16 +6,20 @@
 | 
			
		|||
 | 
			
		||||
#include "surface.hpp"
 | 
			
		||||
 | 
			
		||||
QSWaylandLayerShellIntegration::QSWaylandLayerShellIntegration()
 | 
			
		||||
    : QtWaylandClient::QWaylandShellIntegrationTemplate<QSWaylandLayerShellIntegration>(4) {}
 | 
			
		||||
namespace qs::wayland::layershell {
 | 
			
		||||
 | 
			
		||||
QSWaylandLayerShellIntegration::~QSWaylandLayerShellIntegration() {
 | 
			
		||||
LayerShellIntegration::LayerShellIntegration()
 | 
			
		||||
    : QtWaylandClient::QWaylandShellIntegrationTemplate<LayerShellIntegration>(4) {}
 | 
			
		||||
 | 
			
		||||
LayerShellIntegration::~LayerShellIntegration() {
 | 
			
		||||
	if (this->isInitialized()) {
 | 
			
		||||
		this->destroy();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QtWaylandClient::QWaylandShellSurface*
 | 
			
		||||
QSWaylandLayerShellIntegration::createShellSurface(QtWaylandClient::QWaylandWindow* window) {
 | 
			
		||||
	return new QSWaylandLayerSurface(this, window);
 | 
			
		||||
LayerShellIntegration::createShellSurface(QtWaylandClient::QWaylandWindow* window) {
 | 
			
		||||
	return new LayerSurface(this, window);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::layershell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,14 +5,18 @@
 | 
			
		|||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <qwayland-wlr-layer-shell-unstable-v1.h>
 | 
			
		||||
 | 
			
		||||
class QSWaylandLayerShellIntegration
 | 
			
		||||
    : public QtWaylandClient::QWaylandShellIntegrationTemplate<QSWaylandLayerShellIntegration>
 | 
			
		||||
namespace qs::wayland::layershell {
 | 
			
		||||
 | 
			
		||||
class LayerShellIntegration
 | 
			
		||||
    : public QtWaylandClient::QWaylandShellIntegrationTemplate<LayerShellIntegration>
 | 
			
		||||
    , public QtWayland::zwlr_layer_shell_v1 {
 | 
			
		||||
public:
 | 
			
		||||
	QSWaylandLayerShellIntegration();
 | 
			
		||||
	~QSWaylandLayerShellIntegration() override;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(QSWaylandLayerShellIntegration);
 | 
			
		||||
	LayerShellIntegration();
 | 
			
		||||
	~LayerShellIntegration() override;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(LayerShellIntegration);
 | 
			
		||||
 | 
			
		||||
	QtWaylandClient::QWaylandShellSurface* createShellSurface(QtWaylandClient::QWaylandWindow* window
 | 
			
		||||
	) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::layershell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
#include "surface.hpp"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <any>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <private/qhighdpiscaling_p.h>
 | 
			
		||||
#include <private/qwaylanddisplay_p.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -13,16 +14,20 @@
 | 
			
		|||
#include <qsize.h>
 | 
			
		||||
#include <qtversionchecks.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
#include <qvariant.h>
 | 
			
		||||
#include <qwayland-wlr-layer-shell-unstable-v1.h>
 | 
			
		||||
#include <qwindow.h>
 | 
			
		||||
 | 
			
		||||
#include "../../window/panelinterface.hpp"
 | 
			
		||||
#include "shell_integration.hpp"
 | 
			
		||||
#include "window.hpp"
 | 
			
		||||
#include "wlr_layershell.hpp"
 | 
			
		||||
 | 
			
		||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
 | 
			
		||||
#include <qpoint.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::layershell {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] QtWayland::zwlr_layer_shell_v1::layer toWaylandLayer(const WlrLayer::Enum& layer
 | 
			
		||||
| 
						 | 
				
			
			@ -69,21 +74,72 @@ toWaylandKeyboardFocus(const WlrKeyboardFocus::Enum& focus) noexcept {
 | 
			
		|||
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
QSWaylandLayerSurface::QSWaylandLayerSurface(
 | 
			
		||||
    QSWaylandLayerShellIntegration* shell,
 | 
			
		||||
    QtWaylandClient::QWaylandWindow* window
 | 
			
		||||
)
 | 
			
		||||
void LayerSurfaceBridge::commitState() {
 | 
			
		||||
	if (this->surface) this->surface->commit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LayerSurfaceBridge* LayerSurfaceBridge::get(QWindow* window) {
 | 
			
		||||
	auto v = window->property("layershell_bridge");
 | 
			
		||||
 | 
			
		||||
	if (v.canConvert<LayerSurfaceBridge*>()) {
 | 
			
		||||
		return v.value<LayerSurfaceBridge*>();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LayerSurfaceBridge* LayerSurfaceBridge::init(QWindow* window, LayerSurfaceState state) {
 | 
			
		||||
	auto* bridge = LayerSurfaceBridge::get(window);
 | 
			
		||||
 | 
			
		||||
	if (!bridge) {
 | 
			
		||||
		bridge = new LayerSurfaceBridge(window);
 | 
			
		||||
		window->setProperty("layershell_bridge", QVariant::fromValue(bridge));
 | 
			
		||||
	} else if (!bridge->state.isCompatible(state)) {
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!bridge->surface) {
 | 
			
		||||
		// Qt appears to be resetting the window's screen on creation on some systems. This works around it.
 | 
			
		||||
		auto* screen = window->screen();
 | 
			
		||||
		window->create();
 | 
			
		||||
		window->setScreen(screen);
 | 
			
		||||
 | 
			
		||||
		auto* waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow*>(window->handle());
 | 
			
		||||
		if (waylandWindow == nullptr) {
 | 
			
		||||
			qWarning() << window << "is not a wayland window. Cannot create layershell surface.";
 | 
			
		||||
			return nullptr;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static LayerShellIntegration* layershellIntegration = nullptr; // NOLINT
 | 
			
		||||
		if (layershellIntegration == nullptr) {
 | 
			
		||||
			layershellIntegration = new LayerShellIntegration();
 | 
			
		||||
			if (!layershellIntegration->initialize(waylandWindow->display())) {
 | 
			
		||||
				delete layershellIntegration;
 | 
			
		||||
				layershellIntegration = nullptr;
 | 
			
		||||
				qWarning() << "Failed to initialize layershell integration";
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		waylandWindow->setShellIntegration(layershellIntegration);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bridge->state = std::move(state);
 | 
			
		||||
	bridge->commitState();
 | 
			
		||||
 | 
			
		||||
	return bridge;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LayerSurface::LayerSurface(LayerShellIntegration* shell, QtWaylandClient::QWaylandWindow* window)
 | 
			
		||||
    : QtWaylandClient::QWaylandShellSurface(window) {
 | 
			
		||||
 | 
			
		||||
	auto* qwindow = window->window();
 | 
			
		||||
	this->ext = LayershellWindowExtension::get(qwindow);
 | 
			
		||||
 | 
			
		||||
	if (this->ext == nullptr) {
 | 
			
		||||
		qFatal() << "QSWaylandLayerSurface created with null LayershellWindowExtension";
 | 
			
		||||
	}
 | 
			
		||||
	this->bridge = LayerSurfaceBridge::get(qwindow);
 | 
			
		||||
	if (this->bridge == nullptr) qFatal() << "LayerSurface created with null bridge";
 | 
			
		||||
	const auto& s = this->bridge->state;
 | 
			
		||||
 | 
			
		||||
	wl_output* output = nullptr; // NOLINT (include)
 | 
			
		||||
	if (this->ext->useWindowScreen) {
 | 
			
		||||
	if (!s.compositorPickesScreen) {
 | 
			
		||||
		auto* waylandScreen =
 | 
			
		||||
		    dynamic_cast<QtWaylandClient::QWaylandScreen*>(qwindow->screen()->handle());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,36 +154,28 @@ QSWaylandLayerSurface::QSWaylandLayerSurface(
 | 
			
		|||
	this->init(shell->get_layer_surface(
 | 
			
		||||
	    window->waylandSurface()->object(),
 | 
			
		||||
	    output,
 | 
			
		||||
	    toWaylandLayer(this->ext->mLayer),
 | 
			
		||||
	    this->ext->mNamespace
 | 
			
		||||
	    toWaylandLayer(s.layer),
 | 
			
		||||
	    s.mNamespace
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	this->updateAnchors();
 | 
			
		||||
	this->updateLayer();
 | 
			
		||||
	this->updateMargins();
 | 
			
		||||
	this->updateExclusiveZone();
 | 
			
		||||
	this->updateKeyboardFocus();
 | 
			
		||||
 | 
			
		||||
	// new updates will be sent from the extension
 | 
			
		||||
	this->ext->surface = this;
 | 
			
		||||
 | 
			
		||||
	auto size = constrainedSize(this->ext->mAnchors, window->surfaceSize());
 | 
			
		||||
	auto size = constrainedSize(s.anchors, QHighDpi::toNativePixels(s.implicitSize, qwindow));
 | 
			
		||||
	this->set_size(size.width(), size.height());
 | 
			
		||||
	this->set_anchor(toWaylandAnchors(s.anchors));
 | 
			
		||||
	this->set_margin(
 | 
			
		||||
	    QHighDpi::toNativePixels(s.margins.mTop, qwindow),
 | 
			
		||||
	    QHighDpi::toNativePixels(s.margins.mRight, qwindow),
 | 
			
		||||
	    QHighDpi::toNativePixels(s.margins.mBottom, qwindow),
 | 
			
		||||
	    QHighDpi::toNativePixels(s.margins.mLeft, qwindow)
 | 
			
		||||
	);
 | 
			
		||||
	this->set_exclusive_zone(QHighDpi::toNativePixels(s.exclusiveZone, qwindow));
 | 
			
		||||
	this->set_keyboard_interactivity(toWaylandKeyboardFocus(s.keyboardFocus));
 | 
			
		||||
 | 
			
		||||
	this->bridge->surface = this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QSWaylandLayerSurface::~QSWaylandLayerSurface() {
 | 
			
		||||
	if (this->ext != nullptr) {
 | 
			
		||||
		this->ext->surface = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
LayerSurface::~LayerSurface() { this->destroy(); }
 | 
			
		||||
 | 
			
		||||
	this->destroy();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::zwlr_layer_surface_v1_configure(
 | 
			
		||||
    quint32 serial,
 | 
			
		||||
    quint32 width,
 | 
			
		||||
    quint32 height
 | 
			
		||||
) {
 | 
			
		||||
void LayerSurface::zwlr_layer_surface_v1_configure(quint32 serial, quint32 width, quint32 height) {
 | 
			
		||||
	this->ack_configure(serial);
 | 
			
		||||
 | 
			
		||||
	this->size = QSize(static_cast<qint32>(width), static_cast<qint32>(height));
 | 
			
		||||
| 
						 | 
				
			
			@ -146,51 +194,53 @@ void QSWaylandLayerSurface::zwlr_layer_surface_v1_configure(
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::zwlr_layer_surface_v1_closed() { this->window()->window()->close(); }
 | 
			
		||||
void LayerSurface::zwlr_layer_surface_v1_closed() { this->window()->window()->close(); }
 | 
			
		||||
 | 
			
		||||
bool QSWaylandLayerSurface::isExposed() const { return this->configured; }
 | 
			
		||||
bool LayerSurface::isExposed() const { return this->configured; }
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::applyConfigure() {
 | 
			
		||||
	this->window()->resizeFromApplyConfigure(this->size);
 | 
			
		||||
void LayerSurface::applyConfigure() { this->window()->resizeFromApplyConfigure(this->size); }
 | 
			
		||||
 | 
			
		||||
QWindow* LayerSurface::qwindow() { return this->window()->window(); }
 | 
			
		||||
 | 
			
		||||
void LayerSurface::commit() {
 | 
			
		||||
	const auto& p = this->bridge->state;
 | 
			
		||||
	auto& c = this->committed;
 | 
			
		||||
 | 
			
		||||
	if (p.implicitSize != c.implicitSize || p.anchors != c.anchors) {
 | 
			
		||||
		auto size =
 | 
			
		||||
		    constrainedSize(p.anchors, QHighDpi::toNativePixels(p.implicitSize, this->qwindow()));
 | 
			
		||||
		this->set_size(size.width(), size.height());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p.anchors != c.anchors) {
 | 
			
		||||
		this->set_anchor(toWaylandAnchors(p.anchors));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p.margins != c.margins) {
 | 
			
		||||
		this->set_margin(
 | 
			
		||||
		    QHighDpi::toNativePixels(p.margins.mTop, this->qwindow()),
 | 
			
		||||
		    QHighDpi::toNativePixels(p.margins.mRight, this->qwindow()),
 | 
			
		||||
		    QHighDpi::toNativePixels(p.margins.mBottom, this->qwindow()),
 | 
			
		||||
		    QHighDpi::toNativePixels(p.margins.mLeft, this->qwindow())
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p.layer != c.layer) {
 | 
			
		||||
		this->set_layer(p.layer);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p.exclusiveZone != c.exclusiveZone) {
 | 
			
		||||
		this->set_exclusive_zone(QHighDpi::toNativePixels(p.exclusiveZone, this->qwindow()));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (p.keyboardFocus != c.keyboardFocus) {
 | 
			
		||||
		this->set_keyboard_interactivity(toWaylandKeyboardFocus(p.keyboardFocus));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c = p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::setWindowGeometry(const QRect& geometry) {
 | 
			
		||||
	if (this->ext == nullptr) return;
 | 
			
		||||
	auto size = constrainedSize(this->ext->mAnchors, geometry.size());
 | 
			
		||||
	this->set_size(size.width(), size.height());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QWindow* QSWaylandLayerSurface::qwindow() { return this->window()->window(); }
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::updateLayer() {
 | 
			
		||||
	this->set_layer(toWaylandLayer(this->ext->mLayer));
 | 
			
		||||
	this->window()->waylandSurface()->commit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::updateAnchors() {
 | 
			
		||||
	this->set_anchor(toWaylandAnchors(this->ext->mAnchors));
 | 
			
		||||
	this->setWindowGeometry(this->window()->windowContentGeometry());
 | 
			
		||||
	this->window()->waylandSurface()->commit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::updateMargins() {
 | 
			
		||||
	auto& margins = this->ext->mMargins;
 | 
			
		||||
	this->set_margin(margins.mTop, margins.mRight, margins.mBottom, margins.mLeft);
 | 
			
		||||
	this->window()->waylandSurface()->commit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::updateExclusiveZone() {
 | 
			
		||||
	auto nativeZone = QHighDpi::toNativePixels(this->ext->mExclusiveZone, this->window()->window());
 | 
			
		||||
	this->set_exclusive_zone(nativeZone);
 | 
			
		||||
	this->window()->waylandSurface()->commit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::updateKeyboardFocus() {
 | 
			
		||||
	this->set_keyboard_interactivity(toWaylandKeyboardFocus(this->ext->mKeyboardFocus));
 | 
			
		||||
	this->window()->waylandSurface()->commit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface* popup) {
 | 
			
		||||
void LayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface* popup) {
 | 
			
		||||
	std::any role = popup->surfaceRole();
 | 
			
		||||
 | 
			
		||||
	if (auto* popupRole = std::any_cast<::xdg_popup*>(&role)) { // NOLINT
 | 
			
		||||
| 
						 | 
				
			
			@ -200,3 +250,5 @@ void QSWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface* p
 | 
			
		|||
		           << "as the popup is not an xdg_popup.";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::layershell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,8 @@
 | 
			
		|||
 | 
			
		||||
#include <private/qwaylandshellsurface_p.h>
 | 
			
		||||
#include <private/qwaylandwindow_p.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qsize.h>
 | 
			
		||||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <qtwaylandclientexports.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -9,40 +11,76 @@
 | 
			
		|||
#include <qwindow.h>
 | 
			
		||||
 | 
			
		||||
#include "shell_integration.hpp"
 | 
			
		||||
#include "window.hpp"
 | 
			
		||||
#include "wlr_layershell.hpp"
 | 
			
		||||
 | 
			
		||||
class QSWaylandLayerSurface
 | 
			
		||||
namespace qs::wayland::layershell {
 | 
			
		||||
 | 
			
		||||
struct LayerSurfaceState {
 | 
			
		||||
	QSize implicitSize;
 | 
			
		||||
	Anchors anchors;
 | 
			
		||||
	Margins margins;
 | 
			
		||||
	WlrLayer::Enum layer = WlrLayer::Top;
 | 
			
		||||
	qint32 exclusiveZone = 0;
 | 
			
		||||
	WlrKeyboardFocus::Enum keyboardFocus = WlrKeyboardFocus::None;
 | 
			
		||||
 | 
			
		||||
	bool compositorPickesScreen = true;
 | 
			
		||||
	QString mNamespace = "quickshell";
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool isCompatible(const LayerSurfaceState& other) const {
 | 
			
		||||
		return other.mNamespace == this->mNamespace;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class LayerSurface;
 | 
			
		||||
 | 
			
		||||
class LayerSurfaceBridge: public QObject {
 | 
			
		||||
public:
 | 
			
		||||
	LayerSurfaceState state;
 | 
			
		||||
 | 
			
		||||
	void commitState();
 | 
			
		||||
 | 
			
		||||
	// Returns a bridge if attached, otherwise nullptr.
 | 
			
		||||
	static LayerSurfaceBridge* get(QWindow* window);
 | 
			
		||||
 | 
			
		||||
	// Creates or reuses a bridge on the given window and returns if it compatible, otherwise nullptr.
 | 
			
		||||
	static LayerSurfaceBridge* init(QWindow* window, LayerSurfaceState state);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	explicit LayerSurfaceBridge(QWindow* parent): QObject(parent) {}
 | 
			
		||||
 | 
			
		||||
	LayerSurface* surface = nullptr;
 | 
			
		||||
 | 
			
		||||
	friend class LayerSurface;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class LayerSurface
 | 
			
		||||
    : public QtWaylandClient::QWaylandShellSurface
 | 
			
		||||
    , public QtWayland::zwlr_layer_surface_v1 {
 | 
			
		||||
public:
 | 
			
		||||
	QSWaylandLayerSurface(
 | 
			
		||||
	    QSWaylandLayerShellIntegration* shell,
 | 
			
		||||
	    QtWaylandClient::QWaylandWindow* window
 | 
			
		||||
	);
 | 
			
		||||
	LayerSurface(LayerShellIntegration* shell, QtWaylandClient::QWaylandWindow* window);
 | 
			
		||||
 | 
			
		||||
	~QSWaylandLayerSurface() override;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(QSWaylandLayerSurface);
 | 
			
		||||
	~LayerSurface() override;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(LayerSurface);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool isExposed() const override;
 | 
			
		||||
	void applyConfigure() override;
 | 
			
		||||
	void setWindowGeometry(const QRect& geometry) override;
 | 
			
		||||
	void setWindowGeometry(const QRect& /*geometry*/) override {}
 | 
			
		||||
 | 
			
		||||
	void attachPopup(QtWaylandClient::QWaylandShellSurface* popup) override;
 | 
			
		||||
 | 
			
		||||
	void commit();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void zwlr_layer_surface_v1_configure(quint32 serial, quint32 width, quint32 height) override;
 | 
			
		||||
	void zwlr_layer_surface_v1_closed() override;
 | 
			
		||||
 | 
			
		||||
	QWindow* qwindow();
 | 
			
		||||
	void updateLayer();
 | 
			
		||||
	void updateAnchors();
 | 
			
		||||
	void updateMargins();
 | 
			
		||||
	void updateExclusiveZone();
 | 
			
		||||
	void updateKeyboardFocus();
 | 
			
		||||
 | 
			
		||||
	LayershellWindowExtension* ext;
 | 
			
		||||
	QSize size;
 | 
			
		||||
	LayerSurfaceBridge* bridge;
 | 
			
		||||
	bool configured = false;
 | 
			
		||||
	QSize size;
 | 
			
		||||
 | 
			
		||||
	friend class LayershellWindowExtension;
 | 
			
		||||
	LayerSurfaceState committed;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::layershell
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,153 +0,0 @@
 | 
			
		|||
#include "window.hpp"
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <private/qwaylandwindow_p.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
#include <qvariant.h>
 | 
			
		||||
#include <qwindow.h>
 | 
			
		||||
 | 
			
		||||
#include "../../window/panelinterface.hpp"
 | 
			
		||||
#include "shell_integration.hpp"
 | 
			
		||||
#include "surface.hpp"
 | 
			
		||||
 | 
			
		||||
LayershellWindowExtension::~LayershellWindowExtension() {
 | 
			
		||||
	if (this->surface != nullptr) {
 | 
			
		||||
		this->surface->ext = nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LayershellWindowExtension* LayershellWindowExtension::get(QWindow* window) {
 | 
			
		||||
	auto v = window->property("layershell_ext");
 | 
			
		||||
 | 
			
		||||
	if (v.canConvert<LayershellWindowExtension*>()) {
 | 
			
		||||
		return v.value<LayershellWindowExtension*>();
 | 
			
		||||
	} else {
 | 
			
		||||
		return nullptr;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool LayershellWindowExtension::attach(QWindow* window) {
 | 
			
		||||
	if (this->surface != nullptr)
 | 
			
		||||
		qFatal() << "Cannot change the attached window of a LayershellWindowExtension";
 | 
			
		||||
 | 
			
		||||
	auto* current = LayershellWindowExtension::get(window);
 | 
			
		||||
 | 
			
		||||
	bool hasSurface = false;
 | 
			
		||||
 | 
			
		||||
	if (current != nullptr) {
 | 
			
		||||
		if (current->mNamespace != this->mNamespace) return false;
 | 
			
		||||
 | 
			
		||||
		if (current->surface != nullptr) {
 | 
			
		||||
			if (current->surface->qwindow()->screen() != window->screen()) return false;
 | 
			
		||||
			this->surface = current->surface;
 | 
			
		||||
 | 
			
		||||
			// update window with current settings, leveraging old extension's cached values
 | 
			
		||||
			current->setAnchors(this->mAnchors);
 | 
			
		||||
			current->setMargins(this->mMargins);
 | 
			
		||||
			current->setExclusiveZone(this->mExclusiveZone);
 | 
			
		||||
			current->setLayer(this->mLayer);
 | 
			
		||||
			current->setKeyboardFocus(this->mKeyboardFocus);
 | 
			
		||||
 | 
			
		||||
			this->surface->ext = this;
 | 
			
		||||
			current->surface = nullptr;
 | 
			
		||||
			current->deleteLater();
 | 
			
		||||
 | 
			
		||||
			hasSurface = true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!hasSurface) {
 | 
			
		||||
		// Qt appears to be resetting the window's screen on creation on some systems. This works around it.
 | 
			
		||||
		auto* screen = window->screen();
 | 
			
		||||
		window->create();
 | 
			
		||||
		window->setScreen(screen);
 | 
			
		||||
 | 
			
		||||
		auto* waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow*>(window->handle());
 | 
			
		||||
		if (waylandWindow == nullptr) {
 | 
			
		||||
			qWarning() << window << "is not a wayland window. Cannot create layershell surface.";
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static QSWaylandLayerShellIntegration* layershellIntegration = nullptr; // NOLINT
 | 
			
		||||
		if (layershellIntegration == nullptr) {
 | 
			
		||||
			layershellIntegration = new QSWaylandLayerShellIntegration();
 | 
			
		||||
			if (!layershellIntegration->initialize(waylandWindow->display())) {
 | 
			
		||||
				delete layershellIntegration;
 | 
			
		||||
				layershellIntegration = nullptr;
 | 
			
		||||
				qWarning() << "Failed to initialize layershell integration";
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		waylandWindow->setShellIntegration(layershellIntegration);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	window->setProperty("layershell_ext", QVariant::fromValue(this));
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LayershellWindowExtension::setAnchors(Anchors anchors) {
 | 
			
		||||
	if (anchors != this->mAnchors) {
 | 
			
		||||
		this->mAnchors = anchors;
 | 
			
		||||
		if (this->surface != nullptr) this->surface->updateAnchors();
 | 
			
		||||
		emit this->anchorsChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Anchors LayershellWindowExtension::anchors() const { return this->mAnchors; }
 | 
			
		||||
 | 
			
		||||
void LayershellWindowExtension::setMargins(Margins margins) {
 | 
			
		||||
	if (margins != this->mMargins) {
 | 
			
		||||
		this->mMargins = margins;
 | 
			
		||||
		if (this->surface != nullptr) this->surface->updateMargins();
 | 
			
		||||
		emit this->marginsChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Margins LayershellWindowExtension::margins() const { return this->mMargins; }
 | 
			
		||||
 | 
			
		||||
void LayershellWindowExtension::setExclusiveZone(qint32 exclusiveZone) {
 | 
			
		||||
	if (exclusiveZone != this->mExclusiveZone) {
 | 
			
		||||
		this->mExclusiveZone = exclusiveZone;
 | 
			
		||||
		if (this->surface != nullptr) this->surface->updateExclusiveZone();
 | 
			
		||||
		emit this->exclusiveZoneChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qint32 LayershellWindowExtension::exclusiveZone() const { return this->mExclusiveZone; }
 | 
			
		||||
 | 
			
		||||
void LayershellWindowExtension::setLayer(WlrLayer::Enum layer) {
 | 
			
		||||
	if (layer != this->mLayer) {
 | 
			
		||||
		this->mLayer = layer;
 | 
			
		||||
		if (this->surface != nullptr) this->surface->updateLayer();
 | 
			
		||||
		emit this->layerChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WlrLayer::Enum LayershellWindowExtension::layer() const { return this->mLayer; }
 | 
			
		||||
 | 
			
		||||
void LayershellWindowExtension::setKeyboardFocus(WlrKeyboardFocus::Enum focus) {
 | 
			
		||||
	if (focus != this->mKeyboardFocus) {
 | 
			
		||||
		this->mKeyboardFocus = focus;
 | 
			
		||||
		if (this->surface != nullptr) this->surface->updateKeyboardFocus();
 | 
			
		||||
		emit this->keyboardFocusChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
WlrKeyboardFocus::Enum LayershellWindowExtension::keyboardFocus() const {
 | 
			
		||||
	return this->mKeyboardFocus;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LayershellWindowExtension::setUseWindowScreen(bool value) {
 | 
			
		||||
	this->useWindowScreen = value; // has no effect post configure
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LayershellWindowExtension::setNamespace(QString ns) {
 | 
			
		||||
	if (!this->isConfigured()) this->mNamespace = std::move(ns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString LayershellWindowExtension::ns() const { return this->mNamespace; }
 | 
			
		||||
 | 
			
		||||
bool LayershellWindowExtension::isConfigured() const { return this->surface != nullptr; }
 | 
			
		||||
| 
						 | 
				
			
			@ -1,118 +0,0 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qscreen.h>
 | 
			
		||||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
#include <qwindow.h>
 | 
			
		||||
 | 
			
		||||
#include "../../window/panelinterface.hpp"
 | 
			
		||||
 | 
			
		||||
///! WlrLayershell layer.
 | 
			
		||||
/// See @@WlrLayershell.layer.
 | 
			
		||||
namespace WlrLayer { // NOLINT
 | 
			
		||||
Q_NAMESPACE;
 | 
			
		||||
QML_ELEMENT;
 | 
			
		||||
 | 
			
		||||
enum Enum : quint8 {
 | 
			
		||||
	/// Below bottom
 | 
			
		||||
	Background = 0,
 | 
			
		||||
	/// Above background, usually below windows
 | 
			
		||||
	Bottom = 1,
 | 
			
		||||
	/// Commonly used for panels, app launchers, and docks.
 | 
			
		||||
	/// Usually renders over normal windows and below fullscreen windows.
 | 
			
		||||
	Top = 2,
 | 
			
		||||
	/// Usually renders over fullscreen windows
 | 
			
		||||
	Overlay = 3,
 | 
			
		||||
};
 | 
			
		||||
Q_ENUM_NS(Enum);
 | 
			
		||||
 | 
			
		||||
} // namespace WlrLayer
 | 
			
		||||
 | 
			
		||||
///! WlrLayershell keyboard focus mode
 | 
			
		||||
/// See @@WlrLayershell.keyboardFocus.
 | 
			
		||||
namespace WlrKeyboardFocus { // NOLINT
 | 
			
		||||
Q_NAMESPACE;
 | 
			
		||||
QML_ELEMENT;
 | 
			
		||||
 | 
			
		||||
enum Enum : quint8 {
 | 
			
		||||
	/// No keyboard input will be accepted.
 | 
			
		||||
	None = 0,
 | 
			
		||||
	/// Exclusive access to the keyboard, locking out all other windows.
 | 
			
		||||
	///
 | 
			
		||||
	/// > [!WARNING] You **CANNOT** use this to make a secure lock screen.
 | 
			
		||||
	/// >
 | 
			
		||||
	/// > If you want to make a lock screen, use @@WlSessionLock.
 | 
			
		||||
	Exclusive = 1,
 | 
			
		||||
	/// Access to the keyboard as determined by the operating system.
 | 
			
		||||
	///
 | 
			
		||||
	/// > [!WARNING] On some systems, `OnDemand` may cause the shell window to
 | 
			
		||||
	/// > retain focus over another window unexpectedly.
 | 
			
		||||
	/// > You should try `None` if you experience issues.
 | 
			
		||||
	OnDemand = 2,
 | 
			
		||||
};
 | 
			
		||||
Q_ENUM_NS(Enum);
 | 
			
		||||
 | 
			
		||||
} // namespace WlrKeyboardFocus
 | 
			
		||||
 | 
			
		||||
class QSWaylandLayerSurface;
 | 
			
		||||
 | 
			
		||||
class LayershellWindowExtension: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	LayershellWindowExtension(QObject* parent = nullptr): QObject(parent) {}
 | 
			
		||||
	~LayershellWindowExtension() override;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(LayershellWindowExtension);
 | 
			
		||||
 | 
			
		||||
	// returns the layershell extension if attached, otherwise nullptr
 | 
			
		||||
	static LayershellWindowExtension* get(QWindow* window);
 | 
			
		||||
 | 
			
		||||
	// Attach this layershell extension to the given window.
 | 
			
		||||
	// The extension is reparented to the window and replaces any existing layershell extension.
 | 
			
		||||
	// Returns false if the window cannot be used.
 | 
			
		||||
	bool attach(QWindow* window);
 | 
			
		||||
 | 
			
		||||
	void setAnchors(Anchors anchors);
 | 
			
		||||
	[[nodiscard]] Anchors anchors() const;
 | 
			
		||||
 | 
			
		||||
	void setMargins(Margins margins);
 | 
			
		||||
	[[nodiscard]] Margins margins() const;
 | 
			
		||||
 | 
			
		||||
	void setExclusiveZone(qint32 exclusiveZone);
 | 
			
		||||
	[[nodiscard]] qint32 exclusiveZone() const;
 | 
			
		||||
 | 
			
		||||
	void setLayer(WlrLayer::Enum layer);
 | 
			
		||||
	[[nodiscard]] WlrLayer::Enum layer() const;
 | 
			
		||||
 | 
			
		||||
	void setKeyboardFocus(WlrKeyboardFocus::Enum focus);
 | 
			
		||||
	[[nodiscard]] WlrKeyboardFocus::Enum keyboardFocus() const;
 | 
			
		||||
 | 
			
		||||
	// no effect if configured
 | 
			
		||||
	void setUseWindowScreen(bool value);
 | 
			
		||||
	void setNamespace(QString ns);
 | 
			
		||||
	[[nodiscard]] QString ns() const;
 | 
			
		||||
	[[nodiscard]] bool isConfigured() const;
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void anchorsChanged();
 | 
			
		||||
	void marginsChanged();
 | 
			
		||||
	void exclusiveZoneChanged();
 | 
			
		||||
	void layerChanged();
 | 
			
		||||
	void keyboardFocusChanged();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	// if configured the screen cannot be changed
 | 
			
		||||
	QSWaylandLayerSurface* surface = nullptr;
 | 
			
		||||
 | 
			
		||||
	bool useWindowScreen = false;
 | 
			
		||||
	Anchors mAnchors;
 | 
			
		||||
	Margins mMargins;
 | 
			
		||||
	qint32 mExclusiveZone = 0;
 | 
			
		||||
	WlrLayer::Enum mLayer = WlrLayer::Top;
 | 
			
		||||
	QString mNamespace = "quickshell";
 | 
			
		||||
	WlrKeyboardFocus::Enum mKeyboardFocus = WlrKeyboardFocus::None;
 | 
			
		||||
 | 
			
		||||
	friend class QSWaylandLayerSurface;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -1,29 +1,28 @@
 | 
			
		|||
#include "wlr_layershell.hpp"
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qqmllist.h>
 | 
			
		||||
#include <qquickitem.h>
 | 
			
		||||
#include <qquickwindow.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/qmlscreen.hpp"
 | 
			
		||||
#include "../window/panelinterface.hpp"
 | 
			
		||||
#include "../window/proxywindow.hpp"
 | 
			
		||||
#include "wlr_layershell/window.hpp"
 | 
			
		||||
#include "../../core/qmlscreen.hpp"
 | 
			
		||||
#include "../../window/panelinterface.hpp"
 | 
			
		||||
#include "../../window/proxywindow.hpp"
 | 
			
		||||
#include "surface.hpp"
 | 
			
		||||
 | 
			
		||||
WlrLayershell::WlrLayershell(QObject* parent)
 | 
			
		||||
    : ProxyWindowBase(parent)
 | 
			
		||||
    , ext(new LayershellWindowExtension(this)) {}
 | 
			
		||||
namespace qs::wayland::layershell {
 | 
			
		||||
 | 
			
		||||
WlrLayershell::WlrLayershell(QObject* parent): ProxyWindowBase(parent) {}
 | 
			
		||||
 | 
			
		||||
ProxiedWindow* WlrLayershell::retrieveWindow(QObject* oldInstance) {
 | 
			
		||||
	auto* old = qobject_cast<WlrLayershell*>(oldInstance);
 | 
			
		||||
	auto* window = old == nullptr ? nullptr : old->disownWindow();
 | 
			
		||||
 | 
			
		||||
	if (window != nullptr) {
 | 
			
		||||
		if (this->ext->attach(window)) {
 | 
			
		||||
		this->bridge = LayerSurfaceBridge::init(window, this->computeState());
 | 
			
		||||
		if (this->bridge) {
 | 
			
		||||
			return window;
 | 
			
		||||
		} else {
 | 
			
		||||
			window->deleteLater();
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +35,8 @@ ProxiedWindow* WlrLayershell::retrieveWindow(QObject* oldInstance) {
 | 
			
		|||
ProxiedWindow* WlrLayershell::createQQuickWindow() {
 | 
			
		||||
	auto* window = this->ProxyWindowBase::createQQuickWindow();
 | 
			
		||||
 | 
			
		||||
	if (!this->ext->attach(window)) {
 | 
			
		||||
	this->bridge = LayerSurfaceBridge::init(window, this->computeState());
 | 
			
		||||
	if (!this->bridge) {
 | 
			
		||||
		qWarning() << "Could not attach Layershell extension to new QQuickWindow. Layer will not "
 | 
			
		||||
		              "behave correctly.";
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -47,17 +47,14 @@ ProxiedWindow* WlrLayershell::createQQuickWindow() {
 | 
			
		|||
void WlrLayershell::connectWindow() {
 | 
			
		||||
	this->ProxyWindowBase::connectWindow();
 | 
			
		||||
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	QObject::connect(this->ext, &LayershellWindowExtension::layerChanged, this, &WlrLayershell::layerChanged);
 | 
			
		||||
	QObject::connect(this->ext, &LayershellWindowExtension::keyboardFocusChanged, this, &WlrLayershell::keyboardFocusChanged);
 | 
			
		||||
	QObject::connect(this->ext, &LayershellWindowExtension::anchorsChanged, this, &WlrLayershell::anchorsChanged);
 | 
			
		||||
	QObject::connect(this->ext, &LayershellWindowExtension::exclusiveZoneChanged, this, &WlrLayershell::exclusiveZoneChanged);
 | 
			
		||||
	QObject::connect(this->ext, &LayershellWindowExtension::marginsChanged, this, &WlrLayershell::marginsChanged);
 | 
			
		||||
 | 
			
		||||
	QObject::connect(this, &ProxyWindowBase::widthChanged, this, &WlrLayershell::updateAutoExclusion);
 | 
			
		||||
	QObject::connect(this, &ProxyWindowBase::heightChanged, this, &WlrLayershell::updateAutoExclusion);
 | 
			
		||||
	QObject::connect(this, &WlrLayershell::anchorsChanged, this, &WlrLayershell::updateAutoExclusion);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
 | 
			
		||||
	QObject::connect(
 | 
			
		||||
	    this,
 | 
			
		||||
	    &ProxyWindowBase::heightChanged,
 | 
			
		||||
	    this,
 | 
			
		||||
	    &WlrLayershell::updateAutoExclusion
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	this->updateAutoExclusion();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -71,45 +68,24 @@ bool WlrLayershell::deleteOnInvisible() const {
 | 
			
		|||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::trySetWidth(qint32 implicitWidth) {
 | 
			
		||||
	// only update the actual size if not blocked by anchors
 | 
			
		||||
	if (!this->ext->anchors().horizontalConstraint()) {
 | 
			
		||||
		this->ProxyWindowBase::trySetWidth(implicitWidth);
 | 
			
		||||
void WlrLayershell::onPolished() {
 | 
			
		||||
	if (this->bridge) {
 | 
			
		||||
		this->bridge->state = this->computeState();
 | 
			
		||||
		this->bridge->commitState();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->ProxyWindowBase::onPolished();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::trySetHeight(qint32 implicitHeight) {
 | 
			
		||||
	// only update the actual size if not blocked by anchors
 | 
			
		||||
	if (!this->ext->anchors().verticalConstraint()) {
 | 
			
		||||
		this->ProxyWindowBase::trySetHeight(implicitHeight);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void WlrLayershell::trySetWidth(qint32 /*implicitWidth*/) { this->onStateChanged(); }
 | 
			
		||||
void WlrLayershell::trySetHeight(qint32 /*implicitHeight*/) { this->onStateChanged(); }
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::setScreen(QuickshellScreenInfo* screen) {
 | 
			
		||||
	this->ext->setUseWindowScreen(screen != nullptr);
 | 
			
		||||
	this->compositorPicksScreen = screen == nullptr;
 | 
			
		||||
	this->ProxyWindowBase::setScreen(screen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NOLINTBEGIN
 | 
			
		||||
#define extPair(type, get, set)                                                                    \
 | 
			
		||||
	type WlrLayershell::get() const { return this->ext->get(); }                                     \
 | 
			
		||||
	void WlrLayershell::set(type value) { this->ext->set(value); }
 | 
			
		||||
 | 
			
		||||
extPair(WlrLayer::Enum, layer, setLayer);
 | 
			
		||||
extPair(WlrKeyboardFocus::Enum, keyboardFocus, setKeyboardFocus);
 | 
			
		||||
extPair(Margins, margins, setMargins);
 | 
			
		||||
// NOLINTEND
 | 
			
		||||
 | 
			
		||||
Anchors WlrLayershell::anchors() const { return this->ext->anchors(); }
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::setAnchors(Anchors anchors) {
 | 
			
		||||
	this->ext->setAnchors(anchors);
 | 
			
		||||
	if (!this->window) return;
 | 
			
		||||
 | 
			
		||||
	// explicitly set width values are tracked so the entire screen isn't covered if an anchor is removed.
 | 
			
		||||
	if (!anchors.horizontalConstraint()) this->ProxyWindowBase::trySetWidth(this->implicitWidth());
 | 
			
		||||
	if (!anchors.verticalConstraint()) this->ProxyWindowBase::trySetHeight(this->implicitHeight());
 | 
			
		||||
}
 | 
			
		||||
void WlrLayershell::onStateChanged() { this->schedulePolish(); }
 | 
			
		||||
 | 
			
		||||
bool WlrLayershell::aboveWindows() const { return this->layer() > WlrLayer::Bottom; }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -123,51 +99,33 @@ void WlrLayershell::setFocusable(bool focusable) {
 | 
			
		|||
	this->setKeyboardFocus(focusable ? WlrKeyboardFocus::OnDemand : WlrKeyboardFocus::None);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QString WlrLayershell::ns() const { return this->ext->ns(); }
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::setNamespace(QString ns) {
 | 
			
		||||
	this->ext->setNamespace(std::move(ns));
 | 
			
		||||
	emit this->namespaceChanged();
 | 
			
		||||
LayerSurfaceState WlrLayershell::computeState() const {
 | 
			
		||||
	return LayerSurfaceState {
 | 
			
		||||
	    .implicitSize = QSize(this->implicitWidth(), this->implicitHeight()),
 | 
			
		||||
	    .anchors = this->bAnchors,
 | 
			
		||||
	    .margins = this->bMargins,
 | 
			
		||||
	    .layer = this->bLayer,
 | 
			
		||||
	    .exclusiveZone = this->bcExclusiveZone,
 | 
			
		||||
	    .keyboardFocus = this->bKeyboardFocus,
 | 
			
		||||
	    .compositorPickesScreen = this->compositorPicksScreen,
 | 
			
		||||
	    .mNamespace = this->bNamespace,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qint32 WlrLayershell::exclusiveZone() const { return this->ext->exclusiveZone(); }
 | 
			
		||||
qint32 WlrLayershell::computeExclusiveZone() const {
 | 
			
		||||
	switch (this->bExclusionMode.value()) {
 | 
			
		||||
	case ExclusionMode::Ignore: return -1;
 | 
			
		||||
	case ExclusionMode::Normal: return this->bExclusiveZone;
 | 
			
		||||
	case ExclusionMode::Auto:
 | 
			
		||||
		const auto anchors = this->bAnchors.value();
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::setExclusiveZone(qint32 exclusiveZone) {
 | 
			
		||||
	this->mExclusiveZone = exclusiveZone;
 | 
			
		||||
	this->setExclusionMode(ExclusionMode::Normal);
 | 
			
		||||
	this->ext->setExclusiveZone(exclusiveZone);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ExclusionMode::Enum WlrLayershell::exclusionMode() const { return this->mExclusionMode; }
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::setExclusionMode(ExclusionMode::Enum exclusionMode) {
 | 
			
		||||
	if (exclusionMode == this->mExclusionMode) return;
 | 
			
		||||
	this->mExclusionMode = exclusionMode;
 | 
			
		||||
 | 
			
		||||
	if (exclusionMode == ExclusionMode::Normal) {
 | 
			
		||||
		this->ext->setExclusiveZone(this->mExclusiveZone);
 | 
			
		||||
	} else if (exclusionMode == ExclusionMode::Ignore) {
 | 
			
		||||
		this->ext->setExclusiveZone(-1);
 | 
			
		||||
	} else {
 | 
			
		||||
		this->setAutoExclusion();
 | 
			
		||||
		if (anchors.horizontalConstraint()) return this->height();
 | 
			
		||||
		else if (anchors.verticalConstraint()) return this->width();
 | 
			
		||||
		else return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::setAutoExclusion() {
 | 
			
		||||
	const auto anchors = this->anchors();
 | 
			
		||||
	auto zone = 0;
 | 
			
		||||
 | 
			
		||||
	if (anchors.horizontalConstraint()) zone = this->height();
 | 
			
		||||
	else if (anchors.verticalConstraint()) zone = this->width();
 | 
			
		||||
 | 
			
		||||
	this->ext->setExclusiveZone(zone);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::updateAutoExclusion() {
 | 
			
		||||
	if (this->mExclusionMode == ExclusionMode::Auto) {
 | 
			
		||||
		this->setAutoExclusion();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
void WlrLayershell::updateAutoExclusion() { this->bcExclusiveZone.notify(); }
 | 
			
		||||
 | 
			
		||||
WlrLayershell* WlrLayershell::qmlAttachedProperties(QObject* object) {
 | 
			
		||||
	if (auto* obj = qobject_cast<WaylandPanelInterface*>(object)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -250,3 +208,5 @@ proxyPair(bool, aboveWindows, setAboveWindows);
 | 
			
		|||
 | 
			
		||||
#undef proxyPair
 | 
			
		||||
// NOLINTEND
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::layershell
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,71 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qproperty.h>
 | 
			
		||||
#include <qqmlintegration.h>
 | 
			
		||||
#include <qquickitem.h>
 | 
			
		||||
#include <qquickwindow.h>
 | 
			
		||||
#include <qsize.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/doc.hpp"
 | 
			
		||||
#include "../window/panelinterface.hpp"
 | 
			
		||||
#include "../window/proxywindow.hpp"
 | 
			
		||||
#include "wlr_layershell/window.hpp"
 | 
			
		||||
#include "../../core/doc.hpp"
 | 
			
		||||
#include "../../core/util.hpp"
 | 
			
		||||
#include "../../window/panelinterface.hpp"
 | 
			
		||||
#include "../../window/proxywindow.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::layershell {
 | 
			
		||||
 | 
			
		||||
struct LayerSurfaceState;
 | 
			
		||||
class LayerSurfaceBridge;
 | 
			
		||||
 | 
			
		||||
///! WlrLayershell layer.
 | 
			
		||||
/// See @@WlrLayershell.layer.
 | 
			
		||||
namespace WlrLayer { // NOLINT
 | 
			
		||||
Q_NAMESPACE;
 | 
			
		||||
QML_ELEMENT;
 | 
			
		||||
 | 
			
		||||
enum Enum : quint8 {
 | 
			
		||||
	/// Below bottom
 | 
			
		||||
	Background = 0,
 | 
			
		||||
	/// Above background, usually below windows
 | 
			
		||||
	Bottom = 1,
 | 
			
		||||
	/// Commonly used for panels, app launchers, and docks.
 | 
			
		||||
	/// Usually renders over normal windows and below fullscreen windows.
 | 
			
		||||
	Top = 2,
 | 
			
		||||
	/// Usually renders over fullscreen windows
 | 
			
		||||
	Overlay = 3,
 | 
			
		||||
};
 | 
			
		||||
Q_ENUM_NS(Enum);
 | 
			
		||||
 | 
			
		||||
} // namespace WlrLayer
 | 
			
		||||
 | 
			
		||||
///! WlrLayershell keyboard focus mode
 | 
			
		||||
/// See @@WlrLayershell.keyboardFocus.
 | 
			
		||||
namespace WlrKeyboardFocus { // NOLINT
 | 
			
		||||
Q_NAMESPACE;
 | 
			
		||||
QML_ELEMENT;
 | 
			
		||||
 | 
			
		||||
enum Enum : quint8 {
 | 
			
		||||
	/// No keyboard input will be accepted.
 | 
			
		||||
	None = 0,
 | 
			
		||||
	/// Exclusive access to the keyboard, locking out all other windows.
 | 
			
		||||
	///
 | 
			
		||||
	/// > [!WARNING] You **CANNOT** use this to make a secure lock screen.
 | 
			
		||||
	/// >
 | 
			
		||||
	/// > If you want to make a lock screen, use @@WlSessionLock.
 | 
			
		||||
	Exclusive = 1,
 | 
			
		||||
	/// Access to the keyboard as determined by the operating system.
 | 
			
		||||
	///
 | 
			
		||||
	/// > [!WARNING] On some systems, `OnDemand` may cause the shell window to
 | 
			
		||||
	/// > retain focus over another window unexpectedly.
 | 
			
		||||
	/// > You should try `None` if you experience issues.
 | 
			
		||||
	OnDemand = 2,
 | 
			
		||||
};
 | 
			
		||||
Q_ENUM_NS(Enum);
 | 
			
		||||
 | 
			
		||||
} // namespace WlrKeyboardFocus
 | 
			
		||||
 | 
			
		||||
///! Wlroots layershell window
 | 
			
		||||
/// Decorationless window that can be attached to the screen edges using the [zwlr_layer_shell_v1] protocol.
 | 
			
		||||
| 
						 | 
				
			
			@ -43,13 +98,13 @@ class WlrLayershell: public ProxyWindowBase {
 | 
			
		|||
	// clang-format off
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
	/// The shell layer the window sits in. Defaults to `WlrLayer.Top`.
 | 
			
		||||
	Q_PROPERTY(WlrLayer::Enum layer READ layer WRITE setLayer NOTIFY layerChanged);
 | 
			
		||||
	Q_PROPERTY(qs::wayland::layershell::WlrLayer::Enum layer READ layer WRITE setLayer NOTIFY layerChanged);
 | 
			
		||||
	/// Similar to the class property of windows. Can be used to identify the window to external tools.
 | 
			
		||||
	///
 | 
			
		||||
	/// Cannot be set after windowConnected.
 | 
			
		||||
	Q_PROPERTY(QString namespace READ ns WRITE setNamespace NOTIFY namespaceChanged);
 | 
			
		||||
	/// The degree of keyboard focus taken. Defaults to `KeyboardFocus.None`.
 | 
			
		||||
	Q_PROPERTY(WlrKeyboardFocus::Enum keyboardFocus READ keyboardFocus WRITE setKeyboardFocus NOTIFY keyboardFocusChanged);
 | 
			
		||||
	Q_PROPERTY(qs::wayland::layershell::WlrKeyboardFocus::Enum keyboardFocus READ keyboardFocus WRITE setKeyboardFocus NOTIFY keyboardFocusChanged);
 | 
			
		||||
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(Anchors anchors READ anchors WRITE setAnchors NOTIFY anchorsChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(qint32 exclusiveZone READ exclusiveZone WRITE setExclusiveZone NOTIFY exclusiveZoneChanged);
 | 
			
		||||
| 
						 | 
				
			
			@ -69,31 +124,37 @@ public:
 | 
			
		|||
	void connectWindow() override;
 | 
			
		||||
	[[nodiscard]] bool deleteOnInvisible() const override;
 | 
			
		||||
 | 
			
		||||
	void onPolished() override;
 | 
			
		||||
	void trySetWidth(qint32 implicitWidth) override;
 | 
			
		||||
	void trySetHeight(qint32 implicitHeight) override;
 | 
			
		||||
 | 
			
		||||
	void setScreen(QuickshellScreenInfo* screen) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] WlrLayer::Enum layer() const;
 | 
			
		||||
	void setLayer(WlrLayer::Enum layer); // NOLINT
 | 
			
		||||
	[[nodiscard]] WlrLayer::Enum layer() const { return this->bLayer; }
 | 
			
		||||
	void setLayer(WlrLayer::Enum layer) { this->bLayer = layer; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString ns() const;
 | 
			
		||||
	void setNamespace(QString ns);
 | 
			
		||||
	[[nodiscard]] QString ns() const { return this->bNamespace; }
 | 
			
		||||
	void setNamespace(const QString& ns) { this->bNamespace = ns; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] WlrKeyboardFocus::Enum keyboardFocus() const;
 | 
			
		||||
	void setKeyboardFocus(WlrKeyboardFocus::Enum focus); // NOLINT
 | 
			
		||||
	[[nodiscard]] WlrKeyboardFocus::Enum keyboardFocus() const { return this->bKeyboardFocus; }
 | 
			
		||||
	void setKeyboardFocus(WlrKeyboardFocus::Enum focus) { this->bKeyboardFocus = focus; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] Anchors anchors() const;
 | 
			
		||||
	void setAnchors(Anchors anchors);
 | 
			
		||||
	[[nodiscard]] Anchors anchors() const { return this->bAnchors; }
 | 
			
		||||
	void setAnchors(Anchors anchors) { this->bAnchors = anchors; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] qint32 exclusiveZone() const;
 | 
			
		||||
	void setExclusiveZone(qint32 exclusiveZone);
 | 
			
		||||
	[[nodiscard]] qint32 exclusiveZone() const { return this->bExclusiveZone; }
 | 
			
		||||
	void setExclusiveZone(qint32 exclusiveZone) {
 | 
			
		||||
		Qt::beginPropertyUpdateGroup();
 | 
			
		||||
		this->bExclusiveZone = exclusiveZone;
 | 
			
		||||
		this->bExclusionMode = ExclusionMode::Normal;
 | 
			
		||||
		Qt::endPropertyUpdateGroup();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] ExclusionMode::Enum exclusionMode() const;
 | 
			
		||||
	void setExclusionMode(ExclusionMode::Enum exclusionMode);
 | 
			
		||||
	[[nodiscard]] ExclusionMode::Enum exclusionMode() const { return this->bExclusionMode; }
 | 
			
		||||
	void setExclusionMode(ExclusionMode::Enum exclusionMode) { this->bExclusionMode = exclusionMode; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] Margins margins() const;
 | 
			
		||||
	void setMargins(Margins margins); // NOLINT
 | 
			
		||||
	[[nodiscard]] Margins margins() const { return this->bMargins; }
 | 
			
		||||
	void setMargins(Margins margins) { this->bMargins = margins; }
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool aboveWindows() const;
 | 
			
		||||
	void setAboveWindows(bool aboveWindows);
 | 
			
		||||
| 
						 | 
				
			
			@ -116,12 +177,30 @@ private slots:
 | 
			
		|||
	void updateAutoExclusion();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void setAutoExclusion();
 | 
			
		||||
	[[nodiscard]] LayerSurfaceState computeState() const;
 | 
			
		||||
	[[nodiscard]] qint32 computeExclusiveZone() const;
 | 
			
		||||
 | 
			
		||||
	LayershellWindowExtension* ext;
 | 
			
		||||
	void onStateChanged();
 | 
			
		||||
 | 
			
		||||
	ExclusionMode::Enum mExclusionMode = ExclusionMode::Auto;
 | 
			
		||||
	qint32 mExclusiveZone = 0;
 | 
			
		||||
	bool compositorPicksScreen = true;
 | 
			
		||||
	LayerSurfaceBridge* bridge = nullptr;
 | 
			
		||||
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(WlrLayershell, WlrLayer::Enum, bLayer, WlrLayer::Top, &WlrLayershell::layerChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(WlrLayershell, QString, bNamespace, "quickshell", &WlrLayershell::namespaceChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(WlrLayershell, Anchors, bAnchors, &WlrLayershell::anchorsChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(WlrLayershell, Margins, bMargins, &WlrLayershell::marginsChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(WlrLayershell, qint32, bExclusiveZone, &WlrLayershell::exclusiveZoneChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY(WlrLayershell, WlrKeyboardFocus::Enum, bKeyboardFocus, &WlrLayershell::keyboardFocusChanged);
 | 
			
		||||
	Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(WlrLayershell, ExclusionMode::Enum, bExclusionMode, ExclusionMode::Auto, &WlrLayershell::exclusionModeChanged);
 | 
			
		||||
	Q_OBJECT_COMPUTED_PROPERTY(WlrLayershell, qint32, bcExclusiveZone, &WlrLayershell::computeExclusiveZone);
 | 
			
		||||
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(WlrLayershell, bLayer, onStateChanged, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(WlrLayershell, bAnchors, onStateChanged, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(WlrLayershell, bMargins, onStateChanged, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(WlrLayershell, bcExclusiveZone, onStateChanged, onValueChanged);
 | 
			
		||||
	QS_BINDING_SUBSCRIBE_METHOD(WlrLayershell, bKeyboardFocus, onStateChanged, onValueChanged);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class WaylandPanelInterface: public PanelWindowInterface {
 | 
			
		||||
| 
						 | 
				
			
			@ -194,3 +273,5 @@ private:
 | 
			
		|||
 | 
			
		||||
	friend class WlrLayershell;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::layershell
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +153,7 @@ protected slots:
 | 
			
		|||
	void onMaskChanged();
 | 
			
		||||
	void onMaskDestroyed();
 | 
			
		||||
	void onScreenDestroyed();
 | 
			
		||||
	void onPolished();
 | 
			
		||||
	virtual void onPolished();
 | 
			
		||||
	void runLints();
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue