* feat(terminal): add reconnection buffer support for persistent sessions
Fix two related issues:
1. Reconnecting to persistent sessions shows blank screen - server now
automatically sends historical buffer on reconnection via SessionState
machine with pending_buffer, eliminating the need for client-initiated
buffer requests.
2. Terminal output before view ready causes NaN errors - buffer output
chunks on client side until terminal view has valid dimensions, then
flush in order on first valid resize.
Rust side:
- Introduce SessionState enum (Closed/Active) replacing bool is_opened
- Auto-attach pending buffer on reconnection in handle_open()
- Always drain output channel in read_outputs() to prevent overflow
- Increase channel buffer from 100 to 500
- Optimize get_recent() to collect whole chunks (avoids ANSI truncation)
- Extract create_terminal_data_response() helper (DRY)
- Add reconnected flag to TerminalOpened protobuf message
Flutter side:
- Buffer output chunks until terminal view has valid dimensions
- Flush buffered output on first valid resize via _markViewReady()
- Clear terminal on reconnection to avoid duplicate output from buffer replay
- Fix max_bytes type (u32) to match protobuf definition
- Pass reconnected field through FlutterHandler event
Signed-off-by: fufesou <linlong1266@gmail.com>
* fix(terminal): add two-phase SIGWINCH for TUI app redraw and session remap on reconnection
Fix TUI apps (top, htop) not redrawing after reconnection. A single
resize-then-restore is too fast for ncurses to detect a size change,
so split across two read_outputs() polling cycles (~30ms apart) to
force a full redraw.
Also fix reconnection failure when client terminal_id doesn't match
any surviving server-side session ID by remapping the lowest surviving
session to the requested ID.
Rust side:
- Add two-phase SIGWINCH state machine (SigwinchPhase: TempResize →
Restore → Idle) with retry logic (max 3 attempts per phase)
- Add do_sigwinch_resize() for cross-platform PTY resize (direct PTY
and Windows helper mode)
- Add session remap logic for non-contiguous terminal_id reconnection
- Extract try_send_output() helper with rate-limited drop logging (DRY)
- Add 3-byte limit to UTF-8 continuation byte skipping in get_recent()
to prevent runaway on non-UTF-8 binary data
- Remove reconnected flag from flutter.rs (unused on client side)
Flutter side:
- Add reconnection screen clear and deferred flush logic
- Filter self from persistent_sessions restore list
- Add comments for web-related changes
Signed-off-by: fufesou <linlong1266@gmail.com>
---------
Signed-off-by: fufesou <linlong1266@gmail.com>
* Update Hungarian translations in hu.rs
Translation of new strings and some fixes.
John Fowler.
* Escape quotes in Hungarian language strings
Replacing Hungarian quotation marks
* Update Hungarian translations for various terms
Upload a new translation (hu.rs) file.
* Hungarian language file correction
New character strings translation, error correction.
* - UI display: display_name first
- Fallback: name
- Technical identity: still name
### What changed
- Added account display helpers and display_name state in user model:
- flutter/lib/models/user_model.dart:16
- Account/logout label now uses display_name (@name) when both exist:
- flutter/lib/mobile/pages/settings_page.dart:689
- flutter/lib/desktop/pages/desktop_setting_page.dart:2016
- flutter/lib/desktop/pages/desktop_setting_page.dart:2135
- Desktop Account info now shows both when applicable:
- Display Name: ...
- Username: ...
- flutter/lib/desktop/pages/desktop_setting_page.dart:2039
- Previously done group-list behavior remains:
- group user list displays display_name with name fallback
- flutter/lib/common/widgets/my_group.dart:187
- Persistence path for display_name remains enabled (including group cache/submodule field):
- libs/hbb_common/src/config.rs:2347
- src/client.rs:2630
- LoginRequest.my_name now resolves as:
1. OPTION_DISPLAY_NAME (manual override)
2. user_info.display_name
3. user_info.name
4. OS username fallback
* 1. GUID key (...Uninstall\{GUID}) is MSI-native metadata generated by Windows Installer.
2. Non-GUID key (...Uninstall\RustDesk) is explicitly written by RustDesk’s MSI compatibility component in res/msi/Package/Components/Regs.wxs:44, populated by preprocess.py --arp from .github/workflows/
flutter-build.yml:262.
So they were not using the same EstimatedSize logic:
- MSI GUID key: MSI-calculated size (KB).
- RustDesk key: custom script value from res/msi/preprocess.py:339 (previously bytes, now fixed to KB).
That mismatch is exactly why you saw different sizes.
* improve display name handling
- Append (@username) when multiple users share the same display name
- Trim whitespace from display_name before comparison and display
- Add missing translate() for Logout button on desktop
Signed-off-by: 21pages <sunboeasy@gmail.com>
* group peer filter match both user's display name and user's name
Signed-off-by: 21pages <sunboeasy@gmail.com>
* case-insensitive search in group peer filter
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
* fix(terminal): ensure tab close is resilient to session cleanup failures
- Wrap _closeTerminalSessionIfNeeded in isolated try/catch so that
tabController.closeBy always executes even if FFI calls throw
- Add clarifying comment in handleWindowCloseButton for single-tab
audit dialog flow
* fix(terminal): fix session reconnect ID mismatch and tab close race condition
Remap surviving persistent sessions to client-requested terminal IDs on
reconnect, preventing new shell creation when IDs are non-contiguous.
Snapshot peerTabCount before async operations in _closeTab to avoid race
with concurrent _closeAllTabs clearing the tab controller.
Remove debug log statements.
Signed-off-by: fufesou <linlong1266@gmail.com>
---------
Signed-off-by: fufesou <linlong1266@gmail.com>
- Fix new tab not auto-focusing: add FocusNode to TerminalView and
request focus when tab is selected via tab state listener
- Fix NaN error when data arrives before terminal view layout: buffer
output data until terminal view has valid dimensions, flush on first
valid resize callback
Signed-off-by: fufesou <linlong1266@gmail.com>
Terminal tab keys use the format "peerId_terminalId". The previous code
used split('_')[0] or startsWith('$peerId_') to extract the peerId,
which breaks when the peerId itself contains underscores.
This can happen in two scenarios:
- Hostname-based ID: when OPTION_ALLOW_HOSTNAME_AS_ID is enabled, the
peerId is derived from the system hostname, which commonly contains
underscores (e.g. "my_dev_machine").
- Custom ID: the validation regex ^[a-zA-Z][\w-]{5,15}$ allows
underscores since \w matches [a-zA-Z0-9_], so IDs like "my_dev_01"
are valid.
Fix all three parsing sites in terminal_tab_page.dart to use
lastIndexOf('_'), which is safe because terminalId is always a plain
integer with no underscores.
* fix(macos): sync config to root when root config is empty
Signed-off-by: 21pages <sunboeasy@gmail.com>
* fix(server): gate startup on initial config sync; document CheckIfResendPk limitation
- wait up to 3s for initial root->local config sync before starting server services
- continue startup when timeout is hit, while keeping sync/watch running in background
- avoid blocking non-server process startup
- clarify that CheckIfResendPk only re-registers PK for current ID and does not solve multi-ID when root uses a non-default mac-generated ID
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
* PT-BR language update
@rustdesk
Please merge. Thanks
* Update ptbr.rs
* Update ptbr.rs
Please submit, i will get back soon and finish all other stuff.
* PT-BR language update
Completed all missing PT-BR translations.
* Initial plan
* Fix macOS build: Remove @available check causing linker error
The @available check in GetDisplayName was causing the linker to look for
__isPlatformVersionAtLeast symbol which is not available when targeting
macOS 10.14. Since this function is only used for logging, we simplify it
to return "Unknown" for all displays, avoiding the runtime availability check.
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
* fix(macOS): ___isPlatformVersionAtLeast is not available in macOS 10.14
Signed-off-by: fufesou <linlong1266@gmail.com>
---------
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: rustdesk <71636191+rustdesk@users.noreply.github.com>
zum Zeitpunkt der Anzeige ist der Datenschutz aktiviert bzw. schon beendet.
alternativ ginge auch:
Datenschutzmodus wurde aktiviert
bzw.
Datenschutzmodus wurde beendet
* Update Hungarian translations in hu.rs
Translation of new strings and some fixes.
John Fowler.
* Escape quotes in Hungarian language strings
Replacing Hungarian quotation marks
* Update Hungarian translations for various terms
Upload a new translation (hu.rs) file.
* fix issue: #13911 'Double Click' bug on iPad with Magic Mouse
* remote_input.dart comments - gestures.dart organization and clean states of all interrupted gestures
* feat(macos): add privacy mode support for macOS
## Summary
Add privacy mode functionality for macOS platform, allowing remote
desktop sessions to hide the screen content from local users.
## Changes
### Core Implementation (src/platform/macos.mm)
- Implement screen blackout using CGDisplayGammaTable API
- Implement input blocking using CGEventTap to intercept keyboard/mouse
- Store and restore original gamma values for proper cleanup
### Privacy Mode Integration (src/privacy_mode.rs, src/privacy_mode/macos.rs)
- Add macOS privacy mode implementation with PrivacyMode trait
- Register macOS privacy mode in PRIVACY_MODE_CREATOR
- Set DEFAULT_PRIVACY_MODE_IMPL for macOS platform
- Implement get_supported_privacy_mode_impl() for macOS
### Connection Handling (src/server/connection.rs)
- Add supported_privacy_mode_impl to platform_additions for macOS
- Enable privacy mode toggle in client UI when connecting via LAN IP
### Localization (src/lang/*.rs)
- Add "privacy_mode_impl_macos_tip" translation for en/cn/tw
## Safety & Security
- Implements Drop trait to ensure cleanup on normal exit
- macOS system automatically restores gamma table on process termination
- CGEventTap is automatically released when process terminates
- Tested with SIGKILL to verify crash recovery
## Testing
- Verified privacy mode toggle works via both ID and LAN IP connection
- Verified screen recovery after process crash (kill -9)
- Verified input restoration after process termination
* refactor: use existing 'Privacy mode' translation key
* refactor: rename gamma channel variables for better readability - rename r/g/b to red/green/blue to avoid variable shadowing confusion
* fix: add error handling for gamma table restoration with fallback to system reset
* fix: add error handling for CGEventTapCreate failure in privacy mode
* fix: only set display to black if original gamma was saved successfully
* fix: add error handling for CGSetDisplayTransferByTable when setting display to black
* fix: improve event tap callback to properly distinguish remote input from local input
* fix: missing macos.rs
* Fix: Add display validation before restoring gamma values
* Fix: Add mutex lock for thread safety in MacSetPrivacyMode
* Fix: Handle return values and add missing mouse events in macos privacy mode
* fix: only set conn_id after privacy mode is successfully turned on
* fix: reimplement privacy mode with stable display identification
Address code review concern: original gamma values stored with DisplayID
as key could become stale if display list changes between privacy mode
activations (e.g., display reconnected with different ID).
Solution:
- Use UUID instead of DisplayID as storage key (stable across reconnections)
- Clear g_originalGammas when privacy mode is turned off
- Register CGDisplayReconfigurationCallback to handle hot-plug events
- Validate display state via FindDisplayIdByUUID() before restoration
Key features:
- UUID-based display identification (stable across reconnections)
- Hot-plug support via CGDisplayReconfigurationCallback
- EventTap auto re-enable on system timeout
- Fallback to CGDisplayRestoreColorSyncSettings() for recovery
- Detailed error logging with display name/ID/UUID
* fix: ensure EventTap runs on main thread and improve gamma restore error handling
- Add SetupEventTapOnMainThread() to create EventTap on main thread using dispatch_sync, avoiding potential issues when called from background threads
- Add TeardownEventTapOnMainThread() for consistent cleanup on main thread
- Check [NSThread isMainThread] to avoid deadlock when already on main thread
- Add error tracking for gamma restoration during cleanup
- Use CGDisplayRestoreColorSyncSettings() as fallback when individual gamma restoration fails
* fix: remove invalid eventMask bits that caused undefined behavior in input blocking
* fix: address code review comments for macos privacy mode implementation
Changes to src/privacy_mode/macos.rs:
- Add check_on_conn_id() in turn_on_privacy() to prevent duplicate activation
- Add check_off_conn_id() in turn_off_privacy() to validate connection ID
- Add self.conn_id = 0 in clear() to reset connection state
Changes to src/platform/macos.mm:
- Add link comment for ENIGO_INPUT_EXTRA_VALUE referencing libs/enigo/src/macos/macos_impl.rs
- Fix NSLog format string mismatch (5 placeholders vs 4 values)
- Make ApplyBlackoutToDisplay() return bool for proper error handling
- Return false when UUID is empty since privacy mode requires ALL displays
- Add else branches with logging for:
- CGGetDisplayTransferByTable failures
- Zero gamma table capacity (not supported)
- Zero blackout capacity
- Remove unused g_uuidToDisplayId variable (was only written, never read)
* fix(macos): add early return with privacy mode exit on display hotplug failures
Why large-scale changes are needed:
The code review suggested adding early return when errors occur in
DisplayReconfigurationCallback. However, simply returning early is not
enough - when a newly connected display cannot be blacked out, we must
exit privacy mode entirely to maintain security guarantees.
The challenge is that DisplayReconfigurationCallback already holds
g_privacyModeMutex, so calling MacSetPrivacyMode(false) directly would
cause a deadlock. This necessitated:
1. Extract TurnOffPrivacyModeInternal() - a lock-free internal function
that can be safely called from within the callback
2. Refactor MacSetPrivacyMode(false) branch to use this internal function
3. Add early returns with TurnOffPrivacyModeInternal() calls at each
failure point in DisplayReconfigurationCallback
Changes in DisplayReconfigurationCallback:
- UUID empty: log + exit privacy mode + early return
- Gamma table capacity zero: log + exit privacy mode + early return
- CGGetDisplayTransferByTable fails: log + exit privacy mode + early return
- ApplyBlackoutToDisplay fails: log + exit privacy mode + early return
* fix(macos): address code review feedback and improve privacy mode stability
Code Review Fixes:
- Add detailed comments for potential deadlock scenarios in dispatch_sync
with g_privacyModeMutex (SetupEventTapOnMainThread/TeardownEventTapOnMainThread)
- Use async dispatch for privacy mode shutdown from DisplayReconfigurationCallback
to avoid unregistering callback from within itself
- Extract RestoreAllGammas() helper function to reduce code duplication
- Fix Drop implementation in macos.rs to call self.clear() for consistency
- Add comment explaining why _state parameter is ignored on macOS
- Define DISPLAY_RECONFIG_MONITOR_DURATION_MS and GAMMA_CHECK_INTERVAL_MS constants
- Add gamma restoration when UUID retrieval fails during privacy mode activation
Privacy Mode Stability Improvements (Continuous Resolution Changes):
- Implement continuous gamma value monitoring with timer polling after display
reconfiguration to handle rapid successive resolution changes
- Monitor gamma values every 200ms for 5 seconds after each resolution change
- Automatically reapply blackout if system (ColorSync) restores gamma
- Add IsDisplayBlackedOut() to detect if display gamma has been restored
- Use timestamp-based debouncing: monitoring period automatically extends
when new reconfig events occur during active monitoring
- Ensure blackout remains effective even under continuous resolution changes
where macOS may asynchronously restore gamma values multiple times
This ensures privacy mode remains stable and effective when users rapidly
change display resolution multiple times in succession.
---------
Co-authored-by: libin <libin.chat@outlook.com>
* PT-BR language update
@rustdesk
Please merge. Thanks
* Update ptbr.rs
* Update ptbr.rs
Please submit, i will get back soon and finish all other stuff.