mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-11-07 06:39:10 +08:00
[feat] expose system controls and verify remote unlock completion
This commit is contained in:
@@ -12,6 +12,8 @@ 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();
|
||||
enum class ScreenLockState { unknown, unlocked, locked };
|
||||
ScreenLockState QueryScreenLock();
|
||||
|
||||
} // namespace crossdesk
|
||||
|
||||
|
||||
@@ -2609,6 +2609,7 @@ void GuiApplication::Tick() {
|
||||
}
|
||||
}
|
||||
clipboard_.ApplyPendingRemoteText();
|
||||
ProcessAutomationRequests();
|
||||
#ifndef __OHOS__
|
||||
std::string activation_token;
|
||||
bool activation_requested =
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "runtime/gui_runtime.h"
|
||||
#include <stream_names.h>
|
||||
#include "screen_lock.h"
|
||||
|
||||
namespace crossdesk {
|
||||
void GuiRuntime::HandleAutomation(const std::string& remote_id,
|
||||
@@ -21,7 +22,11 @@ void GuiRuntime::HandleAutomation(const std::string& remote_id,
|
||||
try {
|
||||
const auto method = request.value("method", std::string{});
|
||||
if (method == "capabilities") {
|
||||
reply(true, {{"input_ack", true}, {"clipboard", true}, {"files", true}});
|
||||
json capabilities{{"input_ack", true}, {"clipboard", true}, {"files", true}};
|
||||
#if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__))
|
||||
capabilities["system"] = true;
|
||||
#endif
|
||||
reply(true, std::move(capabilities));
|
||||
} else if (method == "input") {
|
||||
RemoteAction action{};
|
||||
if (!request.contains("arguments") ||
|
||||
@@ -37,6 +42,46 @@ void GuiRuntime::HandleAutomation(const std::string& remote_id,
|
||||
if (result == RemoteInputDispatcher::Result::rejected ||
|
||||
result == RemoteInputDispatcher::Result::overflow)
|
||||
reply(false, "INPUT_REJECTED");
|
||||
} else if (method == "system") {
|
||||
const auto operation = request.at("arguments").at("operation").get<std::string>();
|
||||
if (operation != "status" && operation != "lock" && operation != "send_sas") {
|
||||
reply(false, "INVALID_ARGUMENT");
|
||||
return;
|
||||
}
|
||||
std::lock_guard lock(automation_mutex_);
|
||||
if (automation_closing_ || automation_tasks_.size() >= 32) { reply(false, "BUSY"); return; }
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
|
||||
automation_tasks_.push_back([this, source, remote_id, operation, reply, deadline] {
|
||||
if (std::chrono::steady_clock::now() > deadline) { reply(false, "REQUEST_EXPIRED"); return; }
|
||||
if (ControllerEvents(remote_id) != source || !IsAuthorizedController(remote_id)) return;
|
||||
#if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__))
|
||||
if (operation == "status") {
|
||||
const auto state = QueryScreenLock();
|
||||
json result{{"lock_state", state == ScreenLockState::locked ? "locked"
|
||||
: state == ScreenLockState::unlocked ? "unlocked" : "unknown"}};
|
||||
#ifdef _WIN32
|
||||
result["interactive_stage"] = WindowsInputStage();
|
||||
result["service_available"] = local_service_available_.load();
|
||||
result["send_sas_available"] = local_service_available_.load();
|
||||
#else
|
||||
result["send_sas_available"] = false;
|
||||
#endif
|
||||
reply(true, std::move(result));
|
||||
} else if (operation == "lock") {
|
||||
reply(true, {{"accepted", LockScreen()}});
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
if (!local_service_available_.load()) { reply(false, "SERVICE_UNAVAILABLE"); return; }
|
||||
pending_windows_service_sas_.store(true);
|
||||
reply(true, {{"accepted", true}, {"state", "queued"}});
|
||||
#else
|
||||
reply(false, "UNSUPPORTED_OPERATION");
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
reply(false, "UNSUPPORTED_OPERATION");
|
||||
#endif
|
||||
});
|
||||
} else {
|
||||
reply(false, "UNSUPPORTED_OPERATION");
|
||||
}
|
||||
@@ -44,4 +89,15 @@ void GuiRuntime::HandleAutomation(const std::string& remote_id,
|
||||
reply(false, "INVALID_ARGUMENT");
|
||||
}
|
||||
}
|
||||
|
||||
void GuiRuntime::ProcessAutomationRequests() {
|
||||
std::function<void()> task;
|
||||
{
|
||||
std::lock_guard lock(automation_mutex_);
|
||||
if (automation_tasks_.empty() || automation_closing_) return;
|
||||
task = std::move(automation_tasks_.front());
|
||||
automation_tasks_.pop_front();
|
||||
}
|
||||
try { task(); } catch (...) {}
|
||||
}
|
||||
} // namespace crossdesk
|
||||
|
||||
@@ -65,6 +65,11 @@ GuiRuntime::GuiRuntime()
|
||||
}) {}
|
||||
|
||||
GuiRuntime::~GuiRuntime() {
|
||||
{
|
||||
std::lock_guard lock(automation_mutex_);
|
||||
automation_closing_ = true;
|
||||
automation_tasks_.clear();
|
||||
}
|
||||
remote_input_.Stop();
|
||||
#if _WIN32
|
||||
windows_rdp_handoff_allowed_->store(false, std::memory_order_relaxed);
|
||||
|
||||
@@ -104,6 +104,7 @@ class GuiRuntime : protected gui_detail::GuiState {
|
||||
void HandleServerControllerDisconnected(const std::string& remote_id,
|
||||
const char* reason);
|
||||
void HandleWindowsServiceIntegration();
|
||||
void ProcessAutomationRequests();
|
||||
void HandlePrivacy();
|
||||
void HandleScreenLock();
|
||||
bool IsAuthorizedController(const std::string& remote_id);
|
||||
@@ -153,6 +154,9 @@ class GuiRuntime : protected gui_detail::GuiState {
|
||||
void SendRemoteInput(const RemoteAction& action);
|
||||
InputOutcome ApplyRemoteInput(const std::string& remote_id, const RemoteAction& action);
|
||||
void HandleAutomation(const std::string& remote_id, const nlohmann::json& request);
|
||||
std::mutex automation_mutex_;
|
||||
std::deque<std::function<void()>> automation_tasks_;
|
||||
bool automation_closing_ = false;
|
||||
InputOutcome InjectRemoteMouse(const RemoteAction& action, int display);
|
||||
std::string gallery_group_control_id_;
|
||||
|
||||
|
||||
@@ -7,11 +7,54 @@
|
||||
#include "screen_lock.h"
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
ScreenLockState QueryScreenLock() {
|
||||
dbus_threads_init_default();
|
||||
DBusConnection* connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, nullptr);
|
||||
if (!connection) return ScreenLockState::unknown;
|
||||
dbus_connection_set_exit_on_disconnect(connection, false);
|
||||
ScreenLockState state = ScreenLockState::unknown;
|
||||
DBusMessage* query = dbus_message_new_method_call("org.freedesktop.login1",
|
||||
"/org/freedesktop/login1", "org.freedesktop.login1.Manager", "GetSessionByPID");
|
||||
if (query) {
|
||||
dbus_uint32_t pid = getpid();
|
||||
dbus_message_append_args(query, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID);
|
||||
DBusMessage* session = dbus_connection_send_with_reply_and_block(connection, query, 500, nullptr);
|
||||
dbus_message_unref(query);
|
||||
const char* path = nullptr;
|
||||
if (session && dbus_message_get_args(session, nullptr, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
|
||||
query = dbus_message_new_method_call("org.freedesktop.login1", path,
|
||||
"org.freedesktop.DBus.Properties", "Get");
|
||||
const char* interface = "org.freedesktop.login1.Session";
|
||||
const char* property = "LockedHint";
|
||||
if (query) {
|
||||
dbus_message_append_args(query, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID);
|
||||
DBusMessage* reply = dbus_connection_send_with_reply_and_block(connection, query, 500, nullptr);
|
||||
dbus_message_unref(query);
|
||||
DBusMessageIter outer, value;
|
||||
if (reply && dbus_message_iter_init(reply, &outer) && dbus_message_iter_get_arg_type(&outer) == DBUS_TYPE_VARIANT) {
|
||||
dbus_message_iter_recurse(&outer, &value);
|
||||
if (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_BOOLEAN) {
|
||||
dbus_bool_t locked = false;
|
||||
dbus_message_iter_get_basic(&value, &locked);
|
||||
state = locked ? ScreenLockState::locked : ScreenLockState::unlocked;
|
||||
}
|
||||
}
|
||||
if (reply) dbus_message_unref(reply);
|
||||
}
|
||||
}
|
||||
if (session) dbus_message_unref(session);
|
||||
}
|
||||
dbus_connection_close(connection);
|
||||
dbus_connection_unref(connection);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool LockScreen() {
|
||||
dbus_threads_init_default();
|
||||
DBusError error;
|
||||
|
||||
@@ -7,11 +7,23 @@
|
||||
#include "screen_lock.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
ScreenLockState QueryScreenLock() {
|
||||
const auto session = CGSessionCopyCurrentDictionary();
|
||||
if (!session) return ScreenLockState::unknown;
|
||||
const auto console = CFDictionaryGetValue(session, kCGSessionOnConsoleKey);
|
||||
const auto locked = CFDictionaryGetValue(session, CFSTR("CGSSessionScreenIsLocked"));
|
||||
const auto state = locked == kCFBooleanTrue ? ScreenLockState::locked
|
||||
: console == kCFBooleanTrue ? ScreenLockState::unlocked : ScreenLockState::unknown;
|
||||
CFRelease(session);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool LockScreen() {
|
||||
// macOS has no public immediate-lock API. Resolve login.framework at
|
||||
// runtime so an OS without this private entry point fails gracefully.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "screen_lock.h"
|
||||
#include "service/interactive_desktop.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@@ -12,6 +13,13 @@
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
ScreenLockState QueryScreenLock() {
|
||||
std::string reason;
|
||||
if (IsCurrentSessionUnlocked(&reason)) return ScreenLockState::unlocked;
|
||||
return reason.rfind("session_not_unlocked", 0) == 0 ? ScreenLockState::locked
|
||||
: ScreenLockState::unknown;
|
||||
}
|
||||
|
||||
bool LockScreen() {
|
||||
if (LockWorkStation()) return true;
|
||||
LOG_WARN("LockWorkStation failed: {}", GetLastError());
|
||||
|
||||
@@ -88,7 +88,7 @@ function setup_targets()
|
||||
if is_os("windows") then
|
||||
add_files("apps/desktop/src/platform/windows/system_info.cpp",
|
||||
"apps/desktop/src/platform/windows/screen_lock.cpp")
|
||||
add_syslinks("Advapi32", {public = true})
|
||||
add_syslinks("Advapi32", "Wtsapi32", {public = true})
|
||||
elseif is_os("macosx") then
|
||||
add_files("apps/desktop/src/platform/macos/system_info.cpp",
|
||||
"apps/desktop/src/platform/macos/screen_lock.cpp")
|
||||
|
||||
Reference in New Issue
Block a user