diff options
16 files changed, 539 insertions, 4 deletions
diff --git a/src/3rdparty/wayland/protocols/session-management/REUSE.toml b/src/3rdparty/wayland/protocols/session-management/REUSE.toml new file mode 100644 index 00000000000..585706d8fcd --- /dev/null +++ b/src/3rdparty/wayland/protocols/session-management/REUSE.toml @@ -0,0 +1,12 @@ +version = 1 + +[[annotations]] +path = "xx-session-management-v1.xml" +precedence = "closest" +SPDX-FileCopyrightText = [ + "Copyright 2018 Mike Blumenkrantz", + "Copyright 2018 Samsung Electronics Co., Ltd", + "Copyright 2018 Red Hat Inc." +] + +SPDX-License-Identifier = "MIT" diff --git a/src/3rdparty/wayland/protocols/session-management/qt_attribution.json b/src/3rdparty/wayland/protocols/session-management/qt_attribution.json new file mode 100644 index 00000000000..8539749f112 --- /dev/null +++ b/src/3rdparty/wayland/protocols/session-management/qt_attribution.json @@ -0,0 +1,17 @@ +[ + { + "Id": "wayland-session-management-protocol", + "Name": "Wayland Session Management Protocol", + "QDocModule": "qtwaylandcompositor", + "QtUsage": "Used in the Qt Wayland platform plugin.", + "Files": "xx-session-management-v1.xml", + "Description": "An extension to restore window positions", + "Homepage": "https://wayland.freedesktop.org", + "Version": "experimental V1", + "DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/-/blob/main/experimental/xx-session-management/xx-session-management-v1.xml?ref_type=heads", + "LicenseId": "MIT", + "License": "MIT License", + "LicenseFile": "../MIT_LICENSE.txt", + "Copyright": "Copyright 2018 Mike Blumenkrantz\nCopyright 2018 Samsung Electronics Co., Ltd\nCopyright 2018 Red Hat Inc." + } +] diff --git a/src/3rdparty/wayland/protocols/session-management/xx-session-management-v1.xml b/src/3rdparty/wayland/protocols/session-management/xx-session-management-v1.xml new file mode 100644 index 00000000000..42458d9291a --- /dev/null +++ b/src/3rdparty/wayland/protocols/session-management/xx-session-management-v1.xml @@ -0,0 +1,264 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="xx_session_management_v1"> + <copyright> + Copyright 2018 Mike Blumenkrantz + Copyright 2018 Samsung Electronics Co., Ltd + Copyright 2018 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <description summary="Protocol for managing application sessions"> + This description provides a high-level overview of the interplay between + the interfaces defined this protocol. For details, see the protocol + specification. + + The xx_session_manager protocol declares interfaces necessary to + allow clients to restore toplevel state from previous executions. The + xx_session_manager_v1.get_session request can be used to obtain a + xx_session_v1 resource representing the state of a set of toplevels. + + Clients may obtain the session string to use in future calls through + the xx_session_v1.created event. Compositors will use this string + as an identifiable token for future runs, possibly storing data about + the related toplevels in persistent storage. + + Toplevels are managed through the xx_session_v1.add_toplevel and + xx_session_toplevel_v1.remove pair of requests. Clients will explicitly + request a toplevel to be restored according to prior state through the + xx_session_v1.restore_toplevel request before the toplevel is mapped. + + Warning! The protocol described in this file is currently in the + experimental phase. Backwards incompatible major versions of the + protocol are to be expected. Exposing this protocol without an opt-in + mechanism is discouraged. + </description> + + <interface name="xx_session_manager_v1" version="1"> + <description summary="manage sessions for applications"> + The xx_session_manager interface defines base requests for creating and + managing a session for an application. Sessions persist across application + and compositor restarts unless explicitly destroyed. A session is created + for the purpose of maintaining an application's xdg_toplevel surfaces + across compositor or application restarts. The compositor should remember + as many states as possible for surfaces in a given session, but there is + no requirement for which states must be remembered. + </description> + + <enum name="error"> + <entry name="in_use" summary="a requested session is already in use" + value="1"/> + </enum> + + <enum name="reason"> + <description summary="reason for getting a session"> + The reason may determine in what way a session restores the window + management state of associated toplevels. + + For example newly launched applications might be launched on the active + workspace with restored size and position, while a recovered + applications might restore additional state such as active workspace and + stacking order. + </description> + <entry name="launch" value="1"> + <description summary="an app is newly launched"> + A new app instance is launched, for example from an app launcher. + </description> + </entry> + <entry name="recover" value="2"> + <description summary="an app recovered"> + A app instance is recovering from for example a compositor or app crash. + </description> + </entry> + <entry name="session_restore" value="3"> + <description summary="an app restored"> + A app instance is restored, for example part of a restored session, or + restored from having been temporarily terminated due to resource + constraints. + </description> + </entry> + </enum> + + <request name="destroy" type="destructor"> + <description summary="Destroy this object"> + This has no effect other than to destroy the xx_session_manager object. + </description> + </request> + + <request name="get_session"> + <description summary="create or restore a session"> + Create a session object corresponding to either an existing session + identified by the given session identifier string or a new session. + While the session object exists, the session is considered to be "in + use". + + If a identifier string represents a session that is currently actively + in use by the the same client, an 'in_use' error is raised. If some + other client is currently using the same session, the new session will + replace managing the associated state. + + NULL is passed to initiate a new session. If an id is passed which does + not represent a valid session, the compositor treats it as if NULL had + been passed. + + A client is allowed to have any number of in use sessions at the same + time. + </description> + <arg name="id" type="new_id" interface="xx_session_v1"/> + <arg name="reason" type="uint" enum="reason" + summary="reason for session"/> + <arg name="session" type="string" + summary="the session to restore" + allow-null="true"/> + </request> + </interface> + + <interface name="xx_session_v1" version="1"> + <description summary="A session for an application"> + A xx_session_v1 object represents a session for an application. While the + object exists, all surfaces which have been added to the session will + have states stored by the compositor which can be reapplied at a later + time. Two sessions cannot exist for the same identifier string. + + States for surfaces added to a session are automatically updated by the + compositor when they are changed. + + Surfaces which have been added to a session are automatically removed from + the session if xdg_toplevel.destroy is called for the surface. + </description> + + <enum name="error"> + <entry name="invalid_restore" + summary="restore cannot be performed after initial toplevel commit" + value="1"/> + <entry name="name_in_use" + summary="toplevel name is already in used" + value="2"/> + <entry name="already_mapped" + summary="toplevel was already mapped when restored" + value="3"/> + </enum> + + <request name="destroy" type="destructor"> + <description summary="Destroy the session"> + Destroy a session object, preserving the current state but not continuing + to make further updates if state changes occur. This makes the associated + xx_toplevel_session_v1 objects inert. + </description> + </request> + + <request name="remove" type="destructor"> + <description summary="Remove the session"> + Remove the session, making it no longer available for restoration. A + compositor should in response to this request remove the data related to + this session from its storage. + </description> + </request> + + <request name="add_toplevel"> + <description summary="add a new surface to the session"> + Attempt to add a given surface to the session. The passed name is used + to identify what window is being restored, and may be used store window + specific state within the session. + + Calling this with a toplevel that is already managed by the session with + the same associated will raise an in_use error. + </description> + <arg name="id" type="new_id" interface="xx_toplevel_session_v1"/> + <arg name="toplevel" type="object" interface="xdg_toplevel"/> + <arg name="name" type="string"/> + </request> + + <request name="restore_toplevel"> + <description summary="restore a surface state"> + Inform the compositor that the toplevel associated with the passed name + should have its window management state restored. + + Calling this with a toplevel that is already managed by the session with + the same associated will raise an in_use error. + + This request must be called prior to the first commit on the associated + wl_surface, otherwise an already_mapped error is raised. + + As part of the initial configure sequence, if the toplevel was + successfully restored, a xx_toplevel_session_v1.restored event is + emitted. See the xx_toplevel_session_v1.restored event for further + details. + </description> + <arg name="id" type="new_id" interface="xx_toplevel_session_v1"/> + <arg name="toplevel" type="object" interface="xdg_toplevel"/> + <arg name="name" type="string"/> + </request> + + <event name="created"> + <description summary="newly-created session id"> + Emitted at most once some time after getting a new session object. It + means that no previous state was restored, and a new session was created. + The passed id can be used to restore previous sessions. + </description> + <arg name="id" type="string"/> + </event> + + <event name="restored"> + <description summary="the session has been restored"> + Emitted at most once some time after getting a new session object. It + means that previous state was at least partially restored. The same id + can again be used to restore previous sessions. + </description> + </event> + + <event name="replaced"> + <description summary="the session has been restored"> + Emitted at most once, if the session was taken over by some other + client. When this happens, the session and all its toplevel session + objects become inert, and should be destroyed. + </description> + </event> + </interface> + + <interface name="xx_toplevel_session_v1" version="1"> + <request name="destroy" type="destructor"> + <description summary="Destroy the object"> + Destroy the object. This has no effect window management of the + associated toplevel. + </description> + </request> + + <request name="remove" type="destructor"> + <description summary="remove a surface from the session"> + Remove a specified surface from the session and render any corresponding + xx_toplevel_session_v1 object inert. The compositor should remove any + data related to the toplevel in the corresponding session from its internal + storage. + </description> + </request> + + <event name="restored"> + <description summary="a toplevel's session has been restored"> + The "restored" event is emitted prior to the first + xdg_toplevel.configure for the toplevel. It will only be emitted after + xx_session_v1.restore_toplevel, and the initial empty surface state has + been applied, and it indicates that the surface's session is being + restored with this configure event. + </description> + <arg name="surface" type="object" interface="xdg_toplevel"/> + </event> + </interface> +</protocol> diff --git a/src/gui/kernel/qplatformwindow_p.h b/src/gui/kernel/qplatformwindow_p.h index 5ee14c90e8a..c446ac760c0 100644 --- a/src/gui/kernel/qplatformwindow_p.h +++ b/src/gui/kernel/qplatformwindow_p.h @@ -135,6 +135,7 @@ public: auto role = std::any_cast<T *>(&anyRole); return role ? *role : nullptr; } + virtual void setSessionRestoreId(const QString &role) = 0; Q_SIGNALS: void surfaceCreated(); void surfaceDestroyed(); diff --git a/src/plugins/platforms/wayland/CMakeLists.txt b/src/plugins/platforms/wayland/CMakeLists.txt index 32a4ae34228..254a43c0dc6 100644 --- a/src/plugins/platforms/wayland/CMakeLists.txt +++ b/src/plugins/platforms/wayland/CMakeLists.txt @@ -77,6 +77,7 @@ qt_internal_add_module(WaylandClient qwaylandplatformservices.cpp qwaylandplatformservices_p.h qwaylandpointergestures.cpp qwaylandpointergestures_p.h qwaylandscreen.cpp qwaylandscreen_p.h + qwaylandsessionmanager.cpp qwaylandsessionmanager_p.h qwaylandshellsurface.cpp qwaylandshellsurface_p.h qwaylandshm.cpp qwaylandshm_p.h qwaylandshmbackingstore.cpp qwaylandshmbackingstore_p.h @@ -124,6 +125,8 @@ qt_internal_add_module(WaylandClient ../../../3rdparty/wayland/protocols/viewporter ../../../3rdparty/wayland/protocols/xdg-shell ../../../3rdparty/wayland/protocols/wlr-data-control + ../../../3rdparty/wayland/protocols/session-management + ) qt_internal_add_plugin(QWaylandIntegrationPlugin @@ -164,6 +167,7 @@ qt6_generate_wayland_protocol_client_sources(WaylandClient ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/wayland/extensions/server-buffer-extension.xml ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/wayland/protocols/color-management/xx-color-management-v4.xml ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/wayland/protocols/pointer-warp/pointer-warp-v1.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../../../3rdparty/wayland/protocols/session-management/xx-session-management-v1.xml ) #### Keys ignored in scope 1:.:.:client.pro:<TRUE>: diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index 1356d93abab..9c40103d1e5 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -8,6 +8,7 @@ #include "qwaylandxdgexporterv2_p.h" #include "qwaylandxdgdialogv1_p.h" #include "qwaylandxdgtopleveliconv1_p.h" +#include "qwaylandsessionmanager_p.h" #include <QtWaylandClient/private/qwaylanddisplay_p.h> #include <QtWaylandClient/private/qwaylandwindow_p.h> @@ -23,6 +24,16 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { +template <typename T, auto f> +struct WithDestructor : public T +{ + using T::T; + ~WithDestructor() + { + f(this->object()); + } +}; + QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface) : QtWayland::xdg_toplevel(xdgSurface->get_toplevel()) , m_xdgSurface(xdgSurface) @@ -45,6 +56,12 @@ QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface) m_xdgDialog.reset(m_xdgSurface->m_shell->m_xdgDialogWm->getDialog(object())); m_xdgDialog->set_modal(); } + +#ifndef QT_NO_SESSIONMANAGER + const QString sessionRestoreId = xdgSurface->window()->sessionRestoreId(); + if (!sessionRestoreId.isEmpty() && QWaylandSessionManager::instance()->session()) + m_session.reset(new WithDestructor<QtWayland::xx_toplevel_session_v1, xx_toplevel_session_v1_destroy>(QWaylandSessionManager::instance()->session()->restore_toplevel(object(), sessionRestoreId))); +#endif } QWaylandXdgSurface::Toplevel::~Toplevel() diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h index 236d34c351c..4595940508c 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h @@ -30,6 +30,10 @@ QT_BEGIN_NAMESPACE +namespace QtWayland { + class xx_toplevel_session_v1; +} + namespace QtWaylandClient { class QWaylandDisplay; @@ -41,6 +45,7 @@ class QWaylandXdgExporterV2; class QWaylandXdgDialogWmV1; class QWaylandXdgDialogV1; class QWaylandXdgToplevelIconManagerV1; +class QWaylandTopLevelSession; class Q_WAYLANDCLIENT_EXPORT QWaylandXdgSurface : public QWaylandShellSurface, public QtWayland::xdg_surface { @@ -114,6 +119,7 @@ private: QWaylandXdgToplevelDecorationV1 *m_decoration = nullptr; QScopedPointer<QWaylandXdgExportedV2> m_exported; QScopedPointer<QWaylandXdgDialogV1> m_xdgDialog; + QScopedPointer<QtWayland::xx_toplevel_session_v1> m_session; }; class Positioner : public QtWayland::xdg_positioner { diff --git a/src/plugins/platforms/wayland/qwaylanddisplay.cpp b/src/plugins/platforms/wayland/qwaylanddisplay.cpp index 071ad78f91c..d853fc673e5 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay.cpp +++ b/src/plugins/platforms/wayland/qwaylanddisplay.cpp @@ -53,6 +53,7 @@ #include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> #include <QtWaylandClient/private/qwayland-viewporter.h> #include <QtWaylandClient/private/qwayland-cursor-shape-v1.h> +#include <QtWaylandClient/private/qwayland-xx-session-management-v1.h> #include <QtWaylandClient/private/qwayland-xdg-system-bell-v1.h> #include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h> #include <QtWaylandClient/private/qwayland-wlr-data-control-unstable-v1.h> @@ -803,6 +804,14 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mGlobals.pointerWarp.reset(new WithDestructor<QtWayland::wp_pointer_warp_v1, wp_pointer_warp_v1_destroy>( registry, id, 1)); } +#ifndef QT_NO_SESSIONMANAGER + else if (interface == QLatin1String(QtWayland::xx_session_manager_v1::interface()->name) + && qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_XX_SESSION_MANAGER") > 0) { + mGlobals.xxSessionManager.reset( + new WithDestructor<QtWayland::xx_session_manager_v1, xx_session_manager_v1_destroy>( + registry, id, 1)); + } +#endif mRegistryGlobals.append(RegistryGlobal(id, interface, version, registry)); diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h index 29952886421..91d3497fe51 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h +++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h @@ -56,6 +56,7 @@ namespace QtWayland { class wp_cursor_shape_manager_v1; class wp_fractional_scale_manager_v1; class wp_viewporter; + class xx_session_manager_v1; class xdg_system_bell_v1; class xdg_toplevel_drag_manager_v1; class wp_pointer_warp_v1; @@ -86,6 +87,7 @@ class QWaylandPointerGestures; class QWaylandWindow; class QWaylandIntegration; class QWaylandHardwareIntegration; +class QWaylandSessionManager; class QWaylandSurface; class QWaylandShellIntegration; class QWaylandCursor; @@ -214,6 +216,10 @@ public: { return mGlobals.xdgToplevelDragManager.get(); } + QtWayland::xx_session_manager_v1 *xxSessionManager() const + { + return mGlobals.xxSessionManager.get(); + } QtWayland::xdg_system_bell_v1 *systemBell() const { return mGlobals.systemBell.get(); @@ -360,6 +366,7 @@ private: std::unique_ptr<QtWayland::wp_viewporter> viewporter; std::unique_ptr<QtWayland::wp_fractional_scale_manager_v1> fractionalScaleManager; std::unique_ptr<QtWayland::wp_cursor_shape_manager_v1> cursorShapeManager; + std::unique_ptr<QtWayland::xx_session_manager_v1> xxSessionManager; std::unique_ptr<QtWayland::xdg_system_bell_v1> systemBell; std::unique_ptr<QtWayland::xdg_toplevel_drag_manager_v1> xdgToplevelDragManager; std::unique_ptr<QWaylandWindowManagerIntegration> windowManagerIntegration; diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index 9158013fe78..d66710f4e55 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -56,6 +56,7 @@ #include "qwaylandinputdeviceintegration_p.h" #include "qwaylandinputdeviceintegrationfactory_p.h" #include "qwaylandwindow_p.h" +#include "qwaylandsessionmanager_p.h" #include <QtWaylandClient/private/qwayland-xdg-system-bell-v1.h> @@ -529,6 +530,12 @@ QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QStr } } +QPlatformSessionManager *QWaylandIntegration::createPlatformSessionManager(const QString &id, const QString &key) const +{ + Q_UNUSED(key); + return new QWaylandSessionManager(mDisplay.data(), id); +} + void QWaylandIntegration::reset() { mServerBufferIntegration.reset(); diff --git a/src/plugins/platforms/wayland/qwaylandintegration_p.h b/src/plugins/platforms/wayland/qwaylandintegration_p.h index d799555570d..04a0787d1ef 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration_p.h +++ b/src/plugins/platforms/wayland/qwaylandintegration_p.h @@ -36,6 +36,7 @@ class QWaylandInputDevice; class QWaylandScreen; class QWaylandCursor; class QWaylandPlatformServices; +class QWaylandSessionManager; class Q_WAYLANDCLIENT_EXPORT QWaylandIntegration : public QPlatformIntegration #if QT_CONFIG(opengl) @@ -131,6 +132,7 @@ private: void initializeShellIntegration(); void initializeInputDeviceIntegration(); QWaylandShellIntegration *createShellIntegration(const QString& interfaceName); + QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const override; const QString mPlatformName; QScopedPointer<QPlatformFontDatabase> mFontDb; diff --git a/src/plugins/platforms/wayland/qwaylandsessionmanager.cpp b/src/plugins/platforms/wayland/qwaylandsessionmanager.cpp new file mode 100644 index 00000000000..9539bb15221 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandsessionmanager.cpp @@ -0,0 +1,91 @@ +// Copyright (C) 2024 David Edmundson <[email protected]> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwaylandsessionmanager_p.h" + +#ifndef QT_NO_SESSIONMANAGER + +#include "qwaylanddisplay_p.h" +#include "qwaylandwindow_p.h" + +#include <private/qsessionmanager_p.h> +#include <private/qguiapplication_p.h> + +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandSessionManager::QWaylandSessionManager(QWaylandDisplay *display, const QString &id) + : QObject(nullptr) + , QPlatformSessionManager(id, QString()) + , mDisplay(display) +{ + if (!display->xxSessionManager()) + return; + + // The protocol also exposes a way of supporting crash handling to expose later + startSession(); +} + +QWaylandSession *QWaylandSessionManager::session() const +{ + return mSession.data(); +} + +QWaylandSessionManager *QWaylandSessionManager::instance() +{ + auto *qGuiAppPriv = QGuiApplicationPrivate::instance(); + auto *managerPrivate = static_cast<QSessionManagerPrivate*>(QObjectPrivate::get(qGuiAppPriv->session_manager)); + return static_cast<QWaylandSessionManager *>(managerPrivate->platformSessionManager); +} + +void QWaylandSessionManager::setSessionId(const QString &id) +{ + m_sessionId = id; +} + +void QWaylandSessionManager::startSession() +{ + QtWayland::xx_session_manager_v1::reason restoreReason = QtWayland::xx_session_manager_v1::reason_launch; + if (!sessionId().isEmpty()) { + restoreReason = QtWayland::xx_session_manager_v1::reason_session_restore; + } + mSession.reset(new QWaylandSession(this)); + mSession->init(mDisplay->xxSessionManager()->get_session(restoreReason, sessionId())); + mDisplay->forceRoundTrip(); +} + +QWaylandSession::QWaylandSession(QWaylandSessionManager *sessionManager) + : mSessionManager(sessionManager) +{ +} + +QWaylandSession::~QWaylandSession() { + // There's also remove which is another dtor + // depending on whether we're meant to clean up server side or not + // we might need to expose that later + destroy(); +} + +void QWaylandSession::xx_session_v1_created(const QString &id) { + qCDebug(lcQpaWayland) << "Session created" << id; + mSessionManager->setSessionId(id); +} + +void QWaylandSession::xx_session_v1_restored() { + qCDebug(lcQpaWayland) << "Session restored"; + // session Id won't have change, do nothing +} + +void QWaylandSession::xx_session_v1_replaced() { + qCDebug(lcQpaWayland) << "Session replaced"; + mSessionManager->setSessionId(QString()); +} + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/wayland/qwaylandsessionmanager_p.h b/src/plugins/platforms/wayland/qwaylandsessionmanager_p.h new file mode 100644 index 00000000000..aa9e0eec001 --- /dev/null +++ b/src/plugins/platforms/wayland/qwaylandsessionmanager_p.h @@ -0,0 +1,71 @@ +// Copyright (C) 2024 David Edmundson <[email protected]> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWAYLANDSESSIONMANAGER_H +#define QWAYLANDSESSIONMANAGER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QT_NO_SESSIONMANAGER + +#include <QtGui/qpa/qplatformsessionmanager.h> +#include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtWaylandClient/private/qwayland-xx-session-management-v1.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandDisplay; +class QWaylandWindow; +class QWaylandSession; +class QWaylandSessionManager; + + +class Q_WAYLANDCLIENT_EXPORT QWaylandSession : public QObject, public QtWayland::xx_session_v1 +{ + Q_OBJECT +public: + QWaylandSession(QWaylandSessionManager *sessionManager); + ~QWaylandSession(); + +protected: + void xx_session_v1_created(const QString &id) override; + void xx_session_v1_restored() override; + void xx_session_v1_replaced() override; +private: + QWaylandSessionManager *mSessionManager; +}; + +class Q_WAYLANDCLIENT_EXPORT QWaylandSessionManager : public QObject, public QPlatformSessionManager +{ + Q_OBJECT +public: + static QWaylandSessionManager *instance(); + QWaylandSessionManager(QWaylandDisplay *display, const QString &id); + + QWaylandSession* session() const; +private: + void setSessionId(const QString &id); + void startSession(); + + QWaylandDisplay *mDisplay = nullptr; + QScopedPointer<QWaylandSession> mSession; + friend class QWaylandSession; +}; + +} + +QT_END_NAMESPACE + +#endif +#endif diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 73d90b6c321..cfbc392c319 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qwaylandsessionmanager_p.h" #include "qwaylandwindow_p.h" #include "qwaylandbuffer_p.h" @@ -1934,6 +1935,16 @@ QSurfaceFormat QWaylandWindow::format() const return mSurfaceFormat; } +void QWaylandWindow::setSessionRestoreId(const QString &role) +{ + mSessionRestoreId = role; +} + +QString QWaylandWindow::sessionRestoreId() const +{ + return mSessionRestoreId; +} + } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 5ad826018bb..23c5ed04b8f 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -252,6 +252,8 @@ public: bool windowEvent(QEvent *event) override; QSurfaceFormat format() const override; + void setSessionRestoreId(const QString &role) override; + QString sessionRestoreId() const; public Q_SLOTS: void applyConfigure(); @@ -346,6 +348,7 @@ protected: QWaylandShmBackingStore *mBackingStore = nullptr; QMargins mCustomMargins; + QString mSessionRestoreId; QPointer<QWaylandWindow> mTransientParent; QList<QPointer<QWaylandWindow>> mChildPopups; diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 1155172a0a8..22ca1d238fe 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1360,6 +1360,12 @@ void QWidgetPrivate::create() xcbWindow->setWindowRole(topData()->role); } #endif +#if QT_CONFIG(wayland) + if (!topData()->role.isNull()) { + if (auto *waylandWindow = dynamic_cast<QWaylandWindow*>(win->handle())) + waylandWindow->setSessionRestoreId(topData()->role); + } +#endif QBackingStore *store = q->backingStore(); usesRhiFlush = false; @@ -6359,17 +6365,24 @@ QString QWidget::windowRole() const */ void QWidget::setWindowRole(const QString &role) { -#if QT_CONFIG(xcb) +#if QT_CONFIG(xcb) || QT_CONFIG(wayland) Q_D(QWidget); d->createTLExtra(); d->topData()->role = role; +#else + Q_UNUSED(role); +#endif + if (windowHandle()) { +#if QT_CONFIG(xcb) if (auto *xcbWindow = dynamic_cast<QXcbWindow*>(windowHandle()->handle())) xcbWindow->setWindowRole(role); - } -#else - Q_UNUSED(role); #endif +#if QT_CONFIG(wayland) + if (auto *waylandWindow = dynamic_cast<QWaylandWindow*>(windowHandle()->handle())) + waylandWindow->setSessionRestoreId(role); +#endif + } } /*! |