mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-17 17:33:21 +08:00
[fix] keep Windows privacy screen across display changes and validate covers
This commit is contained in:
@@ -64,6 +64,9 @@ class GuiRuntime : protected gui_detail::GuiState {
|
||||
void HandlePrivacy();
|
||||
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
|
||||
// any connected controller lacks support, or on for the first automatic one.
|
||||
void ApplyPrivacyAdmission(bool automatic_enable);
|
||||
void QueuePrivacyCommand(const std::string& remote_id, const PrivacyCommand& command);
|
||||
|
||||
void CloseRemoteSession(std::shared_ptr<RemoteSession> props);
|
||||
|
||||
@@ -363,14 +363,11 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
||||
{
|
||||
std::unique_lock lock(runtime->connection_status_mutex_);
|
||||
if (status == ConnectionStatus::Connected) {
|
||||
// Wait for explicit desktop capability before enabling privacy or
|
||||
// sending privacy messages. Legacy and web controllers never opt in.
|
||||
runtime->privacy_sessions_.Connected(
|
||||
// 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(
|
||||
remote_id, runtime->config_center_->IsEnablePrivacyScreen());
|
||||
if (!runtime->privacy_sessions_.CanEnable() &&
|
||||
runtime->privacy_.Engaged()) {
|
||||
runtime->privacy_.Disable();
|
||||
}
|
||||
runtime->ApplyPrivacyAdmission(automatic_enable);
|
||||
}
|
||||
runtime->connection_status_[remote_id] = status;
|
||||
runtime->devices_.OnControllerConnectionsChanged();
|
||||
|
||||
@@ -25,14 +25,27 @@ void GuiRuntime::SetControllerPrivacySupport(const std::string& remote_id,
|
||||
bool supported) {
|
||||
std::unique_lock lock(connection_status_mutex_);
|
||||
const auto found = connection_status_.find(remote_id);
|
||||
if (found == connection_status_.end() ||
|
||||
found->second != ConnectionStatus::Connected) return;
|
||||
if (found == connection_status_.end()) return;
|
||||
if (found->second != ConnectionStatus::Connected) {
|
||||
if (found->second == ConnectionStatus::Connecting ||
|
||||
found->second == ConnectionStatus::Gathering) {
|
||||
privacy_sessions_.RememberSupport(remote_id, supported);
|
||||
LOG_INFO("Privacy: retained early controller capability, peer={}, supported={}",
|
||||
remote_id, supported);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const bool automatic_enable =
|
||||
privacy_sessions_.SetSupported(remote_id, supported);
|
||||
ApplyPrivacyAdmission(automatic_enable &&
|
||||
config_center_->IsEnablePrivacyScreen());
|
||||
}
|
||||
|
||||
void GuiRuntime::ApplyPrivacyAdmission(bool automatic_enable) {
|
||||
if (!privacy_sessions_.CanEnable()) {
|
||||
if (privacy_.Engaged()) privacy_.Disable();
|
||||
} else if (automatic_enable && config_center_->IsEnablePrivacyScreen()) {
|
||||
} else if (automatic_enable) {
|
||||
privacy_.EnableOnConnection();
|
||||
}
|
||||
}
|
||||
@@ -45,8 +58,10 @@ void GuiRuntime::QueuePrivacyCommand(const std::string& remote_id,
|
||||
found->second != ConnectionStatus::Connected ||
|
||||
!privacy_sessions_.Supports(remote_id)) return;
|
||||
std::lock_guard lock(privacy_commands_mutex_);
|
||||
if (privacy_commands_.size() < 32)
|
||||
privacy_commands_.emplace_back(remote_id, command);
|
||||
if (privacy_commands_.size() >= 32) return;
|
||||
LOG_INFO("Privacy: queued controller command, peer={}, flag={}", remote_id,
|
||||
static_cast<int>(command.flag));
|
||||
privacy_commands_.emplace_back(remote_id, command);
|
||||
}
|
||||
|
||||
void GuiRuntime::HandlePrivacy() {
|
||||
@@ -71,17 +86,15 @@ void GuiRuntime::HandlePrivacy() {
|
||||
}
|
||||
last_privacy_status_tick_ = 0;
|
||||
}
|
||||
auto status = privacy_.Snapshot();
|
||||
PrivacyStatus status{};
|
||||
const uint64_t now = SDL_GetTicks();
|
||||
if (!peer_ || (last_privacy_status_tick_ != 0 &&
|
||||
status.revision == last_privacy_revision_ &&
|
||||
now - last_privacy_status_tick_ < 1000))
|
||||
return;
|
||||
std::vector<std::string> controllers;
|
||||
{
|
||||
std::shared_lock lock(connection_status_mutex_);
|
||||
status = privacy_.Snapshot();
|
||||
for (const auto& [id, state] : connection_status_)
|
||||
if (state == ConnectionStatus::Connected && privacy_sessions_.Supports(id))
|
||||
if (state == ConnectionStatus::Connected &&
|
||||
privacy_sessions_.Supports(id))
|
||||
controllers.push_back(id);
|
||||
if (!privacy_sessions_.CanEnable()) {
|
||||
status.state = PrivacyState::unsupported;
|
||||
@@ -91,6 +104,10 @@ void GuiRuntime::HandlePrivacy() {
|
||||
"A connected controller does not support privacy screen");
|
||||
}
|
||||
}
|
||||
if (!peer_ || (last_privacy_status_tick_ != 0 &&
|
||||
status.revision == last_privacy_revision_ &&
|
||||
now - last_privacy_status_tick_ < 1000))
|
||||
return;
|
||||
RemoteAction action{};
|
||||
action.type = ControlType::privacy_status;
|
||||
action.ps = status;
|
||||
|
||||
@@ -13,19 +13,37 @@
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
// Guarded by GuiRuntime's connection_status_mutex_. Privacy covers the shared
|
||||
// desktop, so every connected controller must explicitly support it.
|
||||
// Guarded by GuiRuntime's connection_status_mutex_. Privacy covers the local
|
||||
// displays, so every connected controller must explicitly support its controls.
|
||||
class PrivacySessionPolicy {
|
||||
public:
|
||||
void Connected(const std::string& remote_id, bool automatic_privacy) {
|
||||
// Web controllers render through the browser and expose no privacy UI.
|
||||
static bool IsWebController(const std::string& remote_id) {
|
||||
return remote_id.find("web") != std::string::npos;
|
||||
}
|
||||
|
||||
bool Connected(const std::string& remote_id, bool automatic_privacy) {
|
||||
const auto [entry, inserted] = controllers_.try_emplace(remote_id, false);
|
||||
if (inserted && controllers_.size() == 1 && automatic_privacy) {
|
||||
if (inserted && controllers_.size() == 1 && automatic_privacy &&
|
||||
!IsWebController(remote_id)) {
|
||||
automatic_controller_ = remote_id;
|
||||
}
|
||||
const auto early = early_support_.find(remote_id);
|
||||
if (early == early_support_.end()) return false;
|
||||
const bool supported = early->second;
|
||||
early_support_.erase(early);
|
||||
return SetSupported(remote_id, supported);
|
||||
}
|
||||
|
||||
// Only transport-authenticated metadata from a connecting session is cached
|
||||
// by the caller. It grants no control until Connected admits that session.
|
||||
void RememberSupport(const std::string& remote_id, bool supported) {
|
||||
early_support_[remote_id] = supported;
|
||||
}
|
||||
|
||||
void Disconnected(const std::string& remote_id) {
|
||||
controllers_.erase(remote_id);
|
||||
early_support_.erase(remote_id);
|
||||
if (automatic_controller_ == remote_id) automatic_controller_.clear();
|
||||
}
|
||||
|
||||
@@ -34,7 +52,7 @@ class PrivacySessionPolicy {
|
||||
bool SetSupported(const std::string& remote_id, bool supported) {
|
||||
const auto entry = controllers_.find(remote_id);
|
||||
if (entry == controllers_.end()) return false;
|
||||
entry->second = supported && remote_id.find("web") == std::string::npos;
|
||||
entry->second = supported && !IsWebController(remote_id);
|
||||
if (automatic_controller_ != remote_id) return false;
|
||||
automatic_controller_.clear();
|
||||
return CanEnable();
|
||||
@@ -53,9 +71,10 @@ class PrivacySessionPolicy {
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, bool> controllers_;
|
||||
std::unordered_map<std::string, bool> early_support_;
|
||||
std::string automatic_controller_;
|
||||
};
|
||||
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user