From b6ac29d2838bedab513d6cdfb21084b95da0a70c Mon Sep 17 00:00:00 2001
From: tangjie02 <tangjie02@kylinsec.com.cn>
Date: Tue, 9 May 2023 17:06:03 +0800
Subject: [PATCH] feature(lockscreen): Add LockScreenWhenHibernate and
 LockScreenWhenSuspend functions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- 添加当计算机休眠/待机时是否锁屏功能

Relates #68459

Signed-off-by: tangjie02 <tangjie02@kylinsec.com.cn>
---
 ...insec.kiran.session-manager.gschema.xml.in | 10 +++
 data/org.gnome.SessionManager.xml             | 40 +++++++++++
 include/ksm-i.h                               | 12 ++--
 lib/dbus/systemd-login1.cpp                   |  1 +
 src/core/power.cpp                            | 68 +++++++++++++++++--
 src/core/power.h                              |  7 ++
 src/core/presence.cpp                         |  3 -
 src/core/session-manager.cpp                  | 68 +++++++++++++++++++
 src/core/session-manager.h                    | 15 ++++
 9 files changed, 210 insertions(+), 14 deletions(-)

diff --git a/data/com.kylinsec.kiran.session-manager.gschema.xml.in b/data/com.kylinsec.kiran.session-manager.gschema.xml.in
index 6d2b361..b71dd3e 100644
--- a/data/com.kylinsec.kiran.session-manager.gschema.xml.in
+++ b/data/com.kylinsec.kiran.session-manager.gschema.xml.in
@@ -5,6 +5,16 @@
             <summary>Time before session is considered idle</summary>
             <description>The number of minutes of inactivity before the session is considered idle</description>
         </key>
+
+        <key name="screen-locked-when-suspend" type="b">
+            <default>true</default>
+            <description>Whether lock screen when the computer is suspend.</description>
+        </key>
+
+        <key name="screen-locked-when-hibernate" type="b">
+            <default>true</default>
+            <description>Whether lock screen when the computer is hibernate.</description>
+        </key>
     </schema>
 
 </schemalist>
diff --git a/data/org.gnome.SessionManager.xml b/data/org.gnome.SessionManager.xml
index 9b4cec5..92ba554 100644
--- a/data/org.gnome.SessionManager.xml
+++ b/data/org.gnome.SessionManager.xml
@@ -72,6 +72,28 @@
             <description>This gets a list of all the inhibitors that are currently known to the session manager.</description>
         </method>
 
+        <method name="Suspend">
+            <description>Suspend computer.</description>
+        </method>
+
+        <method name="CanSuspend">
+            <arg name="is_available" direction="out" type="b">
+                <summary>True if suspend is available to the user, false otherwise.</summary>
+            </arg>
+            <description>Whether the user can suspend.</description>
+        </method>
+
+        <method name="Hibernate">
+            <description>Hibernate computer.</description>
+        </method>
+
+        <method name="CanHibernate">
+            <arg name="is_available" direction="out" type="b">
+                <summary>True if hibernate is available to the user, false otherwise.</summary>
+            </arg>
+            <description>Whether the user can hibernate.</description>
+        </method>
+
         <method name="Shutdown">
             <description>Shutdown the system.</description>
         </method>
@@ -126,6 +148,14 @@
             <description>Adds the variable name to the application launch environment with the specified value.  May only be used during the Session Manager initialization phase.</description>
         </method>
 
+        <property name="ScreenLockedWhenSuspend" type="b" access="readwrite">
+            <description>Whether lock screen when the computer is suspend.</description>
+        </property>
+
+        <property name="ScreenLockedWhenHibernate" type="b" access="readwrite">
+            <description>Whether lock screen when the computer is hibernate.</description>
+        </property>
+
         <signal name="InhibitorAdded">
             <arg name="cookie" type="u">
                 <summary>The inhibitor cookie.</summary>
@@ -145,5 +175,15 @@
             <description>The stage where session manager is in.</description>
         </signal>
 
+        <signal name="ScreenLockedWhenSuspendChanged">
+            <arg name="screen_locked_when_suspend" type="b" />
+            <description>Whether lock screen when the computer is suspend.</description>
+        </signal>
+
+        <signal name="ScreenLockedWhenHibernateChanged">
+            <arg name="screen_locked_when_hibernate" type="b" />
+            <description>Whether lock screen when the computer is hibernate.</description>
+        </signal>
+
     </interface>
 </node>
diff --git a/include/ksm-i.h b/include/ksm-i.h
index 71376dc..ca4bb86 100644
--- a/include/ksm-i.h
+++ b/include/ksm-i.h
@@ -21,6 +21,8 @@ extern "C"
 
 #define DESKTOP_ENVIRONMENT "KIRAN"
 
