Two changes:
1. src/ui_session_interface.rs reconnect(false): Upstream only ever set
force_relay=true and ignored force_relay=false. This was wrong — the
user has no way to clear the flag. Now reconnect(false) sets
force_relay=false AND resets direct_failures=0, so the next
connect() gets a fresh punch with the full timeout.
2. flutter/lib/common/widgets/peer_card.dart: Add a new menu item
'强制不走中继连接' in all 5 _buildMenuItems implementations (recent,
favorite, ab, discovered, address-book). Click calls
rustDeskWinManager.newRemoteDesktop(id, forceRelay: false) which
triggers a fresh punch. If punch fails, RustDesk's existing
connect() falls back to relay — so the user always ends up with a
working connection (just with a chance of P2P that was retried).
Use case: User's home Mac and office Win on different physical
networks, hbbr at 5 Mbps. By default every connection is relay
(orange banner, no file transfer). Clicking '强制不走中继' forces
a fresh P2P attempt — usually fails, but gives the user the
transparency to know 'yes, it tried P2P first'.
Plan B for the punch UI: don't just grey out file transfer — hide it
entirely when the connection is relay. The user can't try to use it,
and we save the toast explanation.
When punch succeeds (P2P, direct), the menu items reappear.
Applied to:
- Transfer file
- TCP tunneling
(Leaving View camera and Terminal in the menu regardless of mode —
they don't use server bandwidth significantly.)
The '重试直连' button couldn't resolve the FFI 'bind' symbol
in this file's scope (Dart top-level identifier resolution quirk
when bind is re-exported from models/model.dart through common.dart).
Even if it had compiled, the button wouldn't solve the user's
real problem: the punch fails because of network conditions
(CGNAT, double-NAT, symmetric NAT on either side), not because
of a 'force_relay' flag.
What stays working:
- Green ⚡ banner when P2P succeeded
- Orange ⚠ banner when relay fallback
- File-transfer menu item is intercepted with a toast on relay
Next steps for the user to actually get P2P:
- Test on the same LAN (not via 4G/5G or CGNAT)
- Check router has UPnP enabled
- If still fails, ask the hbbs maintainer to improve is_local
detection (compare LAN subnets, not just public IP)
In relay mode, the banner now exposes a single button that calls
sessionReconnect(sessionId, forceRelay=false). This:
- resets the is_force_relay flag in the Session lc state
- triggers a fresh punch attempt (with the same direct_failures counter,
which only shortens the punch timeout, never skips the attempt)
- if punch succeeds, banner flips to green and file transfer re-enables
The button is disabled (greyed out) if sessionId is not yet known.
User's setup has both Macs with global IPv6 (2409:8a1e::, China Mobile),
but punch still falls back to relay. Common cause: CGNAT, double-NAT, or
symmetric NAT. The retry button lets the user test network conditions
without restarting the app.
Removed: my earlier attempt at main_get_logs / session_get_session_id FFI
bridges — couldn't find the right log path / session API, and the
sessionId is already exposed via RemotePage.sessionId getter.
Custom fork feature: surface NAT-punch result to the user so they know
whether the connection is going through their 21117 server (relay) or
directly between the two machines (P2P).
Three changes:
1. flutter/lib/common/shared_state.dart: add ConnectionType.isP2P getter
(true = direct, false = relay) and a top-level isDirectConnection(peerId)
helper that returns true when state is unknown (so UI doesn't briefly
grey out before the connection establishes).
2. flutter/lib/desktop/widgets/punch_status_banner.dart (new): a small
Positioned banner at the top of the connection page.
- P2P success: green ⚡ banner: 'P2P 直连成功 — 走的是两台机器之间的网络,可放心传文件'
- Relay fallback: orange ⚠ banner: '中继模式 — 视频/控制走服务器 21117 中转,请勿传大文件'
The banner is reactive (Obx on ConnectionType.direct Rx<String>).
3. flutter/lib/common/widgets/toolbar.dart: wrap the 'Transfer file' and
'TCP tunneling' menu items' onPressed with an isDirectConnection() guard.
On relay mode, the menu still shows but the click is intercepted with a
toast explaining why the action is blocked.
4. flutter/lib/desktop/pages/remote_page.dart: add the banner via
Positioned(top: 0, left: 0, right: 0) at the end of bodyWidget's Stack.
Why this matters: user has 4 users on 5 Mbps server (114.55.133.123).
When NAT punch fails and the session goes through relay, file transfers
would saturate the server bandwidth. Showing the relay state + blocking
the file-transfer menu protects the server without requiring the user
to remember the technical detail.
- Added flutter/macos/Runner/custom.txt with default-settings.enable-audio=N
- Added custom.txt to Xcode Copy Bundle Resources (PBXResourcesBuildPhase)
- RustDesk's load_custom_client() reads this file and injects into DEFAULT_SETTINGS
- Users can still toggle 'Enable audio' in Settings; this just flips the default
This addresses the user's request to disable all 4 audio behaviors by default
(play remote sound, hear remote mic, voice call, capture host audio), all of
which share the single 'enable-audio' option.