forked from quickshell/quickshell
		
	x11: add XPanelWindow
This commit is contained in:
		
							parent
							
								
									908ba3eef5
								
							
						
					
					
						commit
						73cfeba61b
					
				
					 15 changed files with 804 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -14,6 +14,7 @@ option(SOCKETS "Enable unix socket support" ON)
 | 
			
		|||
option(WAYLAND "Enable wayland support" ON)
 | 
			
		||||
option(WAYLAND_WLR_LAYERSHELL "Support the zwlr_layer_shell_v1 wayland protocol" ON)
 | 
			
		||||
option(WAYLAND_SESSION_LOCK "Support the ext_session_lock_v1 wayland protocol" ON)
 | 
			
		||||
option(X11 "Enable X11 support" ON)
 | 
			
		||||
option(HYPRLAND "Support hyprland specific features" ON)
 | 
			
		||||
option(HYPRLAND_GLOBAL_SHORTCUTS "Hyprland Global Shortcuts" ON)
 | 
			
		||||
option(HYPRLAND_FOCUS_GRAB "Hyprland Focus Grabbing" ON)
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +30,7 @@ if (WAYLAND)
 | 
			
		|||
	message(STATUS "    Wlroots Layershell: ${WAYLAND_WLR_LAYERSHELL}")
 | 
			
		||||
	message(STATUS "    Session Lock: ${WAYLAND_SESSION_LOCK}")
 | 
			
		||||
endif ()
 | 
			
		||||
message(STATUS "  X11: ${X11}")
 | 
			
		||||
message(STATUS "  Services")
 | 
			
		||||
message(STATUS "    StatusNotifier: ${SERVICE_STATUS_NOTIFIER}")
 | 
			
		||||
message(STATUS "    PipeWire: ${SERVICE_PIPEWIRE}")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,16 +62,22 @@ To build quickshell at all, you will need the following packages (names may vary
 | 
			
		|||
 | 
			
		||||
- just
 | 
			
		||||
- cmake
 | 
			
		||||
- pkg-config
 | 
			
		||||
- ninja
 | 
			
		||||
- Qt6 [ QtBase, QtDeclarative ]
 | 
			
		||||
 | 
			
		||||
To build with wayland support you will additionally need:
 | 
			
		||||
- pkg-config
 | 
			
		||||
- wayland
 | 
			
		||||
- wayland-scanner (may be part of wayland on some distros)
 | 
			
		||||
- wayland-protocols
 | 
			
		||||
- Qt6 [ QtWayland ]
 | 
			
		||||
 | 
			
		||||
To build with x11 support you will additionally need:
 | 
			
		||||
- libxcb
 | 
			
		||||
 | 
			
		||||
To build with pipewire support you will additionally need:
 | 
			
		||||
- libpipewire
 | 
			
		||||
 | 
			
		||||
### Building
 | 
			
		||||
 | 
			
		||||
To make a release build of quickshell run:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,8 @@
 | 
			
		|||
  qt6,
 | 
			
		||||
  wayland,
 | 
			
		||||
  wayland-protocols,
 | 
			
		||||
  xorg,
 | 
			
		||||
  pipewire,
 | 
			
		||||
 | 
			
		||||
  gitRev ? (let
 | 
			
		||||
    headExists = builtins.pathExists ./.git/HEAD;
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +26,7 @@
 | 
			
		|||
 | 
			
		||||
  debug ? false,
 | 
			
		||||
  enableWayland ? true,
 | 
			
		||||
  enableX11 ? true,
 | 
			
		||||
  enablePipewire ? true,
 | 
			
		||||
  nvidiaCompat ? false,
 | 
			
		||||
  svgSupport ? true, # you almost always want this
 | 
			
		||||
| 
						 | 
				
			
			@ -42,11 +45,12 @@
 | 
			
		|||
    wayland-scanner
 | 
			
		||||
  ]);
 | 
			
		||||
 | 
			
		||||
  buildInputs = with pkgs; [
 | 
			
		||||
  buildInputs = [
 | 
			
		||||
    qt6.qtbase
 | 
			
		||||
    qt6.qtdeclarative
 | 
			
		||||
  ]
 | 
			
		||||
  ++ (lib.optionals enableWayland [ qt6.qtwayland wayland ])
 | 
			
		||||
  ++ (lib.optionals enableX11 [ xorg.libxcb ])
 | 
			
		||||
  ++ (lib.optionals svgSupport [ qt6.qtsvg ])
 | 
			
		||||
  ++ (lib.optionals enablePipewire [ pipewire ]);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,4 +13,8 @@ if (WAYLAND)
 | 
			
		|||
	add_subdirectory(wayland)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (X11)
 | 
			
		||||
	add_subdirectory(x11)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
add_subdirectory(services)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,6 +117,10 @@ class PanelWindowInterface: public WindowInterface {
 | 
			
		|||
	Q_PROPERTY(qint32 exclusiveZone READ exclusiveZone WRITE setExclusiveZone NOTIFY exclusiveZoneChanged);
 | 
			
		||||
	/// Defaults to `ExclusionMode.Auto`.
 | 
			
		||||
	Q_PROPERTY(ExclusionMode::Enum exclusionMode READ exclusionMode WRITE setExclusionMode NOTIFY exclusionModeChanged);
 | 
			
		||||
	/// If the panel should render above standard windows. Defaults to true.
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(bool aboveWindows READ aboveWindows WRITE setAboveWindows NOTIFY aboveWindowsChanged);
 | 
			
		||||
	/// Defaults to false.
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(bool focusable READ focusable WRITE setFocusable NOTIFY focusableChanged);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
	QSDOC_NAMED_ELEMENT(PanelWindow);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -135,9 +139,17 @@ public:
 | 
			
		|||
	[[nodiscard]] virtual ExclusionMode::Enum exclusionMode() const = 0;
 | 
			
		||||
	virtual void setExclusionMode(ExclusionMode::Enum exclusionMode) = 0;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] virtual bool aboveWindows() const = 0;
 | 
			
		||||
	virtual void setAboveWindows(bool aboveWindows) = 0;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] virtual bool focusable() const = 0;
 | 
			
		||||
	virtual void setFocusable(bool focusable) = 0;
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void anchorsChanged();
 | 
			
		||||
	void marginsChanged();
 | 
			
		||||
	void exclusiveZoneChanged();
 | 
			
		||||
	void exclusionModeChanged();
 | 
			
		||||
	void aboveWindowsChanged();
 | 
			
		||||
	void focusableChanged();
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(); }
 | 
			
		|||
