[fix] align macOS and Linux remote window behavior

This commit is contained in:
dijunkun
2026-11-11 00:24:31 +08:00
parent ed58172d2e
commit 6898df7cdc
9 changed files with 168 additions and 3 deletions
+17
View File
@@ -175,6 +175,23 @@ Type=Application
Categories=Utility;
EOF
# Give shells a desktop entry for the remote window's separate app id. Without
# it, some shells fall back to the process's main launcher and merge the groups;
# Wayland also uses this entry to resolve the icon. Hide it from app menus: a
# fresh launch can open CrossDesk, but cannot recreate a live remote session.
cat > "$DESKTOP_DIR/$PKG_NAME-remote-control.desktop" <<EOF
[Desktop Entry]
Version=1.0
Name=$APP_NAME Remote Control
Comment=Active CrossDesk remote control sessions
Exec=/usr/bin/$PKG_NAME
Icon=$PKG_NAME
StartupWMClass=$PKG_NAME-remote-control
Terminal=false
Type=Application
NoDisplay=true
EOF
# Stage and strip the application once, then package it with two dependency sets.
# Keep the executable, launcher and configuration paths identical when switching.
for package_name in "$PKG_NAME" "$VIRTUAL_PACKAGE_NAME"; do
@@ -2771,6 +2771,11 @@ void GuiApplication::Tick() {
}
}
SyncFileTransferNotifications();
#if defined(__APPLE__)
// Native hide/show may complete after its callback. Re-evaluate after all
// session windows have been synchronized, including the last session close.
if (ui_->tray) ui_->tray->SyncDockVisibility();
#endif
}
void GuiApplication::PollUpdateCheck() {
@@ -3854,6 +3859,11 @@ void GuiApplication::SyncStreamWindow() {
if (!has_sessions) need_to_create_stream_window_ = false;
if (need_to_create_stream_window_ && !ui_->stream) {
ui_->stream.emplace(ui::StreamWindow::create());
#if defined(__linux__) && !defined(__OHOS__)
// Set a per-window identity before Slint asynchronously creates the native
// window. The override survives hide/show and native handle recreation.
(*ui_->stream)->window().set_xdg_app_id("crossdesk-remote-control");
#endif
(*ui_->stream)->set_control_hand_cursor_supported(
SupportsControlHandCursor(ui_->main->window()));
#if (defined(__linux__) && !defined(__OHOS__))
@@ -32,6 +32,8 @@ class MacTray {
~MacTray();
void MinimizeToTray();
// Keep the Dock available while another normal window is open or minimized.
void SyncDockVisibility();
void RemoveTrayIcon();
bool TakeReopenRequest();
@@ -65,8 +65,30 @@ struct MacTrayImpl {
} else if (app_window) {
SDL_HideWindow(app_window);
}
// Keep running through the menu-bar tray without a Dock application icon.
[NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
SyncDockVisibility();
}
void SyncDockVisibility() {
// Cmd-H hides the application as a whole. Keep its Dock entry available
// so it can be unhidden; only closing individual windows enters tray mode.
if ([NSApp isHidden]) return;
bool has_window = false;
for (NSWindow *window in [NSApp windows]) {
// A miniaturized NSWindow may no longer be eligible to become main,
// but it still needs a Dock entry from which the user can restore it.
if ([window isMiniaturized] ||
([window canBecomeMainWindow] && [window isVisible])) {
has_window = true;
break;
}
}
const NSApplicationActivationPolicy policy = has_window
? NSApplicationActivationPolicyRegular
: NSApplicationActivationPolicyAccessory;
if ([NSApp activationPolicy] != policy) {
[NSApp setActivationPolicy:policy];
}
}
void RemoveTrayIcon() {
@@ -191,6 +213,14 @@ struct MacTrayImpl {
return;
}
// Unbundled development builds have no CFBundleIconFile, and winit's
// macOS window-icon setter is a no-op. Set the application icon explicitly
// so the Dock uses the logo instead of the generic executable icon.
NSImage *crossdesk_icon = LoadCrossDeskIcon();
if (crossdesk_icon) {
[NSApp setApplicationIconImage:crossdesk_icon];
}
status_item = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSSquareStatusItemLength];
NSStatusBarButton *button = [status_item button];
@@ -200,7 +230,6 @@ struct MacTrayImpl {
[button setToolTip:NSStringFromUtf8(tooltip)];
NSImage *crossdesk_icon = LoadCrossDeskIcon();
if (crossdesk_icon) {
NSImage *status_icon = [crossdesk_icon copy];
[status_icon setSize:NSMakeSize(18.0, 18.0)];
@@ -328,6 +357,8 @@ void MacActivateWindow(void *appkit_view) {
void MacTray::MinimizeToTray() { impl_->MinimizeToTray(); }
void MacTray::SyncDockVisibility() { impl_->SyncDockVisibility(); }
void MacTray::RemoveTrayIcon() { impl_->RemoveTrayIcon(); }
bool MacTray::TakeReopenRequest() { return impl_->TakeReopenRequest(); }
@@ -174,6 +174,10 @@ bool ConfigureMainWindowChrome(void *opaque_slint_view, bool gallery) {
NSButton *zoom_button =
[window standardWindowButton:NSWindowZoomButton];
if (zoom_button == nil) return false;
// Keep the connection/DeskHub and remote-control windows independently
// switchable even when the user prefers opening documents in tabs.
window.tabbingMode = NSWindowTabbingModeDisallowed;
window.excludedFromWindowsMenu = NO;
if (gallery) gallery_window = window;
if (gallery && gallery_fullscreen) return true;
if (gallery)
@@ -280,6 +284,8 @@ bool ConfigureStreamWindowLiveResize(void *opaque_slint_view) {
NSViewLayerContentsPlacementScaleProportionallyToFit;
stream_window = window;
stream_slint_view = slint_view;
window.tabbingMode = NSWindowTabbingModeDisallowed;
window.excludedFromWindowsMenu = NO;
// Native Space fullscreen cross-fades an AppKit source snapshot over a
// separately rendered destination window. Disable that path for the
// stream window and route the green button to the immediate transition.