[feat] unify Windows unattended hosting and device identity

This commit is contained in:
dijunkun committed 2026-11-10 10:43:43 +08:00
1 parent 930ccd9ea1
commit 4e2532f33c
67 files changed
+3491 -941

No files matched your search

@@ -3,6 +3,7 @@
#include <SDL3/SDL_audio.h>
#include <SDL3/SDL_error.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_timer.h>
#include <display_stream_id.h>
#include <remote_action.h>
@@ -50,7 +51,7 @@ SessionDeviceManager::SessionDeviceManager(GuiRuntime &owner)
owner_.WithController(
id, [&](MiniRtcPeerPtr* peer, const std::string& wire) {
frame.captured_timestamp = MiniRtcGetSystemTimeMicros(peer);
return MiniRtcSendAudioFrameToPeer(peer, &frame,
return PeerSendAudioFrameToPeer(peer, &frame,
owner_.audio_label_.c_str(),
wire.data(), wire.size());
});
@@ -58,6 +59,19 @@ SessionDeviceManager::SessionDeviceManager(GuiRuntime &owner)
SessionDeviceManager::~SessionDeviceManager() { speaker_capture_.Shutdown(); }
#ifdef _WIN32
bool SessionDeviceManager::OwnsNativeCursor() const {
const auto* windows = dynamic_cast<ScreenCapturerWin*>(screen_capturer_);
return windows && windows->OwnsNativeCursor();
}
void SessionDeviceManager::SetNativeCursorPolicy(bool default_enabled, bool current_enabled) {
owner_.host_cursor_default_ = default_enabled;
owner_.host_cursor_preference_ = current_enabled;
if (auto* windows = dynamic_cast<ScreenCapturerWin*>(screen_capturer_))
windows->SetNativeCursorPolicy(default_enabled, current_enabled);
}
#endif
bool SessionDeviceManager::ShouldSendCapturedFrame(
std::chrono::steady_clock::time_point now, int fps) {
const auto interval =
@@ -148,7 +162,8 @@ int SessionDeviceManager::InitializeScreenCapturer() {
screen_capturer_->SetPrivacyController(&owner_.privacy_);
#ifdef _WIN32
if (auto windows = dynamic_cast<ScreenCapturerWin*>(screen_capturer_)) {
windows->SetCaptureMethod(owner_.config_center_->GetScreenCaptureMethod());
windows->SetUnattendedHost(owner_.unattended_host_);
windows->SetNativeCursorPolicy(owner_.host_cursor_default_, owner_.host_cursor_preference_);
}
#endif
@@ -176,6 +191,7 @@ int SessionDeviceManager::InitializeScreenCapturer() {
const auto now_time = std::chrono::steady_clock::now();
bool from_secure_desktop = false;
#ifdef _WIN32
owner_.host_capture_tick_ = SDL_GetTicks();
from_secure_desktop =
ScreenCapturerWin::CurrentFrameIsFromSecureDesktop();
#endif
@@ -236,6 +252,7 @@ int SessionDeviceManager::InitializeScreenCapturer() {
const auto send_started = std::chrono::steady_clock::now();
uint64_t send_errors = 0;
for (const std::string &remote_id : connected_remote_ids) {
if (owner_.managed_host_frontend_ && remote_id.rfind("lan/", 0) != 0) continue;
if (!owner_.preview_subscriptions_.ShouldSend(remote_id, now_time,
stream_id)) continue;
if (owner_.WithController(
@@ -243,7 +260,7 @@ int SessionDeviceManager::InitializeScreenCapturer() {
if (resumed_after_gap || stream_changed)
MiniRtcRequestVideoKeyFrame(peer, stream_id.c_str());
frame.captured_timestamp = MiniRtcGetSystemTimeMicros(peer);
return MiniRtcSendVideoFrameToPeer(peer, &frame, stream_id.c_str(),
return PeerSendVideoFrameToPeer(peer, &frame, stream_id.c_str(),
wire.data(), wire.size());
}) != 0)
++send_errors;
@@ -702,14 +719,20 @@ void SessionDeviceManager::UpdateInteractions() {
#endif
if (owner_.start_screen_capturer_ && !owner_.screen_capturer_is_started_) {
if (StartScreenCapturer() == 0) {
owner_.screen_capturer_is_started_ = true;
const auto now = std::chrono::steady_clock::now();
if (now >= next_capture_start_retry_) {
next_capture_start_retry_ = now + std::chrono::seconds(1);
if (StartScreenCapturer() == 0) {
owner_.screen_capturer_is_started_ = true;
next_capture_start_retry_ = {};
}
}
} else if (!owner_.start_screen_capturer_ &&
owner_.screen_capturer_is_started_) {
StopScreenCapturer();
owner_.screen_capturer_is_started_ = false;
}
if (!owner_.start_screen_capturer_) next_capture_start_retry_ = {};
UpdateScreenSharingPower();
@@ -66,6 +66,8 @@ public:
return remote_mouse_buttons_.Apply(remote_id, flag, std::move(send));
}
#ifdef _WIN32
bool OwnsNativeCursor() const;
void SetNativeCursorPolicy(bool default_enabled, bool current_enabled);
std::string SendServiceMouseCommand(int x, int y, int wheel, int flag);
#endif
// Requests are asynchronous; Stop drains/revokes audio delivery before
@@ -145,6 +147,7 @@ private:
#endif
std::chrono::steady_clock::time_point last_frame_time_{};
std::chrono::steady_clock::time_point next_frame_deadline_{};
std::chrono::steady_clock::time_point next_capture_start_retry_{};
std::mutex capture_metrics_mutex_;
std::chrono::steady_clock::time_point capture_metrics_started_{};
std::chrono::steady_clock::time_point last_capture_callback_{};
@@ -126,7 +126,7 @@ void FileTransferManager::ProcessQueue(std::shared_ptr<RemoteSession> props) {
auto callback = events->EnterCallback();
if (!callback || !events->DataAllowed()) return -1;
const auto wire_id = events->WireId(file.remote_id);
return MiniRtcSendReliableDataFrameToPeer(
return PeerSendReliableDataFrameToPeer(
peer, data, size, file.file_label.c_str(),
wire_id.data(), wire_id.size());
}, file_transfer::Session::Progress{});
@@ -176,7 +176,7 @@ void FileTransferManager::ProcessQueue(std::shared_ptr<RemoteSession> props) {
// Release that reservation even though no data chunk was ever sent.
const auto packet = EncodeFileCancel(id, 0);
const auto wire_id = events->WireId(file.remote_id);
MiniRtcSendReliableDataFrameToPeer(peer, packet.data(), packet.size(),
PeerSendReliableDataFrameToPeer(peer, packet.data(), packet.size(),
file.file_label.c_str(), wire_id.data(), wire_id.size());
}
}
@@ -382,7 +382,7 @@ std::shared_ptr<FileReceiver> FileTransferManager::ReceiverFor(
if (!callback || !events->DataAllowed()) return -1;
const auto bytes = EncodeFileTransferAck(ack);
const auto wire_id = events->WireId(remote);
return MiniRtcSendReliableDataFrameToPeer(
return PeerSendReliableDataFrameToPeer(
peer, bytes.data(), bytes.size(), kFileFeedbackStream,
wire_id.data(), wire_id.size());
});
@@ -533,7 +533,7 @@ bool FileTransferManager::SendBrowser(const std::string& remote, const FileBrows
const auto data = message.dump(-1, ' ', false, FileBrowser::Json::error_handler_t::replace);
if (!peer || data.size() > 60000) return false;
const auto wire_id = events->WireId(remote);
return MiniRtcSendReliableDataFrameToPeer(peer, data.data(), data.size(),
return PeerSendReliableDataFrameToPeer(peer, data.data(), data.size(),
owner_.control_data_label_.c_str(), wire_id.data(), wire_id.size()) == 0;
}
@@ -34,9 +34,6 @@
namespace crossdesk {
namespace {
constexpr uint32_t kCacheV3Magic = 0x33444358; // "XCD3"
constexpr uint32_t kCacheV3Version = 3;
#if defined(__linux__) && !defined(__OHOS__)
constexpr const char *kCredentialCacheNames[] = {
"secure_cache_v3.enc", "secure_cache_v2.enc", "secure_cache.enc"};
@@ -132,16 +129,6 @@ void CopyString(char (&destination)[Size], const char *source) {
}
}
uint32_t CacheChecksum(const void *data, size_t size) {
const auto *bytes = static_cast<const unsigned char *>(data);
uint32_t hash = 2166136261u;
for (size_t i = 0; i < size; ++i) {
hash ^= bytes[i];
hash *= 16777619u;
}
return hash;
}
std::filesystem::path TemporaryPathFor(
const std::filesystem::path &target) {
const auto timestamp = std::chrono::steady_clock::now()
@@ -259,6 +246,15 @@ bool WriteFileAtomically(const std::filesystem::path &target,
SettingsManager::SettingsManager(GuiRuntime &owner) : owner_(owner) {}
bool SettingsManager::ApplyHostIdentity(const std::string& identity, bool self_hosted) {
if (identity.size() != 16 || identity[9] != '@' ||
identity.substr(0, 9).find_first_not_of("0123456789") != std::string::npos)
return false;
std::lock_guard lock(cache_mutex_);
ActivateIdentity(identity.c_str(), self_hosted);
return SaveLocked() == 0;
}
int SettingsManager::Save() {
std::lock_guard<std::mutex> lock(cache_mutex_);
return SaveLocked();
@@ -271,8 +267,8 @@ int SettingsManager::SaveLocked() {
std::memcpy(cache_v2_.iv, owner_.aes128_iv_, sizeof(owner_.aes128_iv_));
CopyString(cache_v2_.self_hosted_id, owner_.self_hosted_id_);
cache_v3_.magic = kCacheV3Magic;
cache_v3_.version = kCacheV3Version;
cache_v3_.magic = credential_cache::kMagic;
cache_v3_.version = credential_cache::kVersion;
cache_v3_.base = cache_v2_;
cache_v3_.pending_identity[sizeof(cache_v3_.pending_identity) - 1] = '\0';
cache_v3_.pending_server_host[sizeof(cache_v3_.pending_server_host) - 1] =
@@ -280,7 +276,7 @@ int SettingsManager::SaveLocked() {
cache_v3_.pending_request_id[sizeof(cache_v3_.pending_request_id) - 1] =
'\0';
cache_v3_.checksum =
CacheChecksum(&cache_v3_, offsetof(CacheV3, checksum));
credential_cache::Checksum(&cache_v3_, offsetof(CacheV3, checksum));
if (!WriteFileAtomically(owner_.cache_path_ + "/secure_cache_v3.enc",
&cache_v3_, sizeof(cache_v3_))) {
@@ -302,7 +298,7 @@ int SettingsManager::SaveLocked() {
LOG_WARN("Failed to update legacy v1 credential cache");
}
return 0;
return owner_.PublishSharedIdentity() ? 0 : -1;
}
bool SettingsManager::ReadV3Locked() {
@@ -325,9 +321,7 @@ bool SettingsManager::ReadV3Locked() {
return false;
}
#endif
if (loaded.magic != kCacheV3Magic ||
loaded.version != kCacheV3Version ||
loaded.checksum != CacheChecksum(&loaded, offsetof(CacheV3, checksum))) {
if (!credential_cache::Valid(loaded)) {
LOG_WARN("Ignore invalid v3 credential cache");
return false;
}
@@ -7,6 +7,7 @@
#include <unordered_map>
#include "thumbnail.h"
#include "common/credential_cache.h"
namespace crossdesk {
@@ -21,6 +22,7 @@ public:
int Save();
int Load();
std::string ActiveIdentity() const;
bool ApplyHostIdentity(const std::string& identity, bool self_hosted);
int LoadRecentConnectionAliases();
int SaveRecentConnectionAliases() const;
@@ -56,46 +58,9 @@ public:
bool ClearPendingPasswordChange();
private:
struct CacheV1 {
char client_id_with_password[17];
int language;
// Reserved legacy slots: retain the on-disk cache layout.
int legacy_video_quality;
int legacy_video_frame_rate;
int video_encode_format;
bool legacy_enable_hardware_video_codec;
bool enable_turn;
bool enable_srtp;
unsigned char key[16];
unsigned char iv[16];
};
struct CacheV2 {
char client_id_with_password[17];
int language;
// Reserved legacy slots: retain the on-disk cache layout.
int legacy_video_quality;
int legacy_video_frame_rate;
int video_encode_format;
bool legacy_enable_hardware_video_codec;
bool enable_turn;
bool enable_srtp;
unsigned char key[16];
unsigned char iv[16];
char self_hosted_id[17];
};
struct CacheV3 {
uint32_t magic;
uint32_t version;
CacheV2 base;
char pending_identity[17];
char pending_server_host[256];
int pending_server_port;
bool pending_self_hosted;
char pending_request_id[64];
uint32_t checksum;
};
using CacheV1 = credential_cache::CacheV1;
using CacheV2 = credential_cache::CacheV2;
using CacheV3 = credential_cache::CacheV3;
int SaveLocked();
bool ReadV3Locked();