forked from quickshell/quickshell
		
	wayland/toplevel_management: add foreign toplevel management
This commit is contained in:
		
							parent
							
								
									5d1def3e49
								
							
						
					
					
						commit
						b5b9c1f6c3
					
				
					 13 changed files with 1026 additions and 2 deletions
				
			
		| 
						 | 
				
			
			@ -5,6 +5,7 @@ Checks: >
 | 
			
		|||
  -*,
 | 
			
		||||
  bugprone-*,
 | 
			
		||||
  -bugprone-easily-swappable-parameters,
 | 
			
		||||
  -bugprone-forward-declararion-namespace,
 | 
			
		||||
  concurrency-*,
 | 
			
		||||
  cppcoreguidelines-*,
 | 
			
		||||
  -cppcoreguidelines-owning-memory,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								BUILD.md
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								BUILD.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -59,20 +59,31 @@ Dependencies:
 | 
			
		|||
 - `wayland-protocols`
 | 
			
		||||
 | 
			
		||||
#### Wlroots Layershell
 | 
			
		||||
Enables wlroots layershell integration through the [wlr-layer-shell-unstable-v1] protocol,
 | 
			
		||||
Enables wlroots layershell integration through the [zwlr-layer-shell-v1] protocol,
 | 
			
		||||
enabling use cases such as bars overlays and backgrounds.
 | 
			
		||||
This feature has no extra dependencies.
 | 
			
		||||
 | 
			
		||||
To disable: `-DWAYLAND_WLR_LAYERSHELL=OFF`
 | 
			
		||||
 | 
			
		||||
[wlr-layer-shell-unstable-v1]: https://wayland.app/protocols/wlr-layer-shell-unstable-v1
 | 
			
		||||
[zwlr-layer-shell-v1]: https://wayland.app/protocols/wlr-layer-shell-unstable-v1
 | 
			
		||||
 | 
			
		||||
#### Session Lock
 | 
			
		||||
Enables session lock support through the [ext-session-lock-v1] protocol,
 | 
			
		||||
which allows quickshell to be used as a session lock under compatible wayland compositors.
 | 
			
		||||
 | 
			
		||||
To disable: `-DWAYLAND_SESSION_LOCK=OFF`
 | 
			
		||||
 | 
			
		||||
[ext-session-lock-v1]: https://wayland.app/protocols/ext-session-lock-v1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Foreign Toplevel Management
 | 
			
		||||
Enables management of windows of other clients through the [zwlr-foreign-toplevel-management-v1] protocol,
 | 
			
		||||
which allows quickshell to be used as a session lock under compatible wayland compositors.
 | 
			
		||||
 | 
			
		||||
[zwlr-foreign-toplevel-management-v1]: https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1
 | 
			
		||||
 | 
			
		||||
To disable: `-DWAYLAND_TOPLEVEL_MANAGEMENT=OFF`
 | 
			
		||||
 | 
			
		||||
### X11
 | 
			
		||||
This feature enables x11 support. Currently this implements panel windows for X11 similarly
 | 
			
		||||
to the wlroots layershell above.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(WAYLAND_TOPLEVEL_MANAGEMENT "Support the zwlr_foreign_toplevel_management_v1 wayland protocol" ON)
 | 
			
		||||
option(X11 "Enable X11 support" ON)
 | 
			
		||||
option(HYPRLAND "Support hyprland specific features" ON)
 | 
			
		||||
option(HYPRLAND_IPC "Hyprland IPC" ON)
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +32,7 @@ message(STATUS "  Wayland: ${WAYLAND}")
 | 
			
		|||
if (WAYLAND)
 | 
			
		||||
	message(STATUS "    Wlroots Layershell: ${WAYLAND_WLR_LAYERSHELL}")
 | 
			
		||||
	message(STATUS "    Session Lock: ${WAYLAND_SESSION_LOCK}")
 | 
			
		||||
	message(STATUS "    Toplevel Management: ${WAYLAND_TOPLEVEL_MANAGEMENT}")
 | 
			
		||||
endif ()
 | 
			
		||||
message(STATUS "  X11: ${X11}")
 | 
			
		||||
message(STATUS "  Services")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,6 +71,11 @@ if (WAYLAND_SESSION_LOCK)
 | 
			
		|||
	add_subdirectory(session_lock)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (WAYLAND_TOPLEVEL_MANAGEMENT)
 | 
			
		||||
	add_subdirectory(toplevel_management)
 | 
			
		||||
	list(APPEND WAYLAND_MODULES Quickshell.Wayland._ToplevelManagement)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (HYPRLAND)
 | 
			
		||||
	add_subdirectory(hyprland)
 | 
			
		||||
endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,5 +4,6 @@ headers = [
 | 
			
		|||
	"wlr_layershell/window.hpp",
 | 
			
		||||
	"wlr_layershell.hpp",
 | 
			
		||||
	"session_lock.hpp",
 | 
			
		||||
	"toplevel_management/qml.hpp",
 | 
			
		||||
]
 | 
			
		||||
-----
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										22
									
								
								src/wayland/toplevel_management/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/wayland/toplevel_management/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