void ProxyWindowBase::onReload(QObject* oldInstance) {
 | 
			
		||||
	this->window = this->retrieveWindow(oldInstance);
 | 
			
		||||
	auto wasVisible = this->window != nullptr && this->window->isVisible();
 | 
			
		||||
	if (this->window == nullptr) this->window = new QQuickWindow();
 | 
			
		||||
	if (this->window == nullptr) this->window = this->createQQuickWindow();
 | 
			
		||||
 | 
			
		||||
	// The qml engine will leave the WindowInterface as owner of everything
 | 
			
		||||
	// nested in an item, so we have to make sure the interface's children
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
#include <qguiapplication.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qqml.h>
 | 
			
		||||
#include <qtenvironmentvariables.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/plugin.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +12,19 @@
 | 
			
		|||
namespace {
 | 
			
		||||
 | 
			
		||||
class WaylandPlugin: public QuickshellPlugin {
 | 
			
		||||
	bool applies() override { return QGuiApplication::platformName() == "wayland"; }
 | 
			
		||||
	bool applies() override {
 | 
			
		||||
		auto isWayland = QGuiApplication::platformName() == "wayland";
 | 
			
		||||
 | 
			
		||||
		if (!isWayland && !qEnvironmentVariable("WAYLAND_DISPLAY").isEmpty()) {
 | 
			
		||||
			qWarning() << "--- WARNING ---";
 | 
			
		||||
			qWarning() << "WAYLAND_DISPLAY is present but QT_QPA_PLATFORM is"
 | 
			
		||||
			           << QGuiApplication::platformName();
 | 
			
		||||
			qWarning() << "If you are actually running wayland, set QT_QPA_PLATFORM to \"wayland\" or "
 | 
			
		||||
			              "most functionality will be broken.";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return isWayland;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void registerTypes() override {
 | 
			
		||||
#ifdef QS_WAYLAND_WLR_LAYERSHELL
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,6 +114,18 @@ void WlrLayershell::setAnchors(Anchors anchors) {
 | 
			
		|||
	if (!anchors.verticalConstraint()) this->ProxyWindowBase::setHeight(this->mHeight);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WlrLayershell::aboveWindows() const { return this->layer() > WlrLayer::Bottom; }
 | 
			
		||||
 | 
			
		||||
void WlrLayershell::setAboveWindows(bool aboveWindows) {
 | 
			
		||||
	this->setLayer(aboveWindows ? WlrLayer::Top : WlrLayer::Bottom);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WlrLayershell::focusable() const { return this->keyboardFocus() != WlrKeyboardFocus::None; }
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -190,6 +202,8 @@ WaylandPanelInterface::WaylandPanelInterface(QObject* parent)
 | 
			
		|||
	QObject::connect(this->layer, &WlrLayershell::marginsChanged, this, &WaylandPanelInterface::marginsChanged);
 | 
			
		||||
	QObject::connect(this->layer, &WlrLayershell::exclusiveZoneChanged, this, &WaylandPanelInterface::exclusiveZoneChanged);
 | 
			
		||||
	QObject::connect(this->layer, &WlrLayershell::exclusionModeChanged, this, &WaylandPanelInterface::exclusionModeChanged);
 | 
			
		||||
	QObject::connect(this->layer, &WlrLayershell::layerChanged, this, &WaylandPanelInterface::aboveWindowsChanged);
 | 
			
		||||
	QObject::connect(this->layer, &WlrLayershell::keyboardFocusChanged, this, &WaylandPanelInterface::focusableChanged);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -224,6 +238,8 @@ proxyPair(Anchors, anchors, setAnchors);
 | 
			
		|||
proxyPair(Margins, margins, setMargins);
 | 
			
		||||
proxyPair(qint32, exclusiveZone, setExclusiveZone);
 | 
			
		||||
proxyPair(ExclusionMode::Enum, exclusionMode, setExclusionMode);
 | 
			
		||||
proxyPair(bool, focusable, setFocusable);
 | 
			
		||||
proxyPair(bool, aboveWindows, setAboveWindows);
 | 
			
		||||
 | 
			
		||||
#undef proxyPair
 | 
			
		||||
// NOLINTEND
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/doc.hpp"
 | 
			
		||||
#include "../core/panelinterface.hpp"
 | 
			
		||||
#include "../core/proxywindow.hpp"
 | 
			
		||||
#include "wlr_layershell/window.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +55,8 @@ class WlrLayershell: public ProxyWindowBase {
 | 
			
		|||
	QSDOC_HIDE Q_PROPERTY(qint32 exclusiveZone READ exclusiveZone WRITE setExclusiveZone NOTIFY exclusiveZoneChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(ExclusionMode::Enum exclusionMode READ exclusionMode WRITE setExclusionMode NOTIFY exclusionModeChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(Margins margins READ margins WRITE setMargins NOTIFY marginsChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(bool aboveWindows READ aboveWindows WRITE setAboveWindows NOTIFY layerChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(bool focusable READ focusable WRITE setFocusable NOTIFY keyboardFocusChanged);
 | 
			
		||||
	QML_ATTACHED(WlrLayershell);
 | 
			
		||||
	QML_ELEMENT;
 | 
			
		||||
	// clang-format on
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +95,12 @@ public:
 | 
			
		|||
	[[nodiscard]] Margins margins() const;
 | 
			
		||||
	void setMargins(Margins margins); // NOLINT
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool aboveWindows() const;
 | 
			
		||||
	void setAboveWindows(bool aboveWindows);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool focusable() const;
 | 
			
		||||
	void setFocusable(bool focusable);
 | 
			
		||||
 | 
			
		||||
	static WlrLayershell* qmlAttachedProperties(QObject* object);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
| 
						 | 
				
			
			@ -161,6 +170,12 @@ public:
 | 
			
		|||
 | 
			
		||||
	[[nodiscard]] ExclusionMode::Enum exclusionMode() const override;
 | 
			
		||||
	void setExclusionMode(ExclusionMode::Enum exclusionMode) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool aboveWindows() const override;
 | 
			
		||||
	void setAboveWindows(bool aboveWindows) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool focusable() const override;
 | 
			
		||||
	void setFocusable(bool focusable) override;
 | 
			
		||||
	// NOLINTEND
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/x11/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/x11/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
find_package(XCB REQUIRED COMPONENTS XCB)
 | 
			
		||||
 | 
			
		||||
qt_add_library(quickshell-x11 STATIC
 | 
			
		||||
	util.cpp
 | 
			
		||||
	panel_window.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
qt_add_qml_module(quickshell-x11
 | 
			
		||||
	URI Quickshell.X11
 | 
			
		||||
	VERSION 0.1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_library(quickshell-x11-init OBJECT init.cpp)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell-x11 PRIVATE ${QT_DEPS} ${XCB_LIBRARIES})
 | 
			
		||||
target_link_libraries(quickshell-x11-init PRIVATE ${QT_DEPS} ${XCB_LIBRARIES})
 | 
			
		||||
 | 
			
		||||
qs_pch(quickshell-x11)
 | 
			
		||||
qs_pch(quickshell-x11plugin)
 | 
			
		||||
qs_pch(quickshell-x11-init)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell PRIVATE quickshell-x11plugin quickshell-x11-init)
 | 
			
		||||
							
								
								
									
										29
									
								
								src/x11/init.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/x11/init.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
#include <qguiapplication.h>
 | 
			
		||||
#include <qqml.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/plugin.hpp"
 | 
			
		||||
#include "panel_window.hpp"
 | 
			
		||||
#include "util.hpp"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
class X11Plugin: public QuickshellPlugin {
 | 
			
		||||
	bool applies() override { return QGuiApplication::platformName() == "xcb"; }
 | 
			
		||||
 | 
			
		||||
	void init() override { XAtom::initAtoms(); }
 | 
			
		||||
 | 
			
		||||
	void registerTypes() override {
 | 
			
		||||
		qmlRegisterType<XPanelInterface>("Quickshell._X11Overlay", 1, 0, "PanelWindow");
 | 
			
		||||
 | 
			
		||||
		qmlRegisterModuleImport(
 | 
			
		||||
		    "Quickshell",
 | 
			
		||||
		    QQmlModuleImportModuleAny,
 | 
			
		||||
		    "Quickshell._X11Overlay",
 | 
			
		||||
		    QQmlModuleImportLatest
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
QS_REGISTER_PLUGIN(X11Plugin);
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
							
								
								
									
										431
									
								
								src/x11/panel_window.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										431
									
								
								src/x11/panel_window.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,431 @@
 | 
			
		|||
#include "panel_window.hpp"
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include <qevent.h>
 | 
			
		||||
#include <qlist.h>
 | 
			
		||||
#include <qnamespace.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qqmlengine.h>
 | 
			
		||||
#include <qqmllist.h>
 | 
			
		||||
#include <qquickwindow.h>
 | 
			
		||||
#include <qscreen.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
#include <xcb/xproto.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/generation.hpp"
 | 
			
		||||
#include "../core/panelinterface.hpp"
 | 
			
		||||
#include "../core/proxywindow.hpp"
 | 
			
		||||
#include "util.hpp"
 | 
			
		||||
 | 
			
		||||
class XPanelStack {
 | 
			
		||||
public:
 | 
			
		||||
	static XPanelStack* instance() {
 | 
			
		||||
		static XPanelStack* stack = nullptr; // NOLINT
 | 
			
		||||
 | 
			
		||||
		if (stack == nullptr) {
 | 
			
		||||
			stack = new XPanelStack();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return stack;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] const QList<XPanelWindow*>& panels(XPanelWindow* panel) {
 | 
			
		||||
		return this->mPanels[EngineGeneration::findObjectGeneration(panel)];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void addPanel(XPanelWindow* panel) {
 | 
			
		||||
		auto& panels = this->mPanels[EngineGeneration::findObjectGeneration(panel)];
 | 
			
		||||
		if (!panels.contains(panel)) {
 | 
			
		||||
			panels.push_back(panel);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void removePanel(XPanelWindow* panel) {
 | 
			
		||||
		auto& panels = this->mPanels[EngineGeneration::findObjectGeneration(panel)];
 | 
			
		||||
		if (panels.removeOne(panel)) {
 | 
			
		||||
			if (panels.isEmpty()) {
 | 
			
		||||
				this->mPanels.erase(EngineGeneration::findObjectGeneration(panel));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// from the bottom up, update all panels
 | 
			
		||||
			for (auto* panel: panels) {
 | 
			
		||||
				panel->updateDimensions();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	std::map<EngineGeneration*, QList<XPanelWindow*>> mPanels;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool XPanelEventFilter::eventFilter(QObject* watched, QEvent* event) {
 | 
			
		||||
	if (event->type() == QEvent::PlatformSurface) {
 | 
			
		||||
		auto* surfaceEvent = static_cast<QPlatformSurfaceEvent*>(event); // NOLINT
 | 
			
		||||
 | 
			
		||||
		if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
 | 
			
		||||
			emit this->surfaceCreated();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return this->QObject::eventFilter(watched, event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XPanelWindow::XPanelWindow(QObject* parent): ProxyWindowBase(parent) {
 | 
			
		||||
	QObject::connect(
 | 
			
		||||
	    &this->eventFilter,
 | 
			
		||||
	    &XPanelEventFilter::surfaceCreated,
 | 
			
		||||
	    this,
 | 
			
		||||
	    &XPanelWindow::xInit
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
XPanelWindow::~XPanelWindow() { XPanelStack::instance()->removePanel(this); }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::connectWindow() {
 | 
			
		||||
	this->ProxyWindowBase::connectWindow();
 | 
			
		||||
 | 
			
		||||
	this->window->installEventFilter(&this->eventFilter);
 | 
			
		||||
	this->connectScreen();
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	QObject::connect(this->window, &QQuickWindow::screenChanged, this, &XPanelWindow::connectScreen);
 | 
			
		||||
	QObject::connect(this->window, &QQuickWindow::visibleChanged, this, &XPanelWindow::updatePanelStack);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
 | 
			
		||||
	// qt overwrites _NET_WM_STATE, so we have to use the qt api
 | 
			
		||||
	// QXcbWindow::WindowType::Dock in qplatformwindow_p.h
 | 
			
		||||
	// see QXcbWindow::setWindowFlags in qxcbwindow.cpp
 | 
			
		||||
	this->window->setProperty("_q_xcb_wm_window_type", 0x000004);
 | 
			
		||||
 | 
			
		||||
	// at least one flag needs to change for the above property to apply
 | 
			
		||||
	this->window->setFlag(Qt::FramelessWindowHint);
 | 
			
		||||
	this->updateAboveWindows();
 | 
			
		||||
	this->updateFocusable();
 | 
			
		||||
 | 
			
		||||
	if (this->window->handle() != nullptr) {
 | 
			
		||||
		this->xInit();
 | 
			
		||||
		this->updatePanelStack();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setWidth(qint32 width) {
 | 
			
		||||
	this->mWidth = width;
 | 
			
		||||
 | 
			
		||||
	// only update the actual size if not blocked by anchors
 | 
			
		||||
	if (!this->mAnchors.horizontalConstraint()) {
 | 
			
		||||
		this->ProxyWindowBase::setWidth(width);
 | 
			
		||||
		this->updateDimensions();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setHeight(qint32 height) {
 | 
			
		||||
	this->mHeight = height;
 | 
			
		||||
 | 
			
		||||
	// only update the actual size if not blocked by anchors
 | 
			
		||||
	if (!this->mAnchors.verticalConstraint()) {
 | 
			
		||||
		this->ProxyWindowBase::setHeight(height);
 | 
			
		||||
		this->updateDimensions();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Anchors XPanelWindow::anchors() const { return this->mAnchors; }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setAnchors(Anchors anchors) {
 | 
			
		||||
	if (this->mAnchors == anchors) return;
 | 
			
		||||
	this->mAnchors = anchors;
 | 
			
		||||
	this->updateDimensions();
 | 
			
		||||
	emit this->anchorsChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qint32 XPanelWindow::exclusiveZone() const { return this->mExclusiveZone; }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setExclusiveZone(qint32 exclusiveZone) {
 | 
			
		||||
	if (this->mExclusiveZone == exclusiveZone) return;
 | 
			
		||||
	this->mExclusiveZone = exclusiveZone;
 | 
			
		||||
	const bool wasNormal = this->mExclusionMode == ExclusionMode::Normal;
 | 
			
		||||
	this->setExclusionMode(ExclusionMode::Normal);
 | 
			
		||||
	if (wasNormal) this->updateStrut();
 | 
			
		||||
	emit this->exclusiveZoneChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ExclusionMode::Enum XPanelWindow::exclusionMode() const { return this->mExclusionMode; }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setExclusionMode(ExclusionMode::Enum exclusionMode) {
 | 
			
		||||
	if (this->mExclusionMode == exclusionMode) return;
 | 
			
		||||
	this->mExclusionMode = exclusionMode;
 | 
			
		||||
	this->updateStrut();
 | 
			
		||||
	emit this->exclusionModeChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Margins XPanelWindow::margins() const { return this->mMargins; }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setMargins(Margins margins) {
 | 
			
		||||
	if (this->mMargins == margins) return;
 | 
			
		||||
	this->mMargins = margins;
 | 
			
		||||
	this->updateDimensions();
 | 
			
		||||
	emit this->marginsChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool XPanelWindow::aboveWindows() const { return this->mAboveWindows; }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setAboveWindows(bool aboveWindows) {
 | 
			
		||||
	if (this->mAboveWindows == aboveWindows) return;
 | 
			
		||||
	this->mAboveWindows = aboveWindows;
 | 
			
		||||
	this->updateAboveWindows();
 | 
			
		||||
	emit this->aboveWindowsChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool XPanelWindow::focusable() const { return this->mFocusable; }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::setFocusable(bool focusable) {
 | 
			
		||||
	if (this->mFocusable == focusable) return;
 | 
			
		||||
	this->mFocusable = focusable;
 | 
			
		||||
	this->updateFocusable();
 | 
			
		||||
	emit this->focusableChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::xInit() { this->updateDimensions(); }
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::connectScreen() {
 | 
			
		||||
	if (this->mTrackedScreen != nullptr) {
 | 
			
		||||
		QObject::disconnect(this->mTrackedScreen, nullptr, this, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->mTrackedScreen = this->window->screen();
 | 
			
		||||
 | 
			
		||||
	if (this->mTrackedScreen != nullptr) {
 | 
			
		||||
		QObject::connect(
 | 
			
		||||
		    this->mTrackedScreen,
 | 
			
		||||
		    &QScreen::geometryChanged,
 | 
			
		||||
		    this,
 | 
			
		||||
		    &XPanelWindow::updateDimensions
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::updateDimensions() {
 | 
			
		||||
	if (this->window == nullptr || this->window->handle() == nullptr) return;
 | 
			
		||||
 | 
			
		||||
	auto screenGeometry = this->window->screen()->virtualGeometry();
 | 
			
		||||
 | 
			
		||||
	if (this->mExclusionMode != ExclusionMode::Ignore) {
 | 
			
		||||
		for (auto* panel: XPanelStack::instance()->panels(this)) {
 | 
			
		||||
			// we only care about windows below us
 | 
			
		||||
			if (panel == this) break;
 | 
			
		||||
 | 
			
		||||
			int side = -1;
 | 
			
		||||
			quint32 exclusiveZone = 0;
 | 
			
		||||
			panel->getExclusion(side, exclusiveZone);
 | 
			
		||||
 | 
			
		||||
			if (exclusiveZone == 0) continue;
 | 
			
		||||
 | 
			
		||||
			auto zone = static_cast<qint32>(exclusiveZone);
 | 
			
		||||
 | 
			
		||||
			screenGeometry.adjust(
 | 
			
		||||
			    side == 0 ? zone : 0,
 | 
			
		||||
			    side == 2 ? zone : 0,
 | 
			
		||||
			    side == 1 ? -zone : 0,
 | 
			
		||||
			    side == 3 ? -zone : 0
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto geometry = QRect();
 | 
			
		||||
 | 
			
		||||
	if (this->mAnchors.horizontalConstraint()) {
 | 
			
		||||
		geometry.setX(screenGeometry.x() + this->mMargins.mLeft);
 | 
			
		||||
		geometry.setWidth(screenGeometry.width() - this->mMargins.mLeft - this->mMargins.mRight);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (this->mAnchors.mLeft) {
 | 
			
		||||
			geometry.setX(screenGeometry.x() + this->mMargins.mLeft);
 | 
			
		||||
		} else if (this->mAnchors.mRight) {
 | 
			
		||||
			geometry.setX(
 | 
			
		||||
			    screenGeometry.x() + screenGeometry.width() - this->mWidth - this->mMargins.mRight
 | 
			
		||||
			);
 | 
			
		||||
		} else {
 | 
			
		||||
			geometry.setX(screenGeometry.x() + screenGeometry.width() / 2 - this->mWidth / 2);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		geometry.setWidth(this->mWidth);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (this->mAnchors.verticalConstraint()) {
 | 
			
		||||
		geometry.setY(screenGeometry.y() + this->mMargins.mTop);
 | 
			
		||||
		geometry.setHeight(screenGeometry.height() - this->mMargins.mTop - this->mMargins.mBottom);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (this->mAnchors.mTop) {
 | 
			
		||||
			geometry.setY(screenGeometry.y() + this->mMargins.mTop);
 | 
			
		||||
		} else if (this->mAnchors.mBottom) {
 | 
			
		||||
			geometry.setY(
 | 
			
		||||
			    screenGeometry.y() + screenGeometry.height() - this->mHeight - this->mMargins.mBottom
 | 
			
		||||
			);
 | 
			
		||||
		} else {
 | 
			
		||||
			geometry.setY(screenGeometry.y() + screenGeometry.height() / 2 - this->mHeight / 2);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		geometry.setHeight(this->mHeight);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->window->setGeometry(geometry);
 | 
			
		||||
	this->updateStrut();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::updatePanelStack() {
 | 
			
		||||
	if (this->window->isVisible()) {
 | 
			
		||||
		XPanelStack::instance()->addPanel(this);
 | 
			
		||||
	} else {
 | 
			
		||||
		XPanelStack::instance()->removePanel(this);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::getExclusion(int& side, quint32& exclusiveZone) {
 | 
			
		||||
	if (this->mExclusionMode == ExclusionMode::Ignore) return;
 | 
			
		||||
 | 
			
		||||
	auto& anchors = this->mAnchors;
 | 
			
		||||
	if (anchors.mLeft || anchors.mRight || anchors.mTop || anchors.mBottom) {
 | 
			
		||||
		if (!anchors.horizontalConstraint()
 | 
			
		||||
		    && (anchors.verticalConstraint() || (!anchors.mTop && !anchors.mBottom)))
 | 
			
		||||
		{
 | 
			
		||||
			side = anchors.mLeft ? 0 : anchors.mRight ? 1 : -1;
 | 
			
		||||
		} else if (!anchors.verticalConstraint()
 | 
			
		||||
						 && (anchors.horizontalConstraint() || (!anchors.mLeft && !anchors.mRight)))
 | 
			
		||||
		{
 | 
			
		||||
			side = anchors.mTop ? 2 : anchors.mBottom ? 3 : -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (side == -1) return;
 | 
			
		||||
 | 
			
		||||
	auto autoExclude = this->mExclusionMode == ExclusionMode::Auto;
 | 
			
		||||
 | 
			
		||||
	if (autoExclude) {
 | 
			
		||||
		if (side == 0 || side == 1) {
 | 
			
		||||
			exclusiveZone = this->mWidth + (side == 0 ? this->mMargins.mLeft : this->mMargins.mRight);
 | 
			
		||||
		} else {
 | 
			
		||||
			exclusiveZone = this->mHeight + (side == 2 ? this->mMargins.mTop : this->mMargins.mBottom);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		exclusiveZone = this->mExclusiveZone;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::updateStrut() {
 | 
			
		||||
	if (this->window == nullptr || this->window->handle() == nullptr) return;
 | 
			
		||||
	auto* conn = x11Connection();
 | 
			
		||||
 | 
			
		||||
	int side = -1;
 | 
			
		||||
	quint32 exclusiveZone = 0;
 | 
			
		||||
 | 
			
		||||
	this->getExclusion(side, exclusiveZone);
 | 
			
		||||
 | 
			
		||||
	if (side == -1 || this->mExclusionMode == ExclusionMode::Ignore) {
 | 
			
		||||
		xcb_delete_property(conn, this->window->winId(), XAtom::_NET_WM_STRUT.atom());
 | 
			
		||||
		xcb_delete_property(conn, this->window->winId(), XAtom::_NET_WM_STRUT_PARTIAL.atom());
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto data = std::array<quint32, 12>();
 | 
			
		||||
	data[side] = exclusiveZone;
 | 
			
		||||
 | 
			
		||||
	// https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45573693101552
 | 
			
		||||
	// assuming "specified in root window coordinates" means relative to the window geometry
 | 
			
		||||
	// in which case only the end position should be set, to the opposite extent.
 | 
			
		||||
	data[side * 2 + 5] = side == 0 || side == 1 ? this->window->height() : this->window->width();
 | 
			
		||||
 | 
			
		||||
	xcb_change_property(
 | 
			
		||||
	    conn,
 | 
			
		||||
	    XCB_PROP_MODE_REPLACE,
 | 
			
		||||
	    this->window->winId(),
 | 
			
		||||
	    XAtom::_NET_WM_STRUT.atom(),
 | 
			
		||||
	    XCB_ATOM_CARDINAL,
 | 
			
		||||
	    32,
 | 
			
		||||
	    4,
 | 
			
		||||
	    data.data()
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	xcb_change_property(
 | 
			
		||||
	    conn,
 | 
			
		||||
	    XCB_PROP_MODE_REPLACE,
 | 
			
		||||
	    this->window->winId(),
 | 
			
		||||
	    XAtom::_NET_WM_STRUT_PARTIAL.atom(),
 | 
			
		||||
	    XCB_ATOM_CARDINAL,
 | 
			
		||||
	    32,
 | 
			
		||||
	    12,
 | 
			
		||||
	    data.data()
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::updateAboveWindows() {
 | 
			
		||||
	if (this->window == nullptr) return;
 | 
			
		||||
 | 
			
		||||
	this->window->setFlag(Qt::WindowStaysOnBottomHint, !this->mAboveWindows);
 | 
			
		||||
	this->window->setFlag(Qt::WindowStaysOnTopHint, this->mAboveWindows);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelWindow::updateFocusable() {
 | 
			
		||||
	if (this->window == nullptr) return;
 | 
			
		||||
	this->window->setFlag(Qt::WindowDoesNotAcceptFocus, !this->mFocusable);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XPanelInterface
 | 
			
		||||
 | 
			
		||||
XPanelInterface::XPanelInterface(QObject* parent)
 | 
			
		||||
    : PanelWindowInterface(parent)
 | 
			
		||||
    , panel(new XPanelWindow(this)) {
 | 
			
		||||
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::windowConnected, this, &XPanelInterface::windowConnected);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::visibleChanged, this, &XPanelInterface::visibleChanged);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::backerVisibilityChanged, this, &XPanelInterface::backingWindowVisibleChanged);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::heightChanged, this, &XPanelInterface::heightChanged);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::widthChanged, this, &XPanelInterface::widthChanged);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::screenChanged, this, &XPanelInterface::screenChanged);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::windowTransformChanged, this, &XPanelInterface::windowTransformChanged);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::colorChanged, this, &XPanelInterface::colorChanged);
 | 
			
		||||
	QObject::connect(this->panel, &ProxyWindowBase::maskChanged, this, &XPanelInterface::maskChanged);
 | 
			
		||||
 | 
			
		||||
	// panel specific
 | 
			
		||||
	QObject::connect(this->panel, &XPanelWindow::anchorsChanged, this, &XPanelInterface::anchorsChanged);
 | 
			
		||||
	QObject::connect(this->panel, &XPanelWindow::marginsChanged, this, &XPanelInterface::marginsChanged);
 | 
			
		||||
	QObject::connect(this->panel, &XPanelWindow::exclusiveZoneChanged, this, &XPanelInterface::exclusiveZoneChanged);
 | 
			
		||||
	QObject::connect(this->panel, &XPanelWindow::exclusionModeChanged, this, &XPanelInterface::exclusionModeChanged);
 | 
			
		||||
	QObject::connect(this->panel, &XPanelWindow::aboveWindowsChanged, this, &XPanelInterface::aboveWindowsChanged);
 | 
			
		||||
	QObject::connect(this->panel, &XPanelWindow::focusableChanged, this, &XPanelInterface::focusableChanged);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XPanelInterface::onReload(QObject* oldInstance) {
 | 
			
		||||
	QQmlEngine::setContextForObject(this->panel, QQmlEngine::contextForObject(this));
 | 
			
		||||
 | 
			
		||||
	auto* old = qobject_cast<XPanelInterface*>(oldInstance);
 | 
			
		||||
	this->panel->reload(old != nullptr ? old->panel : nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QQmlListProperty<QObject> XPanelInterface::data() { return this->panel->data(); }
 | 
			
		||||
ProxyWindowBase* XPanelInterface::proxyWindow() const { return this->panel; }
 | 
			
		||||
QQuickItem* XPanelInterface::contentItem() const { return this->panel->contentItem(); }
 | 
			
		||||
bool XPanelInterface::isBackingWindowVisible() const { return this->panel->isVisibleDirect(); }
 | 
			
		||||
 | 
			
		||||
// NOLINTBEGIN
 | 
			
		||||
#define proxyPair(type, get, set)                                                                  \
 | 
			
		||||
	type XPanelInterface::get() const { return this->panel->get(); }                                 \
 | 
			
		||||
	void XPanelInterface::set(type value) { this->panel->set(value); }
 | 
			
		||||
 | 
			
		||||
proxyPair(bool, isVisible, setVisible);
 | 
			
		||||
proxyPair(qint32, width, setWidth);
 | 
			
		||||
proxyPair(qint32, height, setHeight);
 | 
			
		||||
proxyPair(QuickshellScreenInfo*, screen, setScreen);
 | 
			
		||||
proxyPair(QColor, color, setColor);
 | 
			
		||||
proxyPair(PendingRegion*, mask, setMask);
 | 
			
		||||
 | 
			
		||||
// panel specific
 | 
			
		||||
proxyPair(Anchors, anchors, setAnchors);
 | 
			
		||||
proxyPair(Margins, margins, setMargins);
 | 
			
		||||
proxyPair(qint32, exclusiveZone, setExclusiveZone);
 | 
			
		||||
proxyPair(ExclusionMode::Enum, exclusionMode, setExclusionMode);
 | 
			
		||||
proxyPair(bool, focusable, setFocusable);
 | 
			
		||||
proxyPair(bool, aboveWindows, setAboveWindows);
 | 
			
		||||
 | 
			
		||||
#undef proxyPair
 | 
			
		||||
// NOLINTEND
 | 
			
		||||
							
								
								
									
										160
									
								
								src/x11/panel_window.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/x11/panel_window.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,160 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qqmlintegration.h>
 | 
			
		||||
#include <qquickwindow.h>
 | 
			
		||||
#include <qscreen.h>
 | 
			
		||||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/doc.hpp"
 | 
			
		||||
#include "../core/panelinterface.hpp"
 | 
			
		||||
#include "../core/proxywindow.hpp"
 | 
			
		||||
 | 
			
		||||
class XPanelStack;
 | 
			
		||||
 | 
			
		||||
class XPanelEventFilter: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit XPanelEventFilter(QObject* parent = nullptr): QObject(parent) {}
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void surfaceCreated();
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	bool eventFilter(QObject* watched, QEvent* event) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class XPanelWindow: public ProxyWindowBase {
 | 
			
		||||
	QSDOC_BASECLASS(PanelWindowInterface);
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(Anchors anchors READ anchors WRITE setAnchors NOTIFY anchorsChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(qint32 exclusiveZone READ exclusiveZone WRITE setExclusiveZone NOTIFY exclusiveZoneChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(ExclusionMode::Enum exclusionMode READ exclusionMode WRITE setExclusionMode NOTIFY exclusionModeChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(Margins margins READ margins WRITE setMargins NOTIFY marginsChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(bool aboveWindows READ aboveWindows WRITE setAboveWindows NOTIFY aboveWindowsChanged);
 | 
			
		||||
	QSDOC_HIDE Q_PROPERTY(bool focusable READ focusable WRITE setFocusable NOTIFY focusableChanged);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
	QML_ELEMENT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit XPanelWindow(QObject* parent = nullptr);
 | 
			
		||||
	~XPanelWindow() override;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(XPanelWindow);
 | 
			
		||||
 | 
			
		||||
	void connectWindow() override;
 | 
			
		||||
 | 
			
		||||
	void setWidth(qint32 width) override;
 | 
			
		||||
	void setHeight(qint32 height) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] Anchors anchors() const;
 | 
			
		||||
	void setAnchors(Anchors anchors);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] qint32 exclusiveZone() const;
 | 
			
		||||
	void setExclusiveZone(qint32 exclusiveZone);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] ExclusionMode::Enum exclusionMode() const;
 | 
			
		||||
	void setExclusionMode(ExclusionMode::Enum exclusionMode);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] Margins margins() const;
 | 
			
		||||
	void setMargins(Margins margins);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool aboveWindows() const;
 | 
			
		||||
	void setAboveWindows(bool aboveWindows);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool focusable() const;
 | 
			
		||||
	void setFocusable(bool focusable);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	QSDOC_HIDE void anchorsChanged();
 | 
			
		||||
	QSDOC_HIDE void exclusiveZoneChanged();
 | 
			
		||||
	QSDOC_HIDE void exclusionModeChanged();
 | 
			
		||||
	QSDOC_HIDE void marginsChanged();
 | 
			
		||||
	QSDOC_HIDE void aboveWindowsChanged();
 | 
			
		||||
	QSDOC_HIDE void focusableChanged();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void xInit();
 | 
			
		||||
	void connectScreen();
 | 
			
		||||
	void updateDimensions();
 | 
			
		||||
	void updatePanelStack();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void getExclusion(int& side, quint32& exclusiveZone);
 | 
			
		||||
	void updateStrut();
 | 
			
		||||
	void updateAboveWindows();
 | 
			
		||||
	void updateFocusable();
 | 
			
		||||
 | 
			
		||||
	QPointer<QScreen> mTrackedScreen = nullptr;
 | 
			
		||||
	bool mAboveWindows = true;
 | 
			
		||||
	bool mFocusable = false;
 | 
			
		||||
	Anchors mAnchors;
 | 
			
		||||
	Margins mMargins;
 | 
			
		||||
	qint32 mExclusiveZone = 0;
 | 
			
		||||
	ExclusionMode::Enum mExclusionMode = ExclusionMode::Auto;
 | 
			
		||||
	XPanelEventFilter eventFilter;
 | 
			
		||||
 | 
			
		||||
	friend class XPanelStack;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class XPanelInterface: public PanelWindowInterface {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit XPanelInterface(QObject* parent = nullptr);
 | 
			
		||||
 | 
			
		||||
	void onReload(QObject* oldInstance) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] ProxyWindowBase* proxyWindow() const override;
 | 
			
		||||
	[[nodiscard]] QQuickItem* contentItem() const override;
 | 
			
		||||
 | 
			
		||||
	// NOLINTBEGIN
 | 
			
		||||
	[[nodiscard]] bool isVisible() const override;
 | 
			
		||||
	[[nodiscard]] bool isBackingWindowVisible() const override;
 | 
			
		||||
	void setVisible(bool visible) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] qint32 width() const override;
 | 
			
		||||
	void setWidth(qint32 width) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] qint32 height() const override;
 | 
			
		||||
	void setHeight(qint32 height) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QuickshellScreenInfo* screen() const override;
 | 
			
		||||
	void setScreen(QuickshellScreenInfo* screen) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QColor color() const override;
 | 
			
		||||
	void setColor(QColor color) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] PendingRegion* mask() const override;
 | 
			
		||||
	void setMask(PendingRegion* mask) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QQmlListProperty<QObject> data() override;
 | 
			
		||||
 | 
			
		||||
	// panel specific
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] Anchors anchors() const override;
 | 
			
		||||
	void setAnchors(Anchors anchors) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] Margins margins() const override;
 | 
			
		||||
	void setMargins(Margins margins) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] qint32 exclusiveZone() const override;
 | 
			
		||||
	void setExclusiveZone(qint32 exclusiveZone) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] ExclusionMode::Enum exclusionMode() const override;
 | 
			
		||||
	void setExclusionMode(ExclusionMode::Enum exclusionMode) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool aboveWindows() const override;
 | 
			
		||||
	void setAboveWindows(bool aboveWindows) override;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool focusable() const override;
 | 
			
		||||
	void setFocusable(bool focusable) override;
 | 
			
		||||
	// NOLINTEND
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	XPanelWindow* panel;
 | 
			
		||||
 | 
			
		||||
	friend class WlrLayershell;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										55
									
								
								src/x11/util.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/x11/util.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,55 @@
 | 
			
		|||
#include "util.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qbytearray.h>
 | 
			
		||||
#include <qguiapplication.h>
 | 
			
		||||
#include <qguiapplication_platform.h>
 | 
			
		||||
#include <xcb/xcb.h>
 | 
			
		||||
#include <xcb/xproto.h>
 | 
			
		||||
 | 
			
		||||
xcb_connection_t* x11Connection() {
 | 
			
		||||
	static xcb_connection_t* conn = nullptr; // NOLINT
 | 
			
		||||
 | 
			
		||||
	if (conn == nullptr) {
 | 
			
		||||
		if (auto* x11Application = dynamic_cast<QGuiApplication*>(QGuiApplication::instance())
 | 
			
		||||
		                               ->nativeInterface<QNativeInterface::QX11Application>())
 | 
			
		||||
		{
 | 
			
		||||
			conn = x11Application->connection();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return conn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NOLINTBEGIN
 | 
			
		||||
XAtom XAtom::_NET_WM_STRUT {};
 | 
			
		||||
XAtom XAtom::_NET_WM_STRUT_PARTIAL {};
 | 
			
		||||
// NOLINTEND
 | 
			
		||||
 | 
			
		||||
void XAtom::initAtoms() {
 | 
			
		||||
	_NET_WM_STRUT.init("_NET_WM_STRUT");
 | 
			
		||||
	_NET_WM_STRUT_PARTIAL.init("_NET_WM_STRUT_PARTIAL");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XAtom::init(const QByteArray& name) {
 | 
			
		||||
	this->cookie = xcb_intern_atom(x11Connection(), 0, name.length(), name.data());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool XAtom::isValid() {
 | 
			
		||||
	this->resolve();
 | 
			
		||||
	return this->mAtom != XCB_ATOM_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const xcb_atom_t& XAtom::atom() {
 | 
			
		||||
	this->resolve();
 | 
			
		||||
	return this->mAtom;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void XAtom::resolve() {
 | 
			
		||||
	if (!this->resolved) {
 | 
			
		||||
		this->resolved = true;
 | 
			
		||||
 | 
			
		||||
		auto* reply = xcb_intern_atom_reply(x11Connection(), this->cookie, nullptr);
 | 
			
		||||
		if (reply != nullptr) this->mAtom = reply->atom;
 | 
			
		||||
		free(reply); // NOLINT
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								src/x11/util.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/x11/util.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qbytearray.h>
 | 
			
		||||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <xcb/xcb.h>
 | 
			
		||||
#include <xcb/xproto.h>
 | 
			
		||||
 | 
			
		||||
xcb_connection_t* x11Connection();
 | 
			
		||||
 | 
			
		||||
class XAtom {
 | 
			
		||||
public:
 | 
			
		||||
	[[nodiscard]] bool isValid();
 | 
			
		||||
	[[nodiscard]] const xcb_atom_t& atom();
 | 
			
		||||
 | 
			
		||||
	// NOLINTBEGIN
 | 
			
		||||
	static XAtom _NET_WM_STRUT;
 | 
			
		||||
	static XAtom _NET_WM_STRUT_PARTIAL;
 | 
			
		||||
	// NOLINTEND
 | 
			
		||||
 | 
			
		||||
	static void initAtoms();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void init(const QByteArray& name);
 | 
			
		||||
	void resolve();
 | 
			
		||||
 | 
			
		||||
	bool resolved = false;
 | 
			
		||||
	xcb_atom_t mAtom = XCB_ATOM_NONE;
 | 
			
		||||
	xcb_intern_atom_cookie_t cookie {};
 | 
			
		||||
};
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue