diff --git a/.github/workflows/ddm-archlinux-build.yml b/.github/workflows/ddm-archlinux-build.yml index f9143ed..fff6678 100644 --- a/.github/workflows/ddm-archlinux-build.yml +++ b/.github/workflows/ddm-archlinux-build.yml @@ -1,7 +1,6 @@ name: Build ddm on Arch Linux # Dependencies are based on the official ArchLinux PKGBUILD -# treeland-protocols-git is installed from source on: push: @@ -21,21 +20,7 @@ jobs: - name: Install base dependencies run: | - pacman -S --noconfirm --noprogressbar base-devel bash gcc-libs glibc libxau pam qt6-base qt6-declarative systemd-libs ttf-font xorg-server xorg-xauth git extra-cmake-modules python-docutils qt6-tools treeland-protocols ninja - - - name: Build and Install treeland-protocols - run: | - echo "Building treeland-protocols from source..." - git clone https://github.com/linuxdeepin/treeland-protocols.git --depth 1 - cd treeland-protocols - cmake -B build \ - -DCMAKE_INSTALL_LIBDIR=lib \ - -DCMAKE_INSTALL_LIBEXECDIR=lib \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_BUILD_TYPE=Release - cmake --build build - cmake --install build - echo "✅ treeland-protocols built and installed from source" + pacman -S --noconfirm --noprogressbar base-devel bash gcc-libs glibc libxau pam qt6-base qt6-declarative qt6-remoteobjects systemd-libs ttf-font xorg-server xorg-xauth git extra-cmake-modules python-docutils qt6-tools ninja - uses: actions/checkout@v4 with: diff --git a/.github/workflows/ddm-deepin-build.yml b/.github/workflows/ddm-deepin-build.yml index 2f51267..d1c06bc 100644 --- a/.github/workflows/ddm-deepin-build.yml +++ b/.github/workflows/ddm-deepin-build.yml @@ -23,22 +23,6 @@ jobs: apt-get update apt-get install -y devscripts equivs git - # Save the ddm workspace directory - DDM_DIR="$PWD" - echo "ddm workspace directory: $DDM_DIR" - - # Build and install treeland-protocols locally (required build dependency not from repo) - echo "Building treeland-protocols from source (master)" - cd /tmp - if [ ! -d treeland-protocols ]; then - git clone --depth=1 --branch master --single-branch https://github.com/linuxdeepin/treeland-protocols.git - fi - cd treeland-protocols - mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control - dpkg-buildpackage -uc -us -b - apt-get install -y ../*.deb || dpkg -i ../*.deb || apt-get -f install -y - cd "$DDM_DIR" - # Install build-deps defined in ddm/debian/control mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c1c4e5..626a1d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,14 +73,8 @@ pkg_search_module(LibSystemd REQUIRED IMPORTED_TARGET libsystemd) # XAU pkg_search_module(LibXau REQUIRED IMPORTED_TARGET xau) -# Wayland client -pkg_search_module(WaylandClient REQUIRED IMPORTED_TARGET wayland-client) - -# TreelandProtocols -find_package(TreelandProtocols 0.5.3 REQUIRED) - # Qt6 -find_package(Qt6 CONFIG REQUIRED Core DBus Network) +find_package(Qt6 CONFIG REQUIRED Core DBus Network RemoteObjects) qt_standard_project_setup(REQUIRES 6.6) # Uninstall target diff --git a/debian/control b/debian/control index 118d855..5ee343f 100644 --- a/debian/control +++ b/debian/control @@ -6,12 +6,11 @@ Build-Depends: debhelper-compat (= 13), extra-cmake-modules (>= 1.4.0~), libpam0g-dev, libsystemd-dev [linux-any], - libwayland-dev, libxau-dev, pkg-config, qt6-base-dev (>= 6.6.1~), + qt6-remoteobjects-dev, systemd [linux-any], - treeland-protocols, Standards-Version: 4.6.0 Section: libs Homepage: https://github.com/linuxdeepin/ddm.git diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 1a84cf2..6beb349 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -1,21 +1,3 @@ -# Generate treeland-ddm protocol files -set(TREELAND_DDM_HEADER ${CMAKE_CURRENT_BINARY_DIR}/treeland-ddm-v1.h) -set(TREELAND_DDM_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/treeland-ddm-v1.c) - -add_custom_command( - OUTPUT ${TREELAND_DDM_HEADER} - COMMAND wayland-scanner client-header < ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml > ${TREELAND_DDM_HEADER} - DEPENDS ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml - COMMENT "Generating treeland-ddm-v1.h" -) - -add_custom_command( - OUTPUT ${TREELAND_DDM_SOURCE} - COMMAND wayland-scanner private-code < ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml > ${TREELAND_DDM_SOURCE} - DEPENDS ${TREELAND_PROTOCOLS_DATA_DIR}/treeland-ddm-v1.xml - COMMENT "Generating treeland-ddm-v1.c" -) - configure_file(config.h.in config.h IMMEDIATE @ONLY) set(DAEMON_SOURCES Auth.cpp @@ -30,7 +12,6 @@ set(DAEMON_SOURCES UserSession.cpp XorgDisplayServer.cpp TreelandDisplayServer.cpp - ${TREELAND_DDM_SOURCE} ) qt_add_dbus_adaptor(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.DisplayManager.xml" "DisplayManager.h" DDM::DisplayManager) @@ -59,15 +40,21 @@ qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.fr qt_add_dbus_interface(DAEMON_SOURCES "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.User.xml" "Login1User") add_executable(ddm ${DAEMON_SOURCES}) +qt_add_repc_sources(ddm + ${CMAKE_CURRENT_SOURCE_DIR}/ddmremote.rep +) +qt_add_repc_replicas(ddm + ${CMAKE_CURRENT_SOURCE_DIR}/treelandremote.rep +) target_link_libraries(ddm PRIVATE common PUBLIC Qt6::DBus Qt6::Network + Qt6::RemoteObjects PkgConfig::Pam PkgConfig::LibSystemd - PkgConfig::WaylandClient ) install(TARGETS ddm DESTINATION "${CMAKE_INSTALL_BINDIR}") diff --git a/src/daemon/Display.cpp b/src/daemon/Display.cpp index 0bc69e0..330a007 100644 --- a/src/daemon/Display.cpp +++ b/src/daemon/Display.cpp @@ -27,21 +27,23 @@ #include "DaemonApp.h" #include "DdeSeatdControl.h" #include "DisplayManager.h" -#include "Messages.h" #include "SeatManager.h" #include "SocketServer.h" -#include "SocketWriter.h" #include "TreelandConnector.h" #include "TreelandDisplayServer.h" #include "XorgDisplayServer.h" #include "config.h" #include "Login1Manager.h" +#include "Login1Session.h" #include +#include +#include +#include #include +#include #include -#include #include #include @@ -121,7 +123,7 @@ namespace DDM { Display::Display(SeatManager *parent, QString name) : QObject(parent) , name(name) - , m_socketServer(new SocketServer(this)) { + , m_socketServer(new SocketServer(this, this)) { // Create display server terminalId = fetchAvailableVt(); @@ -135,23 +137,12 @@ namespace DDM { "dde", terminalId); - // connect connected signal - connect(m_socketServer, &SocketServer::connected, this, &Display::connected); - - // connect login signal - connect(m_socketServer, &SocketServer::login, this, &Display::login); - - // connect logout signal - connect(m_socketServer, &SocketServer::logout, this, &Display::logout); - - // connect lock signal - connect(m_socketServer, &SocketServer::lock, this, &Display::lock); - - // connect unlock signal - connect(m_socketServer, &SocketServer::unlock,this, &Display::unlock); // connect login result signals connect(this, &Display::loginFailed, m_socketServer, &SocketServer::loginFailed); + + // connect Treeland lock state → sync to logind + connect(daemonApp->treelandConnector(), &TreelandConnector::lockStateChanged, this, &Display::onTreelandLockStateChanged); } Display::~Display() { @@ -167,6 +158,7 @@ namespace DDM { return; } + m_activeTreelandSessionId = xdgSessionId; m_treeland->activateUser(user, xdgSessionId); if (xdgSessionId > 0 && Logind::isAvailable()) { @@ -187,7 +179,7 @@ namespace DDM { return false; // start socket server - m_socketServer->start("treeland"); + m_socketServer->start(); // Update dbus info DaemonApp::instance()->displayManager()->setAuthInfo(m_socketServer->socketAddress()); @@ -230,21 +222,99 @@ namespace DDM { emit stopped(); } - void Display::connected(QLocalSocket *socket) { - // send logged in users (for possible crash recovery) - SocketWriter writer(socket); - for (Auth *auth : std::as_const(auths)) { - if (auth->sessionOpened) - writer << quint32(DaemonMessages::UserLoggedIn) << auth->user << auth->xdgSessionId; - } + void Display::connected() { + m_socketServer->replayUserSessions(); + } + + void Display::onTreelandLockStateChanged(bool locked) { + m_treelandLocked = locked; + if (m_activeTreelandSessionId <= 0) + return; + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), + Logind::managerPath(), + QDBusConnection::systemBus()); + if (locked) + manager.LockSession(QString::number(m_activeTreelandSessionId)); + else + manager.UnlockSession(QString::number(m_activeTreelandSessionId)); + } + + void Display::watchUserSession(Auth *auth) { + if (!auth || auth->xdgSessionId <= 0) + return; + + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), + Logind::managerPath(), + QDBusConnection::systemBus()); + auto reply = manager.GetSession(QString::number(auth->xdgSessionId)); + auto *watcher = new QDBusPendingCallWatcher(reply, this); + QPointer authPtr(auth); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, authPtr] { + QDBusPendingReply reply = *watcher; + watcher->deleteLater(); + if (!authPtr) + return; + if (reply.isError()) { + qWarning() << "Failed to get logind session path" << authPtr->xdgSessionId << reply.error().message(); + return; + } + + auto *session = new OrgFreedesktopLogin1SessionInterface(Logind::serviceName(), + reply.value().path(), + QDBusConnection::systemBus(), + authPtr); + session->setObjectName(QStringLiteral("logindSessionWatcher")); + connect(session, &OrgFreedesktopLogin1SessionInterface::Lock, this, [this, authPtr] { + if (!authPtr) + return; + if (m_activeTreelandSessionId != authPtr->xdgSessionId) + return; + daemonApp->treelandConnector()->lock(); + }); + connect(session, &OrgFreedesktopLogin1SessionInterface::Unlock, this, [this, authPtr] { + if (!authPtr) + return; + if (m_activeTreelandSessionId != authPtr->xdgSessionId) { + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), + Logind::managerPath(), + QDBusConnection::systemBus()); + manager.LockSession(QString::number(authPtr->xdgSessionId)); + return; + } + if (m_treelandLocked) { + constexpr int windowMs = 2000; + constexpr int maxLockBacks = 3; + const auto now = std::chrono::steady_clock::now().time_since_epoch().count(); + if (now - m_lockBackWindowStart > windowMs) { + m_lockBackWindowStart = now; + m_lockBackCount = 0; + } + if (m_lockBackCount >= maxLockBacks) + return; + OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), + Logind::managerPath(), + QDBusConnection::systemBus()); + manager.LockSession(QString::number(authPtr->xdgSessionId)); + ++m_lockBackCount; + } + }); + }); + } + + void Display::unwatchUserSession(Auth *auth) { + if (!auth) + return; + + const auto watchers = auth->findChildren(QStringLiteral("logindSessionWatcher")); + for (auto *watcher : watchers) + watcher->deleteLater(); } - void Display::login(QLocalSocket *socket, - const QString &user, const QString &password, + void Display::login(const QString &user, const QString &password, const Session &session) { if (user == QLatin1String("dde")) { qWarning() << "Login attempt for user dde"; - emit loginFailed(socket, user); + emit loginFailed(user); return; } @@ -304,7 +374,7 @@ namespace DDM { auths.removeAll(auth); delete auth; } - Q_EMIT loginFailed(socket, user); + Q_EMIT loginFailed(user); return; } @@ -330,7 +400,7 @@ namespace DDM { QByteArray cookie; if (session.isSingleMode()) { auth->type = Treeland; - const int ownerPid = daemonApp->treelandConnector()->mainPid(); + const int ownerPid = daemonApp->treelandConnector()->treelandMainPid(); auth->tty = daemonApp->seatdControl()->createGroupVt(ownerPid, user, sessionId); if (auth->tty <= 0) { qCritical() << "Failed to allocate grouped VT for Treeland user session"; @@ -418,6 +488,8 @@ namespace DDM { connect(auth, &Auth::sessionFinished, this, [this, auth]() { qWarning() << "Session for user" << auth->user << "finished"; + unwatchUserSession(auth); + m_socketServer->removeUserSession(auth->user, auth->xdgSessionId); auths.removeAll(auth); daemonApp->displayManager()->RemoveSession(auth->sessionId); if (auth->type == Treeland) @@ -426,13 +498,15 @@ namespace DDM { }); daemonApp->displayManager()->AddSession(sessionId, name, user, auth->tty); daemonApp->displayManager()->setLastSession(sessionId); + m_socketServer->addUserSession(user, xdgSessionId); + watchUserSession(auth); if (auth->type == Treeland) activateSession(user, xdgSessionId); qInfo() << "Successfully logged in user" << user; } - void Display::logout([[maybe_unused]] QLocalSocket *socket, int id) { + void Display::logout(int id) { qDebug() << "Logout requested for session id" << id; // Do not kill the session leader process before // TerminateSession! Logind will only kill the session's @@ -447,57 +521,4 @@ namespace DDM { manager.TerminateSession(QString::number(id)); } - void Display::lock([[maybe_unused]] QLocalSocket *socket, int id) { - qDebug() << "Lock requested for session id" << id; - - OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), - Logind::managerPath(), - QDBusConnection::systemBus()); - manager.LockSession(QString::number(id)); - } - - void Display::unlock(QLocalSocket *socket, const QString &user, const QString &password) { - if (user == QLatin1String("dde")) { - emit loginFailed(socket, user); - return; - } - - qInfo() << "Start identify user" << user; - - // Only run password check - // - // No user process execution, so the auth can be thrown away - // immediately after use - Auth auth(this, user); - if (!auth.authenticate(password.toLocal8Bit())) { - Q_EMIT loginFailed(socket, user); - return; - } - - // Save last user - DaemonApp::instance()->displayManager()->setLastActivatedUser(user); - if (mainConfig.Users.RememberLastUser.get()) - stateConfig.Last.User.set(user); - else - stateConfig.Last.User.setDefault(); - stateConfig.save(); - - // Find the auth that started the session, which contains full informations - for (auto *auth : std::as_const(auths)) { - if (auth->user == user && auth->xdgSessionId > 0) { - OrgFreedesktopLogin1ManagerInterface manager(Logind::serviceName(), - Logind::managerPath(), - QDBusConnection::systemBus()); - manager.UnlockSession(QString::number(auth->xdgSessionId)); - if (auth->type == Treeland) - activateSession(user, auth->xdgSessionId); - else if (!daemonApp->seatdControl()->requestSwitchVt(auth->tty)) - qWarning() << "Failed to switch to session VT" << auth->tty << "for user" << user; - qInfo() << "Successfully identified user" << user; - return; - } - } - qWarning() << "No active session found for user" << user; - Q_EMIT loginFailed(socket, user); - } } diff --git a/src/daemon/Display.h b/src/daemon/Display.h index f341f10..0b7bb33 100644 --- a/src/daemon/Display.h +++ b/src/daemon/Display.h @@ -29,8 +29,6 @@ #include "Session.h" -class QLocalSocket; - namespace DDM { class Auth; class XorgDisplayServer; @@ -70,6 +68,8 @@ namespace DDM { * @param xdgSessionId Logind session ID */ void activateSession(const QString &user, int xdgSessionId); + void watchUserSession(Auth *auth); + void unwatchUserSession(Auth *auth); /** Seat name */ QString name{}; @@ -95,28 +95,18 @@ namespace DDM { */ void stop(); - /////////////////////////////////////////////////// - // Slots for socket to communicate with Treeland // - /////////////////////////////////////////////////// - - void connected(QLocalSocket *socket); - void login(QLocalSocket *socket, - const QString &user, + void connected(); + void login(const QString &user, const QString &password, const Session &session); - void logout(QLocalSocket *socket, int id); - void lock(QLocalSocket *socket, int id); - void unlock(QLocalSocket *socket, const QString &user, const QString &password); + void logout(int id); + void onTreelandLockStateChanged(bool locked); signals: /** Emitted when stop() */ void stopped(); - ///////////////////////////////////////////////////// - // Signals for socket to communicate with Treeland // - ///////////////////////////////////////////////////// - - void loginFailed(QLocalSocket *socket, const QString &user); + void loginFailed(const QString &user); private: /** Indicates whether the display is started */ @@ -130,6 +120,11 @@ namespace DDM { /** Socket server for communication with Treeland */ SocketServer *m_socketServer { nullptr }; + + int m_activeTreelandSessionId { 0 }; + bool m_treelandLocked { false }; + qint64 m_lockBackWindowStart { 0 }; + int m_lockBackCount { 0 }; }; } diff --git a/src/daemon/SocketServer.cpp b/src/daemon/SocketServer.cpp index a040b00..97adddb 100644 --- a/src/daemon/SocketServer.cpp +++ b/src/daemon/SocketServer.cpp @@ -20,17 +20,25 @@ #include "SocketServer.h" +#include "Auth.h" #include "DaemonApp.h" -#include "Messages.h" +#include "Display.h" #include "PowerManager.h" -#include "SocketWriter.h" #include "TreelandConnector.h" -#include "Utils.h" +#include #include +#include +#include + +static constexpr auto ddmRemoteSocketName = "org.deepin.dde.ddm.qro"; +static constexpr auto ddmRemoteSourceName = "DDMRemote"; namespace DDM { - SocketServer::SocketServer(QObject *parent) : QObject(parent) { + SocketServer::SocketServer(Display *display, QObject *parent) + : DDMRemoteSimpleSource(parent) + , m_display(display) + , m_powerManager(daemonApp->powerManager()) { } QString SocketServer::socketAddress() const { @@ -39,209 +47,141 @@ namespace DDM { return QString(); } - bool SocketServer::start(const QString &displayName) { + bool SocketServer::start() { // check if the server has been created already if (m_server) return false; - QString socketName = QStringLiteral("ddm-%1-%2").arg(displayName).arg(generateName(6)); - // log message qDebug() << "Socket server starting..."; - // create server m_server = new QLocalServer(this); - - // set server options m_server->setSocketOptions(QLocalServer::UserAccessOption); + if (!m_server->listen(QString::fromLatin1(ddmRemoteSocketName))) { + if (m_server->serverError() != QAbstractSocket::AddressInUseError + || !QLocalServer::removeServer(QString::fromLatin1(ddmRemoteSocketName)) + || !m_server->listen(QString::fromLatin1(ddmRemoteSocketName))) { + qCritical() << "Failed to start socket server."; + delete m_server; + m_server = nullptr; + return false; + } + } - // start listening - if (!m_server->listen(socketName)) { - // log message - qCritical() << "Failed to start socket server."; - - // return fail + m_host = new QRemoteObjectHost(this); + if (!m_host->enableRemoting(this, QString::fromLatin1(ddmRemoteSourceName))) { + qCritical() << "Failed to enable DDMRemote source."; + m_server->close(); + delete m_server; + m_server = nullptr; + delete m_host; + m_host = nullptr; return false; } + setHostName(daemonApp->hostName()); + const auto capabilities = m_powerManager->capabilities(); + setCanPowerOff(capabilities & Capability::PowerOff); + setCanReboot(capabilities & Capability::Reboot); + setCanSuspend(capabilities & Capability::Suspend); + setCanHibernate(capabilities & Capability::Hibernate); + setCanHybridSleep(capabilities & Capability::HybridSleep); - // log message qDebug() << "Socket server started."; - - // connect signals connect(m_server, &QLocalServer::newConnection, this, &SocketServer::newConnection); - - // return success return true; } void SocketServer::stop() { - // check flag if (!m_server) return; - // log message qDebug() << "Socket server stopping..."; - - // delete server + if (m_host) { + m_host->deleteLater(); + m_host = nullptr; + } + m_server->close(); m_server->deleteLater(); m_server = nullptr; - - // log message qDebug() << "Socket server stopped."; } void SocketServer::newConnection() { - // get pending connection - QLocalSocket *socket = m_server->nextPendingConnection(); + while (m_server && m_server->hasPendingConnections()) { + auto *socket = m_server->nextPendingConnection(); + if (!socket) + continue; - // connect signals - connect(socket, &QLocalSocket::readyRead, this, &SocketServer::readyRead); - connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); - connect(socket, &QLocalSocket::disconnected, this, [this, socket] { - emit disconnected(socket); - }); + connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); + m_host->addHostSideConnection(socket); + } } - void SocketServer::readyRead() { - QLocalSocket *socket = qobject_cast(sender()); - - // check socket - if (!socket) - return; + bool SocketServer::connectGreeter() { + qDebug() << "Message received from greeter: Connect"; + daemonApp->treelandConnector()->connect(); + m_display->connected(); + return true; + } - // input stream - QDataStream input(socket); - - // Qt's QLocalSocket::readyRead is not designed to be called at every socket.write(), - // so we need to use a loop to read all the signals. - while(socket->bytesAvailable()) { - // read message - quint32 message; - input >> message; - - switch (GreeterMessages(message)) { - case GreeterMessages::Connect: { - // log message - qDebug() << "Message received from greeter: Connect"; - - // Connect wayland socket - QString socketPath; - input >> socketPath; - daemonApp->treelandConnector()->connect(socketPath); - - // send capabilities - SocketWriter(socket) << quint32(DaemonMessages::Capabilities) << quint32(daemonApp->powerManager()->capabilities()); - - // send host name - SocketWriter(socket) << quint32(DaemonMessages::HostName) << daemonApp->hostName(); - - // emit signal - emit connected(socket); - } - break; - case GreeterMessages::Login: { - // log message - qDebug() << "Message received from greeter: Login"; - - // read username, pasword etc. - QString user, password, filename; - Session session; - input >> user >> password >> session; - - // emit signal - emit login(socket, user, password, session); - } - break; - case GreeterMessages::Logout: { - // log message - qDebug() << "Message received from greeter: Logout"; - // read username - int id; - input >> id; - // emit signal - emit logout(socket, id); - } - break; - case GreeterMessages::Lock : { - // log message - qDebug() << "Message received from greeter: Lock"; - int id; - - input >> id; - emit lock(socket, id); - } - break; - case GreeterMessages::Unlock : { - // log message - qDebug() << "Message received from greeter: Unlock"; - QString user; - QString password; - - input >> user >> password; - emit unlock(socket, user, password); - } - break; - case GreeterMessages::PowerOff: { - // log message - qDebug() << "Message received from greeter: PowerOff"; - - // power off - daemonApp->powerManager()->powerOff(); - } - break; - case GreeterMessages::Reboot: { - // log message - qDebug() << "Message received from greeter: Reboot"; - - // reboot - daemonApp->powerManager()->reboot(); - } - break; - case GreeterMessages::Suspend: { - // log message - qDebug() << "Message received from greeter: Suspend"; - - // suspend - daemonApp->powerManager()->suspend(); - } - break; - case GreeterMessages::Hibernate: { - // log message - qDebug() << "Message received from greeter: Hibernate"; - - // hibernate - daemonApp->powerManager()->hibernate(); - } - break; - case GreeterMessages::HybridSleep: { - // log message - qDebug() << "Message received from greeter: HybridSleep"; - // hybrid sleep - daemonApp->powerManager()->hybridSleep(); - } - break; - case GreeterMessages::BackToNormal: { - // log message - qDebug() << "Message received from greeter: Back to normal"; - // hybrid sleep - daemonApp->backToNormal(); - } - break; - default: { - // log message - qWarning() << "Unknown message" << message; - } - } + void SocketServer::replayUserSessions() { + for (Auth *auth : std::as_const(m_display->auths)) { + if (auth->sessionOpened) + addUserSession(auth->user, auth->xdgSessionId); } + } + + void SocketServer::addUserSession(const QString &user, int sessionId) { + if (sessionId > 0) + emit userSessionAdded(user, sessionId); + } + + void SocketServer::removeUserSession(const QString &user, int sessionId) { + if (sessionId > 0) + emit userSessionRemoved(user, sessionId); + } + + bool SocketServer::login(QString user, QString password, int sessionType, QString sessionFile) { + qDebug() << "Message received from greeter: Login"; + Session session(static_cast(sessionType), sessionFile); + m_display->login(user, password, session); + return true; + } + + bool SocketServer::logout(int id) { + qDebug() << "Message received from greeter: Logout"; + m_display->logout(id); + return true; + } + + bool SocketServer::powerOff() { + qDebug() << "Message received from greeter: PowerOff"; + m_powerManager->powerOff(); + return true; + } + + bool SocketServer::reboot() { + qDebug() << "Message received from greeter: Reboot"; + m_powerManager->reboot(); + return true; + } + bool SocketServer::suspend() { + qDebug() << "Message received from greeter: Suspend"; + m_powerManager->suspend(); + return true; } - void SocketServer::loginFailed(QLocalSocket *socket, const QString &user) { - SocketWriter(socket) << quint32(DaemonMessages::LoginFailed) << user; + bool SocketServer::hibernate() { + qDebug() << "Message received from greeter: Hibernate"; + m_powerManager->hibernate(); + return true; } - void SocketServer::informationMessage(QLocalSocket *socket, const QString &message) { - SocketWriter(socket) << quint32(DaemonMessages::InformationMessage) << message; + bool SocketServer::hybridSleep() { + qDebug() << "Message received from greeter: HybridSleep"; + m_powerManager->hybridSleep(); + return true; } } diff --git a/src/daemon/SocketServer.h b/src/daemon/SocketServer.h index e66382d..f781de3 100644 --- a/src/daemon/SocketServer.h +++ b/src/daemon/SocketServer.h @@ -21,6 +21,8 @@ #ifndef DDM_SOCKETSERVER_H #define DDM_SOCKETSERVER_H +#include "rep_ddmremote_source.h" + #include #include @@ -28,42 +30,45 @@ class QLocalServer; class QLocalSocket; +class QRemoteObjectHost; namespace DDM { - class SocketServer : public QObject { + class Display; + class PowerManager; + + class SocketServer : public DDMRemoteSimpleSource { Q_OBJECT Q_DISABLE_COPY(SocketServer) public: - explicit SocketServer(QObject *parent = 0); + explicit SocketServer(Display *display, QObject *parent = 0); - bool start(const QString &sddmName); + bool start(); void stop(); QString socketAddress() const; + void replayUserSessions(); + void addUserSession(const QString &user, int sessionId); + void removeUserSession(const QString &user, int sessionId); + private slots: void newConnection(); - void readyRead(); public slots: - void informationMessage(QLocalSocket *socket, const QString &message); - void loginFailed(QLocalSocket *socket, const QString &user); - - signals: - void login(QLocalSocket *socket, - const QString &user, const QString &password, - const Session &session); - void logout(QLocalSocket *socket, - int id); - void lock(QLocalSocket *socket, - int id); - void unlock(QLocalSocket *socket, - const QString &user, const QString &password); - void connected(QLocalSocket *socket); - void disconnected(QLocalSocket *socket); + bool connectGreeter() override; + bool login(QString user, QString password, int sessionType, QString sessionFile) override; + bool logout(int id) override; + bool powerOff() override; + bool reboot() override; + bool suspend() override; + bool hibernate() override; + bool hybridSleep() override; private: QLocalServer *m_server { nullptr }; + QRemoteObjectHost *m_host { nullptr }; + Display *m_display { nullptr }; + PowerManager *m_powerManager { nullptr }; }; } diff --git a/src/daemon/TreelandConnector.cpp b/src/daemon/TreelandConnector.cpp index 49b9ba1..5bcd349 100644 --- a/src/daemon/TreelandConnector.cpp +++ b/src/daemon/TreelandConnector.cpp @@ -2,23 +2,17 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "TreelandConnector.h" - -#include "treeland-ddm-v1.h" +#include "rep_treelandremote_replica.h" #include #include #include #include #include -#include - -#include -#include +#include +#include -#include #include -#include -#include namespace DDM { @@ -28,6 +22,9 @@ static constexpr auto systemdManagerInterface = "org.freedesktop.systemd1.Manage static constexpr auto systemdPropertiesInterface = "org.freedesktop.DBus.Properties"; static constexpr auto systemdServiceInterface = "org.freedesktop.systemd1.Service"; static constexpr auto treelandUnit = "treeland.service"; +static constexpr auto treelandRemoteSocketName = "org.deepin.dde.treeland.qro"; + +// TreelandConnector TreelandConnector::TreelandConnector(QObject *parent) : QObject(parent) { @@ -38,10 +35,73 @@ TreelandConnector::~TreelandConnector() { } bool TreelandConnector::isConnected() { - return m_ddm; + return m_remoteReplica && m_remoteReplica->state() == QRemoteObjectReplica::Valid; } -int TreelandConnector::mainPid() { +void TreelandConnector::connect() { + ensureRemote(); +} + +void TreelandConnector::disconnect() { + m_remoteReplica.reset(); + if (m_remoteSocket) + m_remoteSocket->disconnectFromServer(); + if (auto *socket = m_remoteSocket.release()) + socket->deleteLater(); + m_remoteNode.reset(); +} + +bool TreelandConnector::ensureRemote() { + if (isConnected()) + return true; + + if (!m_remoteNode) { + m_remoteNode.reset(new QRemoteObjectNode); + } + + if (!m_remoteSocket) { + m_remoteSocket.reset(new QLocalSocket(this)); + QObject::connect(m_remoteSocket.get(), &QLocalSocket::disconnected, this, [this] { + m_remoteReplica.reset(); + if (auto *socket = m_remoteSocket.release()) + socket->deleteLater(); + m_remoteNode.reset(); + }); + + m_remoteSocket->connectToServer(QString::fromLatin1(treelandRemoteSocketName)); + if (!m_remoteSocket->waitForConnected(3000)) { + qWarning() << "Failed to connect Treeland remote socket:" + << m_remoteSocket->errorString(); + m_remoteSocket.reset(); + m_remoteNode.reset(); + return false; + } + + m_remoteNode->addClientSideConnection(m_remoteSocket.get()); + } + + if (!m_remoteReplica) { + m_remoteReplica.reset(m_remoteNode->acquire()); + QObject::connect(m_remoteReplica.get(), &QRemoteObjectReplica::stateChanged, this, + [this](QRemoteObjectReplica::State state, QRemoteObjectReplica::State oldState) { + qInfo() << "Treeland remote replica state changed from" << oldState << "to" << state; + }); + QObject::connect(m_remoteReplica.get(), &TreelandRemoteReplica::lockStateChanged, this, + [this](bool locked) { + qDebug() << "Treeland lock state changed:" << locked; + Q_EMIT lockStateChanged(locked); + }); + if (!m_remoteReplica->waitForSource(3000)) { + qWarning() << "Timed out waiting for Treeland remote source"; + m_remoteReplica.reset(); + return false; + } + } + + return isConnected(); +} + +int TreelandConnector::treelandMainPid() const { QDBusInterface systemd(systemdService, systemdPath, systemdManagerInterface, @@ -75,107 +135,36 @@ int TreelandConnector::mainPid() { return static_cast(pid); } -void TreelandConnector::setPrivateObject(struct treeland_ddm_v1 *ddm) { - m_ddm = ddm; -} - -static void switchToVt([[maybe_unused]] void *data, - [[maybe_unused]] struct treeland_ddm_v1 *ddm, - int32_t vtnr) { - qWarning("Ignoring deprecated treeland switch_to_vt request for VT %d; wlroots/libseat handles VT switching directly", vtnr); -} - -static void acquireVt([[maybe_unused]] void *data, - [[maybe_unused]] struct treeland_ddm_v1 *ddm, - [[maybe_unused]] int32_t vtnr) { -} +// Request wrapper -const struct treeland_ddm_v1_listener treelandDDMListener { - .switch_to_vt = switchToVt, - .acquire_vt = acquireVt, -}; - -static void registerGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - if (strcmp(interface, "treeland_ddm_v1") == 0) { - auto connector = static_cast(data); - auto ddm = static_cast( - wl_registry_bind(registry, name, &treeland_ddm_v1_interface, version) - ); - treeland_ddm_v1_add_listener(ddm, &treelandDDMListener, connector); - connector->setPrivateObject(ddm); - qDebug("Connected to treeland_ddm global object"); +void TreelandConnector::switchToGreeter() { + if (!ensureRemote()) { + qWarning("Treeland is not connected when trying to call switchToGreeter"); + return; } -} -static void removeGlobal([[maybe_unused]] void *data, - [[maybe_unused]] struct wl_registry *registry, - [[maybe_unused]] uint32_t name) { + qDebug("Calling treeland switchToGreeter"); + m_remoteReplica->switchToGreeter(); } -const struct wl_registry_listener registryListener { - .global = registerGlobal, - .global_remove = removeGlobal, -}; - -void TreelandConnector::connect(const QString &socketPath) { - disconnect(); - - m_display = wl_display_connect(qPrintable(socketPath)); - if (m_display == nullptr) { - qWarning("Failed to connect to Treeland Wayland socket %s", qPrintable(socketPath)); +void TreelandConnector::switchToUser(const QString &username) { + if (!ensureRemote()) { + qWarning("Treeland is not connected when trying to call switchToUser"); return; } - auto registry = wl_display_get_registry(m_display); - - wl_registry_add_listener(registry, ®istryListener, this); - wl_display_roundtrip(m_display); - - while (wl_display_dispatch_pending(m_display) > 0) { - } - wl_display_flush(m_display); - m_notifier = new QSocketNotifier(wl_display_get_fd(m_display), QSocketNotifier::Read, this); - QObject::connect(m_notifier, &QSocketNotifier::activated, this, [this] { - if (wl_display_dispatch(m_display) == -1 || wl_display_flush(m_display) == -1) { - if (errno != EAGAIN) { - qWarning("Treeland connection lost!"); - disconnect(); - } - } - }); + qDebug("Calling treeland switchToUser: user=%s", qPrintable(username)); + m_remoteReplica->switchToUser(username); } -void TreelandConnector::disconnect() { - if (m_notifier) { - m_notifier->setEnabled(false); - delete m_notifier; - m_notifier = nullptr; - } - if (m_display) { - wl_display_disconnect(m_display); - m_display = nullptr; - } - m_ddm = nullptr; -} - -void TreelandConnector::switchToGreeter() { - if (isConnected()) { - qDebug("Calling treeland switch_to_greeter"); - treeland_ddm_v1_switch_to_greeter(m_ddm); - wl_display_flush(m_display); - } else { - qWarning("Treeland is not connected when trying to call switchToGreeter"); +void TreelandConnector::lock() { + if (!ensureRemote()) { + qWarning("Treeland is not connected when trying to call lock"); + return; } -} -void TreelandConnector::switchToUser(const QString &username) { - if (isConnected()) { - qDebug("Calling treeland switch_to_user: user=%s", qPrintable(username)); - treeland_ddm_v1_switch_to_user(m_ddm, qPrintable(username)); - wl_display_flush(m_display); - } else { - qWarning("Treeland is not connected when trying to call switchToUser"); - } + qDebug("Calling treeland lock"); + m_remoteReplica->lock(); } } diff --git a/src/daemon/TreelandConnector.h b/src/daemon/TreelandConnector.h index 7261e1c..d7e3f77 100644 --- a/src/daemon/TreelandConnector.h +++ b/src/daemon/TreelandConnector.h @@ -1,12 +1,15 @@ // Copyright (C) 2025-2026 UnionTech Software Technology Co., Ltd. // SPDX-License-Identifier: GPL-2.0-or-later +#pragma once + #include -#include -#include -struct wl_display; -struct treeland_ddm_v1; +#include + +class QLocalSocket; +class QRemoteObjectNode; +class TreelandRemoteReplica; namespace DDM { class TreelandConnector : public QObject { @@ -15,16 +18,21 @@ class TreelandConnector : public QObject { explicit TreelandConnector(QObject *parent = nullptr); ~TreelandConnector(); bool isConnected(); - int mainPid(); - void setPrivateObject(struct treeland_ddm_v1 *ddm); - void connect(const QString &socketPath); + int treelandMainPid() const; + void connect(); void disconnect(); + void switchToGreeter(); void switchToUser(const QString &username); + void lock(); +Q_SIGNALS: + void lockStateChanged(bool locked); private: - struct wl_display *m_display { nullptr }; - QSocketNotifier *m_notifier { nullptr }; - struct treeland_ddm_v1 *m_ddm { nullptr }; + bool ensureRemote(); + + std::unique_ptr m_remoteSocket; + std::unique_ptr m_remoteNode; + std::unique_ptr m_remoteReplica; }; } diff --git a/src/daemon/TreelandDisplayServer.cpp b/src/daemon/TreelandDisplayServer.cpp index 6d1c5bd..a5f8e6e 100644 --- a/src/daemon/TreelandDisplayServer.cpp +++ b/src/daemon/TreelandDisplayServer.cpp @@ -2,18 +2,15 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "TreelandDisplayServer.h" -#include "Messages.h" #include "SocketServer.h" -#include "SocketWriter.h" #include "Display.h" +#include "DaemonApp.h" +#include "TreelandConnector.h" #include #include #include #include -#include -#include -#include #include #include @@ -25,16 +22,6 @@ using namespace DDM; TreelandDisplayServer::TreelandDisplayServer(SocketServer *socketServer, Display *parent) : QObject(parent) , m_socketServer(socketServer) { - connect(m_socketServer, &SocketServer::connected, this, [this, parent](QLocalSocket *socket) { - m_greeterSockets << socket; - qWarning("Treeland greeter socket connected: socket=%p total=%lld", - socket, static_cast(m_greeterSockets.size())); - }); - connect(m_socketServer, &SocketServer::disconnected, this, [this](QLocalSocket *socket) { - m_greeterSockets.removeOne(socket); - qWarning("Treeland greeter socket disconnected: socket=%p total=%lld", - socket, static_cast(m_greeterSockets.size())); - }); } TreelandDisplayServer::~TreelandDisplayServer() { @@ -76,22 +63,14 @@ void TreelandDisplayServer::stop() { } void TreelandDisplayServer::activateUser(const QString &user, int xdgSessionId) { - qDebug("Send greeter activation: user=%s xdgSessionId=%d sockets=%lld", - qPrintable(user), xdgSessionId, static_cast(m_greeterSockets.size())); - for (auto greeter : m_greeterSockets) { - if (user == "dde") { - qDebug("Sending SwitchToGreeter to socket=%p", greeter); - SocketWriter(greeter) << quint32(DaemonMessages::SwitchToGreeter); - } - - qDebug("Sending UserActivateMessage to socket=%p user=%s xdgSessionId=%d", - greeter, qPrintable(user), xdgSessionId); - SocketWriter(greeter) << quint32(DaemonMessages::UserActivateMessage) << user << xdgSessionId; - } + qDebug("Send greeter activation: user=%s xdgSessionId=%d", + qPrintable(user), xdgSessionId); + if (user == "dde") + daemonApp->treelandConnector()->switchToGreeter(); + else + daemonApp->treelandConnector()->switchToUser(user); } void TreelandDisplayServer::onLoginFailed(const QString &user) { - for (auto greeter : m_greeterSockets) { - SocketWriter(greeter) << quint32(DaemonMessages::LoginFailed) << user; - } + m_socketServer->loginFailed(user); } diff --git a/src/daemon/TreelandDisplayServer.h b/src/daemon/TreelandDisplayServer.h index e51eb96..fbadc9f 100644 --- a/src/daemon/TreelandDisplayServer.h +++ b/src/daemon/TreelandDisplayServer.h @@ -4,9 +4,6 @@ #include #include -class QLocalSocket; -class QLocalServer; - namespace DDM { class Display; class SocketServer; @@ -26,7 +23,6 @@ namespace DDM { private: SocketServer *m_socketServer; - QList m_greeterSockets; bool m_started{ false }; }; } diff --git a/src/daemon/ddmremote.rep b/src/daemon/ddmremote.rep new file mode 100644 index 0000000..0bcd93b --- /dev/null +++ b/src/daemon/ddmremote.rep @@ -0,0 +1,23 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: GPL-2.0-or-later + +class DDMRemote { + PROP(QString hostName SOURCEONLYSETTER) + PROP(bool canPowerOff=false SOURCEONLYSETTER) + PROP(bool canReboot=false SOURCEONLYSETTER) + PROP(bool canSuspend=false SOURCEONLYSETTER) + PROP(bool canHibernate=false SOURCEONLYSETTER) + PROP(bool canHybridSleep=false SOURCEONLYSETTER) + SIGNAL(loginFailed(QString user)) + SIGNAL(informationMessage(QString message)) + SIGNAL(userSessionAdded(QString user, int sessionId)) + SIGNAL(userSessionRemoved(QString user, int sessionId)) + SLOT(bool connectGreeter()) + SLOT(bool login(QString user, QString password, int sessionType, QString sessionFile)) + SLOT(bool logout(int id)) + SLOT(bool powerOff()) + SLOT(bool reboot()) + SLOT(bool suspend()) + SLOT(bool hibernate()) + SLOT(bool hybridSleep()) +}; diff --git a/src/daemon/treelandremote.rep b/src/daemon/treelandremote.rep new file mode 100644 index 0000000..8ebdc6a --- /dev/null +++ b/src/daemon/treelandremote.rep @@ -0,0 +1,9 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: GPL-2.0-or-later + +class TreelandRemote { + SLOT(void switchToGreeter()); + SLOT(void switchToUser(QString username)); + SLOT(void lock()); + SIGNAL(lockStateChanged(bool locked)); +};