+/* 为了保持跟第三方应用(firefox、pluma等)兼容,暂时还是使用gnome名字。
+   但后续版本有可能更名,所以最好使用宏定义,不要直接使用字符串常量。*/
 #define KSM_DBUS_NAME "org.gnome.SessionManager"
 #define KSM_DBUS_OBJECT_PATH "/org/gnome/SessionManager"
 #define KSM_DBUS_INTERFACE_NAME "org.gnome.SessionManager"
@@ -34,10 +36,12 @@ extern "C"
 #define KSM_IDLE_DBUS_OBJECT_PATH "/com/kylinsec/Kiran/SessionManager/IdleMonitor"
 
 #define KSM_SCHEMA_ID "com.kylinsec.kiran.session-manager"
-#define KSM_SCHEMA_KEY_SESSION_DAEMONS "session-daemons"
-#define KSM_SCHEMA_KEY_WINDOW_MANAGER "window-manager"
-#define KSM_SCHEMA_KEY_PANEL "panel"
-#define KSM_SCHEMA_KEY_FILE_MANAGER "file-manager"
+// 计算机多久未操作视为空闲
+#define KSM_SCHEMA_KEY_IDLE_DELAY "idle-delay"
+// 待机时是否锁定屏幕
+#define KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_SUSPEND "screen-locked-when-suspend"
+// 休眠时是否锁定屏幕
+#define KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_HIBERNATE "screen-locked-when-hibernate"
 
 // JK: json key
 #define KSM_INHIBITOR_JK_COOKIE "cookie"
diff --git a/lib/dbus/systemd-login1.cpp b/lib/dbus/systemd-login1.cpp
index dea49e8..e2416a3 100644
--- a/lib/dbus/systemd-login1.cpp
+++ b/lib/dbus/systemd-login1.cpp
@@ -188,6 +188,7 @@ bool SystemdLogin1::canDoMethod(const QString &methodName)
     auto canResult = replyMessage.arguments().takeFirst().toString();
 
     KLOG_DEBUG() << "Function " << methodName << " return " << canResult;
+    // TODO: 需要确认challenge状态是否可以执行电源动作
     return (canResult == "yes" || canResult == "challenge");
 }
 
diff --git a/src/core/power.cpp b/src/core/power.cpp
index 2bc3ac5..87200fb 100644
--- a/src/core/power.cpp
+++ b/src/core/power.cpp
@@ -13,6 +13,7 @@
  */
 
 #include "src/core/power.h"
+#include <QGSettings>
 #include "lib/base/base.h"
 #include "lib/dbus/display-manager.h"
 #include "lib/dbus/screensaver.h"
