[fix] preserve remote cursors when macOS hides the pointer, refs #116

This commit is contained in:
dijunkun
2026-10-13 21:49:06 +08:00
parent 2a9887ab8a
commit fd86dba212
2 changed files with 28 additions and 3 deletions
@@ -3112,8 +3112,10 @@ void GuiApplication::SyncStreamWindow() {
}
const ConnectionStatus status = props->connection_status_.load();
bool remote_update_available = false;
HostPlatform remote_platform = HostPlatform::Unknown;
if (status == ConnectionStatus::Connected) {
std::lock_guard lock(props->remote_version_mutex_);
remote_platform = props->remote_platform_;
// Wait for host_info: a version still in transit is not a legacy peer.
if (props->remote_host_info_received_ && !props->remote_update_dismissed_) {
remote_update_available = props->remote_app_version_.empty() ||
@@ -3176,7 +3178,7 @@ void GuiApplication::SyncStreamWindow() {
props->remote_cursor_state_received_;
if (remote_cursor_active) {
remote_cursor_shape = static_cast<int>(
props->cursor_presentation_.Resolve(SDL_GetTicks()));
props->cursor_presentation_.Resolve(SDL_GetTicks(), remote_platform));
}
}
(*ui_->stream)->set_remote_cursor_active(remote_cursor_active);
@@ -33,6 +33,15 @@ class CursorPresentation {
const bool separate =
state.render_mode == CursorRenderMode::separate ||
(state.render_mode == CursorRenderMode::legacy && state.visible);
system_hidden_ = state.render_mode == CursorRenderMode::hidden &&
state.hidden_reason == CursorHiddenReason::system_hidden;
if (separate) {
mac_locator_allowed_ = state.shape != RemoteCursorShape::none;
} else if (!system_hidden_) {
// Embedded cursors and other hiding policies end the separate-cursor
// history. A later system_hidden sample must not revive that overlay.
mac_locator_allowed_ = false;
}
// Old senders cannot distinguish embedded from hidden. Only explicit
// metadata from a new sender can establish the no-device compatibility
// case.
@@ -47,9 +56,21 @@ class CursorPresentation {
void NoteInput(uint64_t now) { last_input_ = now; }
RemoteCursorShape Resolve(uint64_t now) {
RemoteCursorShape Resolve(uint64_t now, HostPlatform remote_platform) {
if (!stale_at_) return RemoteCursorShape::default_cursor;
// macOS can keep its system cursor hidden after typing even while remote
// mouse events move it. Keep a previously separate desktop locator across
// those heartbeats; the video has no pointer to fall back to. On a new
// connection, use an arrow until the first visible shape arrives. The
// protocol cannot distinguish typing from other system_hidden cases;
// restrict this policy to identified Mac hosts and stop on any explicit
// embedded cursor or other hiding policy.
if (remote_platform == HostPlatform::MacOS && system_hidden_ &&
mac_locator_allowed_) {
return last_visible_shape_;
}
const bool recent_input =
last_input_ && now - *last_input_ < kInputActivityMs;
const bool can_recover =
@@ -64,6 +85,8 @@ class CursorPresentation {
private:
bool sample_unavailable_ = false;
bool system_hidden_ = false;
bool mac_locator_allowed_ = true;
bool no_device_ = false;
bool fallback_engaged_ = false;
std::optional<uint64_t> stale_at_;
@@ -74,4 +97,4 @@ class CursorPresentation {
} // namespace crossdesk
#endif
#endif