diff --git a/debian/control b/debian/control
index 5f3205e8dd..9074d89cd6 100644
--- a/debian/control
+++ b/debian/control
@@ -5,7 +5,7 @@ Build-Depends: cmake (>= 3.4~),
debhelper-compat (= 13),
ninja-build,
extra-cmake-modules (>= 1.4.0~),
- libddm-dev,
+ libddm-dev (> 0.2.2),
libdtk6core-bin,
libdtk6core-dev,
libdtk6declarative-dev,
@@ -53,7 +53,7 @@ Depends: qml6-module-qtquick-layouts,
${shlibs:Depends},
Conflicts: libwaylib, qwlroots
Replaces: libwaylib, qwlroots
-Recommends: ddm,
+Recommends: ddm (> 0.2.2),
Description: a Wayland compositor based on wlroots and QML, designed with an attractive and elegant appearance.
Package: treeland-dev
diff --git a/misc/dbus/org.deepin.compositor1.xml b/misc/dbus/org.deepin.compositor1.xml
index 2a61387d78..bf620e651b 100644
--- a/misc/dbus/org.deepin.compositor1.xml
+++ b/misc/dbus/org.deepin.compositor1.xml
@@ -8,5 +8,7 @@
+
+
diff --git a/src/core/shellhandler.cpp b/src/core/shellhandler.cpp
index 105bb4f69b..db227e46c9 100644
--- a/src/core/shellhandler.cpp
+++ b/src/core/shellhandler.cpp
@@ -124,7 +124,10 @@ WXWayland *ShellHandler::createXWayland(WServer *server,
void ShellHandler::removeXWayland(WXWayland *xwayland)
{
m_xwaylands.removeOne(xwayland);
- xwayland->safeDeleteLater();
+ // Ensure xwayland is removed from interfaceList
+ if (auto server = WServer::from(xwayland))
+ server->detach(xwayland);
+ delete xwayland;
}
void ShellHandler::initInputMethodHelper(WServer *server, WSeat *seat)
diff --git a/src/core/treeland.cpp b/src/core/treeland.cpp
index 09ca0fe58c..597b924454 100644
--- a/src/core/treeland.cpp
+++ b/src/core/treeland.cpp
@@ -79,7 +79,7 @@ class TreelandPrivate : public QObject
connect(qmlEngine, &QQmlEngine::quit, q, &Treeland::quit, Qt::QueuedConnection);
helper = qmlEngine->singletonInstance("Treeland", "Helper");
connect(helper, &Helper::requestQuit, q, &Treeland::quit, Qt::QueuedConnection);
- helper->init();
+ helper->init(q);
#ifndef DISABLE_DDM
auto userModel = qmlEngine->singletonInstance("Treeland", "UserModel");
diff --git a/src/core/treeland.h b/src/core/treeland.h
index 36cf55662a..ff174971a9 100644
--- a/src/core/treeland.h
+++ b/src/core/treeland.h
@@ -43,6 +43,7 @@ class Treeland
Q_SIGNALS:
void socketDisconnected();
+ void SessionChanged();
public Q_SLOTS:
bool ActivateWayland(QDBusUnixFileDescriptor fd);
diff --git a/src/greeter/greeterproxy.cpp b/src/greeter/greeterproxy.cpp
index 5fcf3caea7..f172ce0503 100644
--- a/src/greeter/greeterproxy.cpp
+++ b/src/greeter/greeterproxy.cpp
@@ -293,82 +293,14 @@ void GreeterProxy::unlock(const QString &user, const QString &password)
}
}
-static QString getSessionPathByUser(UserPtr userInfo);
-
void GreeterProxy::logout()
{
qCDebug(treelandGreeter) << "Logout.";
d->isLoggedIn = false;
Q_EMIT isLoggedInChanged();
- auto user = userModel()->currentUser();
- QThreadPool::globalInstance()->start([user]() {
- const auto path = getSessionPathByUser(user);
- if (path.isEmpty()) {
- qCWarning(treelandGreeter, "No session logged in.");
- return;
- }
- qCDebug(treelandGreeter) << "Terminate the session" << path;
- auto reply = DDBusSender::system()
- .service("org.freedesktop.login1")
- .path(path)
- .interface("org.freedesktop.login1.Session")
- .method("Terminate")
- .call();
- if (reply.isError()) {
- qCWarning(treelandGreeter) << "Failed to logout, error:" << reply.error().message();
- }
- });
-}
-
-QString getSessionPathByUser(UserPtr userInfo)
-{
- if (!userInfo) {
- qCWarning(treelandGreeter) << "No user logged in.";
- return {};
- }
-
- QDBusPendingReply> sessionsRelpy =
- DDBusSender::system()
- .service("org.freedesktop.login1")
- .path("/org/freedesktop/login1")
- .interface("org.freedesktop.login1.Manager")
- .method("ListSessions")
- .call();
- if (sessionsRelpy.isError()) {
- qCWarning(treelandGreeter) << "Failed to logout, error:" << sessionsRelpy.error().message();
- return {};
- }
-
- QStringList userSessions;
- const auto sessions = sessionsRelpy.value();
- for (auto item : sessions) {
- if (item.uid != userInfo->UID())
- continue;
- // TODO multiple seats.
- if (item.seat.isEmpty())
- continue;
- userSessions << item.path.path();
- }
- std::sort(userSessions.begin(), userSessions.end(), [](const QString &s1, const QString &s2) {
- return s1.localeAwareCompare(s2) > 0;
- });
- for (auto item : userSessions) {
- QDBusPendingReply relpy = DDBusSender::system()
- .service("org.freedesktop.login1")
- .path(item)
- .interface("org.freedesktop.login1.Session")
- .property("Active")
- .get();
- if (relpy.value().toBool())
- return item;
- }
- return {};
-}
-
-void GreeterProxy::activateUser(const QString &user)
-{
- auto userInfo = userModel()->get(user);
- SocketWriter(d->socket) << quint32(GreeterMessages::ActivateUser) << user;
+ auto session = Helper::instance()->activeSession().lock();
+ SocketWriter(d->socket) << quint32(GreeterMessages::Logout) << session->id;
+ Helper::instance()->removeSession(session);
}
void GreeterProxy::connected()
@@ -483,7 +415,8 @@ void GreeterProxy::readyRead()
} break;
case DaemonMessages::UserActivateMessage: {
QString user;
- input >> user;
+ int sessionId;
+ input >> user >> sessionId;
// NOTE: maybe DDM will active dde user.
if (!d->userModel->getUser(user)) {
@@ -494,8 +427,10 @@ void GreeterProxy::readyRead()
}
d->userModel->setCurrentUserName(user);
+ // userLoggedIn signal is connected with Helper::updateActiveUserSession
+ Q_EMIT d->userModel->userLoggedIn(user, sessionId);
- qCInfo(treelandGreeter) << "activate successfully: " << user;
+ qCInfo(treelandGreeter) << "activate successfully: " << user << ", XDG_SESSION_ID: " << sessionId;
} break;
case DaemonMessages::UserLoggedIn: {
QString user;
diff --git a/src/greeter/greeterproxy.h b/src/greeter/greeterproxy.h
index fcdeca60c8..f1da0ecb0f 100644
--- a/src/greeter/greeterproxy.h
+++ b/src/greeter/greeterproxy.h
@@ -80,7 +80,6 @@ public Q_SLOTS:
void init();
void login(const QString &user, const QString &password, int sessionIndex);
- void activateUser(const QString &user);
void unlock(const QString &user, const QString &password);
void logout();
diff --git a/src/greeter/usermodel.h b/src/greeter/usermodel.h
index 9da8cafbed..23fdeed515 100644
--- a/src/greeter/usermodel.h
+++ b/src/greeter/usermodel.h
@@ -86,6 +86,7 @@ class UserModel : public QAbstractListModel
void currentUserNameChanged();
void updateTranslations(const QLocale &locale);
void countChanged();
+ void userLoggedIn(const QString &username, int sessionId);
private Q_SLOTS:
void onUserAdded(quint64 uid);
diff --git a/src/plugins/lockscreen/qml/UserList.qml b/src/plugins/lockscreen/qml/UserList.qml
index b615c5f7bf..238f5f8b35 100644
--- a/src/plugins/lockscreen/qml/UserList.qml
+++ b/src/plugins/lockscreen/qml/UserList.qml
@@ -29,7 +29,6 @@ D.Popup {
function selectCurrentUser(userName, index) {
UserModel.currentUserName = userName
users.lastCheckedIndex = index
- GreeterModel.proxy.activateUser(userName)
userList.close()
}
diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp
index 2d1b1669e4..4522dec292 100644
--- a/src/seat/helper.cpp
+++ b/src/seat/helper.cpp
@@ -38,6 +38,8 @@
#include "common/treelandlogging.h"
#include "modules/ddm/ddminterfacev1.h"
#include "treelandconfig.hpp"
+#include "core/treeland.h"
+#include "greeter/greeterproxy.h"
#include
#include
@@ -169,6 +171,16 @@ static QByteArray readWindowProperty(xcb_connection_t *connection,
return data;
}
+Session::~Session()
+{
+ qCDebug(treelandCore) << "Deleting session for uid:" << uid << socket;
+ Q_EMIT aboutToBeDestroyed();
+ if (xwayland)
+ Helper::instance()->shellHandler()->removeXWayland(xwayland);
+ if (socket)
+ delete socket;
+}
+
Helper *Helper::m_instance = nullptr;
Helper::Helper(QObject *parent)
@@ -892,6 +904,11 @@ void Helper::onSurfaceWrapperAdded(SurfaceWrapper *wrapper)
bool isXwayland = wrapper->type() == SurfaceWrapper::Type::XWayland;
bool isLayer = wrapper->type() == SurfaceWrapper::Type::Layer;
+ connect(m_activeSession.lock().get(), &Session::aboutToBeDestroyed, wrapper, [this, wrapper]() {
+ onSurfaceWrapperAboutToRemove(wrapper);
+ m_rootSurfaceContainer->destroyForSurface(wrapper);
+ });
+
if (isXdgToplevel || isXdgPopup || isLayer) {
auto *attached =
new Personalization(wrapper->shellSurface(), m_personalization, wrapper);
@@ -1044,8 +1061,9 @@ void Helper::deleteTaskSwitch()
}
}
-void Helper::init()
+void Helper::init(Treeland::Treeland *treeland)
{
+ m_treeland = treeland;
auto engine = qmlEngine();
m_userModel = engine->singletonInstance("Treeland", "UserModel");
@@ -1263,13 +1281,9 @@ void Helper::init()
xwaylandOutputManager->setScaleOverride(1.0);
xdgOutputManager->setFilter([this](WClient *client) { return !isXWaylandClient(client); });
xwaylandOutputManager->setFilter([this](WClient *client) { return isXWaylandClient(client); });
- auto updateSession = [this] {
- auto user = m_userModel->currentUser();
- const uid_t uid = user ? user->UID() : getuid();
- updateActiveUserSession(uid);
- };
- updateSession();
- connect(m_userModel, &UserModel::currentUserNameChanged, this, updateSession);
+ // User dde does not has a real Logind session, so just pass 0 as id
+ updateActiveUserSession(QStringLiteral("dde"), 0);
+ connect(m_userModel, &UserModel::userLoggedIn, this, &Helper::updateActiveUserSession);
m_xdgDecorationManager = m_server->attach();
connect(m_xdgDecorationManager,
&WXdgDecorationManager::surfaceModeChanged,
@@ -2175,13 +2189,50 @@ std::shared_ptr Helper::sessionForSocket(WSocket *socket) const
return nullptr;
}
+/**
+ * Get the currently active session
+ *
+ * @returns weak_ptr to the active session
+ */
+std::weak_ptr Helper::activeSession() const
+{
+ return m_activeSession;
+}
+
+/**
+ * Remove a session from the session list
+ *
+ * @param session The session to remove
+ */
+void Helper::removeSession(std::shared_ptr session)
+{
+ if (!session)
+ return;
+
+ if (m_activeSession.lock() == session) {
+ m_activeSession.reset();
+ m_activatedSurface = nullptr;
+ Q_EMIT activatedSurfaceChanged();
+ }
+
+ for (auto s : std::as_const(m_sessions)) {
+ if (s.get() == session.get()) {
+ m_sessions.removeOne(s);
+ break;
+ }
+ }
+
+ session.reset();
+}
+
/**
* Ensure a session exists for the given uid, creating it if necessary
*
+ * @param id An existing logind session ID
* @param uid User ID to ensure session for
* @returns Session for the given uid, or nullptr on failure
*/
-std::shared_ptr Helper::ensureSession(uid_t uid)
+std::shared_ptr Helper::ensureSession(int id, uid_t uid)
{
// Helper lambda to create WSocket and WXWayland
auto createWSocket = [this]() {
@@ -2223,10 +2274,6 @@ std::shared_ptr Helper::ensureSession(uid_t uid)
}
}
});
- connect(xwayland, &QObject::destroyed, this, [this, xwayland]() {
- removeXWayland(xwayland);
- });
-
return xwayland;
};
// Check if session already exists for uid
@@ -2253,67 +2300,49 @@ std::shared_ptr Helper::ensureSession(uid_t uid)
return session;
}
- // Session does not exist, create new session
+ // Session does not exist, create new session with deleter
auto session = std::make_shared();
+ session->id = id;
session->uid = uid;
session->socket = createWSocket();
if (!session->socket) {
+ session.reset();
return nullptr;
}
session->xwayland = createXWayland(session->socket);
if (!session->xwayland) {
- delete session->socket;
+ session.reset();
return nullptr;
}
+
// Add session to list
m_sessions.append(session);
return session;
}
/**
- * Remove the given WXWayland and clean up its session.
- * This is called when an XWayland instance is destroyed.
- *
- * @param xwayland WXWayland to remove
- */
-void Helper::removeXWayland(WXWayland *xwayland)
-{
- if (!xwayland)
- return;
-
- if (auto session = sessionForXWayland(xwayland)) {
- session->xwayland = nullptr;
- session->noTitlebarAtom = XCB_ATOM_NONE;
- }
-
- m_shellHandler->removeXWayland(xwayland);
-}
-
-/**
- * Get the WXWayland for the given uid, optionally creating it if missing
+ * Get the WXWayland for the given uid
*
* @param uid User ID to get WXWayland for
- * @param createIfMissing Whether to create the session if it does not exist
* @returns WXWayland for the given uid, or nullptr if not found/created
*/
-WXWayland *Helper::xwaylandForUid(uid_t uid, bool createIfMissing)
+WXWayland *Helper::xwaylandForUid(uid_t uid)
{
- auto session = createIfMissing ? ensureSession(uid) : sessionForUid(uid);
+ auto session = sessionForUid(uid);
return session ? session->xwayland : nullptr;
}
/**
- * Get the WSocket for the given uid, optionally creating it if missing
+ * Get the WSocket for the given uid
*
* @param uid User ID to get WSocket for
- * @param createIfMissing Whether to create the session if it does not exist
* @returns WSocket for the given uid, or nullptr if not found/created
*/
-WSocket *Helper::waylandSocketForUid(uid_t uid, bool createIfMissing)
+WSocket *Helper::waylandSocketForUid(uid_t uid)
{
- auto session = createIfMissing ? ensureSession(uid) : sessionForUid(uid);
+ auto session = sessionForUid(uid);
return session ? session->socket : nullptr;
}
@@ -2348,16 +2377,16 @@ WXWayland *Helper::defaultXWaylandSocket() const
* This will update XWayland visibility and emit socketFileChanged if the
* active session changed.
*
- * @param uid User ID to set as active session
+ * @param username Username to set as active session
*/
-void Helper::updateActiveUserSession(uid_t uid)
+void Helper::updateActiveUserSession(const QString &username, int id)
{
// Get previous active session
auto previous = m_activeSession.lock();
// Get new session for uid, creating if necessary
- auto session = ensureSession(uid);
+ auto session = ensureSession(id, getpwnam(username.toLocal8Bit().data())->pw_uid);
if (!session) {
- qCWarning(treelandInput) << "Failed to ensure session for uid" << uid;
+ qCWarning(treelandInput) << "Failed to ensure session for user" << username;
return;
}
do {
@@ -2370,14 +2399,16 @@ void Helper::updateActiveUserSession(uid_t uid)
previous->socket->setEnabled(false);
session->socket->setEnabled(true);
Q_EMIT socketFileChanged();
+ // Notify session changed through DBus, treeland-sd will listen it to update envs
+ Q_EMIT m_treeland->SessionChanged();
// Store last active surface of previous session
if (previous)
previous->lastActivatedSurface = m_activatedSurface;
// Restore last activated surface for new session
if (session->lastActivatedSurface)
- m_activatedSurface = session->lastActivatedSurface;
+ setActivatedSurface(session->lastActivatedSurface);
else
- m_activatedSurface = nullptr;
+ setActivatedSurface(nullptr);
Q_EMIT activatedSurfaceChanged();
} while (false);
diff --git a/src/seat/helper.h b/src/seat/helper.h
index 6345ecfca5..b10ab23e35 100644
--- a/src/seat/helper.h
+++ b/src/seat/helper.h
@@ -104,16 +104,28 @@ struct wlr_idle_inhibitor_v1;
struct wlr_output_power_v1_set_mode_event;
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request;
+namespace Treeland {
+class Treeland;
+}
+
QW_BEGIN_NAMESPACE
class qw_ext_foreign_toplevel_image_capture_source_manager_v1;
QW_END_NAMESPACE
-struct Session {
+struct Session : QObject {
+ Q_OBJECT
+public:
+ int id = 0;
uid_t uid = 0;
WSocket *socket = nullptr;
WXWayland *xwayland = nullptr;
quint32 noTitlebarAtom = XCB_ATOM_NONE;
SurfaceWrapper *lastActivatedSurface = nullptr;
+
+ ~Session();
+
+Q_SIGNALS:
+ void aboutToBeDestroyed();
};
class Helper : public WSeatEventFilter
@@ -162,7 +174,7 @@ class Helper : public WSeatEventFilter
ShellHandler *shellHandler() const;
Workspace *workspace() const;
- void init();
+ void init(Treeland::Treeland *treeland);
TogglableGesture *multiTaskViewGesture() const
{
@@ -189,8 +201,13 @@ class Helper : public WSeatEventFilter
void addSocket(WSocket *socket);
void removeXWayland(WXWayland *xwayland);
- WXWayland *xwaylandForUid(uid_t uid, bool createIfMissing = true);
- WSocket *waylandSocketForUid(uid_t uid, bool createIfMissing = true);
+ void removeSession(std::shared_ptr session);
+ WXWayland *xwaylandForUid(uid_t uid);
+ WSocket *waylandSocketForUid(uid_t uid);
+ std::shared_ptr sessionForUid(uid_t uid) const;
+ std::shared_ptr sessionForXWayland(WXWayland *xwayland) const;
+ std::shared_ptr sessionForSocket(WSocket *socket) const;
+ std::weak_ptr activeSession() const;
WSocket *defaultWaylandSocket() const;
WXWayland *defaultXWaylandSocket() const;
@@ -325,15 +342,13 @@ private Q_SLOTS:
void updateIdleInhibitor();
void setNoAnimation(bool noAnimation);
- std::shared_ptr sessionForUid(uid_t uid) const;
- std::shared_ptr sessionForXWayland(WXWayland *xwayland) const;
- std::shared_ptr sessionForSocket(WSocket *socket) const;
- std::shared_ptr ensureSession(uid_t uid);
- void updateActiveUserSession(uid_t uid);
+ std::shared_ptr ensureSession(int id, uid_t uid);
+ void updateActiveUserSession(const QString &username, int id);
bool isXWaylandClient(WClient *client);
static Helper *m_instance;
TreelandConfig *m_config = nullptr;
+ Treeland::Treeland *m_treeland = nullptr;
FpsDisplayManager *m_fpsManager = nullptr;
CurrentMode m_currentMode{ CurrentMode::Normal };
diff --git a/src/systemd-socket.cpp b/src/systemd-socket.cpp
index e24f30ba81..b61e939e48 100644
--- a/src/systemd-socket.cpp
+++ b/src/systemd-socket.cpp
@@ -23,6 +23,21 @@
typedef QMap StringMap;
Q_DECLARE_METATYPE(StringMap)
+class SignalReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ SignalReceiver(std::function activateFdFunc, QObject *parent = nullptr)
+ : QObject(parent), activateFd(activateFdFunc) {
+ }
+public Q_SLOTS:
+ void onSessionChanged() {
+ activateFd();
+ }
+private:
+ std::function activateFd;
+};
+
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
@@ -51,8 +66,8 @@ int main(int argc, char *argv[])
QDBusUnixFileDescriptor unixFileDescriptor(SD_LISTEN_FDS_START);
- auto active = [unixFileDescriptor, type](const QDBusConnection &connection) {
- auto activateFd = [unixFileDescriptor, type, connection] {
+ auto active = [unixFileDescriptor, type](QDBusConnection connection) {
+ auto activateFd = [unixFileDescriptor, type, &connection] {
QDBusInterface updateFd("org.deepin.Compositor1",
"/org/deepin/Compositor1",
"org.deepin.Compositor1",
@@ -113,6 +128,14 @@ int main(int argc, char *argv[])
compositorWatcher,
activateFd);
+ // Listen to SessionChanged signal and activate fd when session changed
+ connection.connect("org.deepin.Compositor1",
+ "/org/deepin/Compositor1",
+ "org.deepin.Compositor1",
+ "SessionChanged",
+ new SignalReceiver(activateFd),
+ SLOT(onSessionChanged()));
+
activateFd();
};
@@ -121,3 +144,5 @@ int main(int argc, char *argv[])
return app.exec();
}
+
+#include "systemd-socket.moc"
\ No newline at end of file
diff --git a/waylib/src/server/kernel/wsocket.cpp b/waylib/src/server/kernel/wsocket.cpp
index c6aa634875..377b5f3093 100644
--- a/waylib/src/server/kernel/wsocket.cpp
+++ b/waylib/src/server/kernel/wsocket.cpp
@@ -251,10 +251,11 @@ void WSocketPrivate::addClient(WClient *client)
class Q_DECL_HIDDEN WClientPrivate : public WObjectPrivate
{
public:
- WClientPrivate(wl_client *handle, WSocket *socket, WClient *qq)
+ WClientPrivate(wl_client *handle, WSocket *socket, WClient *qq, bool isWlClientOwned)
: WObjectPrivate(qq)
, handle(handle)
, socket(socket)
+ , isWlClientOwned(isWlClientOwned)
{
auto listener = new WlClientDestroyListener(qq);
wl_client_add_destroy_listener(handle, &listener->destroy);
@@ -277,12 +278,13 @@ class Q_DECL_HIDDEN WClientPrivate : public WObjectPrivate
WSocket *socket = nullptr;
mutable QSharedPointer credentials;
mutable int pidFD = -1;
+ bool isWlClientOwned = true;
};
void WlClientDestroyListener::handle_destroy(wl_listener *listener, void *data)
{
WlClientDestroyListener *self = wl_container_of(listener, self, destroy);
- if (self->client) {
+ if (self->client && self->client->handle()) {
Q_ASSERT(reinterpret_cast(data) == self->client->handle());
self->client->d_func()->handle = nullptr;
auto socket = self->client->socket();
@@ -294,9 +296,9 @@ void WlClientDestroyListener::handle_destroy(wl_listener *listener, void *data)
delete self;
}
-WClient::WClient(wl_client *client, WSocket *socket)
+WClient::WClient(wl_client *client, WSocket *socket, bool isWlClientOwned)
: QObject(nullptr)
- , WObject(*new WClientPrivate(client, socket, this))
+ , WObject(*new WClientPrivate(client, socket, this, isWlClientOwned))
{
}
@@ -467,7 +469,7 @@ void WSocket::close()
if (!d->clients.isEmpty()) {
for (auto client : std::as_const(d->clients))
- delete client;
+ removeClient(client);
d->clients.clear();
Q_EMIT clientsChanged();
@@ -720,7 +722,7 @@ WClient *WSocket::addClient(int fd)
return wclient;
}
-WClient *WSocket::addClient(wl_client *client)
+WClient *WSocket::addClient(wl_client *client, bool isWlClientOwned)
{
W_D(WSocket);
@@ -731,7 +733,7 @@ WClient *WSocket::addClient(wl_client *client)
if (d->clients.contains(wclient))
return wclient;
} else {
- wclient = new WClient(client, this);
+ wclient = new WClient(client, this, isWlClientOwned);
}
d->addClient(wclient);
@@ -756,6 +758,14 @@ bool WSocket::removeClient(WClient *client)
return false;
Q_EMIT aboutToBeDestroyedClient(client);
+
+ auto handle = client->handle();
+ if (handle && client->d_func()->isWlClientOwned) {
+ // Set handle to nullptr to prevent handle_destroy from calling removeClient again
+ client->d_func()->handle = nullptr;
+ wl_client_destroy(handle);
+ }
+
delete client;
Q_EMIT clientsChanged();
diff --git a/waylib/src/server/kernel/wsocket.h b/waylib/src/server/kernel/wsocket.h
index 8faf098ee1..7f022374a6 100644
--- a/waylib/src/server/kernel/wsocket.h
+++ b/waylib/src/server/kernel/wsocket.h
@@ -52,7 +52,7 @@ public Q_SLOTS:
private:
friend class WSocket;
friend class WlClientDestroyListener;
- explicit WClient(wl_client *client, WSocket *socket);
+ explicit WClient(wl_client *client, WSocket *socket, bool isWlClientOwned = true);
~WClient() = default;
using QObject::deleteLater;
};
@@ -94,7 +94,7 @@ class WAYLIB_SERVER_EXPORT WSocket : public QObject, public WObject
bool listen(struct wl_display *display);
WClient *addClient(int fd);
- WClient *addClient(wl_client *client);
+ WClient *addClient(wl_client *client, bool isWlClientOwned = true);
bool removeClient(wl_client *client);
bool removeClient(WClient *client);
const QList &clients() const;
diff --git a/waylib/src/server/protocols/wxwayland.cpp b/waylib/src/server/protocols/wxwayland.cpp
index 7da8ca25b2..f78286a1a2 100644
--- a/waylib/src/server/protocols/wxwayland.cpp
+++ b/waylib/src/server/protocols/wxwayland.cpp
@@ -339,7 +339,7 @@ void WXWayland::create(WServer *server)
auto s = qw_xwayland_server::from(handle->handle()->server);
QObject::connect(s, &qw_xwayland_server::notify_start, this, [d] {
- d->socket->addClient(d->waylandClient());
+ d->socket->addClient(d->waylandClient(), false);
});
}