forked from quickshell/quickshell
wayland/toplevel_management: add foreign toplevel management
This commit is contained in:
parent
5d1def3e49
commit
b5b9c1f6c3
|
@ -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…
Reference in a new issue