@@ -22,6 +23,7 @@ namespace Kiran
 {
 Power::Power(QObject *parent) : QObject(parent)
 {
+    this->m_settings = new QGSettings(KSM_SCHEMA_ID, "", this);
 }
 
 void Power::init()
@@ -54,7 +56,7 @@ bool Power::canPowerAction(PowerAction powerAction)
 
 bool Power::doPowerAction(PowerAction powerAction)
 {
-    KLOG_DEBUG() << "Power action: " << powerAction;
+    KLOG_DEBUG() << "Do power action: " << this->powerActionEnum2Str(powerAction);
 
     switch (powerAction)
     {
@@ -84,20 +86,50 @@ bool Power::switchUser()
 
 bool Power::suspend()
 {
+    uint32_t throttle = 0;
+
     RETURN_VAL_IF_TRUE(!SystemdLogin1::getDefault()->canSuspend(), false);
 
-    // 这里忽略锁屏失败的情况
-    ScreenSaver::getDefault()->lock();
-    return SystemdLogin1::getDefault()->suspend();
+    // 挂起之前判断是否锁定屏幕
+    auto lockscreen = this->m_settings->get(KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_SUSPEND).toBool();
+    if (lockscreen)
+    {
+        throttle = ScreenSaver::getDefault()->lockAndThrottle("suspend");
+    }
+
+    auto retval = SystemdLogin1::getDefault()->suspend();
+
+    ScreenSaver::getDefault()->poke();
+    if (throttle)
+    {
+        ScreenSaver::getDefault()->removeThrottle(throttle);
+    }
+
+    return retval;
 }
 
 bool Power::hibernate()
 {
+    uint32_t throttle = 0;
+
     RETURN_VAL_IF_TRUE(!SystemdLogin1::getDefault()->canHibernate(), false);
 
-    // 这里忽略锁屏失败的情况
-    ScreenSaver::getDefault()->lock();
-    return SystemdLogin1::getDefault()->hibernate();
+    // 休眠之前判断是否锁定屏幕
+    auto lockscreen = this->m_settings->get(KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_HIBERNATE).toBool();
+    if (lockscreen)
+    {
+        throttle = ScreenSaver::getDefault()->lockAndThrottle("hibernate");
+    }
+
+    auto retval = SystemdLogin1::getDefault()->hibernate();
+
+    ScreenSaver::getDefault()->poke();
+    if (throttle)
+    {
+        ScreenSaver::getDefault()->removeThrottle(throttle);
+    }
+
+    return retval;
 }
 
 bool Power::shutdown()
@@ -112,4 +144,26 @@ bool Power::reboot()
     return SystemdLogin1::getDefault()->reboot();
 }
 
+QString Power::powerActionEnum2Str(PowerAction powerAction)
+{
+    switch (powerAction)
+    {
+    case PowerAction::POWER_ACTION_SWITCH_USER:
+        return "switch user";
+    case PowerAction::POWER_ACTION_LOGOUT:
+        return "logout";
+    case PowerAction::POWER_ACTION_SUSPEND:
+        return "suspend";
+    case PowerAction::POWER_ACTION_HIBERNATE:
+        return "hibernate";
+    case PowerAction::POWER_ACTION_SHUTDOWN:
+        return "shutdown";
+    case PowerAction::POWER_ACTION_REBOOT:
+        return "reboot";
+    default:
+        break;
+    }
+    return "unknown";
+}
+
 }  // namespace Kiran
\ No newline at end of file
diff --git a/src/core/power.h b/src/core/power.h
index cbc2b21..14bcdda 100644
--- a/src/core/power.h
+++ b/src/core/power.h
@@ -17,6 +17,8 @@
 #include <QObject>
 #include "ksm-i.h"
 
+class QGSettings;
+
 namespace Kiran
 {
 class Power : public QObject
@@ -44,5 +46,10 @@ private:
     bool shutdown();
     // 重启
     bool reboot();
+
+    QString powerActionEnum2Str(PowerAction powerAction);
+
+private:
+    QGSettings *m_settings;
 };
 }  // namespace Kiran
\ No newline at end of file
diff --git a/src/core/presence.cpp b/src/core/presence.cpp
index f41fa24..d7e73f3 100644
--- a/src/core/presence.cpp
+++ b/src/core/presence.cpp
@@ -22,9 +22,6 @@
 
 namespace Kiran
 {
-#define KSM_SCHEMA_ID "com.kylinsec.kiran.session-manager"
-#define KSM_SCHEMA_KEY_IDLE_DELAY "idleDelay"
-
 Presence::Presence(QObject *parent) : QObject(parent),
                                       m_idleMonitorProxy(nullptr),
                                       m_enabledIdleTimeout(true),
diff --git a/src/core/session-manager.cpp b/src/core/session-manager.cpp
index 1f33db7..12dcc15 100644
--- a/src/core/session-manager.cpp
+++ b/src/core/session-manager.cpp
@@ -73,6 +73,39 @@ void SessionManager::start()
     this->processPhase();
 }
 
+bool SessionManager::screenLockedWhenHibernate()
+{
+    return this->m_settings->get(KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_HIBERNATE).toBool();
+}
+
+void SessionManager::setScreenLockedWhenHibernate(bool screenLockedWhenHibernate)
+{
+    if (screenLockedWhenHibernate != this->screenLockedWhenHibernate())
+    {
+        this->m_settings->set(KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_HIBERNATE, screenLockedWhenHibernate);
+        Q_EMIT this->ScreenLockedWhenHibernateChanged(screenLockedWhenHibernate);
+    }
+}
+
+bool SessionManager::screenLockedWhenSuspend()
+{
+    return this->m_settings->get(KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_SUSPEND).toBool();
+}
+
+void SessionManager::setScreenLockedWhenSuspend(bool screenLockedWhenSuspend)
+{
+    if (screenLockedWhenSuspend != this->screenLockedWhenSuspend())
+    {
+        this->m_settings->set(KSM_SCHEMA_KEY_SCREEN_LOCKED_WHEN_SUSPEND, screenLockedWhenSuspend);
+        Q_EMIT this->ScreenLockedWhenSuspendChanged(screenLockedWhenSuspend);
+    }
+}
+
+bool SessionManager::CanHibernate()
+{
+    return this->m_power->canPowerAction(PowerAction::POWER_ACTION_HIBERNATE);
+}
+
 bool SessionManager::CanLogout()
 {
     return true;
@@ -88,6 +121,11 @@ bool SessionManager::CanShutdown()
     return this->m_power->canPowerAction(PowerAction::POWER_ACTION_SHUTDOWN);
 }
 
+bool SessionManager::CanSuspend()
+{
+    return this->m_power->canPowerAction(PowerAction::POWER_ACTION_SUSPEND);
+}
+
 QString SessionManager::GetInhibitor(uint cookie)
 {
     QJsonDocument jsonDoc;
@@ -133,6 +171,21 @@ QString SessionManager::GetInhibitors()
     return QString(jsonDoc.toJson());
 }
 
+void SessionManager::Hibernate()
+{
+    if (this->m_currentPhase > KSMPhase::KSM_PHASE_RUNNING)
+    {
+        DBUS_ERROR_REPLY_AND_RET(QDBusError::InvalidArgs, KSMErrorCode::ERROR_MANAGER_PHASE_INVALID);
+    }
+
+    if (!this->m_power->canPowerAction(PowerAction::POWER_ACTION_HIBERNATE))
+    {
+        DBUS_ERROR_REPLY_AND_RET(QDBusError::AccessDenied, KSMErrorCode::ERROR_MANAGER_POWER_ACTION_UNSUPPORTED);
+    }
+
+    this->m_power->doPowerAction(PowerAction::POWER_ACTION_HIBERNATE);
+}
+
 uint SessionManager::SessionManager::Inhibit(const QString &appID,
                                              uint toplevelXID,
                                              const QString &reason,
@@ -246,6 +299,21 @@ void SessionManager::Shutdown()
     this->startNextPhase();
 }
 
+void SessionManager::Suspend()
+{
+    if (this->m_currentPhase > KSMPhase::KSM_PHASE_RUNNING)
+    {
+        DBUS_ERROR_REPLY_AND_RET(QDBusError::InvalidArgs, KSMErrorCode::ERROR_MANAGER_PHASE_INVALID);
+    }
+
+    if (!this->m_power->canPowerAction(PowerAction::POWER_ACTION_SUSPEND))
+    {
+        DBUS_ERROR_REPLY_AND_RET(QDBusError::AccessDenied, KSMErrorCode::ERROR_MANAGER_POWER_ACTION_UNSUPPORTED);
+    }
+
+    this->m_power->doPowerAction(PowerAction::POWER_ACTION_SUSPEND);
+}
+
 void SessionManager::Uninhibit(uint inhibitCookie)
 {
     auto inhibitor = InhibitorManager::getInstance()->getInhibitor(inhibitCookie);
diff --git a/src/core/session-manager.h b/src/core/session-manager.h
index 8e6ffab..3d934e2 100644
--- a/src/core/session-manager.h
+++ b/src/core/session-manager.h
@@ -40,6 +40,9 @@ class SessionManager : public QObject,
                        protected QDBusContext
 {
     Q_OBJECT
+
+    Q_PROPERTY(bool ScreenLockedWhenHibernate READ screenLockedWhenHibernate WRITE setScreenLockedWhenHibernate)
+    Q_PROPERTY(bool ScreenLockedWhenSuspend READ screenLockedWhenSuspend WRITE setScreenLockedWhenSuspend)
 public:
     SessionManager(AppManager *appManager,
                    ClientManager *clientManager,
@@ -57,13 +60,22 @@ public:
     // 会话开始
     void start();
 
+    bool screenLockedWhenHibernate();
+    void setScreenLockedWhenHibernate(bool screenLockedWhenHibernate);
+
+    bool screenLockedWhenSuspend();
+    void setScreenLockedWhenSuspend(bool screenLockedWhenSuspend);
+
 public Q_SLOTS:  // METHODS
+    bool CanHibernate();
     bool CanLogout();
     bool CanReboot();
     bool CanShutdown();
+    bool CanSuspend();
     // 获取抑制器
     QString GetInhibitor(uint cookie);
     QString GetInhibitors();
+    void Hibernate();
     // 添加抑制器
     uint Inhibit(const QString &appID, uint toplevelXID, const QString &reason, uint flags);
     // 判断指定flags的抑制器是否存在
@@ -76,12 +88,15 @@ public Q_SLOTS:  // METHODS
     // 添加会话程序的环境变量
     void Setenv(const QString &name, const QString &value);
     void Shutdown();
+    void Suspend();
     // 删除抑制器
     void Uninhibit(uint inhibitCookie);
 Q_SIGNALS:  // SIGNALS
     void InhibitorAdded(uint cookie);
     void InhibitorRemoved(uint cookie);
     void PhaseChanged(int phase);
+    void ScreenLockedWhenHibernateChanged(bool screen_locked_when_hibernate);
+    void ScreenLockedWhenSuspendChanged(bool screen_locked_when_suspend);
 
 public Q_SLOTS:
     // 应用启动超时
-- 
2.36.1