From 0cbdbb0c96ce2f780c0a955c4d78a207d65cc1e7 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Sun, 25 Oct 2026 01:35:40 +0800 Subject: [PATCH] [feat] add lock screen on disconnect option, refs #125 --- apps/desktop/src/common/screen_lock.h | 18 ++++++ .../src/config_center/config_center.cpp | 14 +++++ .../desktop/src/config_center/config_center.h | 3 + .../src/gui/application/gui_application.cpp | 16 +++++ .../assets/localization/localization_data.h | 2 + .../src/gui/runtime/connection_runtime.cpp | 14 +++++ .../src/gui/runtime/disconnect_lock_policy.h | 41 +++++++++++++ apps/desktop/src/gui/runtime/gui_runtime.h | 3 + .../src/gui/runtime/peer_event_handler.cpp | 1 + apps/desktop/src/gui/ui/main_window.slint | 12 +++- apps/desktop/src/gui/ui/ui_localization.h | 1 + .../linux/headless/headless_settings.cpp | 3 + .../src/platform/linux/screen_lock.cpp | 60 +++++++++++++++++++ .../src/platform/macos/screen_lock.cpp | 35 +++++++++++ .../application/unattended_application.cpp | 2 + .../src/platform/windows/screen_lock.cpp | 21 +++++++ apps/desktop/xmake/targets.lua | 7 ++- 17 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 apps/desktop/src/common/screen_lock.h create mode 100644 apps/desktop/src/gui/runtime/disconnect_lock_policy.h create mode 100644 apps/desktop/src/platform/linux/screen_lock.cpp create mode 100644 apps/desktop/src/platform/macos/screen_lock.cpp create mode 100644 apps/desktop/src/platform/windows/screen_lock.cpp diff --git a/apps/desktop/src/common/screen_lock.h b/apps/desktop/src/common/screen_lock.h new file mode 100644 index 00000000..08ba549c --- /dev/null +++ b/apps/desktop/src/common/screen_lock.h @@ -0,0 +1,18 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-10-25 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _SCREEN_LOCK_H_ +#define _SCREEN_LOCK_H_ + +namespace crossdesk { + +// Called on the application thread. True means the OS accepted the request, +// not that the asynchronous transition to the lock screen has finished. +bool LockScreen(); + +} // namespace crossdesk + +#endif diff --git a/apps/desktop/src/config_center/config_center.cpp b/apps/desktop/src/config_center/config_center.cpp index b873f354..6860fd3f 100644 --- a/apps/desktop/src/config_center/config_center.cpp +++ b/apps/desktop/src/config_center/config_center.cpp @@ -137,6 +137,8 @@ int ConfigCenter::Load() { enable_daemon_ = ini_->GetBoolValue(section_, "enable_daemon", enable_daemon_); enable_privacy_screen_.store( ini_->GetBoolValue(section_, "enable_privacy_screen", true)); + lock_on_disconnect_.store( + ini_->GetBoolValue(section_, "lock_on_disconnect", false)); save_remote_previews_.store( ini_->GetBoolValue(section_, "save_remote_previews", true)); portable_service_prompt_suppressed_ = @@ -232,6 +234,7 @@ int ConfigCenter::Save() { ini_->SetBoolValue(section_, "enable_daemon", enable_daemon_); ini_->SetBoolValue(section_, "enable_privacy_screen", enable_privacy_screen_.load()); + ini_->SetBoolValue(section_, "lock_on_disconnect", lock_on_disconnect_.load()); ini_->SetBoolValue(section_, "save_remote_previews", save_remote_previews_.load()); ini_->SetBoolValue(section_, "portable_service_prompt_suppressed", @@ -409,6 +412,17 @@ int ConfigCenter::SetPrivacyScreen(bool enable_privacy_screen) { return 0; } +int ConfigCenter::SetLockOnDisconnect(bool enabled) { + if (StoreValues({{"lock_on_disconnect", enabled ? "true" : "false"}}) != 0) + return -1; + lock_on_disconnect_.store(enabled); + return 0; +} + +bool ConfigCenter::IsLockOnDisconnect() const { + return lock_on_disconnect_.load(); +} + int ConfigCenter::SetSaveRemotePreviews(bool enabled) { if (StoreValues({{"save_remote_previews", enabled ? "true" : "false"}}) != 0) { return -1; diff --git a/apps/desktop/src/config_center/config_center.h b/apps/desktop/src/config_center/config_center.h index fef88eea..68915f67 100644 --- a/apps/desktop/src/config_center/config_center.h +++ b/apps/desktop/src/config_center/config_center.h @@ -49,6 +49,7 @@ class ConfigCenter { int SetAutostart(bool enable_autostart); int SetDaemon(bool enable_daemon); int SetPrivacyScreen(bool enable_privacy_screen); + int SetLockOnDisconnect(bool enabled); int SetSaveRemotePreviews(bool enabled); int SetPortableServicePromptSuppressed(bool suppressed); int SetSponsorshipPromptDismissedVersion(const std::string& version); @@ -73,6 +74,7 @@ class ConfigCenter { bool IsEnableAutostart() const; bool IsEnableDaemon() const; bool IsEnablePrivacyScreen() const; + bool IsLockOnDisconnect() const; bool IsSaveRemotePreviews() const; bool IsPortableServicePromptSuppressed() const; std::string GetSponsorshipPromptDismissedVersion() const; @@ -104,6 +106,7 @@ class ConfigCenter { bool enable_autostart_ = false; bool enable_daemon_ = false; std::atomic enable_privacy_screen_{true}; + std::atomic lock_on_disconnect_{false}; std::atomic save_remote_previews_{true}; bool portable_service_prompt_suppressed_ = false; std::string sponsorship_prompt_dismissed_version_; diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index 0cf7c31e..9e9fb20b 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -1501,6 +1501,7 @@ void GuiApplication::ResetSettingsUi(bool gallery_window) { main->set_save_remote_previews(config_center_->IsSaveRemotePreviews()); main->set_privacy_on_connect_enabled( config_center_->IsEnablePrivacyScreen()); + main->set_lock_on_disconnect_enabled(config_center_->IsLockOnDisconnect()); #ifdef _WIN32 main->set_capture_method_visible(true); main->set_capture_method_index( @@ -1508,8 +1509,10 @@ void GuiApplication::ResetSettingsUi(bool gallery_window) { #endif #if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__)) main->set_privacy_setting_available(true); + main->set_lock_on_disconnect_available(true); #else main->set_privacy_setting_available(false); + main->set_lock_on_disconnect_available(false); #endif #if defined(__OHOS__) // The sandbox default is not the system desktop. Show the actual receive @@ -2583,6 +2586,7 @@ void GuiApplication::Tick() { HandlePendingPresenceProbe(); HandlePresenceProbeTimeout(); HandlePrivacy(); + HandleScreenLock(); HandleWindowsServiceIntegration(); #if (defined(__linux__) && !defined(__OHOS__)) && !defined(__APPLE__) SyncXWaylandWindowActivation(); @@ -4541,6 +4545,17 @@ bool GuiApplication::SaveSettingsFromUi(bool gallery_window) { : "Could not save direct IP settings. Try again.")); return false; } +#if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__)) + if (config_center_->SetLockOnDisconnect(main->get_lock_on_disconnect_enabled()) != 0) { + if (wall_changed && !ui_->gallery.Save(ui_->gallery_path)) + LOG_ERROR("Failed to restore DeskHub settings after lock preference save failure"); + main->set_settings_save_error(UiText( + localization_language_index_ == 0 ? "无法保存断开后锁屏设置,请重试。" + : localization_language_index_ == 2 ? "Не удалось сохранить настройку блокировки. Повторите попытку." + : "Could not save lock screen settings. Try again.")); + return false; + } +#endif language_button_value_ = localization::detail::ClampLanguageIndex(main->get_language_index()); video_encode_format_button_value_ = std::clamp(main->get_codec_index(), 0, 1); @@ -6518,6 +6533,7 @@ void GuiApplication::Cleanup() { keyboard_.ReleaseAllRemotePressedKeys("application_exit"); devices_.DestroyDevices(); privacy_.Shutdown(); + HandleScreenLock(); devices_.DestroyFactories(); CloseAllRemoteSessions(); WaitForSessionCleanup(); diff --git a/apps/desktop/src/gui/assets/localization/localization_data.h b/apps/desktop/src/gui/assets/localization/localization_data.h index 5c348b58..7ba4ec0e 100644 --- a/apps/desktop/src/gui/assets/localization/localization_data.h +++ b/apps/desktop/src/gui/assets/localization/localization_data.h @@ -339,6 +339,8 @@ struct TranslationRow { X(enable_daemon, u8"启用守护进程:", "Enable Daemon:", u8"Включить демон:") \ X(privacy_on_connect, u8"被连接时开启隐私屏:", "Privacy screen on connect:", \ u8"Приватность при подключении:") \ + X(lock_on_disconnect, u8"连接断开后锁屏:", "Lock screen on disconnect:", \ + u8"Блокировка при отключении:") \ X(takes_effect_after_restart, u8"重启后生效", "Takes effect after restart", \ u8"Вступит в силу после перезапуска") \ X(select_file, u8"选择文件发送", "Select File to Send", \ diff --git a/apps/desktop/src/gui/runtime/connection_runtime.cpp b/apps/desktop/src/gui/runtime/connection_runtime.cpp index c7496571..1e5c355e 100644 --- a/apps/desktop/src/gui/runtime/connection_runtime.cpp +++ b/apps/desktop/src/gui/runtime/connection_runtime.cpp @@ -14,6 +14,7 @@ #include "platform.h" #include "platform/video_renderer.h" #include "rd_log.h" +#include "screen_lock.h" #include "runtime/device_presence_request.h" #include "runtime/gui_runtime.h" @@ -198,6 +199,8 @@ void GuiRuntime::HandleServerControllerDisconnected( { std::unique_lock lock(connection_status_mutex_); connection_status_.erase(remote_id); + disconnect_lock_.Disconnected(remote_id, + config_center_->IsLockOnDisconnect()); remote_input_.Disconnect(remote_id); devices_.OnControllerConnectionsChanged(); connection_host_names_.erase(remote_id); @@ -258,6 +261,17 @@ void GuiRuntime::HandleServerControllerDisconnected( audio_capture_ = false; } +void GuiRuntime::HandleScreenLock() { + // Serialize request consumption with incoming admission so a reconnect + // cancels a pending lock before it can interrupt the new controller. + std::unique_lock lock(connection_status_mutex_); + if (!disconnect_lock_.TakeRequest(config_center_->IsLockOnDisconnect())) + return; +#if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__)) + if (LockScreen()) LOG_INFO("Requested screen lock after last controller disconnected"); +#endif +} + int GuiRuntime::RequestSingleDevicePresence(const std::string& remote_id, const char* password, bool remember_password) { diff --git a/apps/desktop/src/gui/runtime/disconnect_lock_policy.h b/apps/desktop/src/gui/runtime/disconnect_lock_policy.h new file mode 100644 index 00000000..89f847a2 --- /dev/null +++ b/apps/desktop/src/gui/runtime/disconnect_lock_policy.h @@ -0,0 +1,41 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-10-25 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _DISCONNECT_LOCK_POLICY_H_ +#define _DISCONNECT_LOCK_POLICY_H_ + +#include +#include +#include + +namespace crossdesk { + +// Guarded by GuiRuntime's connection_status_mutex_. Only incoming sessions +// that reached Connected can request a lock; terminal callbacks may repeat. +class DisconnectLockPolicy { + public: + void Connected(const std::string& remote_id) { + controllers_.insert(remote_id); + pending_ = false; + } + + void Disconnected(const std::string& remote_id, bool enabled) { + if (controllers_.erase(remote_id) && controllers_.empty() && enabled) + pending_ = true; + } + + bool TakeRequest(bool enabled) { + return std::exchange(pending_, false) && enabled && controllers_.empty(); + } + + private: + std::unordered_set controllers_; + bool pending_ = false; +}; + +} // namespace crossdesk + +#endif diff --git a/apps/desktop/src/gui/runtime/gui_runtime.h b/apps/desktop/src/gui/runtime/gui_runtime.h index fea29d9f..d008368a 100644 --- a/apps/desktop/src/gui/runtime/gui_runtime.h +++ b/apps/desktop/src/gui/runtime/gui_runtime.h @@ -22,6 +22,7 @@ #include "network/lan/lan_session_manager.h" #include "privacy_controller.h" #include "runtime/gui_state.h" +#include "runtime/disconnect_lock_policy.h" #include "runtime/announcement_inbox.h" #include "runtime/peer_event_handler.h" #include "runtime/privacy_session_policy.h" @@ -102,6 +103,7 @@ class GuiRuntime : protected gui_detail::GuiState { const char* reason); void HandleWindowsServiceIntegration(); void HandlePrivacy(); + void HandleScreenLock(); bool IsAuthorizedController(const std::string& remote_id); void SetControllerPrivacySupport(const std::string& remote_id, bool supported); // Caller holds connection_status_mutex_ exclusively. Turns privacy off when @@ -157,6 +159,7 @@ class GuiRuntime : protected gui_detail::GuiState { bool unattended_host_ = false; AnnouncementInbox announcements_; PrivacySessionPolicy privacy_sessions_; + DisconnectLockPolicy disconnect_lock_; std::mutex privacy_commands_mutex_; std::deque> privacy_commands_; uint32_t last_privacy_revision_ = 0; diff --git a/apps/desktop/src/gui/runtime/peer_event_handler.cpp b/apps/desktop/src/gui/runtime/peer_event_handler.cpp index c3ee32d7..406f5d59 100644 --- a/apps/desktop/src/gui/runtime/peer_event_handler.cpp +++ b/apps/desktop/src/gui/runtime/peer_event_handler.cpp @@ -458,6 +458,7 @@ void PeerEventHandler::DispatchConnectionStatus(ConnectionStatus status, { std::unique_lock lock(runtime->connection_status_mutex_); if (status == ConnectionStatus::Connected) { + runtime->disconnect_lock_.Connected(remote_id); // Local privacy requires explicit desktop capability. Host info may // arrive before Connected; consume that saved capability here. const bool automatic_enable = runtime->privacy_sessions_.Connected( diff --git a/apps/desktop/src/gui/ui/main_window.slint b/apps/desktop/src/gui/ui/main_window.slint index 2cad8a3b..65dc844d 100644 --- a/apps/desktop/src/gui/ui/main_window.slint +++ b/apps/desktop/src/gui/ui/main_window.slint @@ -80,6 +80,7 @@ export global UiStrings { in-out property save-remote-previews: "Save remote screen previews:"; in-out property unknown-platform: "Unknown platform"; in-out property privacy-on-connect: "Privacy screen on connect"; + in-out property lock-on-disconnect: "Lock screen on disconnect"; in-out property file-save-path: "File save path"; in-out property default-desktop: "Desktop"; in-out property server-host: "Server host"; @@ -648,6 +649,8 @@ export component MainWindow inherits Window { // Show the preference on every platform; only adapted hosts can change it. in property privacy-setting-available: false; in-out property privacy-on-connect-enabled: true; + in property lock-on-disconnect-available: false; + in-out property lock-on-disconnect-enabled: false; in-out property file-save-path: ""; in-out property server-host: ""; in-out property server-port: ""; @@ -2686,8 +2689,13 @@ export component MainWindow inherits Window { font-size: 11px; color: WorkbenchStyle.muted; wrap: word-wrap; } SettingsGroup { - title: GalleryStrings.tr("隐私保护", "Privacy", "Приватность"); rows: 1; - SettingsToggleRow { label: UiStrings.privacy-on-connect; checked <=> root.privacy-on-connect-enabled; enabled: root.privacy-setting-available; divider: false; } + title: GalleryStrings.tr("隐私保护", "Privacy", "Приватность"); rows: 2; + SettingsToggleRow { label: UiStrings.privacy-on-connect; checked <=> root.privacy-on-connect-enabled; enabled: root.privacy-setting-available; } + SettingsToggleRow { label: UiStrings.lock-on-disconnect; checked <=> root.lock-on-disconnect-enabled; enabled: root.lock-on-disconnect-available; divider: false; } + } + Text { + text: GalleryStrings.tr("最后一个远程控制端断开后,自动锁定本机。", "Lock this device when the last remote controller disconnects.", "Блокировать это устройство после отключения последнего управляющего клиента."); + font-size: 11px; color: WorkbenchStyle.muted; wrap: word-wrap; } SettingsGroup { title: GalleryStrings.tr("本地存储", "Local storage", "Локальное хранение"); rows: 2; diff --git a/apps/desktop/src/gui/ui/ui_localization.h b/apps/desktop/src/gui/ui/ui_localization.h index f6792573..b9bec49e 100644 --- a/apps/desktop/src/gui/ui/ui_localization.h +++ b/apps/desktop/src/gui/ui/ui_localization.h @@ -102,6 +102,7 @@ inline int ApplyMainWindowStrings( strings.set_save_remote_previews(Text(localization::save_remote_previews[language])); strings.set_unknown_platform(Text(localization::unknown_platform[language])); strings.set_privacy_on_connect(Text(localization::privacy_on_connect[language])); + strings.set_lock_on_disconnect(Text(localization::lock_on_disconnect[language])); strings.set_file_save_path( Text(localization::file_transfer_save_path[language])); strings.set_default_desktop(Text(localization::default_desktop[language])); diff --git a/apps/desktop/src/platform/linux/headless/headless_settings.cpp b/apps/desktop/src/platform/linux/headless/headless_settings.cpp index 8000346a..3f585c7c 100644 --- a/apps/desktop/src/platform/linux/headless/headless_settings.cpp +++ b/apps/desktop/src/platform/linux/headless/headless_settings.cpp @@ -40,6 +40,7 @@ std::vector GetConsoleSettings(const ConfigCenter& config) { {"server_host", "console_setting_host", config.GetSignalServerHost(), "example.com"}, {"server_port", "console_setting_port", std::to_string(config.GetSignalServerPort()), "1..65535"}, {"privacy", "console_setting_privacy", OnOff(config.IsEnablePrivacyScreen()), "on / off"}, + {"lock_on_disconnect", "lock_on_disconnect", OnOff(config.IsLockOnDisconnect()), "on / off"}, {"file_path", "console_setting_files", config.GetFileTransferSavePath().empty() ? "default" : config.GetFileTransferSavePath(), "/path/to/directory / default"}, {"autostart", "console_setting_autostart", OnOff(config.IsEnableAutostart()), "on / off"}, {"daemon", "console_setting_daemon", OnOff(config.IsEnableDaemon()), "on / off"}, @@ -120,6 +121,8 @@ SettingResult ApplyConsoleSetting(ConfigCenter& config, const std::string& key, save = [&config, enabled] { return config.SetPrivacyScreen(enabled); }; effect = SettingEffect::next_connection; message = "console_settings_next_connection"; + } else if (key == "lock_on_disconnect") { + save = [&config, enabled] { return config.SetLockOnDisconnect(enabled); }; } else if (key == "autostart") { save = [&config, enabled] { return config.SetAutostart(enabled); }; effect = SettingEffect::next_login; diff --git a/apps/desktop/src/platform/linux/screen_lock.cpp b/apps/desktop/src/platform/linux/screen_lock.cpp new file mode 100644 index 00000000..92eeafa2 --- /dev/null +++ b/apps/desktop/src/platform/linux/screen_lock.cpp @@ -0,0 +1,60 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-10-25 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#include "screen_lock.h" + +#include + +#include "rd_log.h" + +namespace crossdesk { + +bool LockScreen() { + dbus_threads_init_default(); + DBusError error; + dbus_error_init(&error); + DBusConnection* connection = dbus_bus_get_private(DBUS_BUS_SESSION, &error); + if (!connection) { + LOG_WARN("Cannot connect to screen lock service: {}", + error.message ? error.message : "session bus unavailable"); + dbus_error_free(&error); + return false; + } + dbus_connection_set_exit_on_disconnect(connection, false); + + // Address the current desktop's session bus, including headless desktops; + // never lock every logged-in user or an unrelated system login session. + struct Service { const char* name; const char* path; }; + constexpr Service services[] = { + {"org.freedesktop.ScreenSaver", "/ScreenSaver"}, + {"org.gnome.ScreenSaver", "/org/gnome/ScreenSaver"}, + {"org.cinnamon.ScreenSaver", "/org/cinnamon/ScreenSaver"}, + {"org.mate.ScreenSaver", "/org/mate/ScreenSaver"}, + {"org.xfce.ScreenSaver", "/org/xfce/ScreenSaver"}, + }; + bool accepted = false; + for (const auto& service : services) { + DBusMessage* message = dbus_message_new_method_call( + service.name, service.path, service.name, "Lock"); + if (!message) break; + dbus_message_set_auto_start(message, false); + DBusMessage* reply = dbus_connection_send_with_reply_and_block( + connection, message, 1000, &error); + dbus_message_unref(message); + if (reply) { + accepted = dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN; + dbus_message_unref(reply); + } + dbus_error_free(&error); + if (accepted) break; + } + dbus_connection_close(connection); + dbus_connection_unref(connection); + if (!accepted) LOG_WARN("No desktop screen lock service accepted the request"); + return accepted; +} + +} // namespace crossdesk diff --git a/apps/desktop/src/platform/macos/screen_lock.cpp b/apps/desktop/src/platform/macos/screen_lock.cpp new file mode 100644 index 00000000..861dcf41 --- /dev/null +++ b/apps/desktop/src/platform/macos/screen_lock.cpp @@ -0,0 +1,35 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-10-25 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#include "screen_lock.h" + +#include + +#include "rd_log.h" + +namespace crossdesk { + +bool LockScreen() { + // macOS has no public immediate-lock API. Resolve login.framework at + // runtime so an OS without this private entry point fails gracefully. + static void* framework = dlopen( + "/System/Library/PrivateFrameworks/login.framework/login", + RTLD_LAZY | RTLD_LOCAL); + using LockScreenFunction = int (*)(); + static const auto lock = framework + ? reinterpret_cast( + dlsym(framework, "SACLockScreenImmediate")) + : nullptr; + if (!lock) { + LOG_WARN("macOS screen lock API is unavailable"); + return false; + } + const int result = lock(); + if (result != 0) LOG_WARN("macOS screen lock request failed: {}", result); + return result == 0; +} + +} // namespace crossdesk diff --git a/apps/desktop/src/platform/windows/gui/application/unattended_application.cpp b/apps/desktop/src/platform/windows/gui/application/unattended_application.cpp index b8ad2144..a29ba85a 100644 --- a/apps/desktop/src/platform/windows/gui/application/unattended_application.cpp +++ b/apps/desktop/src/platform/windows/gui/application/unattended_application.cpp @@ -45,6 +45,7 @@ int GuiApplication::RunUnattended(void* stop_event) { if (CreateConnectionPeer() != 0) CloseConnectionPeer(); } HandleConnectionStatusChange(); + HandleScreenLock(); HandleWindowsServiceIntegration(); devices_.UpdateInteractions(); ShareLocalCursorState(); @@ -71,6 +72,7 @@ int GuiApplication::RunUnattended(void* stop_event) { devices_.DestroyDevices(); devices_.DestroyFactories(); privacy_.Shutdown(); + HandleScreenLock(); WriteUnattendedStatus(settings_.ActiveIdentity(), false, session); SDL_Quit(); return 0; diff --git a/apps/desktop/src/platform/windows/screen_lock.cpp b/apps/desktop/src/platform/windows/screen_lock.cpp new file mode 100644 index 00000000..6baf2630 --- /dev/null +++ b/apps/desktop/src/platform/windows/screen_lock.cpp @@ -0,0 +1,21 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-10-25 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#include "screen_lock.h" + +#include + +#include "rd_log.h" + +namespace crossdesk { + +bool LockScreen() { + if (LockWorkStation()) return true; + LOG_WARN("LockWorkStation failed: {}", GetLastError()); + return false; +} + +} // namespace crossdesk diff --git a/apps/desktop/xmake/targets.lua b/apps/desktop/xmake/targets.lua index 0a8a39ad..7255b917 100644 --- a/apps/desktop/xmake/targets.lua +++ b/apps/desktop/xmake/targets.lua @@ -86,11 +86,14 @@ function setup_targets() add_packages("concurrentqueue") add_files("apps/desktop/src/common/*.cpp") if is_os("windows") then - add_files("apps/desktop/src/platform/windows/system_info.cpp") + add_files("apps/desktop/src/platform/windows/system_info.cpp", + "apps/desktop/src/platform/windows/screen_lock.cpp") elseif is_os("macosx") then - add_files("apps/desktop/src/platform/macos/system_info.cpp") + add_files("apps/desktop/src/platform/macos/system_info.cpp", + "apps/desktop/src/platform/macos/screen_lock.cpp") elseif is_os("linux") then add_files("apps/desktop/src/platform/linux/system_info.cpp", + "apps/desktop/src/platform/linux/screen_lock.cpp", "apps/desktop/src/platform/linux/common/*.cpp") add_includedirs("apps/desktop/src/platform/linux/common") end