diff --git a/src/core/lockscreen.cpp b/src/core/lockscreen.cpp index ff6e9693f..482c3680b 100644 --- a/src/core/lockscreen.cpp +++ b/src/core/lockscreen.cpp @@ -8,6 +8,7 @@ #include "seat/helper.h" #include "utils/cmdline.h" #include "common/treelandlogging.h" +#include "greeter/greeterproxy.h" #ifdef EXT_SESSION_LOCK_V1 #include @@ -20,9 +21,10 @@ WAYLIB_SERVER_USE_NAMESPACE -LockScreen::LockScreen(ILockScreen *impl, SurfaceContainer *parent) +LockScreen::LockScreen(ILockScreen *impl, SurfaceContainer *parent, GreeterProxy *greeterProxy) : SurfaceContainer(parent) , m_impl(impl) + , m_greeterProxy(greeterProxy) , m_delayTimer(std::make_unique(new QTimer)) { connect(m_delayTimer.get(), &QTimer::timeout, this, &LockScreen::unlock); @@ -40,10 +42,8 @@ void LockScreen::lock() setVisible(true); - for (const auto &[k, v] : m_components) { - v->setProperty("currentMode", static_cast(LockScreen::CurrentMode::Lock)); - QMetaObject::invokeMethod(v.get(), "start", QVariant::fromValue(true)); - } + if (!m_greeterProxy->isLocked()) + m_greeterProxy->lock(); } void LockScreen::shutdown() @@ -61,10 +61,8 @@ void LockScreen::shutdown() setVisible(true); - for (const auto &[k, v] : m_components) { - v->setProperty("currentMode", static_cast(LockScreen::CurrentMode::Shutdown)); - QMetaObject::invokeMethod(v.get(), "start", QVariant::fromValue(true)); - } + if (!m_greeterProxy->showShutdownView()) + m_greeterProxy->setShowShutdownView(true); } void LockScreen::switchUser() @@ -82,10 +80,7 @@ void LockScreen::switchUser() setVisible(true); - for (const auto &[k, v] : m_components) { - v->setProperty("currentMode", static_cast(LockScreen::CurrentMode::SwitchUser)); - QMetaObject::invokeMethod(v.get(), "start", QVariant::fromValue(true)); - } + Q_EMIT m_greeterProxy->switchUser(); } void LockScreen::addOutput(Output *output) @@ -105,10 +100,6 @@ void LockScreen::addOutput(Output *output) auto *item = m_impl->createLockScreen(output, this); - if (isVisible()) { - QMetaObject::invokeMethod(item, "start", QVariant::fromValue(false)); - } - connect(item, SIGNAL(animationPlayed()), this, SLOT(onAnimationPlayed())); connect(item, SIGNAL(animationPlayFinished()), this, SLOT(onAnimationPlayFinished())); @@ -314,10 +305,7 @@ void LockScreen::onExternalLockAbandoned() { } m_fallbackItems.clear(); - for (const auto &[k, v] : m_components) { - v->setProperty("currentMode", static_cast(LockScreen::CurrentMode::Lock)); - QMetaObject::invokeMethod(v.get(), "start", QVariant::fromValue(true)); - } + m_greeterProxy->lock(); } void LockScreen::createFallbackItem(WOutputItem *outputItem) diff --git a/src/core/lockscreen.h b/src/core/lockscreen.h index 253702870..f73931938 100644 --- a/src/core/lockscreen.h +++ b/src/core/lockscreen.h @@ -8,6 +8,7 @@ #include #include +class GreeterProxy; class QTimer; class ILockScreen; @@ -35,7 +36,7 @@ class LockScreen : public SurfaceContainer }; Q_ENUM(CurrentMode) - explicit LockScreen(ILockScreen *impl, SurfaceContainer *parent); + explicit LockScreen(ILockScreen *impl, SurfaceContainer *parent, GreeterProxy *greeterProxy); bool available() const; bool isLocked() const; @@ -71,6 +72,7 @@ private Q_SLOTS: private: ILockScreen *m_impl{ nullptr }; + GreeterProxy *m_greeterProxy{ nullptr }; std::map> m_components; std::unique_ptr m_delayTimer; #ifdef EXT_SESSION_LOCK_V1 diff --git a/src/core/qml/PrimaryOutput.qml b/src/core/qml/PrimaryOutput.qml index 1b89a7a7c..e0d9bacdb 100644 --- a/src/core/qml/PrimaryOutput.qml +++ b/src/core/qml/PrimaryOutput.qml @@ -120,44 +120,16 @@ OutputItem { target: wallpaper scale: 1.4 } - }, - State { - name: "ScaleWithoutAnimation" - PropertyChanges { - target: wallpaper - scale: 1.4 - } } ] - transitions: [ - Transition { - from: "*" - to: "Normal" - PropertyAnimation { - property: "scale" - duration: 1000 - easing.type: Easing.OutExpo - } - }, - Transition { - from: "*" - to: "Scale" - PropertyAnimation { - property: "scale" - duration: 1000 - easing.type: Easing.OutExpo - } - }, - Transition { - from: "*" - to: "ScaleWithoutAnimation" - PropertyAnimation { - property: "scale" - duration: 0 - } + Behavior on scale { + enabled: GreeterProxy.showAnimation + NumberAnimation { + duration: 1000 + easing.type: Easing.OutExpo } - ] + } } } diff --git a/src/greeter/greeterproxy.cpp b/src/greeter/greeterproxy.cpp index f745c8599..565a6f1fa 100644 --- a/src/greeter/greeterproxy.cpp +++ b/src/greeter/greeterproxy.cpp @@ -1,294 +1,252 @@ -/*************************************************************************** - * Copyright (c) 2015 Pier Luigi Fiorini - * Copyright (c) 2013 Abdurrahman AVCI - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - ***************************************************************************/ +// Copyright (C) 2025 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "greeterproxy.h" -#include "DDMDisplayManager.h" +// Treeland #include "greeter/sessionmodel.h" #include "greeter/usermodel.h" #include "seat/helper.h" #include "session/session.h" #include "common/treelandlogging.h" +#include "core/lockscreen.h" -#include -#include +// DDM #include #include #include #include -#include -#include -#include - -#include +// Qt #include #include -#include -#include -#include #include #include #include +#include +#include +#include +#include +// Waylib #include +// System +#include +#include +#include + using namespace DDM; -class GreeterProxyPrivate -{ -public: - SessionModel *sessionModel{ nullptr }; - UserModel *userModel{ nullptr }; - QLocalSocket *socket{ nullptr }; - org::deepin::DisplayManager *ddmDisplayManager{ nullptr }; - QDBusUnixFileDescriptor authFd; - QString hostName; - bool canPowerOff{ false }; - bool canReboot{ false }; - bool canSuspend{ false }; - bool canHibernate{ false }; - bool canHybridSleep{ false }; - bool isLocked{ false }; - bool isLoggedIn{ false }; -}; +///////////////////// +// Local Functions // +///////////////////// -GreeterProxy::GreeterProxy(QObject *parent) - : QObject(parent) - , d(new GreeterProxyPrivate()) +static bool localValidation(const QString &user, const QString &password) { - qDBusRegisterMetaType(); - qDBusRegisterMetaType>(); - - d->socket = new QLocalSocket(this); - - // connect signals - connect(d->socket, &QLocalSocket::connected, this, &GreeterProxy::connected); - connect(d->socket, &QLocalSocket::disconnected, this, &GreeterProxy::disconnected); - connect(d->socket, &QLocalSocket::readyRead, this, &GreeterProxy::readyRead); - connect(d->socket, &QLocalSocket::errorOccurred, this, &GreeterProxy::error); - - connect(this, &GreeterProxy::loginSucceeded, this, [this]([[maybe_unused]] QString user) { - d->isLoggedIn = true; - Q_EMIT isLoggedInChanged(); - }); + auto utf8Password = password.toUtf8(); + struct pam_conv conv = { + []([[maybe_unused]] int num_msg, + [[maybe_unused]] const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr) { + // pam uses free, we must malloc + auto *reply = static_cast(malloc(sizeof(pam_response))); + reply->resp = strdup(static_cast(appdata_ptr)); // Send password to PAM + reply->resp_retcode = 0; + *resp = reply; + return PAM_SUCCESS; + }, + static_cast(utf8Password.data()), + }; - d->ddmDisplayManager = new org::deepin::DisplayManager("org.deepin.DisplayManager", - "/org/deepin/DisplayManager", - QDBusConnection::systemBus()); - connect(d->ddmDisplayManager, - &org::deepin::DisplayManager::AuthInfoChanged, - this, - &GreeterProxy::updateAuthSocket); + pam_handle_t *pamh = nullptr; - updateAuthSocket(); -} + int retval = pam_start("login", user.toUtf8().data(), &conv, &pamh); + if (retval != PAM_SUCCESS) { + return false; + } -GreeterProxy::~GreeterProxy() -{ - delete d; -} + retval = pam_authenticate(pamh, 0); + pam_end(pamh, retval); -const QString &GreeterProxy::hostName() const -{ - return d->hostName; + return retval == PAM_SUCCESS; } -SessionModel *GreeterProxy::sessionModel() const +static inline UserModel *userModel() { - return d->sessionModel; + return Helper::instance()->userModel(); } -void GreeterProxy::setSessionModel(SessionModel *model) +static inline SessionModel *sessionModel() { - d->sessionModel = model; - Q_EMIT sessionModelChanged(model); + return Helper::instance()->sessionModel(); } -UserModel *GreeterProxy::userModel() const -{ - return d->userModel; -} +///////////////////////////////// +// Constructor & Deconstructor // +///////////////////////////////// -void GreeterProxy::setUserModel(UserModel *model) +GreeterProxy::GreeterProxy(QObject *parent) + : QObject(parent) { - d->userModel = model; - Q_EMIT userModelChanged(model); -} + m_socket = new QLocalSocket(this); -bool GreeterProxy::isLocked() const -{ - return d->isLocked; -} + // connect signals + connect(m_socket, &QLocalSocket::connected, this, &GreeterProxy::connected); + connect(m_socket, &QLocalSocket::disconnected, this, &GreeterProxy::disconnected); + connect(m_socket, &QLocalSocket::readyRead, this, &GreeterProxy::readyRead); + connect(m_socket, &QLocalSocket::errorOccurred, this, &GreeterProxy::error); -bool GreeterProxy::isLoggedIn() const -{ - return d->isLoggedIn; -} + auto conn = QDBusConnection::systemBus(); + conn.connect(Logind::serviceName(), + Logind::managerPath(), + Logind::managerIfaceName(), + "SessionNew", + this, + SLOT(onSessionNew(QString, QDBusObjectPath))); + conn.connect(Logind::serviceName(), + Logind::managerPath(), + Logind::managerIfaceName(), + "SessionRemoved", + this, + SLOT(onSessionRemoved(QString, QDBusObjectPath))); + conn.connect("org.deepin.DisplayManager", + "/org/deepin/DisplayManager", + "org.deepin.DisplayManager", + "AuthInfoChanged", + this, + SLOT(updateAuthSocket())); -bool GreeterProxy::canPowerOff() const -{ - return d->canPowerOff; + updateAuthSocket(); } -bool GreeterProxy::canReboot() const -{ - return d->canReboot; -} +GreeterProxy::~GreeterProxy() { } -bool GreeterProxy::canSuspend() const -{ - return d->canSuspend; -} +//////////////////////// +// Properties setters // +//////////////////////// -bool GreeterProxy::canHibernate() const -{ - return d->canHibernate; +void GreeterProxy::setShowShutdownView(bool show) { + if (m_showShutdownView != show) { + m_showShutdownView = show; + Q_EMIT showShutdownViewChanged(show); + } } -bool GreeterProxy::canHybridSleep() const -{ - return d->canHybridSleep; +void GreeterProxy::setLock(bool isLocked) +{ + if (isLocked && !m_isLocked) { + m_isLocked = true; + if (m_lockScreen && !m_lockScreen->isVisible()) + m_lockScreen->lock(); + Q_EMIT lockChanged(true); + } else if (!isLocked && m_isLocked) { + m_failedAttempts = 0; + Q_EMIT failedAttemptsChanged(0); + m_isLocked = false; + if (m_lockScreen && m_lockScreen->isVisible()) + Q_EMIT m_lockScreen->unlock(); + Q_EMIT lockChanged(false); + } + if (m_showShutdownView) + setShowShutdownView(false); } -bool GreeterProxy::isConnected() const -{ - return d->socket->state() == QLocalSocket::ConnectedState; -} +/////////// +// Slots // +/////////// void GreeterProxy::powerOff() { - SocketWriter(d->socket) << quint32(GreeterMessages::PowerOff); + SocketWriter(m_socket) << quint32(GreeterMessages::PowerOff); } void GreeterProxy::reboot() { - SocketWriter(d->socket) << quint32(GreeterMessages::Reboot); + SocketWriter(m_socket) << quint32(GreeterMessages::Reboot); } void GreeterProxy::suspend() { - SocketWriter(d->socket) << quint32(GreeterMessages::Suspend); + SocketWriter(m_socket) << quint32(GreeterMessages::Suspend); } void GreeterProxy::hibernate() { - SocketWriter(d->socket) << quint32(GreeterMessages::Hibernate); + SocketWriter(m_socket) << quint32(GreeterMessages::Hibernate); } void GreeterProxy::hybridSleep() { - SocketWriter(d->socket) << quint32(GreeterMessages::HybridSleep); -} - -void GreeterProxy::init() -{ - auto conn = QDBusConnection::systemBus(); - conn.connect(Logind::serviceName(), - Logind::managerPath(), - Logind::managerIfaceName(), - "SessionNew", - this, - SLOT(onSessionNew(QString, QDBusObjectPath))); - conn.connect(Logind::serviceName(), - Logind::managerPath(), - Logind::managerIfaceName(), - "SessionRemoved", - this, - SLOT(onSessionRemoved(QString, QDBusObjectPath))); + SocketWriter(m_socket) << quint32(GreeterMessages::HybridSleep); } void GreeterProxy::login(const QString &user, const QString &password, const int sessionIndex) { - if (!d->socket->isValid()) { + if (!m_socket->isValid()) { qCDebug(treelandGreeter) << "Socket is not valid. Local password check."; if (localValidation(user, password)) { - Q_EMIT loginSucceeded(user); + setLock(false); } else { - Q_EMIT loginFailed(user); + Q_EMIT failedAttemptsChanged(++m_failedAttempts); } return; } - if (!d->sessionModel) { - qCCritical(treelandGreeter) << "Session model is not set."; - return; - } - // get model index - QModelIndex index = d->sessionModel->index(sessionIndex, 0); + QModelIndex index = sessionModel()->index(sessionIndex, 0); // send command to the daemon DDM::Session::Type type = - static_cast(d->sessionModel->data(index, SessionModel::TypeRole).toInt()); - QString name = d->sessionModel->data(index, SessionModel::FileRole).toString(); + static_cast(sessionModel()->data(index, SessionModel::TypeRole).toInt()); + QString name = sessionModel()->data(index, SessionModel::FileRole).toString(); + qCInfo(treelandGreeter) << "Logging user" << user << "in with" << type << "session" << name; DDM::Session session(type, name); - SocketWriter(d->socket) << quint32(GreeterMessages::Login) << user << password << session; + SocketWriter(m_socket) << quint32(GreeterMessages::Login) << user << password << session; } void GreeterProxy::unlock(const QString &user, const QString &password) { - if (!d->socket->isValid()) { + if (!m_socket->isValid()) { qCDebug(treelandGreeter) << "Socket is not valid. Local password check."; if (localValidation(user, password)) { - Q_EMIT loginSucceeded(user); + setLock(false); } else { - Q_EMIT loginFailed(user); + Q_EMIT failedAttemptsChanged(++m_failedAttempts); } return; } auto userInfo = userModel()->get(user); if (userInfo.isValid()) { - SocketWriter(d->socket) << quint32(GreeterMessages::Unlock) << user << password; + qCInfo(treelandGreeter) << "Unlocking user" << user; + SocketWriter(m_socket) << quint32(GreeterMessages::Unlock) << user << password; } } void GreeterProxy::logout() { - qCDebug(treelandGreeter) << "Logout."; - d->isLoggedIn = false; - Q_EMIT isLoggedInChanged(); auto session = Helper::instance()->sessionManager()->activeSession().lock(); - SocketWriter(d->socket) << quint32(GreeterMessages::Logout) << session->id(); + qCInfo(treelandGreeter) << "Logging user" << session->username() << "out with session id" << session->id(); + SocketWriter(m_socket) << quint32(GreeterMessages::Logout) << session->id(); } -void GreeterProxy::connected() +void GreeterProxy::lock() { - qCDebug(treelandGreeter) << "Connected to the daemon."; - - SocketWriter(d->socket) << quint32(GreeterMessages::Connect) - << Helper::instance()->sessionManager()->globalSession()->socket()->fullServerName(); -} - -void GreeterProxy::disconnected() -{ - qCDebug(treelandGreeter) << "Disconnected from the daemon."; - - Q_EMIT socketDisconnected(); + auto session = Helper::instance()->sessionManager()->activeSession().lock(); + if (!session || session->username() == "dde") { + qCInfo(treelandGreeter) << "Trying to lock when no user session active, show lockscreen directly."; + setLock(true); + return; + } + qCInfo(treelandGreeter) << "Locking user" << session->username() << "with session id" << session->id(); + SocketWriter(m_socket) << quint32(GreeterMessages::Lock) << session->id(); } -void GreeterProxy::error() -{ - qCCritical(treelandGreeter) << "Socket error: " << d->socket->errorString(); -} +////////////////////////////// +// Logind session listeners // +////////////////////////////// void GreeterProxy::onSessionNew(const QString &id, [[maybe_unused]] const QDBusObjectPath &path) { @@ -296,14 +254,12 @@ void GreeterProxy::onSessionNew(const QString &id, [[maybe_unused]] const QDBusO const char *session = sessionBa.constData(); char *username = nullptr; char *service = nullptr; - auto guard = std::unique_ptr>( - nullptr, - [username, service]([[maybe_unused]] void *) { - if (username) - free(username); - if (service) - free(service); - }); + auto guard = qScopeGuard([&]() { + if (username) + free(username); + if (service) + free(service); + }); if (sd_session_get_username(session, &username) < 0) { qCWarning(treelandGreeter) << "sd_session_get_username() failed for session id:" << id; return; @@ -318,25 +274,150 @@ void GreeterProxy::onSessionNew(const QString &id, [[maybe_unused]] const QDBusO qCInfo(treelandGreeter) << "New session added: id=" << id << ", user=" << user; userModel()->updateUserLoginState(user, true); // userLoggedIn signal is connected with Helper::updateActiveUserSession - Q_EMIT d->userModel->userLoggedIn(user, id.toInt()); - updateLocketState(); + Q_EMIT userModel()->userLoggedIn(user, id.toInt()); + + // Connect to Lock/Unlock signals + auto conn = QDBusConnection::systemBus(); + conn.connect(Logind::serviceName(), + path.path(), + Logind::sessionIfaceName(), + "Lock", + this, + SLOT(onSessionLock())); + conn.connect(Logind::serviceName(), + path.path(), + Logind::sessionIfaceName(), + "Unlock", + this, + SLOT(onSessionUnlock())); + + if (userModel()->currentUserName() == user) + setLock(false); + if (!m_hasActiveSession) { + m_hasActiveSession = true; + Q_EMIT hasActiveSessionChanged(true); + } } } void GreeterProxy::onSessionRemoved(const QString &id, [[maybe_unused]] const QDBusObjectPath &path) { + // Disconnect from Lock/Unlock signals, if any + auto conn = QDBusConnection::systemBus(); + conn.disconnect(Logind::serviceName(), + path.path(), + Logind::sessionIfaceName(), + "Lock", + this, + SLOT(onSessionLock())); + conn.disconnect(Logind::serviceName(), + path.path(), + Logind::sessionIfaceName(), + "Unlock", + this, + SLOT(onSessionUnlock())); + auto session = Helper::instance()->sessionManager()->sessionForId(id.toInt()); if (session) { - userModel()->updateUserLoginState(session->username(), false); - updateLocketState(); + QString username = session->username(); + qCInfo(treelandGreeter) << "Session removed: id=" << id << ", user=" << username; + if (Helper::instance()->sessionManager()->activeSession().lock() == session) + setLock(true); + userModel()->updateUserLoginState(username, false); Helper::instance()->sessionManager()->removeSession(session); } + + if (m_hasActiveSession && Helper::instance()->sessionManager()->sessions().isEmpty()) { + m_hasActiveSession = false; + Q_EMIT hasActiveSessionChanged(false); + } +} + +void GreeterProxy::onSessionLock() +{ + const QString path = message().path(); + QThreadPool::globalInstance()->start([this, path] { + OrgFreedesktopLogin1SessionInterface session("org.freedesktop.login1", + path, + QDBusConnection::systemBus()); + int id = session.id().toInt(); + qCInfo(treelandGreeter) << "Lock signal received for session id:" << id; + auto activeSession = Helper::instance()->sessionManager()->activeSession().lock(); + if (!activeSession) + qCWarning(treelandGreeter) + << "Lock signal received for non-exist session id:" << id << ", ignore."; + else if (activeSession->id() != id) + qCWarning(treelandGreeter) + << "Lock signal received for non-active session id:" << id << ", ignore."; + else + QMetaObject::invokeMethod(this, [this] { + setLock(true); + }); + }); +} + +void GreeterProxy::onSessionUnlock() +{ + const QString path = message().path(); + QThreadPool::globalInstance()->start([this, path] { + OrgFreedesktopLogin1SessionInterface session("org.freedesktop.login1", + path, + QDBusConnection::systemBus()); + int id = session.id().toInt(); + const QString username = session.name(); + qCInfo(treelandGreeter) << "Unlock signal received for session id:" << id; + auto activeSession = Helper::instance()->sessionManager()->activeSession().lock(); + if (!activeSession) { + qCWarning(treelandGreeter) + << "Unlock signal received for non-exist session id:" << id << ", ignore."; + } else if (activeSession->id() != id) { + qCWarning(treelandGreeter) + << "Unlock signal received for non-active session id:" << id << ", lock it back."; + QMetaObject::invokeMethod(this, [this, id] { + SocketWriter(m_socket) << quint32(GreeterMessages::Lock) << QString::number(id); + }); + } else { + QMetaObject::invokeMethod(this, [this] { + setLock(false); + }); + } + }); +} + +/////////////////////// +// DDM Communication // +/////////////////////// + +bool GreeterProxy::isConnected() const +{ + return m_socket->state() == QLocalSocket::ConnectedState; +} + +void GreeterProxy::connected() +{ + qCInfo(treelandGreeter) << "Connected to the ddm"; + + SocketWriter(m_socket) + << quint32(GreeterMessages::Connect) + << Helper::instance()->sessionManager()->globalSession()->socket()->fullServerName(); +} + +void GreeterProxy::disconnected() +{ + qCWarning(treelandGreeter) << "Disconnected from the ddm"; + + Q_EMIT socketDisconnected(); +} + +void GreeterProxy::error() +{ + qCCritical(treelandGreeter) << "Socket error: " << m_socket->errorString(); } void GreeterProxy::readyRead() { // input stream - QDataStream input(d->socket); + QDataStream input(m_socket); while (input.device()->bytesAvailable()) { // read message @@ -353,35 +434,27 @@ void GreeterProxy::readyRead() input >> capabilities; // parse capabilities - d->canPowerOff = capabilities & Capability::PowerOff; - d->canReboot = capabilities & Capability::Reboot; - d->canSuspend = capabilities & Capability::Suspend; - d->canHibernate = capabilities & Capability::Hibernate; - d->canHybridSleep = capabilities & Capability::HybridSleep; + m_canPowerOff = capabilities & Capability::PowerOff; + m_canReboot = capabilities & Capability::Reboot; + m_canSuspend = capabilities & Capability::Suspend; + m_canHibernate = capabilities & Capability::Hibernate; + m_canHybridSleep = capabilities & Capability::HybridSleep; // Q_EMIT signals - Q_EMIT canPowerOffChanged(d->canPowerOff); - Q_EMIT canRebootChanged(d->canReboot); - Q_EMIT canSuspendChanged(d->canSuspend); - Q_EMIT canHibernateChanged(d->canHibernate); - Q_EMIT canHybridSleepChanged(d->canHybridSleep); + Q_EMIT canPowerOffChanged(m_canPowerOff); + Q_EMIT canRebootChanged(m_canReboot); + Q_EMIT canSuspendChanged(m_canSuspend); + Q_EMIT canHibernateChanged(m_canHibernate); + Q_EMIT canHybridSleepChanged(m_canHybridSleep); } break; case DaemonMessages::HostName: { qCDebug(treelandGreeter) << "Message received from daemon: HostName"; // read host name - input >> d->hostName; + input >> m_hostName; // Q_EMIT signal - Q_EMIT hostNameChanged(d->hostName); - } break; - case DaemonMessages::LoginSucceeded: { - QString user; - input >> user; - - qCDebug(treelandGreeter) << "Message received from daemon: LoginSucceeded:" << user; - - Q_EMIT loginSucceeded(user); + Q_EMIT hostNameChanged(m_hostName); } break; case DaemonMessages::LoginFailed: { QString user; @@ -389,7 +462,7 @@ void GreeterProxy::readyRead() qCDebug(treelandGreeter) << "Message received from daemon: LoginFailed" << user; - Q_EMIT loginFailed(user); + Q_EMIT failedAttemptsChanged(++m_failedAttempts); } break; case DaemonMessages::InformationMessage: { QString message; @@ -400,8 +473,7 @@ void GreeterProxy::readyRead() } break; case DaemonMessages::SwitchToGreeter: { qCInfo(treelandGreeter) << "switch to greeter"; - Helper::instance()->showLockScreen(); - Q_EMIT switchToGreeter(); + lock(); } break; case DaemonMessages::UserActivateMessage: { QString user; @@ -409,14 +481,13 @@ void GreeterProxy::readyRead() input >> user >> sessionId; // NOTE: maybe DDM will active dde user. - if (!d->userModel->getUser(user)) { + if (!userModel()->getUser(user)) { qCInfo(treelandGreeter) << "activate user, but switch to greeter"; - Helper::instance()->showLockScreen(); - Q_EMIT switchToGreeter(); + lock(); break; } - d->userModel->setCurrentUserName(user); + userModel()->setCurrentUserName(user); qCInfo(treelandGreeter) << "activate successfully: " << user << ", XDG_SESSION_ID: " << sessionId; } break; @@ -427,10 +498,10 @@ void GreeterProxy::readyRead() // This will happen after a crash recovery of treeland qCInfo(treelandGreeter) << "User " << user << " is already logged in"; - auto userPtr = d->userModel->getUser(user); + auto userPtr = userModel()->getUser(user); if (userPtr) { - userPtr.get()->setLogined(true); - Q_EMIT d->userModel->userLoggedIn(user, sessionId); + userModel()->updateUserLoginState(user, true); + Q_EMIT userModel()->userLoggedIn(user, sessionId); } else { qCWarning(treelandGreeter) << "User " << user << " logged in but not found"; } @@ -442,60 +513,24 @@ void GreeterProxy::readyRead() } } -bool GreeterProxy::localValidation(const QString &user, const QString &password) const -{ - auto utf8Password = password.toUtf8(); - struct pam_conv conv = { - []([[maybe_unused]] int num_msg, - [[maybe_unused]] const struct pam_message **msg, - struct pam_response **resp, - void *appdata_ptr) { - // pam uses free, we must malloc - auto *reply = static_cast(malloc(sizeof(pam_response))); - reply->resp = strdup(static_cast(appdata_ptr)); // 将密码传递给PAM - reply->resp_retcode = 0; - *resp = reply; - return PAM_SUCCESS; - }, - static_cast(utf8Password.data()), - }; - - pam_handle_t *pamh = nullptr; - - int retval = pam_start("login", user.toUtf8().data(), &conv, &pamh); - if (retval != PAM_SUCCESS) { - return false; - } - - retval = pam_authenticate(pamh, 0); - pam_end(pamh, retval); - - return retval == PAM_SUCCESS; -} - void GreeterProxy::updateAuthSocket() { - const QString &socket = d->ddmDisplayManager->AuthInfo(); - - if (d->socket->state() == QLocalSocket::ConnectedState) { - d->socket->disconnectFromServer(); - } - - d->socket->connectToServer(socket); -} - -void GreeterProxy::updateLocketState() -{ - if (!d->userModel) - return; - qCInfo(treelandGreeter) << "Update lock state"; - bool locked = false; - if (auto user = d->userModel->currentUser()) { - locked = user->logined(); - } + QThreadPool::globalInstance()->start([this]() { + QDBusInterface manager("org.deepin.DisplayManager", + "/org/deepin/DisplayManager", + "org.deepin.DisplayManager", + QDBusConnection::systemBus()); + QDBusReply reply = manager.call("AuthInfo"); + if (!reply.isValid()) { + qCWarning(treelandGreeter) << "Failed to get auth info from display manager:" << reply.error().message(); + return; + } + const QString &socket = reply.value(); + QMetaObject::invokeMethod(this, [this, socket] { + if (m_socket->state() == QLocalSocket::ConnectedState) + m_socket->disconnectFromServer(); - if (d->isLocked != locked) { - d->isLocked = locked; - Q_EMIT isLockedChanged(); - } + m_socket->connectToServer(socket); + }); + }); } diff --git a/src/greeter/greeterproxy.h b/src/greeter/greeterproxy.h index f52fabae5..9f1f998d8 100644 --- a/src/greeter/greeterproxy.h +++ b/src/greeter/greeterproxy.h @@ -1,121 +1,331 @@ -/*************************************************************************** - * Copyright (c) 2013 Abdurrahman AVCI - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - ***************************************************************************/ - -#ifndef SDDM_GREETERPROXY_H -#define SDDM_GREETERPROXY_H - -#include "greeter/sessionmodel.h" -#include "greeter/usermodel.h" +// Copyright (C) 2025 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#pragma once + +#include "QDBusContext" #include #include +#include class QLocalSocket; -class GreeterProxyPrivate; +class LockScreen; -class GreeterProxy : public QObject +class GreeterProxy + : public QObject + , protected QDBusContext { Q_OBJECT Q_DISABLE_COPY(GreeterProxy) QML_NAMED_ELEMENT(GreeterProxy) + QML_SINGLETON + + //////////////// + // Properties // + //////////////// - Q_PROPERTY(QString hostName READ hostName NOTIFY hostNameChanged) - Q_PROPERTY(bool canPowerOff READ canPowerOff NOTIFY canPowerOffChanged) - Q_PROPERTY(bool canReboot READ canReboot NOTIFY canRebootChanged) - Q_PROPERTY(bool canSuspend READ canSuspend NOTIFY canSuspendChanged) - Q_PROPERTY(bool canHibernate READ canHibernate NOTIFY canHibernateChanged) - Q_PROPERTY(bool canHybridSleep READ canHybridSleep NOTIFY canHybridSleepChanged) - Q_PROPERTY(SessionModel* sessionModel READ sessionModel WRITE setSessionModel NOTIFY sessionModelChanged) - Q_PROPERTY(UserModel* userModel READ userModel WRITE setUserModel NOTIFY userModelChanged) - Q_PROPERTY(bool isLocked READ isLocked NOTIFY isLockedChanged) - Q_PROPERTY(bool isLoggedIn READ isLoggedIn NOTIFY isLoggedInChanged) + Q_PROPERTY(QString hostName READ hostName NOTIFY hostNameChanged) + Q_PROPERTY(bool canPowerOff READ canPowerOff NOTIFY canPowerOffChanged) + Q_PROPERTY(bool canReboot READ canReboot NOTIFY canRebootChanged) + Q_PROPERTY(bool canSuspend READ canSuspend NOTIFY canSuspendChanged) + Q_PROPERTY(bool canHibernate READ canHibernate NOTIFY canHibernateChanged) + Q_PROPERTY(bool canHybridSleep READ canHybridSleep NOTIFY canHybridSleepChanged) + + Q_PROPERTY(bool isLocked READ isLocked NOTIFY lockChanged) + Q_PROPERTY(int failedAttempts READ failedAttempts NOTIFY failedAttemptsChanged) + Q_PROPERTY(bool showShutdownView READ showShutdownView WRITE setShowShutdownView NOTIFY showShutdownViewChanged) + Q_PROPERTY(bool showAnimation READ showAnimation NOTIFY showAnimationChanged) + Q_PROPERTY(bool hasActiveSession READ hasActiveSession NOTIFY hasActiveSessionChanged) public: explicit GreeterProxy(QObject *parent = nullptr); ~GreeterProxy(); - const QString &hostName() const; + ////////////////////// + // Property getters // + ////////////////////// - bool canPowerOff() const; - bool canReboot() const; - bool canSuspend() const; - bool canHibernate() const; - bool canHybridSleep() const; + /** + * @brief Get the host name + * @return Host name + */ + inline const QString &hostName() const { return m_hostName; }; - bool isConnected() const; - bool isLoggedIn() const; + /** + * @brief Get the power off capability + * @return true if can power off + */ + inline bool canPowerOff() const { return m_canPowerOff; }; + + /** + * @brief Get the reboot capability + * @return true if can reboot + */ + inline bool canReboot() const { return m_canReboot; }; + + /** + * @brief Get the suspend capability + * @return true if can suspend + */ + inline bool canSuspend() const { return m_canSuspend; }; + + /** + * @brief Get the hibernate capability + * @return true if can hibernate + */ + inline bool canHibernate() const { return m_canHibernate; }; + + /** + * @brief Get the hybrid sleep capability + * @return true if can hybrid sleep + */ + inline bool canHybridSleep() const { return m_canHybridSleep; }; + + /** + * @brief Get the greeter's lock state + * QML elements should listen to this property to show/hide the lock screen + * + * @return true if is locked + */ + inline bool isLocked() const { return m_isLocked; }; + + /** + * @brief Get the number of failed login attempts (password incorrect) + * The value is reset to 0 when unlocked successfully + * QML elements should listen to this property to detect failed login/unlock attempts + * + * @return Number of failed attempts + */ + inline int failedAttempts() const { return m_failedAttempts; }; + + /** + * @brief Get whether the shutdown view is shown + * QML elements should listen to this property to show/hide the shutdown view + * + * @return true if shutdown view is shown + */ + inline bool showShutdownView() const { return m_showShutdownView; }; + + /** + * @brief Get whether to show animation on lock/unlock + * QML elements should listen to this property to enable/disable animation + * + * @return true if show animation + */ + inline bool showAnimation() const { return m_showAnimation; }; + + /** + * @brief Get whether there is an active user session + * QML elements should listen to this property to show/hide session related UI + * + * @return true if has active session + */ + inline bool hasActiveSession() const { return m_hasActiveSession; }; + + ///////////////////////////// + // Public property setters // + ///////////////////////////// - SessionModel *sessionModel() const; - void setSessionModel(SessionModel *model); + /** + * @brief Set whether to show the shutdown view + * QML elements should set this property to show/hide the shutdown view + * + * @param show true to show shutdown view, false to hide + */ + void setShowShutdownView(bool show); - UserModel *userModel() const; - void setUserModel(UserModel *model); + //////////////////// + // Public methods // + //////////////////// - bool isLocked() const; + /** + * @brief Check if the DDM socket is connected + * @return true if connected + */ + bool isConnected() const; + + /** + * @brief Set the LockScreen instance + * This is necessary for the GreeterProxy to control the lock screen visibility + * + * @param lockScreen LockScreen instance + */ + inline void setLockScreen(LockScreen *lockScreen) { m_lockScreen = lockScreen; }; public Q_SLOTS: + + ///////////// + // Actions // + ///////////// + + /** @brief Power off the system. Need to be connected with DDM. */ void powerOff(); + + /** @brief Reboot the system. Need to be connected with DDM. */ void reboot(); + + /** @brief Suspend the system. Need to be connected with DDM. */ void suspend(); + + /** @brief Hibernate the system. Need to be connected with DDM. */ void hibernate(); + + /** @brief Hybrid sleep the system. Need to be connected with DDM. */ void hybridSleep(); - void init(); + /** @brief Login given user with given password for given desktop session. + * This function will call DDM to perform the login. + * + * Listen to org.freedesktop.login1.Manager.SessionNew signal to + * detect if new sessions successfully logged in, and listen to + * failedAttempts property to detect failed login attempts. + * + * @param user Username + * @param password Password + * @param sessionIndex Index of the desktop session in the session model + */ void login(const QString &user, const QString &password, int sessionIndex); + + /** @brief Lock the current active session. + * This function will call DDM to perform the lock. + * + * Listen to org.freedesktop.login1.Session.Lock signal to detect + * if the session is successfully locked. + */ + void lock(); + + /** @brief Unlock given user with given password. + * This function will call DDM to perform the unlock. + * + * Listen to org.freedesktop.login1.Session.Unlock signal to + * detect if the session is successfully unlocked, and listen to + * failedAttempts property to detect failed unlock attempts. + * + * @param user Username + * @param password Password + */ void unlock(const QString &user, const QString &password); + + /** @brief Logout the current active session. + * This function will call DDM to perform the logout. + * + * Listen to org.freedesktop.login1.Manager.SessionRemoved signal to + * detect if the session is successfully logged out. + */ void logout(); private Q_SLOTS: + + /////////////////////// + // DDM Communication // + /////////////////////// + void connected(); void disconnected(); void readyRead(); void error(); + + ////////////////////////////// + // Logind session listeners // + ////////////////////////////// + + /** @brief Listener for org.freederktop.login1.Manager.SessionNew */ void onSessionNew(const QString &id, const QDBusObjectPath &session); + + /** @brief Listener for org.freederktop.login1.Manager.SessionRemoved */ void onSessionRemoved(const QString &id, const QDBusObjectPath &session); + /** @brief Listener for org.freederktop.login1.Session.Lock */ + void onSessionLock(); + + /** @brief Listener for org.freederktop.login1.Session.Unlock */ + void onSessionUnlock(); + Q_SIGNALS: void informationMessage(const QString &message); - void hostNameChanged(const QString &hostName); - void canPowerOffChanged(bool canPowerOff); - void canRebootChanged(bool canReboot); - void canSuspendChanged(bool canSuspend); - void canHibernateChanged(bool canHibernate); - void canHybridSleepChanged(bool canHybridSleep); - void sessionModelChanged(SessionModel *model); - void userModelChanged(UserModel *model); + + void switchUser(); void socketDisconnected(); - void loginFailed(const QString &user); - void loginSucceeded(const QString &user); - void switchToGreeter(); - void isLockedChanged(); - void isLoggedInChanged(); + + ///////////////////////////// + // Property change signals // + ///////////////////////////// + + /** @brief Emitted when host name changes. See hostName() */ + void hostNameChanged(const QString &hostName); + + /** @brief Emitted when power off capability changes. See canPowerOff() */ + void canPowerOffChanged (bool canPowerOff); + + /** @brief Emitted when reboot capability changes. See canReboot() */ + void canRebootChanged (bool canReboot); + + /** @brief Emitted when suspend capability changes. See canSuspend() */ + void canSuspendChanged (bool canSuspend); + + /** @brief Emitted when hibernate capability changes. See canHibernate() */ + void canHibernateChanged (bool canHibernate); + + /** @brief Emitted when hybrid sleep capability changes. See canHybridSleep() */ + void canHybridSleepChanged (bool canHybridSleep); + + /** @brief Emitted when lock state changes. See isLocked() */ + void lockChanged (bool isLocked); + + /** @brief Emitted when failed attempts changes. See failedAttempts() */ + void failedAttemptsChanged (int failedAttempts); + + /** @brief Emitted when showShutdownView changes. See showShutdownView() */ + void showShutdownViewChanged (bool showShutdownView); + + /** @brief Emitted when showAnimation changes. See showAnimation() */ + void showAnimationChanged (bool showAnimation); + + /** @brief Emitted when hasActiveSession changes. See hasActiveSession() */ + void hasActiveSessionChanged (bool hasActiveSession); private: - bool localValidation(const QString &user, const QString &password) const; + + ///////////////////// + // Private methods // + ///////////////////// + + /** + * @brief Set the lock state + * This is the internal method to set the lock state (lockscreen + * visibility, etc.) directly without calling DDM and + * communicating with systemd-logind. + * + * @param isLocked true to set locked, false to set unlocked + */ + void setLock(bool isLocked); + + /** + * @brief Update the DDM communication socket + */ void updateAuthSocket(); - void updateLocketState(); -private: - GreeterProxyPrivate *d{ nullptr }; -}; + ///////////////////// + // Property values // + ///////////////////// + + QLocalSocket *m_socket{ nullptr }; + LockScreen *m_lockScreen{ nullptr }; -#endif // SDDM_GREETERPROXY_H + QString m_hostName{}; + + bool m_canPowerOff { false }; + bool m_canReboot { false }; + bool m_canSuspend { false }; + bool m_canHibernate { false }; + bool m_canHybridSleep { false }; + + bool m_isLocked { false }; + int m_failedAttempts { 0 }; + bool m_showShutdownView { false }; + bool m_showAnimation { true }; + bool m_hasActiveSession { false }; +}; diff --git a/src/greeter/sessionmodel.cpp b/src/greeter/sessionmodel.cpp index f8413a91d..53cac6016 100644 --- a/src/greeter/sessionmodel.cpp +++ b/src/greeter/sessionmodel.cpp @@ -29,23 +29,8 @@ using namespace DDM; -class SessionModelPrivate -{ -public: - ~SessionModelPrivate() - { - qDeleteAll(sessions); - sessions.clear(); - } - - int lastIndex{ 0 }; - QStringList displayNames; - QVector sessions; -}; - SessionModel::SessionModel(QObject *parent) : QAbstractListModel(parent) - , d(new SessionModelPrivate()) { // Check for flag to show Wayland sessions bool dri_active = QFileInfo::exists(QStringLiteral("/dev/dri")); @@ -56,6 +41,7 @@ SessionModel::SessionModel(QObject *parent) populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); populate(Session::X11Session, mainConfig.X11.SessionDir.get()); endResetModel(); + Q_EMIT currentIndexChanged(m_currentIndex); // refresh everytime a file is changed, added or removed QFileSystemWatcher *watcher = new QFileSystemWatcher(this); @@ -63,12 +49,15 @@ SessionModel::SessionModel(QObject *parent) // Recheck for flag to show Wayland sessions bool dri_active = QFileInfo::exists(QStringLiteral("/dev/dri")); beginResetModel(); - d->sessions.clear(); - d->displayNames.clear(); + qDeleteAll(m_sessions); + m_sessions.clear(); + m_displayNames.clear(); if (dri_active) populate(Session::WaylandSession, mainConfig.Wayland.SessionDir.get()); populate(Session::X11Session, mainConfig.X11.SessionDir.get()); + m_currentIndex = 0; endResetModel(); + Q_EMIT currentIndexChanged(m_currentIndex); }); watcher->addPaths(mainConfig.Wayland.SessionDir.get()); watcher->addPaths(mainConfig.X11.SessionDir.get()); @@ -76,7 +65,8 @@ SessionModel::SessionModel(QObject *parent) SessionModel::~SessionModel() { - delete d; + qDeleteAll(m_sessions); + m_sessions.clear(); } QHash SessionModel::roleNames() const @@ -93,23 +83,26 @@ QHash SessionModel::roleNames() const return roleNames; } -int SessionModel::lastIndex() const +void SessionModel::setCurrentIndex(int index) { - return d->lastIndex; + if (m_currentIndex != index) { + m_currentIndex = index; + Q_EMIT currentIndexChanged(index); + } } int SessionModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : static_cast(d->sessions.length()); + return parent.isValid() ? 0 : static_cast(m_sessions.length()); } QVariant SessionModel::data(const QModelIndex &index, int role) const { - if (index.row() < 0 || index.row() >= d->sessions.count()) + if (index.row() < 0 || index.row() >= m_sessions.count()) return QVariant(); // get session - Session *session = d->sessions[index.row()]; + Session *session = m_sessions[index.row()]; Q_ASSERT(session); // return correct value @@ -121,7 +114,7 @@ QVariant SessionModel::data(const QModelIndex &index, int role) const case TypeRole: return session->type(); case NameRole: - if (d->displayNames.count(session->displayName()) > 1 + if (m_displayNames.count(session->displayName()) > 1 && session->type() == Session::WaylandSession) return tr("%1 (Wayland)").arg(session->displayName()); return session->displayName(); @@ -171,22 +164,22 @@ void SessionModel::populate(DDM::Session::Type type, const QStringList &dirPaths } } // add to sessions list - // TODO: only show support sessions(X-DDE-SINGLE-WAYLAND) if (execAllowed) { - d->displayNames.append(si->displayName()); + m_displayNames.append(si->displayName()); if (si->displayName() == QStringLiteral("Treeland")) - d->sessions.prepend(si); + m_sessions.prepend(si); else - d->sessions.push_back(si); + m_sessions.push_back(si); } else { delete si; } } // find out index of the last session - for (int i = 0; i < d->sessions.size(); ++i) { - if (d->sessions.at(i)->fileName() == stateConfig.Last.Session.get()) { - d->lastIndex = i; - break; + if (mainConfig.Users.RememberLastSession.get()) + for (int i = 0; i < m_sessions.size(); ++i) { + if (m_sessions.at(i)->fileName() == stateConfig.Last.Session.get()) { + m_currentIndex = i; + break; + } } - } } diff --git a/src/greeter/sessionmodel.h b/src/greeter/sessionmodel.h index 50a69d402..07f62807b 100644 --- a/src/greeter/sessionmodel.h +++ b/src/greeter/sessionmodel.h @@ -18,8 +18,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ***************************************************************************/ -#ifndef SDDM_SESSIONMODEL_H -#define SDDM_SESSIONMODEL_H +#pragma once #include @@ -27,13 +26,11 @@ #include #include -class SessionModelPrivate; - class SessionModel : public QAbstractListModel { Q_OBJECT Q_DISABLE_COPY(SessionModel) - Q_PROPERTY(int lastIndex READ lastIndex CONSTANT) + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged) QML_NAMED_ELEMENT(SessionModel) QML_SINGLETON @@ -54,17 +51,21 @@ class SessionModel : public QAbstractListModel QHash roleNames() const override; - int lastIndex() const; + inline int currentIndex() const { return m_currentIndex; }; + void setCurrentIndex(int index); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; -private: - SessionModelPrivate *d{ nullptr }; +Q_SIGNALS: + void currentIndexChanged(int index); +private: void populate(DDM::Session::Type type, const QStringList &dirPaths); + + int m_currentIndex{ 0 }; + QStringList m_displayNames{}; + QList m_sessions{}; }; QML_DECLARE_TYPE(SessionModel) - -#endif // SDDM_SESSIONMODEL_H diff --git a/src/greeter/user.cpp b/src/greeter/user.cpp index 9a1b17265..f0ca7e286 100644 --- a/src/greeter/user.cpp +++ b/src/greeter/user.cpp @@ -11,7 +11,7 @@ struct UserPrivate { - bool logined{ false }; + bool loggedIn{ false }; bool noPasswdLogin{ false }; quint64 uid{ 0 }; quint64 gid{ 0 }; @@ -115,14 +115,14 @@ const QLocale &User::locale() const noexcept return d->locale; } -bool User::logined() const noexcept +bool User::loggedIn() const noexcept { - return d->logined; + return d->loggedIn; } -void User::setLogined(bool newState) const noexcept +void User::setLoggedIn(bool newState) const noexcept { - d->logined = newState; + d->loggedIn = newState; } void User::updateLimitTime(const QString &time) noexcept diff --git a/src/greeter/user.h b/src/greeter/user.h index 067b5efc4..82341f3f8 100644 --- a/src/greeter/user.h +++ b/src/greeter/user.h @@ -31,10 +31,10 @@ class User : public QObject [[nodiscard]] const QString &homeDir() const noexcept; [[nodiscard]] const QUrl &iconFile() const noexcept; [[nodiscard]] const QString &passwordHint() const noexcept; - [[nodiscard]] bool logined() const noexcept; + [[nodiscard]] bool loggedIn() const noexcept; [[nodiscard]] const QLocale &locale() const noexcept; [[nodiscard]] static QString toString(AccountTypes type) noexcept; - void setLogined(bool newState) const noexcept; + void setLoggedIn(bool newState) const noexcept; void updateLimitTime(const QString &time) noexcept; void setWaylandSocket(std::shared_ptr); diff --git a/src/greeter/usermodel.cpp b/src/greeter/usermodel.cpp index a6016c034..b2ed65edb 100644 --- a/src/greeter/usermodel.cpp +++ b/src/greeter/usermodel.cpp @@ -139,10 +139,10 @@ QHash UserModel::roleNames() const names[HomeDirRole] = QByteArrayLiteral("homeDir"); names[IconRole] = QByteArrayLiteral("icon"); names[NoPasswordRole] = QByteArrayLiteral("noPassword"); - names[LoginedRole] = QByteArrayLiteral("logined"); + names[LoggedInRole] = QByteArrayLiteral("loggedIn"); names[IdentityRole] = QByteArrayLiteral("identity"); names[PasswordHintRole] = QByteArrayLiteral("passwordHint"); - names[LocaleRole] = QByteArrayLiteral("LocaleRole"); + names[LocaleRole] = QByteArrayLiteral("locale"); return names; } @@ -161,14 +161,14 @@ int UserModel::rowCount(const QModelIndex &parent) const return parent.isValid() ? 0 : static_cast(d->users.length()); } -void UserModel::updateUserLoginState(const QString &username, bool logined) +void UserModel::updateUserLoginState(const QString &username, bool loggedIn) { auto user = std::find_if(d->users.begin(), d->users.end(), [&username](const UserPtr &user) { return user->userName() == username; }); if (user != d->users.end()) { - (*user)->setLogined(logined); + (*user)->setLoggedIn(loggedIn); auto pos = std::distance(d->users.end(), user); Q_EMIT dataChanged(index(0, pos - 1), index(0, pos)); } @@ -179,7 +179,7 @@ void UserModel::updateUserLoginState(const QString &username, bool logined) void UserModel::clearUserLoginState() { for (auto &user : d->users) { - user->setLogined(false); + user->setLoggedIn(false); } Q_EMIT layoutChanged(); @@ -206,8 +206,8 @@ QVariant UserModel::data(const QModelIndex &index, int role) const return user->iconFile(); case NoPasswordRole: return user->noPasswdLogin(); - case LoginedRole: - return user->logined(); + case LoggedInRole: + return user->loggedIn(); case IdentityRole: return user->identity(); case PasswordHintRole: @@ -234,7 +234,7 @@ QVariant UserModel::get(const QString &username) const map["realName"] = user->fullName(); map["homeDir"] = user->homeDir(); map["noPassword"] = user->noPasswdLogin(); - map["logined"] = user->logined(); + map["loggedIn"] = user->loggedIn(); map["identity"] = user->identity(); map["passwordHint"] = user->passwordHint(); map["locale"] = user->locale(); @@ -258,7 +258,7 @@ QVariant UserModel::get(int index) const map["realName"] = user->fullName(); map["homeDir"] = user->homeDir(); map["noPassword"] = user->noPasswdLogin(); - map["logined"] = user->logined(); + map["loggedIn"] = user->loggedIn(); map["identity"] = user->identity(); map["passwordHint"] = user->passwordHint(); map["locale"] = user->locale(); diff --git a/src/greeter/usermodel.h b/src/greeter/usermodel.h index 3ee58aae8..654367514 100644 --- a/src/greeter/usermodel.h +++ b/src/greeter/usermodel.h @@ -50,7 +50,7 @@ class UserModel : public QAbstractListModel HomeDirRole, IconRole, NoPasswordRole, - LoginedRole, + LoggedInRole, IdentityRole, PasswordHintRole, LocaleRole @@ -76,7 +76,7 @@ class UserModel : public QAbstractListModel UserPtr currentUser() const; void updateUserLimits(const QString &userName, const QString &time) const noexcept; void setCurrentUserName(const QString &userName) noexcept; - void updateUserLoginState(const QString &username, bool logined); + void updateUserLoginState(const QString &username, bool loggedIn); void clearUserLoginState(); [[nodiscard]] bool containsAllUsers() const; diff --git a/src/plugins/lockscreen/CMakeLists.txt b/src/plugins/lockscreen/CMakeLists.txt index 4622209b5..21bab0155 100644 --- a/src/plugins/lockscreen/CMakeLists.txt +++ b/src/plugins/lockscreen/CMakeLists.txt @@ -4,10 +4,6 @@ find_package(Dtk6 REQUIRED COMPONENTS SystemSettings Declarative) # Include translation utilities include(${CMAKE_SOURCE_DIR}/cmake/TranslationUtils.cmake) -set_source_files_properties(qml/GreeterModel.qml PROPERTIES - QT_QML_SINGLETON_TYPE TRUE -) - qt_add_library(lockscreen SHARED lockscreenplugin.h lockscreenplugin.cpp @@ -19,7 +15,6 @@ qt_add_qml_module(lockscreen logoprovider.h logoprovider.cpp QML_FILES - qml/GreeterModel.qml qml/HintLabel.qml qml/LoginAnimation.qml qml/QuickAction.qml diff --git a/src/plugins/lockscreen/qml/ControlAction.qml b/src/plugins/lockscreen/qml/ControlAction.qml index 8e73e1407..4389a8a72 100644 --- a/src/plugins/lockscreen/qml/ControlAction.qml +++ b/src/plugins/lockscreen/qml/ControlAction.qml @@ -10,21 +10,24 @@ import Treeland RowLayout { id: bottomGroup - property int buttonSize: 30 spacing: 15 - property bool powerVisible: powerList.visible required property Item rootItem - signal lock() + property int buttonSize: 30 + property bool powerVisible: powerList.visible + + /**************/ + /* Components */ + /**************/ // TODO: Design the interface of session selection D.Button { id: sessionItem Layout.alignment: Qt.AlignHCenter - visible: !GreeterModel.proxy.isLoggedIn + visible: !GreeterProxy.hasActiveSession contentItem: D.IconLabel { - text: SessionModel.data(SessionModel.index(GreeterModel.currentSession, 0), SessionModel.NameRole) + text: SessionModel.data(SessionModel.index(SessionModel.currentIndex, 0), SessionModel.NameRole) color: "white" } @@ -44,12 +47,6 @@ RowLayout { } } - function showUserList() - { - userItem.expand = true - userList.open() - } - ControlActionItem { id: userItem Layout.alignment: Qt.AlignHCenter @@ -140,4 +137,13 @@ RowLayout { onClicked: actionItem.clicked() } } + + /*****************************/ + /* Functions and Connections */ + /*****************************/ + + function showUserList() { + userItem.expand = true + userList.open() + } } diff --git a/src/plugins/lockscreen/qml/Greeter.qml b/src/plugins/lockscreen/qml/Greeter.qml index 9b1bb917c..f3375a82c 100644 --- a/src/plugins/lockscreen/qml/Greeter.qml +++ b/src/plugins/lockscreen/qml/Greeter.qml @@ -9,43 +9,15 @@ import LockScreen FocusScope { id: root clip: true - enum CurrentMode { - Lock = 1, - Shutdown = 2, - SwitchUser = 3 - } signal animationPlayed signal animationPlayFinished required property QtObject output required property QtObject outputItem - property int currentMode: Greeter.CurrentMode.Lock property string primaryOutputName visible: primaryOutputName === "" || primaryOutputName === output.name - function start(showAnimation) - { - if (showAnimation === undefined) { - showAnimation = true - } - lockView.showAnimation = showAnimation - lockView.forceActiveFocus() - if (showAnimation) { - wallpaperController.type = WallpaperController.Scale - } else { - wallpaperController.type = WallpaperController.ScaleWithoutAnimation - } - switch (root.currentMode) { - case Greeter.CurrentMode.Lock: - lockView.start() - break; - case Greeter.CurrentMode.SwitchUser: - lockView.showUserView() - break; - } - } - x: outputItem.x y: outputItem.y width: outputItem.width @@ -53,11 +25,15 @@ FocusScope { palette.windowText: Qt.rgba(1.0, 1.0, 1.0, 1.0) + /**************/ + /* Components */ + /**************/ + WallpaperController { id: wallpaperController output: root.output lock: true - type: WallpaperController.Normal + type: (GreeterProxy.isLocked || GreeterProxy.showShutdownView) ? WallpaperController.Scale : WallpaperController.Normal } // prevent event passing through greeter @@ -72,8 +48,9 @@ FocusScope { color: 'black' opacity: wallpaperController.type === WallpaperController.Normal ? 0 : 0.6 Behavior on opacity { + enabled: GreeterProxy.showAnimation PropertyAnimation { - duration: wallpaperController.type === WallpaperController.ScaleWithoutAnimation ? 0 : 1000 + duration: 1000 easing.type: Easing.OutExpo } } @@ -81,13 +58,7 @@ FocusScope { LockView { id: lockView - visible: root.currentMode === Greeter.CurrentMode.Lock || - root.currentMode === Greeter.CurrentMode.SwitchUser anchors.fill: parent - onQuit: function () { - wallpaperController.type = WallpaperController.Normal - root.animationPlayed() - } onAnimationPlayFinished: function () { if (lockView.state === LoginAnimation.Hide) { root.animationPlayFinished() @@ -97,21 +68,40 @@ FocusScope { ShutdownView { id: shutdownView - visible: root.currentMode === Greeter.CurrentMode.Shutdown + visible: GreeterProxy.showShutdownView anchors.fill: parent - onClicked: function () { - wallpaperController.type = WallpaperController.Normal - root.animationPlayed() - root.animationPlayFinished() + onSwitchUser: { + root.switchUser() } - onSwitchUser: function () { - root.currentMode = Greeter.CurrentMode.Lock - lockView.showUserView() + } + + /*****************************/ + /* Functions and Connections */ + /*****************************/ + + function switchUser() { + GreeterProxy.lock() + lockView.showUserView() + } + + Connections { + target: GreeterProxy + + function onLockChanged(isLocked) { + if (!isLocked) + root.animationPlayed() } - onLock: function () { - root.currentMode = Greeter.CurrentMode.Lock - lockView.start() + + function onShowShutdownViewChanged(show) { + if (!show && !GreeterProxy.isLocked) { + root.animationPlayed() + root.animationPlayFinished() + } + } + + function onSwitchUser() { + root.switchUser() } } diff --git a/src/plugins/lockscreen/qml/GreeterModel.qml b/src/plugins/lockscreen/qml/GreeterModel.qml deleted file mode 100644 index 6bdc16b29..000000000 --- a/src/plugins/lockscreen/qml/GreeterModel.qml +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2023 justforlxz . -// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -pragma Singleton - -import QtQuick -import Treeland -import LockScreen - -Item { - enum GreeterState { - NotReady = 0, - AuthSucceeded = 1, - AuthFailed = 2, - Quit = 3 - } - - readonly property var currentUser: UserModel.currentUserName - property int currentSession - property var state: GreeterModel.NotRady - readonly property SessionModel sessionModel: SessionModel - readonly property GreeterProxy proxy: proxy - readonly property LogoProvider logoProvider: logoProvider - - function quit() { - state = GreeterModel.Quit - } - - Connections { - target: UserModel - function onUpdateTranslations(locale) { - console.log("translation updated") - logoProvider.updateLocale(locale) - Treeland.retranslate() - } - } - - GreeterProxy { - id: proxy - sessionModel: SessionModel - userModel: UserModel - - function checkUser(userName) { - let user = UserModel.get(UserModel.currentUserName) - console.log("last activate user:",user.name,"current user:",userName) - return user.name === userName - } - - onLoginSucceeded: function(userName) { - if (!checkUser(userName)) { - return - } - - state = GreeterModel.AuthSucceeded - } - - onLoginFailed: function(userName) { - if (!checkUser(userName)) { - return - } - - state = GreeterModel.AuthFailed - } - - Component.onCompleted: { - proxy.init() - } - } - - LogoProvider { - id: logoProvider - } - - Component.onCompleted: { - GreeterModel.currentSession = SessionModel.lastIndex - } -} diff --git a/src/plugins/lockscreen/qml/LockView.qml b/src/plugins/lockscreen/qml/LockView.qml index a38bf1f29..1542a03f6 100644 --- a/src/plugins/lockscreen/qml/LockView.qml +++ b/src/plugins/lockscreen/qml/LockView.qml @@ -4,42 +4,16 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import Treeland -import Treeland FocusScope { id: root - property bool showAnimation: true property int state: LoginAnimation.Show readonly property bool powerVisible: controlAction.powerVisible - signal quit() signal animationPlayFinished() - function start() { - root.state = LoginAnimation.Show - if (showAnimation) { - leftAnimation.item.start({x: root.x - quickAction.width, y: quickAction.y}, {x: quickAction.x, y: quickAction.y}) - logoAnimation.item.start({x: root.x - logo.width, y: logo.y}, {x: logo.x, y: logo.y}) - rightAnimation.item.start({x: root.width + userInput.width, y: userInput.y}, {x: userInput.x, y: userInput.y}) - bottomAnimation.item.start({x: controlAction.x, y: controlAction.y + controlAction.height}, {x: controlAction.x, y: controlAction.y}) - } else { - leftAnimation.item.skip({x: quickAction.x, y: quickAction.y}) - logoAnimation.item.skip({x: logo.x, y: logo.y}) - rightAnimation.item.skip({x: userInput.x, y: userInput.y}) - bottomAnimation.item.skip({x: controlAction.x, y: controlAction.y}) - } - } - - function showUserView() - { - root.animationPlayFinished.connect(root.__showUserList) - start() - } - - function __showUserList() - { - controlAction.showUserList() - root.animationPlayFinished.disconnect(root.__showUserList) - } + /**************/ + /* Components */ + /**************/ Loader { id: leftAnimation @@ -130,15 +104,19 @@ FocusScope { left: leftComp.left } + LogoProvider { + id: logoProvider + } + Image { id: logoPic - source: GreeterModel.logoProvider.logo + source: logoProvider.logo height: 32 fillMode: Image.PreserveAspectFit } Text { - text: GreeterModel.logoProvider.version + text: logoProvider.version font.weight: Font.Normal font.pixelSize: 14 color: Qt.rgba(1, 1, 1, 153 / 255) @@ -164,44 +142,45 @@ FocusScope { right: rightComp.right } rootItem: root - onLock: { - root.start() - } } + /*****************************/ + /* Functions and Connections */ + /*****************************/ + Connections { - target: GreeterModel - function onStateChanged() { - switch (GreeterModel.state) { - case GreeterModel.AuthSucceeded: { - userInput.userAuthSuccessed() - userInput.updateHintMsg(userInput.normalHint) - GreeterModel.quit() - } - break - case GreeterModel.AuthFailed: { - userInput.userAuthFailed() - userInput.updateHintMsg(qsTr("Password is incorrect.")) - } - break - case GreeterModel.Quit: { - root.state = LoginAnimation.Hide - if (showAnimation) { - leftAnimation.item.start({x: quickAction.x, y: quickAction.y}, {x: root.x - quickAction.width, y: quickAction.y}) - logoAnimation.item.start({x: logo.x, y: logo.y}, {x: root.x - logo.width, y: logo.y}) - rightAnimation.item.start({x: userInput.x, y: userInput.y}, {x: root.width + userInput.width, y: userInput.y}) - bottomAnimation.item.start({x: controlAction.x, y: controlAction.y}, {x: controlAction.x, y: controlAction.y + controlAction.height}) - } else { - leftAnimation.item.skip({x: root.x - quickAction.width, y: quickAction.y}) - logoAnimation.item.skip({x: root.x - logo.width, y: logo.y}) - rightAnimation.item.skip({x: root.width + userInput.width, y: userInput.y}) - bottomAnimation.item.skip({x: controlAction.x, y: controlAction.y + controlAction.height}) - } - - root.quit() - } - break + target: GreeterProxy + function onLockChanged(isLocked) { + if (isLocked) { + root.forceActiveFocus() + root.visible = true + root.state = LoginAnimation.Show + leftAnimation.item.start({x: root.x - quickAction.width, y: quickAction.y}, {x: quickAction.x, y: quickAction.y}) + logoAnimation.item.start({x: root.x - logo.width, y: logo.y}, {x: logo.x, y: logo.y}) + rightAnimation.item.start({x: root.width + userInput.width, y: userInput.y}, {x: userInput.x, y: userInput.y}) + bottomAnimation.item.start({x: controlAction.x, y: controlAction.y + controlAction.height}, {x: controlAction.x, y: controlAction.y}) + } else { + root.state = LoginAnimation.Hide + leftAnimation.item.start({x: quickAction.x, y: quickAction.y}, {x: root.x - quickAction.width, y: quickAction.y}) + logoAnimation.item.start({x: logo.x, y: logo.y}, {x: root.x - logo.width, y: logo.y}) + rightAnimation.item.start({x: userInput.x, y: userInput.y}, {x: root.width + userInput.width, y: userInput.y}) + bottomAnimation.item.start({x: controlAction.x, y: controlAction.y}, {x: controlAction.x, y: controlAction.y + controlAction.height}) } } } + + onAnimationPlayFinished: { + if (!GreeterProxy.isLocked) { + root.visible = false + } + } + + function showUserView() { + root.animationPlayFinished.connect(root.__showUserList) + } + + function __showUserList() { + controlAction.showUserList() + root.animationPlayFinished.disconnect(root.__showUserList) + } } diff --git a/src/plugins/lockscreen/qml/LoginAnimation.qml b/src/plugins/lockscreen/qml/LoginAnimation.qml index 820ac66a9..47c20ce6c 100644 --- a/src/plugins/lockscreen/qml/LoginAnimation.qml +++ b/src/plugins/lockscreen/qml/LoginAnimation.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only import QtQuick +import Treeland Item { id: root @@ -20,22 +21,22 @@ Item { required property var target function start(pos, to) { - xAni.from = pos.x - xAni.to = to.x + if (GreeterProxy.showAnimation) { + xAni.from = pos.x + xAni.to = to.x - yAni.from = pos.y - yAni.to = to.y + yAni.from = pos.y + yAni.to = to.y - effect.sourceItem = root.target + effect.sourceItem = root.target - visible = true - animation.start() - } - - function skip(to) { - target.x = to.x - target.y = to.y - stop() + visible = true + animation.start() + } else { + target.x = to.x + target.y = to.y + stop() + } } function stop() { diff --git a/src/plugins/lockscreen/qml/PowerList.qml b/src/plugins/lockscreen/qml/PowerList.qml index 5e8d5d468..df87d146f 100644 --- a/src/plugins/lockscreen/qml/PowerList.qml +++ b/src/plugins/lockscreen/qml/PowerList.qml @@ -12,7 +12,6 @@ FocusScope { id: root property alias modelChildren: objModel.children property alias leftModelChildren: leftObjeModel.children - signal lock() implicitWidth: layout.width implicitHeight: layout.height @@ -32,36 +31,34 @@ FocusScope { ShutdownButton { id: powerOff - enabled: GreeterModel.proxy.canPowerOff + enabled: GreeterProxy.canPowerOff text: qsTr("Shut Down") icon.name: "login_shutdown" - onClicked: GreeterModel.proxy.powerOff() + onClicked: GreeterProxy.powerOff() } ShutdownButton { - enabled: GreeterModel.proxy.canReboot + enabled: GreeterProxy.canReboot text: qsTr("Reboot") icon.name: "login_reboot" - onClicked: GreeterModel.proxy.reboot() + onClicked: GreeterProxy.reboot() } ShutdownButton { - enabled: GreeterModel.proxy.canSuspend + enabled: GreeterProxy.canSuspend text: qsTr("Suspend") icon.name: "login_suspend" onClicked: { - root.lock() - GreeterModel.proxy.suspend() + GreeterProxy.suspend() } } ShutdownButton { - enabled: GreeterModel.proxy.canHibernate + enabled: GreeterProxy.canHibernate text: qsTr("Hibernate") icon.name: "login_hibernate" onClicked: { - root.lock() - GreeterModel.proxy.hibernate() + GreeterProxy.hibernate() } } } diff --git a/src/plugins/lockscreen/qml/SessionList.qml b/src/plugins/lockscreen/qml/SessionList.qml index 35f02c10d..b127b0944 100644 --- a/src/plugins/lockscreen/qml/SessionList.qml +++ b/src/plugins/lockscreen/qml/SessionList.qml @@ -17,10 +17,6 @@ Popup { radius: 12 } - function updateCurrentSession(index) { - GreeterModel.currentSession = index - } - ListView { id: list spacing: 1 @@ -56,7 +52,7 @@ Popup { } onClicked: (mouse) => { mouse.accepted = false - updateCurrentSession(index) + SessionModel.currentIndex = index popup.close() } } @@ -93,7 +89,7 @@ Popup { } } Component.onCompleted: { - list.currentIndex = GreeterModel.currentSession + list.currentIndex = SessionModel.currentIndex } } diff --git a/src/plugins/lockscreen/qml/ShutdownView.qml b/src/plugins/lockscreen/qml/ShutdownView.qml index d40b932d5..58d999de0 100644 --- a/src/plugins/lockscreen/qml/ShutdownView.qml +++ b/src/plugins/lockscreen/qml/ShutdownView.qml @@ -8,14 +8,12 @@ import QtQuick.Controls FocusScope { id: root - signal clicked() signal switchUser() - signal lock() MouseArea { anchors.fill: parent enabled: true - onClicked: root.clicked() + onClicked: GreeterProxy.showShutdownView = false } PowerList { @@ -29,26 +27,24 @@ FocusScope { modelChildren: [ ShutdownButton { + visible: !GreeterProxy.isLocked text: qsTr("lock") icon.name: "login_lock" - onClicked: root.lock() + onClicked: GreeterProxy.lock() }, ShutdownButton { + visible: !GreeterProxy.isLocked text: qsTr("switch user") icon.name: "login_switchuser" enabled: UserModel.count > 1 onClicked: root.switchUser() }, ShutdownButton { + visible: !GreeterProxy.isLocked text: qsTr("Logout") icon.name: "login_logout" - onClicked: { - root.lock() - GreeterModel.proxy.logout() - } + onClicked: GreeterProxy.logout() } ] - - onLock: root.lock() } } diff --git a/src/plugins/lockscreen/qml/UserInput.qml b/src/plugins/lockscreen/qml/UserInput.qml index 056579ea6..af6dc6c19 100644 --- a/src/plugins/lockscreen/qml/UserInput.qml +++ b/src/plugins/lockscreen/qml/UserInput.qml @@ -14,43 +14,14 @@ Item { property string normalHint: qsTr("Please enter password") - function updateUser() { - let currentUser = UserModel.get(UserModel.currentUserName) - username.text = currentUser.realName.length === 0 ? currentUser.name : currentUser.realName - passwordField.text = '' - avatar.fallbackSource = currentUser.icon - updateHintMsg(normalHint) - } - - function userLogin() { - let user = UserModel.get(UserModel.currentUserName) - if (user.logined) { - GreeterModel.proxy.unlock(user.name, passwordField.text) - return - } - - GreeterModel.proxy.login(user.name, passwordField.text, - GreeterModel.currentSession) - } - - Connections { - target: UserModel - function onUpdateTranslations() { - updateUser() - } - } - - Connections { - target: GreeterModel - function onCurrentUserChanged() { - updateUser() - } - } + /**************/ + /* Components */ + /**************/ Item { width: 32 height: 32 - visible: GreeterModel.proxy.isLocked + visible: GreeterProxy.isLocked anchors { horizontalCenter: parent.horizontalCenter bottom: parent.top @@ -325,26 +296,59 @@ Item { } } - Component.onCompleted: { - updateUser() - } + /*****************************/ + /* Functions and Connections */ + /*****************************/ - onActiveFocusChanged: { - if (activeFocus) passwordField.forceActiveFocus() + function updateUser() { + let currentUser = UserModel.get(UserModel.currentUserName) + username.text = currentUser.realName.length === 0 ? currentUser.name : currentUser.realName + passwordField.text = '' + avatar.fallbackSource = currentUser.icon + hintText.text = normalHint } - function updateHintMsg(msg) { - hintText.text = msg + function userLogin() { + let user = UserModel.get(UserModel.currentUserName) + if (user.loggedIn) + GreeterProxy.unlock(user.name, passwordField.text) + else + GreeterProxy.login(user.name, passwordField.text, SessionModel.currentIndex) } - function userAuthSuccessed() { - passwordField.text = "" + Connections { + target: GreeterProxy + function onFailedAttemptsChanged (attempts) { + if (attempts > 0) { + passwordField.selectAll() + if (loginGroup.activeFocus) { + passwordField.forceActiveFocus() + } + hintText.text = qsTr("Password is incorrect.") + } else { + passwordField.text = "" + hintText.text = normalHint + } + } } - function userAuthFailed() { - passwordField.selectAll() - if (loginGroup.activeFocus) { - passwordField.forceActiveFocus() + Connections { + target: UserModel + + function onUpdateTranslations(locale) { + updateUser() } + + function onCurrentUserNameChanged(name) { + updateUser() + } + } + + Component.onCompleted: { + updateUser() + } + + onActiveFocusChanged: { + if (activeFocus) passwordField.forceActiveFocus() } } diff --git a/src/plugins/lockscreen/qml/UserList.qml b/src/plugins/lockscreen/qml/UserList.qml index 238f5f8b3..4e9795f2a 100644 --- a/src/plugins/lockscreen/qml/UserList.qml +++ b/src/plugins/lockscreen/qml/UserList.qml @@ -112,7 +112,7 @@ D.Popup { right: parent.right bottom: parent.bottom } - visible: model.logined + visible: model.loggedIn Rectangle { anchors.centerIn: parent diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp index 17e7652f4..027cba545 100644 --- a/src/seat/helper.cpp +++ b/src/seat/helper.cpp @@ -18,7 +18,9 @@ #include "core/shellhandler.h" #include "core/treeland.h" #include "core/windowpicker.h" +#include "greeter/greeterproxy.h" #include "greeter/usermodel.h" +#include "greeter/sessionmodel.h" #include "input/inputdevice.h" #include "interfaces/multitaskviewinterface.h" #include "modules/app-id-resolver/appidresolver.h" @@ -1193,7 +1195,9 @@ void Helper::init(Treeland::Treeland *treeland) connect(m_sessionManager, &SessionManager::sessionChanged, treeland, &Treeland::Treeland::SessionChanged); auto engine = qmlEngine(); + m_greeterProxy = engine->singletonInstance("Treeland", "GreeterProxy"); m_userModel = engine->singletonInstance("Treeland", "UserModel"); + m_sessionModel = engine->singletonInstance("Treeland", "SessionModel"); engine->setContextForObject(m_renderWindow, engine->rootContext()); engine->setContextForObject(m_renderWindow->contentItem(), engine->rootContext()); @@ -2222,10 +2226,12 @@ void Helper::setLockScreenImpl(ILockScreen *impl) return; } - m_lockScreen = new LockScreen(impl, m_rootSurfaceContainer); + m_lockScreen = new LockScreen(impl, m_rootSurfaceContainer, m_greeterProxy); m_lockScreen->setZ(RootSurfaceContainer::LockScreenZOrder); m_lockScreen->setVisible(false); + m_greeterProxy->setLockScreen(m_lockScreen); + for (auto *output : m_rootSurfaceContainer->outputs()) { m_lockScreen->addOutput(output); } @@ -2285,7 +2291,6 @@ void Helper::showLockScreen(bool switchToGreeter) m_lockScreen->lock(); // send DDM switch to greeter mode - // FIXME: DDM and Treeland should listen to the lock signal of login1 if (switchToGreeter) { QThreadPool::globalInstance()->start([]() { QDBusInterface interface("org.freedesktop.DisplayManager", @@ -2425,10 +2430,6 @@ void Helper::onPrepareForSleep(bool sleep) } } -UserModel *Helper::userModel() const { - return m_userModel; -} - DDMInterfaceV1 *Helper::ddmInterfaceV1() const { return m_ddmInterfaceV1; } diff --git a/src/seat/helper.h b/src/seat/helper.h index c644896d1..392a04bb0 100644 --- a/src/seat/helper.h +++ b/src/seat/helper.h @@ -85,6 +85,7 @@ class DDEShellManagerInterfaceV1; class DDMInterfaceV1; class ForeignToplevelV1; class FpsDisplayManager; +class GreeterProxy; class ILockScreen; class IMultitaskView; class LockScreen; @@ -100,6 +101,7 @@ class RootSurfaceContainer; class ScreensaverInterfaceV1; class SessionManager; class SettingManager; +class SessionModel; class ShellHandler; class ShortcutManagerV2; class ShortcutRunner; @@ -210,7 +212,8 @@ class Helper : public WSeatEventFilter Output* getOutputAtCursor() const; - UserModel *userModel() const; + inline UserModel *userModel() const { return m_userModel; }; + inline SessionModel *sessionModel() const { return m_sessionModel; }; DDMInterfaceV1 *ddmInterfaceV1() const; void activateSession(); @@ -391,6 +394,8 @@ private Q_SLOTS: IMultitaskView *m_multitaskView{ nullptr }; UserModel *m_userModel{ nullptr }; + SessionModel *m_sessionModel{ nullptr }; + GreeterProxy *m_greeterProxy{ nullptr }; bool m_blockActivateSurface{ false }; diff --git a/src/wallpaper/wallpapercontroller.cpp b/src/wallpaper/wallpapercontroller.cpp index d36cf5fc6..c3aefea31 100644 --- a/src/wallpaper/wallpapercontroller.cpp +++ b/src/wallpaper/wallpapercontroller.cpp @@ -63,9 +63,6 @@ void WallpaperController::updateState() case Scale: state = "Scale"; break; - case ScaleWithoutAnimation: - state = "ScaleWithoutAnimation"; - break; } proxy->setState(state); diff --git a/src/wallpaper/wallpapercontroller.h b/src/wallpaper/wallpapercontroller.h index 816f92545..5dbe2b9fb 100644 --- a/src/wallpaper/wallpapercontroller.h +++ b/src/wallpaper/wallpapercontroller.h @@ -30,7 +30,6 @@ class WallpaperController : public QObject { Normal, Scale, - ScaleWithoutAnimation, }; Q_ENUM(WallpaperType)