qt_add_library(quickshell-wayland-toplevel-management STATIC
 | 
			
		||||
	manager.cpp
 | 
			
		||||
	handle.cpp
 | 
			
		||||
	qml.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
qt_add_qml_module(quickshell-wayland-toplevel-management
 | 
			
		||||
	URI Quickshell.Wayland._ToplevelManagement
 | 
			
		||||
	VERSION 0.1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
wl_proto(quickshell-wayland-toplevel-management
 | 
			
		||||
	wlr-foreign-toplevel-management-unstable-v1
 | 
			
		||||
	"${CMAKE_CURRENT_SOURCE_DIR}/wlr-foreign-toplevel-management-unstable-v1.xml"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell-wayland-toplevel-management PRIVATE ${QT_DEPS} wayland-client)
 | 
			
		||||
 | 
			
		||||
qs_pch(quickshell-wayland-toplevel-management)
 | 
			
		||||
qs_pch(quickshell-wayland-toplevel-managementplugin)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell PRIVATE quickshell-wayland-toplevel-managementplugin)
 | 
			
		||||
							
								
								
									
										228
									
								
								src/wayland/toplevel_management/handle.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/wayland/toplevel_management/handle.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,228 @@
 | 
			
		|||
#include "handle.hpp"
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include <private/qwaylanddisplay_p.h>
 | 
			
		||||
#include <private/qwaylandinputdevice_p.h>
 | 
			
		||||
#include <private/qwaylandintegration_p.h>
 | 
			
		||||
#include <private/qwaylandscreen_p.h>
 | 
			
		||||
#include <private/qwaylandwindow_p.h>
 | 
			
		||||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qloggingcategory.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qscreen.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <wayland-util.h>
 | 
			
		||||
 | 
			
		||||
#include "manager.hpp"
 | 
			
		||||
#include "qwayland-wlr-foreign-toplevel-management-unstable-v1.h"
 | 
			
		||||
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::toplevel_management::impl {
 | 
			
		||||
 | 
			
		||||
QString ToplevelHandle::appId() const { return this->mAppId; }
 | 
			
		||||
QString ToplevelHandle::title() const { return this->mTitle; }
 | 
			
		||||
QVector<QScreen*> ToplevelHandle::visibleScreens() const { return this->mVisibleScreens; }
 | 
			
		||||
ToplevelHandle* ToplevelHandle::parent() const { return this->mParent; }
 | 
			
		||||
bool ToplevelHandle::activated() const { return this->mActivated; }
 | 
			
		||||
bool ToplevelHandle::maximized() const { return this->mMaximized; }
 | 
			
		||||
bool ToplevelHandle::minimized() const { return this->mMinimized; }
 | 
			
		||||
bool ToplevelHandle::fullscreen() const { return this->mFullscreen; }
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::activate() {
 | 
			
		||||
	auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
 | 
			
		||||
	auto* inputDevice = display->lastInputDevice();
 | 
			
		||||
	if (inputDevice == nullptr) return;
 | 
			
		||||
	this->QtWayland::zwlr_foreign_toplevel_handle_v1::activate(inputDevice->object());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::setMaximized(bool maximized) {
 | 
			
		||||
	if (maximized) this->set_maximized();
 | 
			
		||||
	else this->unset_maximized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::setMinimized(bool minimized) {
 | 
			
		||||
	if (minimized) this->set_minimized();
 | 
			
		||||
	else this->unset_minimized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::setFullscreen(bool fullscreen) {
 | 
			
		||||
	if (fullscreen) this->set_fullscreen(nullptr);
 | 
			
		||||
	else this->unset_fullscreen();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::fullscreenOn(QScreen* screen) {
 | 
			
		||||
	auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(screen->handle());
 | 
			
		||||
	this->set_fullscreen(waylandScreen != nullptr ? waylandScreen->output() : nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::setRectangle(QWindow* window, QRect rect) {
 | 
			
		||||
	if (window == nullptr) {
 | 
			
		||||
		// will be cleared by the compositor if the surface is destroyed
 | 
			
		||||
		if (this->rectWindow != nullptr) {
 | 
			
		||||
			auto* waylandWindow =
 | 
			
		||||
			    dynamic_cast<QtWaylandClient::QWaylandWindow*>(this->rectWindow->handle());
 | 
			
		||||
 | 
			
		||||
			if (waylandWindow != nullptr) {
 | 
			
		||||
				this->set_rectangle(waylandWindow->surface(), 0, 0, 0, 0);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		QObject::disconnect(this->rectWindow, nullptr, this, nullptr);
 | 
			
		||||
		this->rectWindow = nullptr;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (this->rectWindow != window) {
 | 
			
		||||
		if (this->rectWindow != nullptr) {
 | 
			
		||||
			QObject::disconnect(this->rectWindow, nullptr, this, nullptr);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this->rectWindow = window;
 | 
			
		||||
		QObject::connect(window, &QObject::destroyed, this, &ToplevelHandle::onRectWindowDestroyed);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (auto* waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow*>(window->handle())) {
 | 
			
		||||
		this->set_rectangle(waylandWindow->surface(), rect.x(), rect.y(), rect.width(), rect.height());
 | 
			
		||||
	} else {
 | 
			
		||||
		QObject::connect(window, &QWindow::visibleChanged, this, [this, window, rect]() {
 | 
			
		||||
			if (window->isVisible()) {
 | 
			
		||||
				if (window->handle() == nullptr) {
 | 
			
		||||
					window->create();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				auto* waylandWindow = dynamic_cast<QtWaylandClient::QWaylandWindow*>(window->handle());
 | 
			
		||||
				this->set_rectangle(
 | 
			
		||||
				    waylandWindow->surface(),
 | 
			
		||||
				    rect.x(),
 | 
			
		||||
				    rect.y(),
 | 
			
		||||
				    rect.width(),
 | 
			
		||||
				    rect.height()
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::onRectWindowDestroyed() { this->rectWindow = nullptr; }
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_done() {
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "got done";
 | 
			
		||||
	auto wasReady = this->isReady;
 | 
			
		||||
	this->isReady = true;
 | 
			
		||||
 | 
			
		||||
	if (!wasReady) {
 | 
			
		||||
		emit this->ready();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_closed() {
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "closed";
 | 
			
		||||
	this->destroy();
 | 
			
		||||
	emit this->closed();
 | 
			
		||||
	delete this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_app_id(const QString& appId) {
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "got appid" << appId;
 | 
			
		||||
	this->mAppId = appId;
 | 
			
		||||
	emit this->appIdChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_title(const QString& title) {
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "got toplevel" << title;
 | 
			
		||||
	this->mTitle = title;
 | 
			
		||||
	emit this->titleChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_state(wl_array* stateArray) {
 | 
			
		||||
	auto activated = false;
 | 
			
		||||
	auto maximized = false;
 | 
			
		||||
	auto minimized = false;
 | 
			
		||||
	auto fullscreen = false;
 | 
			
		||||
 | 
			
		||||
	// wl_array_for_each is illegal in C++ so it is manually expanded.
 | 
			
		||||
	auto* state = static_cast<::zwlr_foreign_toplevel_handle_v1_state*>(stateArray->data);
 | 
			
		||||
	auto size = stateArray->size / sizeof(::zwlr_foreign_toplevel_handle_v1_state);
 | 
			
		||||
	for (size_t i = 0; i < size; i++) {
 | 
			
		||||
		auto flag = state[i]; // NOLINT
 | 
			
		||||
		switch (flag) {
 | 
			
		||||
		case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED: activated = true; break;
 | 
			
		||||
		case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED: maximized = true; break;
 | 
			
		||||
		case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED: minimized = true; break;
 | 
			
		||||
		case ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN: fullscreen = true; break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "got state update - activated:" << activated
 | 
			
		||||
	                               << "maximized:" << maximized << "minimized:" << minimized
 | 
			
		||||
	                               << "fullscreen:" << fullscreen;
 | 
			
		||||
 | 
			
		||||
	if (activated != this->mActivated) {
 | 
			
		||||
		this->mActivated = activated;
 | 
			
		||||
		emit this->activatedChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (maximized != this->mMaximized) {
 | 
			
		||||
		this->mMaximized = maximized;
 | 
			
		||||
		emit this->maximizedChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (minimized != this->mMinimized) {
 | 
			
		||||
		this->mMinimized = minimized;
 | 
			
		||||
		emit this->minimizedChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (fullscreen != this->mFullscreen) {
 | 
			
		||||
		this->mFullscreen = fullscreen;
 | 
			
		||||
		emit this->fullscreenChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_enter(wl_output* output) {
 | 
			
		||||
	auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
 | 
			
		||||
	auto* screen = display->screenForOutput(output)->screen();
 | 
			
		||||
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "got output enter" << screen;
 | 
			
		||||
 | 
			
		||||
	this->mVisibleScreens.push_back(screen);
 | 
			
		||||
	emit this->visibleScreenAdded(screen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_output_leave(wl_output* output) {
 | 
			
		||||
	auto* display = QtWaylandClient::QWaylandIntegration::instance()->display();
 | 
			
		||||
	auto* screen = display->screenForOutput(output)->screen();
 | 
			
		||||
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "got output leave" << screen;
 | 
			
		||||
 | 
			
		||||
	emit this->visibleScreenRemoved(screen);
 | 
			
		||||
	this->mVisibleScreens.removeOne(screen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::zwlr_foreign_toplevel_handle_v1_parent(
 | 
			
		||||
    ::zwlr_foreign_toplevel_handle_v1* parent
 | 
			
		||||
) {
 | 
			
		||||
	auto* handle = ToplevelManager::instance()->handleFor(parent);
 | 
			
		||||
	qCDebug(logToplevelManagement) << this << "got parent" << handle;
 | 
			
		||||
 | 
			
		||||
	if (handle != this->mParent) {
 | 
			
		||||
		if (this->mParent != nullptr) {
 | 
			
		||||
			QObject::disconnect(this->mParent, nullptr, this, nullptr);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this->mParent = handle;
 | 
			
		||||
 | 
			
		||||
		if (handle != nullptr) {
 | 
			
		||||
			QObject::connect(handle, &ToplevelHandle::closed, this, &ToplevelHandle::onParentClosed);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		emit this->parentChanged();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelHandle::onParentClosed() {
 | 
			
		||||
	this->mParent = nullptr;
 | 
			
		||||
	emit this->parentChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::toplevel_management::impl
 | 
			
		||||
							
								
								
									
										77
									
								
								src/wayland/toplevel_management/handle.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/wayland/toplevel_management/handle.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qscreen.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qwayland-wlr-foreign-toplevel-management-unstable-v1.h>
 | 
			
		||||
#include <qwindow.h>
 | 
			
		||||
 | 
			
		||||
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::toplevel_management::impl {
 | 
			
		||||
 | 
			
		||||
class ToplevelHandle
 | 
			
		||||
    : public QObject
 | 
			
		||||
    , public QtWayland::zwlr_foreign_toplevel_handle_v1 {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	[[nodiscard]] QString appId() const;
 | 
			
		||||
	[[nodiscard]] QString title() const;
 | 
			
		||||
	[[nodiscard]] QVector<QScreen*> visibleScreens() const;
 | 
			
		||||
	[[nodiscard]] ToplevelHandle* parent() const;
 | 
			
		||||
	[[nodiscard]] bool activated() const;
 | 
			
		||||
	[[nodiscard]] bool maximized() const;
 | 
			
		||||
	[[nodiscard]] bool minimized() const;
 | 
			
		||||
	[[nodiscard]] bool fullscreen() const;
 | 
			
		||||
 | 
			
		||||
	void activate();
 | 
			
		||||
	void setMaximized(bool maximized);
 | 
			
		||||
	void setMinimized(bool minimized);
 | 
			
		||||
	void setFullscreen(bool fullscreen);
 | 
			
		||||
	void fullscreenOn(QScreen* screen);
 | 
			
		||||
	void setRectangle(QWindow* window, QRect rect);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	// sent after the first done event.
 | 
			
		||||
	void ready();
 | 
			
		||||
	// sent right before delete this.
 | 
			
		||||
	void closed();
 | 
			
		||||
 | 
			
		||||
	void appIdChanged();
 | 
			
		||||
	void titleChanged();
 | 
			
		||||
	void visibleScreenAdded(QScreen* screen);
 | 
			
		||||
	void visibleScreenRemoved(QScreen* screen);
 | 
			
		||||
	void parentChanged();
 | 
			
		||||
	void activatedChanged();
 | 
			
		||||
	void maximizedChanged();
 | 
			
		||||
	void minimizedChanged();
 | 
			
		||||
	void fullscreenChanged();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void onParentClosed();
 | 
			
		||||
	void onRectWindowDestroyed();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_done() override;
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_closed() override;
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_app_id(const QString& appId) override;
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_title(const QString& title) override;
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_state(wl_array* stateArray) override;
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_output_enter(wl_output* output) override;
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_output_leave(wl_output* output) override;
 | 
			
		||||
	void zwlr_foreign_toplevel_handle_v1_parent(::zwlr_foreign_toplevel_handle_v1* parent) override;
 | 
			
		||||
 | 
			
		||||
	bool isReady = false;
 | 
			
		||||
	QString mAppId;
 | 
			
		||||
	QString mTitle;
 | 
			
		||||
	QVector<QScreen*> mVisibleScreens;
 | 
			
		||||
	ToplevelHandle* mParent = nullptr;
 | 
			
		||||
	bool mActivated = false;
 | 
			
		||||
	bool mMaximized = false;
 | 
			
		||||
	bool mMinimized = false;
 | 
			
		||||
	bool mFullscreen = false;
 | 
			
		||||
	QWindow* rectWindow = nullptr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::toplevel_management::impl
 | 
			
		||||
							
								
								
									
										67
									
								
								src/wayland/toplevel_management/manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/wayland/toplevel_management/manager.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
#include "manager.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qloggingcategory.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qwaylandclientextension.h>
 | 
			
		||||
 | 
			
		||||
#include "handle.hpp"
 | 
			
		||||
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::toplevel_management::impl {
 | 
			
		||||
 | 
			
		||||
Q_LOGGING_CATEGORY(logToplevelManagement, "quickshell.wayland.toplevelManagement", QtWarningMsg);
 | 
			
		||||
 | 
			
		||||
ToplevelManager::ToplevelManager(): QWaylandClientExtensionTemplate(3) { this->initialize(); }
 | 
			
		||||
 | 
			
		||||
bool ToplevelManager::available() const { return this->isActive(); }
 | 
			
		||||
 | 
			
		||||
const QVector<ToplevelHandle*>& ToplevelManager::readyToplevels() const {
 | 
			
		||||
	return this->mReadyToplevels;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ToplevelHandle* ToplevelManager::handleFor(::zwlr_foreign_toplevel_handle_v1* toplevel) {
 | 
			
		||||
	if (toplevel == nullptr) return nullptr;
 | 
			
		||||
 | 
			
		||||
	for (auto* other: this->mToplevels) {
 | 
			
		||||
		if (other->object() == toplevel) return other;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ToplevelManager* ToplevelManager::instance() {
 | 
			
		||||
	static auto* instance = new ToplevelManager(); // NOLINT
 | 
			
		||||
	return instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelManager::zwlr_foreign_toplevel_manager_v1_toplevel(
 | 
			
		||||
    ::zwlr_foreign_toplevel_handle_v1* toplevel
 | 
			
		||||
) {
 | 
			
		||||
	auto* handle = new ToplevelHandle();
 | 
			
		||||
	QObject::connect(handle, &ToplevelHandle::closed, this, &ToplevelManager::onToplevelClosed);
 | 
			
		||||
	QObject::connect(handle, &ToplevelHandle::ready, this, &ToplevelManager::onToplevelReady);
 | 
			
		||||
 | 
			
		||||
	qCDebug(logToplevelManagement) << "Toplevel handle created" << handle;
 | 
			
		||||
	this->mToplevels.push_back(handle);
 | 
			
		||||
 | 
			
		||||
	// Not done in constructor as a close could technically be picked up immediately on init,
 | 
			
		||||
	// making touching the handle a UAF.
 | 
			
		||||
	handle->init(toplevel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelManager::onToplevelReady() {
 | 
			
		||||
	auto* handle = qobject_cast<ToplevelHandle*>(this->sender());
 | 
			
		||||
	this->mReadyToplevels.push_back(handle);
 | 
			
		||||
	emit this->toplevelReady(handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelManager::onToplevelClosed() {
 | 
			
		||||
	auto* handle = qobject_cast<ToplevelHandle*>(this->sender());
 | 
			
		||||
	this->mReadyToplevels.removeOne(handle);
 | 
			
		||||
	this->mToplevels.removeOne(handle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::toplevel_management::impl
 | 
			
		||||
							
								
								
									
										47
									
								
								src/wayland/toplevel_management/manager.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/wayland/toplevel_management/manager.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qcontainerfwd.h>
 | 
			
		||||
#include <qloggingcategory.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qwayland-wlr-foreign-toplevel-management-unstable-v1.h>
 | 
			
		||||
#include <qwaylandclientextension.h>
 | 
			
		||||
 | 
			
		||||
#include "wayland-wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::toplevel_management::impl {
 | 
			
		||||
 | 
			
		||||
class ToplevelHandle;
 | 
			
		||||
 | 
			
		||||
Q_DECLARE_LOGGING_CATEGORY(logToplevelManagement);
 | 
			
		||||
 | 
			
		||||
class ToplevelManager
 | 
			
		||||
    : public QWaylandClientExtensionTemplate<ToplevelManager>
 | 
			
		||||
    , public QtWayland::zwlr_foreign_toplevel_manager_v1 {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	[[nodiscard]] bool available() const;
 | 
			
		||||
	[[nodiscard]] const QVector<ToplevelHandle*>& readyToplevels() const;
 | 
			
		||||
	[[nodiscard]] ToplevelHandle* handleFor(::zwlr_foreign_toplevel_handle_v1* toplevel);
 | 
			
		||||
 | 
			
		||||
	static ToplevelManager* instance();
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void toplevelReady(ToplevelHandle* toplevel);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	explicit ToplevelManager();
 | 
			
		||||
 | 
			
		||||
	void zwlr_foreign_toplevel_manager_v1_toplevel(::zwlr_foreign_toplevel_handle_v1* toplevel
 | 
			
		||||
	) override;
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void onToplevelReady();
 | 
			
		||||
	void onToplevelClosed();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	QVector<ToplevelHandle*> mToplevels;
 | 
			
		||||
	QVector<ToplevelHandle*> mReadyToplevels;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::toplevel_management::impl
 | 
			
		||||
							
								
								
									
										153
									
								
								src/wayland/toplevel_management/qml.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								src/wayland/toplevel_management/qml.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,153 @@
 | 
			
		|||
#include "qml.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
 | 
			
		||||
#include "../../core/model.hpp"
 | 
			
		||||
#include "../../core/proxywindow.hpp"
 | 
			
		||||
#include "../../core/qmlscreen.hpp"
 | 
			
		||||
#include "../../core/windowinterface.hpp"
 | 
			
		||||
#include "handle.hpp"
 | 
			
		||||
#include "manager.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::toplevel_management {
 | 
			
		||||
 | 
			
		||||
Toplevel::Toplevel(impl::ToplevelHandle* handle, QObject* parent): QObject(parent), handle(handle) {
 | 
			
		||||
	// clang-format off
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::closed, this, &Toplevel::onClosed);
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::appIdChanged, this, &Toplevel::appIdChanged);
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::titleChanged, this, &Toplevel::titleChanged);
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::parentChanged, this, &Toplevel::parentChanged);
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::activatedChanged, this, &Toplevel::activatedChanged);
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::maximizedChanged, this, &Toplevel::maximizedChanged);
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::minimizedChanged, this, &Toplevel::minimizedChanged);
 | 
			
		||||
	QObject::connect(handle, &impl::ToplevelHandle::fullscreenChanged, this, &Toplevel::fullscreenChanged);
 | 
			
		||||
	// clang-format on
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Toplevel::onClosed() {
 | 
			
		||||
	emit this->closed();
 | 
			
		||||
	delete this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Toplevel::activate() { this->handle->activate(); }
 | 
			
		||||
 | 
			
		||||
QString Toplevel::appId() const { return this->handle->appId(); }
 | 
			
		||||
QString Toplevel::title() const { return this->handle->title(); }
 | 
			
		||||
 | 
			
		||||
Toplevel* Toplevel::parent() const {
 | 
			
		||||
	return ToplevelManager::instance()->forImpl(this->handle->parent());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Toplevel::activated() const { return this->handle->activated(); }
 | 
			
		||||
 | 
			
		||||
bool Toplevel::maximized() const { return this->handle->maximized(); }
 | 
			
		||||
void Toplevel::setMaximized(bool maximized) { this->handle->setMaximized(maximized); }
 | 
			
		||||
 | 
			
		||||
bool Toplevel::minimized() const { return this->handle->minimized(); }
 | 
			
		||||
void Toplevel::setMinimized(bool minimized) { this->handle->setMinimized(minimized); }
 | 
			
		||||
 | 
			
		||||
bool Toplevel::fullscreen() const { return this->handle->fullscreen(); }
 | 
			
		||||
void Toplevel::setFullscreen(bool fullscreen) { this->handle->setFullscreen(fullscreen); }
 | 
			
		||||
 | 
			
		||||
void Toplevel::fullscreenOn(QuickshellScreenInfo* screen) {
 | 
			
		||||
	auto* qscreen = screen != nullptr ? screen->screen : nullptr;
 | 
			
		||||
	this->handle->fullscreenOn(qscreen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Toplevel::setRectangle(QObject* window, QRect rect) {
 | 
			
		||||
	auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window);
 | 
			
		||||
 | 
			
		||||
	if (proxyWindow == nullptr) {
 | 
			
		||||
		if (auto* iface = qobject_cast<WindowInterface*>(window)) {
 | 
			
		||||
			proxyWindow = iface->proxyWindow();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (proxyWindow != this->rectWindow) {
 | 
			
		||||
		if (this->rectWindow != nullptr) {
 | 
			
		||||
			QObject::disconnect(this->rectWindow, nullptr, this, nullptr);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this->rectWindow = proxyWindow;
 | 
			
		||||
 | 
			
		||||
		if (proxyWindow != nullptr) {
 | 
			
		||||
			QObject::connect(
 | 
			
		||||
			    proxyWindow,
 | 
			
		||||
			    &QObject::destroyed,
 | 
			
		||||
			    this,
 | 
			
		||||
			    &Toplevel::onRectangleProxyDestroyed
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			QObject::connect(
 | 
			
		||||
			    proxyWindow,
 | 
			
		||||
			    &ProxyWindowBase::windowConnected,
 | 
			
		||||
			    this,
 | 
			
		||||
			    &Toplevel::onRectangleProxyConnected
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this->rectangle = rect;
 | 
			
		||||
	this->handle->setRectangle(proxyWindow->backingWindow(), rect);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Toplevel::unsetRectangle() { this->setRectangle(nullptr, QRect()); }
 | 
			
		||||
 | 
			
		||||
void Toplevel::onRectangleProxyConnected() {
 | 
			
		||||
	this->handle->setRectangle(this->rectWindow->backingWindow(), this->rectangle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Toplevel::onRectangleProxyDestroyed() {
 | 
			
		||||
	this->rectWindow = nullptr;
 | 
			
		||||
	this->rectangle = QRect();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ToplevelManager::ToplevelManager() {
 | 
			
		||||
	auto* manager = impl::ToplevelManager::instance();
 | 
			
		||||
 | 
			
		||||
	QObject::connect(
 | 
			
		||||
	    manager,
 | 
			
		||||
	    &impl::ToplevelManager::toplevelReady,
 | 
			
		||||
	    this,
 | 
			
		||||
	    &ToplevelManager::onToplevelReady
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	for (auto* handle: manager->readyToplevels()) {
 | 
			
		||||
		this->onToplevelReady(handle);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Toplevel* ToplevelManager::forImpl(impl::ToplevelHandle* impl) const {
 | 
			
		||||
	if (impl == nullptr) return nullptr;
 | 
			
		||||
 | 
			
		||||
	for (auto* toplevel: this->mToplevels.valueList()) {
 | 
			
		||||
		if (toplevel->handle == impl) return toplevel;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjectModel<Toplevel>* ToplevelManager::toplevels() { return &this->mToplevels; }
 | 
			
		||||
 | 
			
		||||
void ToplevelManager::onToplevelReady(impl::ToplevelHandle* handle) {
 | 
			
		||||
	auto* toplevel = new Toplevel(handle, this);
 | 
			
		||||
	QObject::connect(toplevel, &Toplevel::closed, this, &ToplevelManager::onToplevelClosed);
 | 
			
		||||
	this->mToplevels.insertObject(toplevel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ToplevelManager::onToplevelClosed() {
 | 
			
		||||
	auto* toplevel = qobject_cast<Toplevel*>(this->sender());
 | 
			
		||||
	this->mToplevels.removeObject(toplevel);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ToplevelManager* ToplevelManager::instance() {
 | 
			
		||||
	static auto* instance = new ToplevelManager(); // NOLINT
 | 
			
		||||
	return instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObjectModel<Toplevel>* ToplevelManagerQml::toplevels() {
 | 
			
		||||
	return ToplevelManager::instance()->toplevels();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::toplevel_management
 | 
			
		||||
							
								
								
									
										140
									
								
								src/wayland/toplevel_management/qml.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								src/wayland/toplevel_management/qml.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qqmlintegration.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
 | 
			
		||||
#include "../../core/model.hpp"
 | 
			
		||||
#include "../../core/proxywindow.hpp"
 | 
			
		||||
#include "../../core/qmlscreen.hpp"
 | 
			
		||||
 | 
			
		||||
namespace qs::wayland::toplevel_management {
 | 
			
		||||
 | 
			
		||||
namespace impl {
 | 
			
		||||
class ToplevelManager;
 | 
			
		||||
class ToplevelHandle;
 | 
			
		||||
} // namespace impl
 | 
			
		||||
 | 
			
		||||
///! Window from another application.
 | 
			
		||||
/// A window/toplevel from another application, retrievable from
 | 
			
		||||
/// the [ToplevelManager](../toplevelmanager).
 | 
			
		||||
class Toplevel: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
	Q_PROPERTY(QString appId READ appId NOTIFY appIdChanged);
 | 
			
		||||
	Q_PROPERTY(QString title READ title NOTIFY titleChanged);
 | 
			
		||||
	/// Parent toplevel if this toplevel is a modal/dialog, otherwise null.
 | 
			
		||||
	Q_PROPERTY(Toplevel* parent READ parent NOTIFY parentChanged);
 | 
			
		||||
	/// If the window is currently activated or focused.
 | 
			
		||||
	///
 | 
			
		||||
	/// Activation can be requested with the `activate()` function.
 | 
			
		||||
	Q_PROPERTY(bool activated READ activated NOTIFY activatedChanged);
 | 
			
		||||
	/// If the window is currently maximized.
 | 
			
		||||
	///
 | 
			
		||||
	/// Maximization can be requested by setting this property, though it may
 | 
			
		||||
	/// be ignored by the compositor.
 | 
			
		||||
	Q_PROPERTY(bool maximized READ maximized WRITE setMaximized NOTIFY maximizedChanged);
 | 
			
		||||
	/// If the window is currently minimized.
 | 
			
		||||
	///
 | 
			
		||||
	/// Minimization can be requested by setting this property, though it may
 | 
			
		||||
	/// be ignored by the compositor.
 | 
			
		||||
	Q_PROPERTY(bool minimized READ minimized WRITE setMinimized NOTIFY minimizedChanged);
 | 
			
		||||
	/// If the window is currently fullscreen.
 | 
			
		||||
	///
 | 
			
		||||
	/// Fullscreen can be requested by setting this property, though it may
 | 
			
		||||
	/// be ignored by the compositor.
 | 
			
		||||
	/// Fullscreen can be requested on a specific screen with the `fullscreenOn()` function.
 | 
			
		||||
	Q_PROPERTY(bool fullscreen READ fullscreen WRITE setFullscreen NOTIFY fullscreenChanged);
 | 
			
		||||
	QML_ELEMENT;
 | 
			
		||||
	QML_UNCREATABLE("Toplevels must be acquired from the ToplevelManager.");
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit Toplevel(impl::ToplevelHandle* handle, QObject* parent);
 | 
			
		||||
 | 
			
		||||
	/// Request that this toplevel is activated.
 | 
			
		||||
	/// The request may be ignored by the compositor.
 | 
			
		||||
	Q_INVOKABLE void activate();
 | 
			
		||||
 | 
			
		||||
	/// Request that this toplevel is fullscreened on a specific screen.
 | 
			
		||||
	/// The request may be ignored by the compositor.
 | 
			
		||||
	Q_INVOKABLE void fullscreenOn(QuickshellScreenInfo* screen);
 | 
			
		||||
 | 
			
		||||
	/// Provide a hint to the compositor where the visual representation
 | 
			
		||||
	/// of this toplevel is relative to a quickshell window.
 | 
			
		||||
	/// This hint can be used visually in operations like minimization.
 | 
			
		||||
	Q_INVOKABLE void setRectangle(QObject* window, QRect rect);
 | 
			
		||||
	Q_INVOKABLE void unsetRectangle();
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] QString appId() const;
 | 
			
		||||
	[[nodiscard]] QString title() const;
 | 
			
		||||
	[[nodiscard]] Toplevel* parent() const;
 | 
			
		||||
	[[nodiscard]] bool activated() const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool maximized() const;
 | 
			
		||||
	void setMaximized(bool maximized);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool minimized() const;
 | 
			
		||||
	void setMinimized(bool minimized);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool fullscreen() const;
 | 
			
		||||
	void setFullscreen(bool fullscreen);
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void closed();
 | 
			
		||||
	void appIdChanged();
 | 
			
		||||
	void titleChanged();
 | 
			
		||||
	void parentChanged();
 | 
			
		||||
	void activatedChanged();
 | 
			
		||||
	void maximizedChanged();
 | 
			
		||||
	void minimizedChanged();
 | 
			
		||||
	void fullscreenChanged();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void onClosed();
 | 
			
		||||
	void onRectangleProxyConnected();
 | 
			
		||||
	void onRectangleProxyDestroyed();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	impl::ToplevelHandle* handle;
 | 
			
		||||
	ProxyWindowBase* rectWindow = nullptr;
 | 
			
		||||
	QRect rectangle;
 | 
			
		||||
 | 
			
		||||
	friend class ToplevelManager;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ToplevelManager: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Toplevel* forImpl(impl::ToplevelHandle* impl) const;
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] ObjectModel<Toplevel>* toplevels();
 | 
			
		||||
 | 
			
		||||
	static ToplevelManager* instance();
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
	void onToplevelReady(impl::ToplevelHandle* handle);
 | 
			
		||||
	void onToplevelClosed();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	explicit ToplevelManager();
 | 
			
		||||
 | 
			
		||||
	ObjectModel<Toplevel> mToplevels {this};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
///! Exposes a list of Toplevels.
 | 
			
		||||
/// Exposes a list of windows from other applications as [Toplevel](../toplevel)s via the
 | 
			
		||||
/// [zwlr-foreign-toplevel-management-v1](https://wayland.app/protocols/wlr-foreign-toplevel-management-unstable-v1)
 | 
			
		||||
/// wayland protocol.
 | 
			
		||||
class ToplevelManagerQml: public QObject {
 | 
			
		||||
	Q_OBJECT;
 | 
			
		||||
	Q_PROPERTY(ObjectModel<Toplevel>* toplevels READ toplevels CONSTANT);
 | 
			
		||||
	QML_NAMED_ELEMENT(ToplevelManager);
 | 
			
		||||
	QML_SINGLETON;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	explicit ToplevelManagerQml(QObject* parent = nullptr): QObject(parent) {}
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] static ObjectModel<Toplevel>* toplevels();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace qs::wayland::toplevel_management
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,270 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<protocol name="wlr_foreign_toplevel_management_unstable_v1">
 | 
			
		||||
  <copyright>
 | 
			
		||||
    Copyright © 2018 Ilia Bozhinov
 | 
			
		||||
 | 
			
		||||
    Permission to use, copy, modify, distribute, and sell this
 | 
			
		||||
    software and its documentation for any purpose is hereby granted
 | 
			
		||||
    without fee, provided that the above copyright notice appear in
 | 
			
		||||
    all copies and that both that copyright notice and this permission
 | 
			
		||||
    notice appear in supporting documentation, and that the name of
 | 
			
		||||
    the copyright holders not be used in advertising or publicity
 | 
			
		||||
    pertaining to distribution of the software without specific,
 | 
			
		||||
    written prior permission.  The copyright holders make no
 | 
			
		||||
    representations about the suitability of this software for any
 | 
			
		||||
    purpose.  It is provided "as is" without express or implied
 | 
			
		||||
    warranty.
 | 
			
		||||
 | 
			
		||||
    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 | 
			
		||||
    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 | 
			
		||||
    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 | 
			
		||||
    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
			
		||||
    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 | 
			
		||||
    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 | 
			
		||||
    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
 | 
			
		||||
    THIS SOFTWARE.
 | 
			
		||||
  </copyright>
 | 
			
		||||
 | 
			
		||||
  <interface name="zwlr_foreign_toplevel_manager_v1" version="3">
 | 
			
		||||
    <description summary="list and control opened apps">
 | 
			
		||||
      The purpose of this protocol is to enable the creation of taskbars
 | 
			
		||||
      and docks by providing them with a list of opened applications and
 | 
			
		||||
      letting them request certain actions on them, like maximizing, etc.
 | 
			
		||||
 | 
			
		||||
      After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
 | 
			
		||||
      toplevel window will be sent via the toplevel event
 | 
			
		||||
    </description>
 | 
			
		||||
 | 
			
		||||
    <event name="toplevel">
 | 
			
		||||
      <description summary="a toplevel has been created">
 | 
			
		||||
        This event is emitted whenever a new toplevel window is created. It
 | 
			
		||||
        is emitted for all toplevels, regardless of the app that has created
 | 
			
		||||
        them.
 | 
			
		||||
 | 
			
		||||
        All initial details of the toplevel(title, app_id, states, etc.) will
 | 
			
		||||
        be sent immediately after this event via the corresponding events in
 | 
			
		||||
        zwlr_foreign_toplevel_handle_v1.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <request name="stop">
 | 
			
		||||
      <description summary="stop sending events">
 | 
			
		||||
        Indicates the client no longer wishes to receive events for new toplevels.
 | 
			
		||||
        However the compositor may emit further toplevel_created events, until
 | 
			
		||||
        the finished event is emitted.
 | 
			
		||||
 | 
			
		||||
        The client must not send any more requests after this one.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <event name="finished" type="destructor">
 | 
			
		||||
      <description summary="the compositor has finished with the toplevel manager">
 | 
			
		||||
        This event indicates that the compositor is done sending events to the
 | 
			
		||||
        zwlr_foreign_toplevel_manager_v1. The server will destroy the object
 | 
			
		||||
        immediately after sending this request, so it will become invalid and
 | 
			
		||||
        the client should free any resources associated with it.
 | 
			
		||||
      </description>
 | 
			
		||||
    </event>
 | 
			
		||||
  </interface>
 | 
			
		||||
 | 
			
		||||
  <interface name="zwlr_foreign_toplevel_handle_v1" version="3">
 | 
			
		||||
    <description summary="an opened toplevel">
 | 
			
		||||
      A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
 | 
			
		||||
      window. Each app may have multiple opened toplevels.
 | 
			
		||||
 | 
			
		||||
      Each toplevel has a list of outputs it is visible on, conveyed to the
 | 
			
		||||
      client with the output_enter and output_leave events.
 | 
			
		||||
    </description>
 | 
			
		||||
 | 
			
		||||
    <event name="title">
 | 
			
		||||
      <description summary="title change">
 | 
			
		||||
        This event is emitted whenever the title of the toplevel changes.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="title" type="string"/>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <event name="app_id">
 | 
			
		||||
      <description summary="app-id change">
 | 
			
		||||
        This event is emitted whenever the app-id of the toplevel changes.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="app_id" type="string"/>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <event name="output_enter">
 | 
			
		||||
      <description summary="toplevel entered an output">
 | 
			
		||||
        This event is emitted whenever the toplevel becomes visible on
 | 
			
		||||
        the given output. A toplevel may be visible on multiple outputs.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="output" type="object" interface="wl_output"/>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <event name="output_leave">
 | 
			
		||||
      <description summary="toplevel left an output">
 | 
			
		||||
        This event is emitted whenever the toplevel stops being visible on
 | 
			
		||||
        the given output. It is guaranteed that an entered-output event
 | 
			
		||||
        with the same output has been emitted before this event.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="output" type="object" interface="wl_output"/>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <request name="set_maximized">
 | 
			
		||||
      <description summary="requests that the toplevel be maximized">
 | 
			
		||||
        Requests that the toplevel be maximized. If the maximized state actually
 | 
			
		||||
        changes, this will be indicated by the state event.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <request name="unset_maximized">
 | 
			
		||||
      <description summary="requests that the toplevel be unmaximized">
 | 
			
		||||
        Requests that the toplevel be unmaximized. If the maximized state actually
 | 
			
		||||
        changes, this will be indicated by the state event.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <request name="set_minimized">
 | 
			
		||||
      <description summary="requests that the toplevel be minimized">
 | 
			
		||||
        Requests that the toplevel be minimized. If the minimized state actually
 | 
			
		||||
        changes, this will be indicated by the state event.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <request name="unset_minimized">
 | 
			
		||||
      <description summary="requests that the toplevel be unminimized">
 | 
			
		||||
        Requests that the toplevel be unminimized. If the minimized state actually
 | 
			
		||||
        changes, this will be indicated by the state event.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <request name="activate">
 | 
			
		||||
      <description summary="activate the toplevel">
 | 
			
		||||
        Request that this toplevel be activated on the given seat.
 | 
			
		||||
        There is no guarantee the toplevel will be actually activated.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="seat" type="object" interface="wl_seat"/>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <enum name="state">
 | 
			
		||||
      <description summary="types of states on the toplevel">
 | 
			
		||||
        The different states that a toplevel can have. These have the same meaning
 | 
			
		||||
        as the states with the same names defined in xdg-toplevel
 | 
			
		||||
      </description>
 | 
			
		||||
 | 
			
		||||
      <entry name="maximized"  value="0" summary="the toplevel is maximized"/>
 | 
			
		||||
      <entry name="minimized"  value="1" summary="the toplevel is minimized"/>
 | 
			
		||||
      <entry name="activated"  value="2" summary="the toplevel is active"/>
 | 
			
		||||
      <entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
 | 
			
		||||
    </enum>
 | 
			
		||||
 | 
			
		||||
    <event name="state">
 | 
			
		||||
      <description summary="the toplevel state changed">
 | 
			
		||||
        This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
 | 
			
		||||
        is created and each time the toplevel state changes, either because of a
 | 
			
		||||
        compositor action or because of a request in this protocol.
 | 
			
		||||
      </description>
 | 
			
		||||
 | 
			
		||||
      <arg name="state" type="array"/>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <event name="done">
 | 
			
		||||
      <description summary="all information about the toplevel has been sent">
 | 
			
		||||
        This event is sent after all changes in the toplevel state have been
 | 
			
		||||
        sent.
 | 
			
		||||
 | 
			
		||||
        This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
 | 
			
		||||
        to be seen as atomic, even if they happen via multiple events.
 | 
			
		||||
      </description>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <request name="close">
 | 
			
		||||
      <description summary="request that the toplevel be closed">
 | 
			
		||||
        Send a request to the toplevel to close itself. The compositor would
 | 
			
		||||
        typically use a shell-specific method to carry out this request, for
 | 
			
		||||
        example by sending the xdg_toplevel.close event. However, this gives
 | 
			
		||||
        no guarantees the toplevel will actually be destroyed. If and when
 | 
			
		||||
        this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
 | 
			
		||||
        be emitted.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <request name="set_rectangle">
 | 
			
		||||
      <description summary="the rectangle which represents the toplevel">
 | 
			
		||||
        The rectangle of the surface specified in this request corresponds to
 | 
			
		||||
        the place where the app using this protocol represents the given toplevel.
 | 
			
		||||
        It can be used by the compositor as a hint for some operations, e.g
 | 
			
		||||
        minimizing. The client is however not required to set this, in which
 | 
			
		||||
        case the compositor is free to decide some default value.
 | 
			
		||||
 | 
			
		||||
        If the client specifies more than one rectangle, only the last one is
 | 
			
		||||
        considered.
 | 
			
		||||
 | 
			
		||||
        The dimensions are given in surface-local coordinates.
 | 
			
		||||
        Setting width=height=0 removes the already-set rectangle.
 | 
			
		||||
      </description>
 | 
			
		||||
 | 
			
		||||
      <arg name="surface" type="object" interface="wl_surface"/>
 | 
			
		||||
      <arg name="x" type="int"/>
 | 
			
		||||
      <arg name="y" type="int"/>
 | 
			
		||||
      <arg name="width" type="int"/>
 | 
			
		||||
      <arg name="height" type="int"/>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <enum name="error">
 | 
			
		||||
      <entry name="invalid_rectangle" value="0"
 | 
			
		||||
        summary="the provided rectangle is invalid"/>
 | 
			
		||||
    </enum>
 | 
			
		||||
 | 
			
		||||
    <event name="closed">
 | 
			
		||||
      <description summary="this toplevel has been destroyed">
 | 
			
		||||
        This event means the toplevel has been destroyed. It is guaranteed there
 | 
			
		||||
        won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
 | 
			
		||||
        toplevel itself becomes inert so any requests will be ignored except the
 | 
			
		||||
        destroy request.
 | 
			
		||||
      </description>
 | 
			
		||||
    </event>
 | 
			
		||||
 | 
			
		||||
    <request name="destroy" type="destructor">
 | 
			
		||||
      <description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
 | 
			
		||||
        Destroys the zwlr_foreign_toplevel_handle_v1 object.
 | 
			
		||||
 | 
			
		||||
        This request should be called either when the client does not want to
 | 
			
		||||
        use the toplevel anymore or after the closed event to finalize the
 | 
			
		||||
        destruction of the object.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <!-- Version 2 additions -->
 | 
			
		||||
 | 
			
		||||
    <request name="set_fullscreen" since="2">
 | 
			
		||||
      <description summary="request that the toplevel be fullscreened">
 | 
			
		||||
        Requests that the toplevel be fullscreened on the given output. If the
 | 
			
		||||
        fullscreen state and/or the outputs the toplevel is visible on actually
 | 
			
		||||
        change, this will be indicated by the state and output_enter/leave
 | 
			
		||||
        events.
 | 
			
		||||
 | 
			
		||||
        The output parameter is only a hint to the compositor. Also, if output
 | 
			
		||||
        is NULL, the compositor should decide which output the toplevel will be
 | 
			
		||||
        fullscreened on, if at all.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <request name="unset_fullscreen" since="2">
 | 
			
		||||
      <description summary="request that the toplevel be unfullscreened">
 | 
			
		||||
        Requests that the toplevel be unfullscreened. If the fullscreen state
 | 
			
		||||
        actually changes, this will be indicated by the state event.
 | 
			
		||||
      </description>
 | 
			
		||||
    </request>
 | 
			
		||||
 | 
			
		||||
    <!-- Version 3 additions -->
 | 
			
		||||
 | 
			
		||||
    <event name="parent" since="3">
 | 
			
		||||
      <description summary="parent change">
 | 
			
		||||
        This event is emitted whenever the parent of the toplevel changes.
 | 
			
		||||
 | 
			
		||||
        No event is emitted when the parent handle is destroyed by the client.
 | 
			
		||||
      </description>
 | 
			
		||||
      <arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
 | 
			
		||||
    </event>
 | 
			
		||||
  </interface>
 | 
			
		||||
</protocol>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue