[fix] hide privacy controls on locked desktops across clients

This commit is contained in:
dijunkun
2026-10-27 03:46:19 +08:00
parent 922cd41f70
commit 0e052e6d7a
15 changed files with 92 additions and 37 deletions
@@ -2168,7 +2168,7 @@ void GuiApplication::BindStreamCallbacks() {
const auto controls = GetPrivacyControlState(
status, props->privacy_status_received_, props->privacy_command_pending_,
now - props->privacy_status_tick_, now - props->privacy_command_tick_);
if (!controls.can_toggle) return;
if (!props->privacy_desktop_available_ || !controls.can_toggle) return;
action.pc.flag = controls.active ? PrivacyCommandFlag::disable
: PrivacyCommandFlag::enable;
action.pc.block_local_input = true;
@@ -3831,8 +3831,10 @@ void GuiApplication::SyncStreamWindow() {
(*ui_->stream)->set_privacy_active(controls.active);
(*ui_->stream)->set_privacy_off(connected && controls.off);
(*ui_->stream)->set_privacy_unsupported(controls.unsupported);
(*ui_->stream)->set_privacy_visible(props->privacy_desktop_available_);
(*ui_->stream)->set_privacy_can_toggle(
props->control_mouse_ && connected && controls.can_toggle);
props->privacy_desktop_available_ && props->control_mouse_ &&
connected && controls.can_toggle);
const int language = localization_language_index_;
std::string text = !connected || !props->privacy_status_received_
? localization::privacy_screen[language]
@@ -193,6 +193,7 @@ struct RemoteSession {
std::mutex privacy_status_mutex_;
PrivacyStatus privacy_status_{};
bool privacy_status_received_ = false;
bool privacy_desktop_available_ = true;
bool privacy_command_pending_ = false;
uint64_t privacy_command_tick_ = 0;
uint32_t privacy_request_revision_ = 0;
+12 -10
View File
@@ -300,6 +300,7 @@ export component StreamWindow inherits Window {
in property <[string]> displays;
in-out property <int> selected-display: 0;
in property <bool> mouse-control-enabled: true;
in property <bool> privacy-visible: true;
in property <bool> privacy-can-toggle: false;
in property <bool> privacy-active: false;
in property <bool> privacy-off: false;
@@ -363,6 +364,7 @@ export component StreamWindow inherits Window {
private property <bool> control-visual-expanded: false;
private property <length> control-toolbar-height: 38px;
private property <length> control-button-step: 32px;
private property <length> control-privacy-width: root.privacy-visible ? root.control-button-step : 0px;
private property <length> control-disconnect-gap: 8px;
private property <length> control-buttons-x: root.control-docked-left ? 43px : 7px;
private property <length> control-panel-gap: 8px;
@@ -517,7 +519,7 @@ export component StreamWindow inherits Window {
if display-button.has-hover { return display-button.tooltip-info; }
if shortcut-button.has-hover { return shortcut-button.tooltip-info; }
if mouse-button.has-hover { return mouse-button.tooltip-info; }
if privacy-button.has-hover { return privacy-button.tooltip-info; }
if root.privacy-visible && privacy-button.has-hover { return privacy-button.tooltip-info; }
if video-button.has-hover { return video-button.tooltip-info; }
if audio-button.has-hover { return audio-button.tooltip-info; }
if file-button.has-hover { return file-button.tooltip-info; }
@@ -1083,7 +1085,7 @@ export component StreamWindow inherits Window {
control := Rectangle {
x: root.control-dragging ? root.control-drag-x : root.control-docked-left ? 0px : parent.width - self.width;
y: min(max(0px, root.control-pos-y), parent.height - self.height);
width: root.control-visual-expanded ? min(80px + 9 * root.control-button-step + root.control-disconnect-gap, parent.width) : root.control-toolbar-height;
width: root.control-visual-expanded ? min(80px + 8 * root.control-button-step + root.control-privacy-width + root.control-disconnect-gap, parent.width) : root.control-toolbar-height;
height: root.control-toolbar-height;
z: 10;
@@ -1157,7 +1159,7 @@ export component StreamWindow inherits Window {
reveal-progress: root.control-content-progress;
visible: root.control-content-visible;
action-enabled: root.control-expanded;
x: root.control-buttons-x + 4 * root.control-button-step;
x: root.control-buttons-x + 3 * root.control-button-step + root.control-privacy-width;
y: (parent.height - self.height) / 2;
icon: FontAwesomeIcons.volume-high; slashed: !root.audio-enabled;
tooltip: root.audio-enabled ? StreamStrings.mute : StreamStrings.audio;
@@ -1167,7 +1169,7 @@ export component StreamWindow inherits Window {
reveal-progress: root.control-content-progress;
visible: root.control-content-visible;
action-enabled: root.control-expanded;
x: root.control-buttons-x + 5 * root.control-button-step;
x: root.control-buttons-x + 4 * root.control-button-step + root.control-privacy-width;
y: (parent.height - self.height) / 2;
icon: FontAwesomeIcons.folder-open; tooltip: StreamStrings.select-file;
clicked => { root.close-control-menus(); root.select-file(); }
@@ -1176,7 +1178,7 @@ export component StreamWindow inherits Window {
reveal-progress: root.control-content-progress;
visible: root.control-content-visible;
action-enabled: root.control-expanded;
x: root.control-buttons-x + 6 * root.control-button-step;
x: root.control-buttons-x + 5 * root.control-button-step + root.control-privacy-width;
y: (parent.height - self.height) / 2;
icon: "\u{f1de}"; selected: root.video-menu-open;
tooltip: StreamStrings.video-settings;
@@ -1191,7 +1193,7 @@ export component StreamWindow inherits Window {
reveal-progress: root.control-content-progress;
visible: root.control-content-visible;
action-enabled: root.control-expanded;
x: root.control-buttons-x + 7 * root.control-button-step;
x: root.control-buttons-x + 6 * root.control-button-step + root.control-privacy-width;
y: (parent.height - self.height) / 2;
icon: FontAwesomeIcons.signal; selected: root.stats-visible;
tooltip: root.stats-visible ? StreamStrings.hide-stats : StreamStrings.show-stats;
@@ -1201,7 +1203,7 @@ export component StreamWindow inherits Window {
reveal-progress: root.control-content-progress;
visible: root.control-content-visible;
action-enabled: root.control-expanded;
x: root.control-buttons-x + 8 * root.control-button-step;
x: root.control-buttons-x + 7 * root.control-button-step + root.control-privacy-width;
y: (parent.height - self.height) / 2;
icon: root.fullscreen-enabled ? FontAwesomeIcons.compress : FontAwesomeIcons.expand;
tooltip: root.fullscreen-enabled ? StreamStrings.exit-fullscreen : StreamStrings.fullscreen;
@@ -1217,7 +1219,7 @@ export component StreamWindow inherits Window {
reveal-progress: root.control-content-progress;
visible: root.control-content-visible;
action-enabled: root.control-expanded;
x: root.control-buttons-x + 9 * root.control-button-step + root.control-disconnect-gap;
x: root.control-buttons-x + 8 * root.control-button-step + root.control-privacy-width + root.control-disconnect-gap;
y: (parent.height - self.height) / 2;
icon: FontAwesomeIcons.xmark; tooltip: StreamStrings.disconnect;
danger: true;
@@ -1252,13 +1254,13 @@ export component StreamWindow inherits Window {
privacy-button := ControlBarButton {
reveal-progress: root.control-content-progress;
visible: root.control-content-visible;
visible: root.control-content-visible && root.privacy-visible;
x: root.control-buttons-x + 3 * root.control-button-step;
y: (parent.height - self.height) / 2;
icon: FontAwesomeIcons.user-secret;
slashed: root.privacy-off && !root.privacy-unsupported;
muted: root.privacy-unsupported;
action-enabled: root.control-expanded && root.privacy-can-toggle;
action-enabled: root.control-expanded && root.privacy-visible && root.privacy-can-toggle;
tooltip: root.privacy-status-text
+ (root.privacy-can-toggle ? "\n" + (root.privacy-active
? StreamStrings.privacy-disable : StreamStrings.privacy-enable) : "");
@@ -7,6 +7,7 @@ void GuiRuntime::ResetRemoteServiceStatus(RemoteSession& props) {
std::lock_guard lock(props.privacy_status_mutex_);
props.privacy_status_ = {};
props.privacy_status_received_ = false;
props.privacy_desktop_available_ = true;
props.privacy_command_pending_ = false;
props.privacy_status_tick_ = 0;
}
@@ -17,6 +18,12 @@ void GuiRuntime::ResetRemoteServiceStatus(RemoteSession& props) {
void GuiRuntime::ApplyRemoteServiceStatus(RemoteSession& props,
const ServiceStatus& status) {
{
std::lock_guard lock(props.privacy_status_mutex_);
const std::string stage = status.interactive_stage;
props.privacy_desktop_available_ = stage != "lock-screen" &&
stage != "credential-ui" && stage != "secure-desktop";
}
props.remote_service_status_received_ = true;
props.remote_service_available_ = status.available;
props.remote_interactive_stage_ = status.interactive_stage;