100 Commits
Author SHA1 Message Date
fufesou 3c69194ac4 fix(windows): continue removal after configuration sync errors (#16865)
* fix(windows): continue removal after configuration sync errors

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): revoke local trust after removal sync errors

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-08 22:28:28 +08:00
fufesou 2228db8a10 fix(windows): retry service IPC before terminating the GUI (#16913)
Give the Start service caller one second to arrange its replacement and exit before retrying main IPC startup. Keep this workaround in the server startup thread and preserve the existing termination path when the retry fails.

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-08 16:17:26 +08:00
fufesou 78450f4987 Fix/windows installer directory guard (#16908)
* fix(windows): guard staged installer directories during handoff

Create restrictive staging directories and retain non-delete-sharing handles while CMD consumes packages, scripts and shortcuts. Adapt output paths and cleanup, and add a directory-replacement regression.

Validated with 11 focused Windows tests. Directory protection remains tied to the lifetime of the creating process.

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(windows): streamline installer directory guard tests

Keep installer-file tests in their original inline module and remove the duplicate CMD-copy check from the directory replacement regression. Preserve directory protection and existing handoff coverage.

Validation: Windows library test compilation, four focused tests, rustfmt and git diff --check.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): use plain rd for empty staging directories

Remove the inapplicable /q switch from non-recursive staging-directory cleanup. Keep recursive deletion confined to the runner directory.

Validated with Windows library test compilation, four focused tests and isolated CMD probes for owner termination, nonempty staging directories and exit-code preservation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(windows): pin installer staging directory permissions

Check effective ordinary-owner directory rights independently of the sharing guard. Clarify guard lifetime and the Rust and CMD cleanup responsibilities.

Validation: Windows library test compilation, four focused tests, rustfmt and git diff --check.
Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-08 13:06:43 +08:00
fufesou 9382ab2607 fix: ci (#16890)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-06 14:17:10 +08:00
fufesou 91e43e24ea fix: clarify unlimited trusted-device duration (#16886)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-06 09:30:31 +08:00
fufesou cbde394666 fix(windows): address IPC review follow-ups (#16885)
Require a dedicated child success exit code so an unmatched test filter cannot pass the GUI startup regression. Replace the ACE type binding with a local constant and drop the unused SystemServices feature.

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-05 23:43:54 +08:00
fufesou 83e508cfdf Fix/windows ipc server auth (#16755)
* fix(ipc): authenticate Windows named pipe servers

Restrict main and service pipe instance creation and authenticate the
connected server before exchanging IPC data. Open clients with data access
that excludes FILE_CREATE_PIPE_INSTANCE.

Require SYSTEM ownership for service channels. For main IPC, also accept a
same-user server with a restricted DACL, matching session and executable,
and sufficient elevation. This includes the UI hosting main IPC while the
service is unavailable, without relying on installation or service config.

Elevated clients reject non-elevated servers, and SYSTEM clients reject
user-owned pipes. New clients reject old permissive listeners until those
processes restart on the fixed version.

Add native Windows regression tests for instance-creation permissions,
DACL rejection before application data, and normal client communication.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(ipc): clarify Windows pipe security policy

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(ipc): bound the Windows server rejection test

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(ipc): bound the complete Windows client communication test

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): keep portable GUI alive on IPC startup failure

Only attempt GUI fallback hosting when the main IPC pipe is absent. Bind its listener before marking hosting active or starting rendezvous, and return on bind failure without terminating the GUI.

Preserve IPC authentication and explicit server-process behavior. Cover rejected listeners, bind collisions, and standalone IPC communication with Windows regression tests.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(windows): simplify portable IPC startup fallback

Rely on exclusive listener creation instead of a separate pipe-presence probe. Keep GUI bind failures recoverable and require a successful bind before starting host services.

Retain the subprocess regression for bind-failure process survival, removing redundant GUI tests and their helper parameters and delay.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-05 22:35:07 +08:00
fufesou 13cccb2f7b Fix/windows installer temp junction (#16871)
* fix(windows): support secure installer temp junctions

Preserve Windows Temp when persistent ACLs are available, validate resolved junction targets, and fall back to ProgramData only when the target volume lacks ACL support. Guard parent directories while creating protected installer files and cover the Windows sharing behavior with regression tests.

Refs rustdesk/rustdesk#16841

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): simplify installer temp junction fallback

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(windows): cover installer staging path selection

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-05 15:52:35 +08:00
fufesou 79c63639ea refact(ci): update, metadata, nightly (#16868)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-05 15:06:53 +08:00
fufesou 29d1ab26a1 fix(sciter): avoid double DPI scaling of main window minimum size (#16824)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-02 22:15:10 +08:00
fufesou 82301f1776 Fix/windows install directory confirmation (#16783)
* fix(windows): warn before removing installation directory

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): show inline installation directory warning

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(windows): simplify installation directory warning

Key the warning widget by its path and remove the optional widget test suite.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): limit installation warnings to the selected directory

Share install-directory-not-empty-tip between inline and confirmation warnings, and remove the separate previous-installation warning.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): skip directory warnings when reinstalling in place

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-01 13:39:54 +08:00
fufesou c5201d600b Allow trusted devices for direct IP connections (#16778)
* Allow trusted devices for direct IP connections

Signed-off-by: fufesou <linlong1266@gmail.com>

* test: preserve existing V2 trusted-device credentials

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-11-01 13:39:15 +08:00
fufesou 3b7e2cebd9 fix: restore readiness after service restart and macOS update (#16780)
* fix: restore readiness after service restart and macOS update

Wake rendezvous retry waits when a manual restart is requested. For app-only macOS updates with an in-process backend, wait for the old process to exit before relaunching the updated app so it cannot attach to the departing backend.

Signed-off-by: fufesou <linlong1266@gmail.com>

* test: cover service restart and macOS update readiness

Signed-off-by: fufesou <linlong1266@gmail.com>

* test: remove macOS relaunch script regression test

Signed-off-by: fufesou <linlong1266@gmail.com>

* docs: explain macOS update relaunch ordering

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: isolate macOS update relaunch process group

Signed-off-by: fufesou <linlong1266@gmail.com>

* docs: clarify macOS relaunch process group isolation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: clarify macOS update restart failure

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-31 19:59:06 +08:00
fufesou eab27cc249 Build Windows custom client packers in CI (#16781)
* Build Windows custom client packers in CI

Verify payload signatures, build templates for all three Windows architectures, and include the packers in the unsigned release archive.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ci): preserve unsigned Windows releases without signing

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ci): align template packer DPI manifests

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-31 15:40:32 +08:00
fufesou e1ca09766c refact(update): change the pub key for metadata (#16758)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-30 02:29:40 +08:00
fufesou eba0a6e552 fix(web): preserve peer card clicks with semantics enabled (#16709)
* fix(web): preserve peer card clicks with semantics enabled

Give the More menu semantic tap and double-click actions, and expose multi-selection through separate checkboxes. Exclude redundant outer desktop web tap semantics while preserving card-body double-click connections.

Cover mouse single/double clicks and semantic activation under the parent card gesture detector.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): reuse accessible More control in portrait cards

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): keep peer More taps immediate and cancellable

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): keep keyboard wrapper out of accessibility focus

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-29 19:30:58 +08:00
fufesou bb8083cb2f udpate hbb_common (#16737)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-28 16:27:44 +08:00
fufesou 350966b331 Add trusted-device V2 with legacy upgrade compatibility (#16548)
* fix: secure trusted devices with host-issued V2 tokens

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve trusted-device management and ordinary authentication

Restore selected-device deletion using nonsecret record identifiers and acknowledged V2 revocation errors.

Preserve password and TOTP authentication when the V2 authority or cached controller credentials are unavailable. Disable V2 for the affected attempt while retaining authentication, configuration, and revocation rejection.

Validation: 20 selected Rust tests, Flutter-feature Rust compilation, focused Dart analysis, formatting, and whitespace checks passed.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: simplify trusted-device caching and stabilize tests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: persist trusted-device grants in host configuration

Store host grants in RustDesk2.toml and preserve revocation across configuration writes and service transitions. Use local authority on macOS when no background service is installed.

Preserve both configuration imports by checking Config2 eligibility before revocation writes. Cover the password and 2FA import regression alongside the existing trusted-device tests.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: store trusted-device credentials in peer configuration

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: minimize trusted-device persistence changes

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor: minimize trusted-device integration changes

Keep ordinary policy writes on the generic configuration path and document authentication updates. Restore the macOS installer to master, rename the V2 version gate, and relocate the existing client regression test.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: harden trusted-device revocation and persistence

Recover corrupt grant data without skipping authentication-write validation, and avoid revoking trust when IPC synchronizes an unchanged salt. Make desktop management IPC async and keep runtime creation at the Flutter worker boundary.

Update hbb_common to preserve V2 credentials when configuration reads fail, and extend existing regressions for clear, salt synchronization, and persistence failures.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve trusted-device validation and no-op settings

Validate trust before connection side effects and retain tickets for the final check when the authority is unavailable after TOTP. Disable enrollment on that path.

Skip revocation for password operations that the existing setter treats as no-ops, extend the existing IPC regression, and clean up client state on initialization or credential errors.

Validation: 36 targeted native tests, Flutter-feature Rust check, formatting, and git diff --check passed.
Signed-off-by: fufesou <linlong1266@gmail.com>

* feat: add trusted-device expiry and per-device renewal

Keep legacy records for password-and-TOTP migration, encrypt host grant storage, and add configurable trust periods that revoke grants when changed. Allow renewing individual unexpired devices from the manager, hide renewal for never-expiring grants, and preserve the mobile table height constraints.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): report trusted-device cleanup failure before uninstall

Revoke local trusted-device grants before dispatching the uninstall closure so cleanup failures return false without starting the uninstall script. Keep the existing synchronous and asynchronous script execution paths.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve trusted-device settings and revoke macOS service grants

Default unsupported expiration values on read while keeping explicit edits strict. Allow password/TOTP when stored grants are out of sync, after validating the authentication scope.

Revoke root-owned grants during macOS service removal. Bump hbb_common for malformed-file recovery and legacy-record preservation, and retain the reviewed regression coverage.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: avoid duplicate trusted-device revocation during config sync

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve trusted-device revocation during config mismatches

Allow grant clearing when authentication snapshots are unavailable and validate existing snapshots before applying authentication updates. Restore optimistic caching for non-auth bulk options and restrict the trusted-device IPC helper to desktop targets.

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat: preserve trusted devices when trust duration changes

Adjust existing grant deadlines for duration changes and startup policy overrides, support 90 days, and use an adaptive inline duration selector without the obsolete revocation warning.

Consolidate management IPC and trim redundant tests while retaining authentication and revocation coverage.

Validation: four existing focused Rust tests and the startup override reproduction passed; Windows default/service and Flutter-feature cargo checks passed.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: finish trusted-device notice cleanup and duration sync

Remove the recurring legacy notice, unused response metadata and translation key. Allow duration-only saves with a readable inactive grant scope while preserving storage and authentication errors.

Update hbb_common to 040bf3f662ae7422f2f9570701cc8451f7bb1d59, which removes the unused legacy detector.

Validation: four existing focused Rust tests passed; Windows default/service and Flutter-feature cargo checks passed. No new tests were added.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: refine trusted-device duration layout

Indent the duration row beneath the trusted-device option, add vertical spacing, and compact the desktop selector. Mobile sizing and other dropdowns remain unchanged.

Validation: targeted Dart analysis passed with existing informational diagnostics. Actual window rendering was not exercised.
Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: adjust indentation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve trusted devices across version upgrades

Honor existing legacy trust without creating new legacy registrations. Import live records into encrypted grants, apply the current expiration policy, and migrate consenting controllers to V2 tokens while retaining grouped revocation.

Advertise V2 capability explicitly and hide enrollment for mixed-version sessions. Update existing coverage and use the paired hbb_common migration API.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* Align the compact trusted-device duration selector on mobile

Signed-off-by: fufesou <linlong1266@gmail.com>

* Default trusted devices to 90 days and drop unreleased V2 migrations

Signed-off-by: fufesou <linlong1266@gmail.com>

* Avoid masking macOS daemon stop failures during uninstall

Signed-off-by: fufesou <linlong1266@gmail.com>

* restore uninstall.scpt, do not clear trusted devices on uninstall

Signed-off-by: fufesou <linlong1266@gmail.com>

* Preserve trusted devices when uninstalling services

Signed-off-by: fufesou <linlong1266@gmail.com>

* Preserve trusted devices when importing host configuration

Signed-off-by: fufesou <linlong1266@gmail.com>

* Recover from unreadable trusted-device grants

Log and persistently revoke invalid or undecryptable V2 payloads so password and TOTP can enroll a new device. Keep configuration read and write errors explicit and extend existing tests to cover recovery and rejection of old tokens.

Signed-off-by: fufesou <linlong1266@gmail.com>

* Preserve trusted devices across service transitions

Run grant operations in the accepting server and synchronize current grants and revocations through the existing service configuration channel. Keep ordinary settings saves from restoring stale credentials and retain root peer verification.

Recognize explicit configuration errors as live service responses during IPC probing, so duplicate startup does not remove the active socket.

Validated with five trusted-device tests, seven Linux IPC filesystem tests, an isolated IPC regression probe, and Windows Flutter and Linux compilation checks. Installed-service transitions and macOS runtime behavior remain untested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* Keep trusted-device grants under the service authority

Preserve root-owned grants when copying service configuration and route installed-host trust operations through the root service. Keep local authentication edits available when the Linux service is explicitly stopped, then synchronize pending revocation before using service grants.

Serialize pending revocation synchronization and cover service copies, stale user snapshots, and stopped-service authentication updates with the existing focused tests.

Signed-off-by: fufesou <linlong1266@gmail.com>

* Replace same-ID grants when enrolling trusted devices

Signed-off-by: fufesou <linlong1266@gmail.com>

* Allow macOS security edits after service removal

Reuse pending trusted-device revocation for offline password and 2FA changes. Honor pending revocation during installation copies and before root-authority requests and configuration synchronization.

Validation: five focused macOS tests, macOS and Windows cargo checks, and targeted formatting checks.
Signed-off-by: fufesou <linlong1266@gmail.com>

* Allow macOS security edits before daemon reinstallation

Detect an absent daemon using retained service ownership instead of the incoming-connections stop flag. General Start resumes connections without reinstalling the daemon, so local authentication edits must continue using pending revocation until installation.

Adjust the existing recovery test to check that toggling incoming connections does not make a standalone profile service-owned.

Signed-off-by: fufesou <linlong1266@gmail.com>

* Allow re-enrollment after invalid controller credentials

Ignore malformed or undecryptable stored token payloads with a throttled warning while preserving file access errors and host identity checks. Fresh authorized enrollment can replace the unusable credential.

Restore logged, non-propagating IPC errors for non-authentication option changes and extend the existing credential recovery test.

Signed-off-by: fufesou <linlong1266@gmail.com>

* Update hbb_common to serialize peer deletion

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): preserve trusted devices across service removal

Hand off the current root grant store to the removing user and restore that user's latest grants on reinstallation. Keep other retained profiles from becoming authoritative, and preserve user settings when the service has no optional configuration.

Validate the macOS build, five trusted-device tests, isolated headless transfer cases, and AppleScript syntax. Real privileged service transitions remain untested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(trusted-devices): use local grants and existing config sync

Keep grant decisions in the accepting host process and transfer grants and removals through the existing background configuration sync. Remove root-only grant RPC, ownership handoff, pending revocation, and dedicated copy commands; restore the master service lifecycle on Linux and macOS.

Preserve V2 credentials, authentication revocation, and ordinary-save protections. Accept the existing asynchronous sync and multiple-user limitations.

Validated Windows and macOS Flutter checks, the Linux library/tests/service check, four macOS trusted-device tests, and AppleScript syntax. Real service transitions remain untested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(trusted-devices): preserve migrated tokens at capacity

Raise the shared V2 token limit to 128 and order newly migrated tokens by issuance time without extending their legacy expiration. Extend the existing migration regression coverage to verify token reuse and expiry preservation at capacity.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): preserve trust across service stop and start

Export current on-disk grants and explicitly synchronize them into the GUI configuration at startup and before stopping a running service. Abort the stop if synchronization fails so the next service import cannot use a stale local copy.

Extend the existing synchronization regression coverage to check revoked, retained and newly enrolled devices and the actual import-source file. Remove unrelated import logging changes.

Validation: trusted-device tests passed on Windows and macOS (4 each); changed-line formatting and git diff --check passed. Real Windows GUI/UAC service transitions remain untested.
Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(trusted-devices): simplify local authorization

Replace local authorization envelopes with typed operations while retaining locked authentication checks and persistence. Remove redundant configuration and Dart wrappers without changing credential formats or management requests.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): allow service removal when host is unavailable

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): preserve trust revocations before full uninstall

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): sync trust changes to the retained user config

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): sync trust after security setting changes

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-28 10:27:44 +08:00
fufesou d0a1af8c51 feat(android): transfer files directly through SAF folders (#16644)
* feat(android): transfer files directly through SAF folders

Let Android users choose folders in the existing file-transfer browser
and stream documents through provider descriptors without workspace copies.
Keep local browsing separate from host sharing, and restrict new peer
document requests to the current shared folder.

Preserve traversal checks, overwrite protection, cancellation, empty files,
and provider errors through completion. Run Android provider IO on blocking
workers and reuse the native directory-listing bridge. Retain the existing
workspace import/export flow and non-Android file-transfer behavior.

Validation: all 11 base tests pass, including shared-root confinement,
document IO, cancellation, provider failures, and resume cleanup. macOS Rust
checking and isolated Kotlin compilation pass. Flutter analysis reports no
errors, with existing warnings. Full Android builds and device tests remain
unperformed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): group storage locations in the file menu

Replace the standalone folder icon and ambiguous RustDesk entry with adjacent RustDesk storage and Other folders actions in the existing menu. Reuse the current storage handlers and register the two localized labels.

Validation: targeted Flutter analysis reports no errors and six existing diagnostics. Translation consistency and git diff checks pass. Android device interaction was not tested.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file-transfer): keep batch deletion running across refreshes

Snapshot selected files before awaiting deletion confirmations so directory refreshes cannot invalidate the batch iterator. Defer refreshes for awaited file deletions until the batch finishes, while retaining refreshes for late completions.

Retain one regression test for completing all three confirmations after selection reconciliation. All nine file-model tests pass; focused analysis reports no errors and one existing informational diagnostic. Android visual behavior was not tested on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(android): narrow SAF scope and move folder selection to settings

Move shared-folder selection from Share screen permissions to Settings > Transfer file, preserving live connection locking and managed settings visibility.

Trim optional SAF resume and thread-identity assertions while retaining transfer and authorization regressions. Split the independent batch-deletion fix into commit 06313a9aa on fix/file-transfer-batch-deletion.

Validation: 19 base unit tests and 11 Flutter file-model/download-path tests passed. Focused Flutter analysis reports only existing informational diagnostics.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clean up failed SAF transfers and reset navigation

Cancel pending SAF writes when the client receives a transfer error or the connection manager handles WriteError. Log cleanup failures without replacing the original transfer error or discarding data retained after publication fails.

Clear local navigation history after successfully selecting a storage root, so Back does not attempt to return to the previous workspace.

Validation: 19 base unit tests and 11 Flutter file-model/download-path tests passed. macOS cargo check passed; focused Flutter analysis reports only existing diagnostics. Android build and device validation were not performed.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): guard SAF overwrites and clean up interrupted receives

Require matching per-file confirmation before SAF downloads replace an
existing destination, including transfers from peers without overwrite
detection. Enforce the policy before creating a temporary document.

Cancel unfinished SAF receives when the Android client session exits.
Delete newly created temporary documents if opening them fails, preserving
the original error and reporting cleanup failures. Keep publication-failure
recovery and non-Android transfer paths unchanged.

Validation: 19 base unit tests and macOS cargo check --lib --features flutter
passed. The document storage Kotlin modules compiled against Android SDK 35.
Full Android builds and device validation were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clean up SAF uploads on host disconnect

Cancel pending SAF receives after the Android connection manager removes
a closed connection, including receiver EOF. Log cleanup failures without
preventing cleanup of other jobs. Preserve publication-failure recovery.

Validation: 19 base unit tests and macOS cargo check --lib --features flutter
passed. Android host disconnect behavior still requires device validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore storage root after failed folder selection

Restore the previous local home when opening a selected SAF folder or the
RustDesk workspace fails or throws. Preserve navigation history on failure
and retain existing error reporting.

Keep the home assignment before the asynchronous read so superseded read
completions cannot overwrite a newer storage selection.

Validation: 11 existing file-model/download-path Flutter tests passed.
Focused analysis reports no errors and six pre-existing diagnostics.
Changed-method formatting and whitespace checks passed. Android device
validation of overlapping storage switches remains pending.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): enforce shared storage and protect SAF overwrites

Authorize peer filesystem requests against the currently shared root so
remembered workspace paths cannot bypass a selected SAF folder. Offer
text-only RustDesk storage and Other folders choices under Change path,
including an explicit switch back to the application workspace.

Reject SAF host overwrites from peers without overwrite-detection support,
while preserving modern senders' existing confirmation flow. Cover legacy
creation, rejected replacement, and modern replacement in the Rust fixture.

Validation: 19 base tests, 11 Flutter tests, focused Dart analysis, Android
Rust/APK builds, and whitespace checks passed. Device checks verified
storage switching, cancellation, old-path rejection, and Android-initiated
three-file overwrite/Skip batches against macOS. The separate legacy-host
overwrite guard was verified by Rust tests and was not in the device APK.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): reject partial SAF listings and restore folder roots

Reject provider cursors reporting loading or errors before using their
cached rows as a complete directory listing. Propagate the error through
the existing folder-transfer initialization and browsing paths.

After restoring a local SAF directory, derive its navigation root from
the displayed document path so Home and breadcrumbs use the selected
tree independently of the host's shared-folder setting.

Validation: Kotlin storage modules compile; an Android runtime cursor
check covers complete, loading, and error results. Eleven existing
Flutter tests and three temporary reconnect checks pass. Focused Dart
analysis reports only the existing setter lint; diff checks pass.
Cloud-provider transfers and navigation in a rebuilt APK were not tested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): discard pending SAF writes after block errors

Cancel pending document receives when a data-block write fails, removing
temporary data before cancellation or disconnect. Preserve the original
write error and throttle cleanup-error logging. Keep publication-failure
retention unchanged.

Extend the existing provider tests with a real descriptor I/O failure
and a cleanup-failure case.

Validation:
- Regression failed before the fix.
- cargo test --offline -p base --lib: 19 passed within 60 seconds.
- Rustfmt and git diff --check passed.
- No Android build or on-device storage-exhaustion test was performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(android): guide first-time file transfer folder setup

Prompt users to choose the folder remote devices may access when first
opening File transfer, manually starting the service, or enabling file
transfer. Offer RustDesk storage and the existing SAF picker, and explain
where to change the shared folder later in Settings.

Remember successful setup or explicit Skip, while allowing canceled setup
to be retried. Honor existing settings and permission restrictions. Use
the native translation placeholder convention for the settings path.

Validation:
- Two onboarding widget tests pass, including the breadcrumb regression.
- Flutter analysis and Dart formatting checks pass for the new files.
- Staged diff whitespace checks pass.

Android system-picker and service integration were not tested on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): group file transfer folder choices

Replace plain list rows with an indented group of full-width outlined
buttons. Tighten spacing while preserving the standard Android touch
target and allowing labels to wrap.

Validation: both existing onboarding widget tests, scoped Flutter
analysis, Dart formatting, and diff whitespace checks pass.
Android visual validation was not performed on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clean up SAF finalization and guard source resume

Discard temporary documents when flushing, syncing, or closing a receive
fails before publication. Attempt discard even when cancellation cannot
close the descriptor, and retain both errors when cleanup also fails.
Keep recovery documents after publication failures.

Reject nonzero source offsets when the SAF modification time is unknown,
so same-size changes cannot combine with a previous partial download.
Report that the transfer must restart; fresh sends remain supported.

Validation: all 19 base library tests pass, including finalization and
cancellation cleanup, combined provider errors, unknown-timestamp resume,
fresh sends, and failed-publication retention. Tests used a 60-second
limit. Rustfmt and diff whitespace checks pass. Android device/provider
failure testing and a rebuilt APK were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file-transfer): restore reliable batch deletion

Snapshot selected entries before awaiting deletion confirmations so
refreshes cannot invalidate the batch iterator. Defer refreshes for
awaited file deletions until the batch finishes, while preserving
refreshes for late completions.

Completing the batch lets the existing mobile delete handler clear the
selection and exit selection mode. Restore the previously split fix on
the SAF branch with one five-file regression test.

Validation: all nine file-model tests pass. The regression reproduced
Concurrent modification during iteration before the fix. Focused Flutter
analysis has no errors or warnings, with one existing setter-style
informational diagnostic. Formatting and diff checks pass. Android device
deletion and selection-mode rendering were not tested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(android): store logs in app media storage

Write release logs to Android/media/com.carriez.flutter_hbb/RustDesk,
using Android's package-specific media directory and the existing logger.
Include this directory in Export Logs alongside legacy logs and recordings,
and restrict the added export access to the canonical log directory.

Suggest Android/media for the first SAF folder selection on API 26+.

Validation: Android ARM64 release cargo check and Kotlin compilation passed.
Focused Flutter analysis found no new diagnostics; git diff --check passed.
Full APK linking is blocked by missing libvpx.a in the build environment.
On-device log writing, export, and initial picker location remain unverified.

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(android): keep logs in a user-authorized shared folder

Write release logs through SAF in Android/media/RustDesk instead of the
package-owned media directory. Require the independent log-folder grant
before starting the service, and include these logs in Export Logs while
retaining export of existing logs and recordings.

Keep daily/size rotation and bounded retention within the log backend.
Report provider failures explicitly and route JNI lifetime diagnostics
directly to logcat to avoid re-entering JNI during native thread teardown.
Allow a deleted log folder to be selected and created again.

Validation: Android ARM64 release cargo check, Kotlin compilation, and two
existing Flutter setup tests passed. A temporary real-JVM reproduction
using the production writer passed native thread exit at debug and trace
levels after reproducing the earlier crash. Full Rust/APK builds passed
before the final JNI and missing-folder fixes; those fixes passed Rust and
Kotlin checks. The final APK and folder authorization, rotation, export,
and uninstall retention have not been tested on an Android device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): validate SAF parent names and restore workspace navigation

Reject a newly created SAF parent when the provider changes its display
name, before receiving data into that directory. This prevents sanitized
names from splitting one source folder into multiple destination folders
while reporting success. An adjusted empty directory can remain.

Restore the local navigation root for saved paths inside the actual app
workspace before checking the saved directory. Keep that choice separate
from the host's shared SAF root and leave peer access checks unchanged.

Validation: 12 tracked Flutter file-model/download-path tests passed.
Three temporary controller tests passed for workspace root/subfolder
restoration, local SAF restoration, and workspace-prefix rejection; the
workspace case failed before the fix. Kotlin compilation, focused Dart
analysis, and diff checks passed, with one existing analysis info.
No rebuilt APK or device provider-name/navigation test was performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): authorize log storage before outgoing connections

Require the existing log-folder authorization before opening an Android remote-control, file-transfer, camera, or terminal session. Canceling or failing authorization leaves the connection unstarted; check that the page is still mounted before navigation.

Validation: two temporary authorization regression tests and two existing setup widget tests passed. Focused analysis reported only unchanged diagnostics, and git diff --check passed. No rebuilt APK or device validation.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clear terminal admin intent on canceled log setup

Clear IS_TERMINAL_ADMIN when the Android log-folder authorization guard aborts a terminal launch. A canceled administrator-terminal attempt can no longer pass its one-shot intent to a later ordinary terminal connection. Successful authorization keeps the existing session initialization behavior.

Validation: the temporary regression test reproduced the stale flag before the fix; all three temporary widget tests pass after it, including successful administrator intent preservation. Focused Dart analysis reports only unchanged diagnostics, and git diff --check passes. Android authentication dialogs were not tested on a device.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): clarify log setup and default folder selection

Show a concise log-directory prompt before opening the picker, and skip
the prompt when existing log access is ready. Start first file-transfer
folder selection in Documents, excluding the separate log grant from
the check for previous transfer-folder selections.

Validation:
- Focused Flutter analysis, locale consistency, and diff checks passed.
- Six temporary authorization/dialog tests passed during implementation.
- The review rerun of existing setup tests timed out while loading (60s).
- Kotlin/APK builds and Android device validation were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): check log readiness before boot hosting

Run the log-directory preflight off the receiver thread before creating
the hosting service. Abort startup with an explicit error when logging
is unavailable, and finish the asynchronous broadcast on every path.

Validation: four temporary JVM startup tests, Android Kotlin compilation,
and git diff --check passed. Release APK reboot and provider validation
were not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): simplify log setup and preserve reconnect paths

Use a single Choose folder action with clearer log-storage guidance and
request Android/media/RustDesk as the initial picker location. Reuse the
existing directory ID and add the button label to the locale maps.

Restore the workspace root before opening each Android local reconnect
candidate, preserving the currently browsed directory ahead of saved
locations while retaining the existing fallback order.

Validation: 14 existing Flutter tests and two temporary Android-controller
regression tests passed. Scoped Dart analysis and git diff --check passed,
with only the existing setter-style informational diagnostic.

Android compilation and device validation were not performed for these
changes. The initial picker location remains a system hint and can be
ignored if the directory does not exist.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): prioritize log setup and separate picker defaults

Authorize log storage before showing first-use file-transfer setup.
Always request Documents for transfer folder selection, independently
of previously granted folders.

Request Android/media/RustDesk for logs when accessible, otherwise use
Android/media so the existing authorization flow can create the child.
Remove the obsolete log-grant classifier.

Validation: two temporary setup-order tests, three temporary JVM picker
tests, and two existing Flutter setup tests passed. Scoped Dart analysis,
Android Kotlin compilation, and git diff --check passed.

Actual picker behavior was not tested on a device. Initial locations
remain system hints supported from API 26.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: translation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): preserve default logs and simplify folder setup

Limit folder setup prompts to hosting startup and remember successful
manual shared-folder selections. Outgoing sessions and file-transfer
permission toggles no longer request folder configuration.

Keep release logs in Config::log_path() until public storage is authorized,
and use that original directory when public writes fail. A failed default
writer must not prevent logging to a working public folder. Report the
fallback explicitly and retain both errors when neither destination works.

Use Android/media/<installed AppName>/Logs for public logs. Resolve and
create only authorized descendants, verify provider-created directory
names, and update picker and startup messages. Keep hosting authorization
and the Documents transfer-picker default unchanged.

Validation:
- Two focused Rust regressions using the real writer and flexi_logger with
  a supplied Android bridge: default/public routing, fallback and failures.
- Five temporary Flutter checks and two existing folder-setup widget tests.
- Two temporary Kotlin checks for standard/custom app names, authorized
  directory creation and independent picker defaults.
- Android :app:compileDebugKotlin -Ptarget-platform=android-arm64 passed.
- Scoped Dart analysis matches HEAD's existing diagnostics; rustfmt and
  git diff --check passed.

The temporary checks supply Android APIs; release APK logging, permission
revocation, fallback and reboot behavior have not been verified on device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): simplify log folder selection errors

Show "Please select Android/media." when a log-folder selection is invalid.
Display the platform error message instead of the full PlatformException,
while keeping the complete error in debug output. Directory validation and
permission handling are unchanged.

Validation: scoped Dart analysis, both existing folder-setup widget tests,
and git diff --check passed. No APK rebuild or device verification.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): simplify the log folder picker location

Always request Android/media as the initial log authorization location
and show the same short path in the picker title. Keep creating and
using <AppName>/Logs internally.

Remove the obsolete filesystem probe for an existing Logs directory.
Preserve accepted grants, logging policy and the Documents default for
the separate file-transfer picker.

Validation: two temporary picker checks with supplied Android APIs,
Android :app:compileDebugKotlin -Ptarget-platform=android-arm64, and
git diff --check passed. Actual system-picker positioning has not
been verified on a device.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep hosting independent of log access

Restore boot and native service startup without requiring public log
folder authorization. The existing logger uses the original directory
when public logging is unconfigured or fails. Removing the early service
stops also avoids the foreground-service startup failure path.

Keep log and shared-folder setup on deliberate hosting startup, but allow
hosting confirmation after cancellation or setup failure. Request log
setup once, before shared-folder setup.

Route Android SAF AllFiles requests through the existing CM enumeration
and result channel so the connection loop can continue processing messages.
Preserve path validation, file-count limits and ordinary filesystem paths.

Validation:
- Android Kotlin compilation and ARM64 release cargo check passed.
- Focused Dart analysis and both existing folder-setup tests passed.
- Temporary source-based startup, cancellation, log fallback and SAF
  dispatch checks passed using supplied platform/provider APIs.
- git diff --check passed; no new rustfmt differences from HEAD.

The CM unit test timed out during dependency compilation at the 60-second
limit and did not execute. No APK/device reboot, revocation or slow-provider
validation was performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): reject SAF root deletion before traversal

Reject the selected SAF tree root before recursive enumeration or deletion,
so a failed root-deletion request cannot remove its empty subdirectories.
Keep ordinary subdirectory deletion on the existing path and strengthen
one existing root-rejection assertion.

Validation:
- cargo test -p base --lib fs::android (2 passed)
- Three temporary source-backed filesystem checks passed for root
  preservation, ordinary subtree deletion and nonempty subtree rejection
- rustfmt --check and git diff --check passed
- No Android device validation performed

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): reject stale SAF source metadata

Check provider size and modification time after opening a SAF source against
its listed metadata. Reject changed sources before advertising a stale digest,
preventing resumed downloads from combining old and new file contents.

Treat negative provider timestamps as unknown instead of rejecting the entire
listing. Offset resume remains disallowed for sources with unknown timestamps.

Validation:
- cargo test -p base --lib: 20 tests passed.
- Standalone Rust transfer checks: reject a changed source, restart with the
  current contents, and preserve resume for an unchanged source.
- rustfmt --check and git diff --check passed.
- No Android APK build or device validation performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): move shared folder selection under permissions

Place Change Path below Transfer file on the Share screen page, retaining
custom-client visibility and active-session permission restrictions.
Remove the Settings entry and shorten the first-use sharing prompt.

Keep the existing translation key and update its English and Chinese
display text. Adjust the existing setup widget test to match.

Validation:
- Existing setup widget tests: 2 passed.
- Focused Dart analysis: no errors or warnings; existing page deprecation
  notices remain.
- Locale consistency, Dart formatting, and git diff --check passed.
- No Android APK build or device layout validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): tidy shared folder option layout

Render Change Path as a compact, full-width sub-option below Transfer
file, with one directional inset and a trailing chevron. This removes
the extra mobile text-button padding while preserving permission locking.

Validation:
- Focused Dart analysis: no errors or warnings; three existing deprecation
  notices remain.
- Dart formatting and git diff --check passed.
- No new tests for this presentation-only change; updated device layout
  has not been validated on Android.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): authorize legacy public logs without a folder picker

On Android versions below 8, request the system storage permission and
write configured public logs to Android/media/<AppName>/Logs. Limit
WRITE_EXTERNAL_STORAGE to API 25; keep the existing SAF flow on newer
Android versions. General storage permission alone does not redirect logs
before explicit setup.

Keep the original log destination before setup and on public-write errors.
Permission denial completes promptly and does not prevent hosting. Retain
log rotation and export, and reopen the writer during readiness checks if
the fixed directory has been moved or removed.

Validation:
- Nox Android 7.1.2: permission grant/denial, original-log fallback, fixed
  public destination, persisted setup, and actual Rust logs after restart.
- Isolated device regression: exported public logs match the source bytes;
  moved-directory readiness fails before the fix and passes afterward.
- Full ARMv7 Flutter debug APK with the release Rust library built and
  installed successfully.
- Two Flutter setup tests, scoped Dart analysis, whitespace checks, and
  a fresh CodeRabbit review passed.

Android 5/6 and Android 8+ device testing was not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore SAF log setup and surface folder errors

Use SAF for public logs on all supported Android versions. Remove the
legacy direct writer and WRITE_EXTERNAL_STORAGE permission, preserving
the original logging fallback and normal picker cancellation.

Keep log-folder errors visible until acknowledged, then exit the current
manual hosting setup instead of advancing to file-transfer setup. Translate
the error text, add Chinese translations, and register the missing key in
the language catalogs. Boot startup and existing services are unchanged.

Validation:
- Two existing Flutter file-transfer setup tests passed.
- Focused Flutter analysis and git diff --check passed.
- Built the release ARMv7 Rust library and Flutter debug APK.
- On Nox Android 7.1.2, invalid-root selection showed the persistent Chinese
  error; acknowledgement exited setup without starting hosting.
- Ordinary picker cancellation continued the existing optional setup flow.
- APK inspection found no READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE,
  or MANAGE_EXTERNAL_STORAGE permissions.

Device validation used the debug APK on Nox; other Android versions and
a full release APK were not device-tested for this change.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): bind SAF digests to opened files and simplify setup

Read size and modification time from the opened document descriptor so
replacing a source path cannot pair old data with a newer resume digest.
Refresh stale metadata for regular files and keep streams non-resumable
while preserving fresh sequential sends.

Shorten the shared-folder setup prompt and direct log-folder selection to
Android/media. Remove obsolete app-media log-directory queries and export
allowances; retain SAF logs, original app-data logs, and recordings export.

Validation:
- Focused base Android tests: 3 passed; the replacement regression failed
  before the descriptor fix and passed afterward.
- Existing Flutter setup and file-model tests: 11 passed.
- ARMv7 release Rust library and Flutter debug APK builds passed.
- Scoped CodeRabbit re-review: 0 findings; git diff --check passed.

No Android device validation was performed for this revision.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): avoid blocking log callers during folder setup

Bypass per-record JSON/JNI dispatch after discovering that public logging
is unconfigured. Publish routing changes from the application-owned Kotlin
logger so a late fallback response cannot undo successful folder setup.

Return contended public writes to the existing original-directory writer
instead of waiting for provider operations. Keep the JNI callback available
in debug Android builds without changing their logcat logger, and remove
the unused English tree-picker title.

Validation:
- ARMv7 release Rust build and full Flutter debug APK packaging passed.
- Android 7 provider contention check reproduced the wait before the fix
  and passed with the rebuilt Kotlin code and native library.
- Temporary native routing test passed: one initial backend lookup,
  subsequent bypass, re-enabling, and all records in the fallback file.
- Formatting and diff checks passed; scoped CodeRabbit findings were
  reviewed against the Rust fallback and picker callers.

The full APK was built but not installed for this change. Device checks
used the existing isolated probe; full release-app end-to-end validation
remains pending. No permanent test infrastructure was added.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): allow slower SAF empty-directory scans

Use the existing 30-second remote-directory timeout only for SAF scans. Ordinary filesystem scans keep the two-second timeout, and the request lifecycle is unchanged.

Add a regression for a SAF response arriving after three seconds. The file-model suite passes all 10 tests. Targeted analysis reports only the existing setter lint; Android device validation was not performed. Scans exceeding 30 seconds can still time out.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): isolate SAF empty-directory scan lifecycles

Only remove an empty-directory request when its timeout still owns the registered completer. Cancel pending SAF scans when the remote session changes so the same directory can be retried immediately. Ordinary filesystem reconnect behavior and timeout durations remain unchanged.

Share regression coverage for completed and reconnected scans, keeping each retry pending across the previous timer deadline without adding production state.

Validation: all 11 file-model tests pass. Test-file analysis and whitespace checks pass; targeted production analysis reports only the existing setter lint. No Android build or device validation was performed for this change.
Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(android): deduplicate SAF helpers and test setup

Share local storage navigation, tree-picker Intent construction, and
provider listing readiness checks. Preserve independent picker defaults,
error messages, failed-navigation rollback, and authorization lifecycles.

Reuse Rust transfer-test fixtures and remove text-only setup assertions
while retaining behavioral regression coverage. Reduce the branch diff
by 78 lines without changing transfer or logging policy.

Validation:
- Focused Rust SAF tests: 3 passed.
- Four focused Flutter suites: 20 passed; the two setup tests were rerun
  successfully after removing text-only assertions.
- Kotlin 2.1.21 / Android SDK 36 compilation passed for all six document
  integration sources in an isolated Linux directory.
- git diff --check passed. No APK build or device validation this round.

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore(android): organize SAF tests and complete zh-TW label

Move the two SAF test files into libs/base/src/fs/tests and update the
module path, preserving their contents and module hierarchy. Fill the
missing Traditional Chinese translation for Choose folder.

Validation: cargo test -p base --lib fs::android passed (3 tests).
Verified both moved files are byte-identical and all six new SAF/logging
keys have nonempty Traditional Chinese translations. Diff check passed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep hosting available and reject stale SAF scans

Keep the log-folder error dialog, skip remaining folder setup after an
error, and continue to the existing hosting confirmation. Public-log
failures retain the existing default-directory logging fallback.

Reject remote SAF empty-directory requests if the session changes while
awaiting bridge dispatch. Reuse the existing session generation and leave
ordinary filesystem requests unchanged. Extend the existing lifecycle
test to cover reconnecting during dispatch.

Validation: 21 focused Flutter tests passed. A temporary startup-flow
check passed for setup error, success and cancellation. Analysis reports
only the existing setter-return-type info. Diff check passed; CodeRabbit
review completed with no findings. No Android device validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): avoid logging raw folder setup errors

Log fixed status messages for Android log-folder setup failures instead
of provider-derived exception text. Preserve detailed errors in the
dialog and keep the existing folder setup and hosting flow unchanged.

Validation: focused Flutter analysis and both existing folder-setup
widget tests passed. Diff check passed. No Android device validation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): offload access checks and restore identical-file hints

Run Android peer-path authorization on blocking workers for initial
folders, file actions and rename destinations. Preserve fail-closed
access decisions and the existing non-Android path.

Set the SAF identical-file hint only for matching sizes and known,
matching modification times. Keep overwrite confirmation and reject
receive resume. Extend the existing transfer test to cover the hint.

Validation:
- cargo test -p base --lib fs::android: 3 passed.
- Android ARMv7 API 21 release cargo check with flutter,hwcodec passed
  using an isolated copy of HEAD plus these changes, verified by SHA256.
- macOS cargo check --lib --features flutter passed for the access fix.
- Temporary scheduling check failed before the fix and passed afterward.
- Scoped CodeRabbit reviews and diff checks passed.
No APK build or device testing was performed for this follow-up.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): limit SAF dispatch and clarify log exports

Use blocking workers only for document-backed file operations. Keep
ordinary Android filesystem calls on their original execution path and
retain asynchronous peer-path authorization checks.

Report success and failure counts when log export succeeds only partly.
Skip the destination picker when there are no default log sources and no
public-folder permission. Preserve public-log permission errors.

Validation:
- cargo test -p base --lib fs::: 19 passed.
- Flutter file-model and Android setup suites: 14 passed.
- Android ARMv7 API 21 release cargo check with flutter,hwcodec passed
  against an isolated source copy, verified by SHA256.
- Temporary Dart feedback and Kotlin export-handler checks failed before
  the fix and passed afterward.
- Focused Dart analysis found only existing diagnostics; diff checks passed.
No APK build or device testing was performed for this follow-up.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): allow longer SAF deletion scans

Use the existing 30-second deadline when recursively scanning SAF folders
before deletion. Keep the two-second timeout for ordinary filesystem
paths, with request handling and deletion behavior unchanged.

Validation:
- Existing Flutter file-model and Android setup suites: 14 passed.
- Targeted Dart analysis found no new diagnostics.
- git diff --check passed.
No Android APK build or device testing was performed for this fix.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep public logging active after cleanup failures

Log a warning when deleting an expired log fails, and continue writing to
the newly opened public log. Keep creation, open, and write failures on
the existing fallback path. Failed deletions may leave extra old logs.

Validation:
- Four temporary Kotlin checks passed: deletion false/exception, normal
  rotation, and propagation of a genuine log-open failure.
- Actual document-storage Kotlin sources compiled against Android API 36.
- git diff --check passed. No APK build or device test performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): keep accepted SAF download file lists immutable

Reject replacement of an accepted generic SAF download file list before
any mutation, so per-file overwrite approval cannot be redirected to a
different destination. Preserve the ordinary download and upload paths.

Extend the existing overwrite test and construct each receive fixture
with its initial file list only once.

Validation:
- The added assertion failed before the fix; all 19 filesystem tests
  passed afterward: cargo test --offline -p base --lib fs::.
- Scoped CodeRabbit review completed with no findings.
- No new rustfmt differences; git diff --check passed.
- No Android build or device test performed for this fix.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): preserve secondary document-transfer exceptions

Attach cleanup and backup-restore exceptions to the original error before
wrapping it in IOException. Preserve the existing error messages, primary
cause, and transfer control flow.

Validation:
- Confirmed the diff contains only the two addSuppressed calls.
- git diff --check passed.
- No Android build or device test performed for this diagnostic change.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): log authorization failures with throttling

Report document access-check errors at warning level and blocking-task
failures at error level. Throttle each site to one record per minute while
preserving existing deny decisions and failure messages.

Validation:
- Existing hbb_common log_throttle tests: 4 passed.
- Rust formatting and git diff --check passed.
- Android target build and device testing not performed.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-28 10:03:43 +08:00
fufesou 2b6fedfc8e fix(windows): protect installer handoff and use native service control (#16562)
* fix(windows): use native APIs for manual service control

rustdesk/rustdesk#16557

Replace elevated service-control batch scripts with SCM operations
and native tray shortcuts. Preserve SYSTEM configuration import
and propagate helper failures.

* fix(windows): preserve service state during manual control

Keep existing service registration and synchronize failed operations with SCM state. Run the installed legacy importer directly as SYSTEM, bound its exit waits, and keep auxiliary cleanup errors separate from service lifecycle results.

* fix(windows): preserve legacy service import without debug privilege

* fix(windows): replace installer certutil and findstr

Use native SHA-256 verification and shortcut decoding, and execute the protected batch script in a single cmd process. Preserve protected staging, verification receipts and failure exit codes.

Restore the master service-control flow and remove the separate SCM and Task Scheduler implementation to keep the antivirus fix focused and preserve legacy configuration import.

Validation: Windows Rust library build, nine focused tests, and isolated single-cmd success, failure, tamper rejection and cleanup checks passed. Bitdefender validation remains pending.

* fix(windows): preserve installer helper path compatibility

* fix(windows): preserve caret-only installer helper paths

* fix(windows): protect installer data and use native service control

Stage installer scripts, shortcuts and verified updates with restricted ACLs
before elevation. Remove the elevated portable-verifier launch while keeping
certutil and findstr out of the shared installer handoff.

Use the installed Flutter runner for native normal-service create/start and
stop/delete operations. Preserve existing registrations on repeated setup,
legacy configuration import, and compatibility with older installed runners.
Report installer failures and document the security and compatibility reasons.

Validation: Windows Rust test build and Release runner build passed; six
isolated service checks and runner exit-code checks passed. Focused Rust
installer/update tests passed 8/9; the directory-cleanup ACL test also fails in
the earlier binary. Final service lifecycle and Bitdefender testing is pending.

* fix(windows): clean staged installer files on UAC cancellation

Retain DELETE handles until the elevated cmd connects to a local pipe, then
release the handles before allowing the BAT to run. This preserves protected
staging while allowing cancellation cleanup and cmd file access.

Handle interrupted handoffs, supply the byte-count pointer required on
Windows 7, and explain early service dispatch in the C++ runner. Keep the
staging tests inline with their implementation.

Validation: Windows Rust test build, focused installer-file and handoff
tests, rustfmt, and staged diff checks passed. The pre-existing directory
ACL test failure is unchanged. Actual UAC cancellation, Windows 7 runtime,
and final-build Bitdefender validation remain pending.

* fix(windows): roll back new service registration on start failure

Delete a registration created by this attempt if startup fails, while preserving existing services and already-running success. Keep the original startup error and report rollback failures separately.

Validation: eight isolated service checks and the Release Flutter runner build passed. Real service startup failure and final-build Bitdefender validation remain pending.

* fix(windows): continue uninstall after service stop errors

Log an unsuccessful stop and still request deletion so the uninstall
script can reach its existing taskkill and cleanup commands. Treat an
already marked service as a satisfied deletion request, while preserving
other deletion errors and the normal stop wait.

Validation: all twelve isolated service-control checks passed, including
already-marked, stop-rejected, stop-timeout and deletion-denied cases.
The Release Flutter runner build and existing argument/exit-code checks
passed. Real SCM failure scenarios and Bitdefender remain unverified.

* fix(windows): finish installer setup before service startup

Complete SAS policy and printer setup before native service activation so a startup failure cannot skip independent installation steps. Preserve the startup error, rollback and legacy command sequences, and document uninstall_service's continuation result.

Validation: Windows Rust test compilation, four isolated command cases and two existing installer handoff tests passed. CodeRabbit completed with no findings. Actual installer, SCM and Bitdefender validation remains pending.

* fix(windows): use native service control for Sciter

Share the existing native service operations with Sciter and dispatch
--service-control before UI initialization. Keep Flutter's entry point and
the legacy commands for a different installed executable unchanged.

Validated the Windows x64 Sciter build, Flutter-feature test build,
x86 C++ bridge compilation, and real EXE/BAT error propagation. Final
Bitdefender installation and service lifecycle validation remain pending.

* fix(windows): propagate negative service-control exit codes

Treat every nonzero service-control exit status as a failure so crash and loader error codes cannot be reported as success. Verified six real cmd.exe exit-code cases; CodeRabbit review completed with no findings. Actual service crashes were not exercised.

* fix(windows): handle UAC cancellation and scope service rollback

Preserve the typed elevation error and its diagnostic details so canceled UAC prompts do not show an installation-failure dialog.

Use create-service for normal installation and create-service-with-rollback for manual Start Service. Retain a new registration after a normal installation startup error; roll back only the manual path that restores stop-service=Y.

Validation: Windows Rust test compilation, Release Flutter runner and Sciter builds, cancellation/error-chain checks, isolated service-result cases, and real executable argument/exit propagation checks passed. Actual UAC cancellation and Bitdefender were not rerun.
2026-10-22 13:34:49 +08:00
fufesou b44b71cfab Fix/download path validation (#16601)
* fix: validate download destinations and peer filename rules

Reject unsafe or linked download destinations and validate empty-directory
paths before creating them. Bind overwrite approval to accepted transfer
entries and propagate receiver errors to the UI.

Validate remote listings using the peer's filename rules so Unix names
remain browsable on Windows. Reject locally unsafe basenames before
starting downloads from either the Flutter or legacy UI.

Finish the previous file before validating the next digest so rejecting
the next destination preserves the file already received.

Require confirmation before restarting interrupted downloads with stale
staging files. Preserve matching resumes and cover receiver recovery.

Retain explicit overwrite rejection for peers older than 1.1.10 that
cannot negotiate confirmation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: use async I/O for download preparation and resume

Use Tokio filesystem APIs for download setup and resume metadata checks. Run the Windows link-count query on the blocking pool and flush digest writes before returning.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-21 11:55:00 +08:00
fufesou c7f212df2c fix: warn about insecure custom API server connections (#16616)
* fix: warn about insecure custom API server connections

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: limit API server warning to HTTP addresses

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: resolve API warnings against effective server configuration

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: reuse common sync for API server previews

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: keep server settings usable on API preview errors

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-20 22:52:48 +08:00
fufesou f705902ec3 fix(windows): move installer scripts into protected temp directories (#16554)
* fix(windows): move installer scripts into protected temp directories

Create a private directory under Windows Temp with its final DACL in
CreateDirectoryW. Grant Administrators and SYSTEM full access and use
OWNER RIGHTS to suppress the ordinary creator's implicit WRITE_DAC.

Keep the existing elevated system-cmd copy and SHA-256 verification flow.
Keep auxiliary files in the same directory, report cleanup failures, and
check command length before creating the directory.

This avoids PowerShell, helper executables, and application startup or DLL
linking changes. The Windows Temp parent must retain its standard protection
against deletion by unprivileged users. UAC cancellation can leave an empty
protected directory; the error reports its path.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): preserve installer results when cleanup fails

Keep the original installer exit code and report remaining temporary directories or inspection errors as warnings with their paths. Include the directory path in creation errors.

Restore the existing handoff assertions in a separate test module, add a locked-directory regression for successful and failed installers, and remove the unrelated ERRORLEVEL environment change.

Validation: scoped formatting and whitespace checks passed. Native cmd probes reproduced the failure before the fix and preserved both exit codes afterwards. No project build or Rust test execution was performed.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): clarify installer cleanup warnings

Describe protected directories as possibly remaining when cleanup cannot be verified. Remove the cleanup-only test added during review while retaining existing handoff coverage.

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(windows): keep installer handoff tests inline

Restore the inline test module and original helper order to reduce the diff against master. Keep the directory cleanup assertions and original bootstrap checks.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): include paths in installer directory check errors

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): clear inherited ERRORLEVEL before installation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): avoid duplicate directory error messages

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(windows): verify installer directory permissions

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-15 17:33:35 +08:00
fufesou 0921a7432f feat(update): verify signed update metadata (#15914)
* feat(update): verify signed update metadata

* fix(update): harden metadata publishing and app names

- share strict app-name validation across Windows and macOS
- preserve published sidecars until replacements are ready
- select macOS test features by target architecture
- update the signing dependency and URL validation coverage

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): prevent replacing published release assets

- block reruns after signed update metadata is published
- clarify the security rationale for app-name validation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): cache TLS responses and avoid double translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: reduce the diffs

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(update): remove obsolete update paths

- remove unused FFI update branches
- delete legacy Windows and macOS update helpers
- simplify macOS daemon updates and redundant gating

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(update): secure same-identity macOS offline updates

- verify DMGs using signed metadata, size, and SHA-256
- enforce package and bundle identity plus macOS signature checks
- support custom release policies and propagate manual update failures

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): verify Windows installers with Authenticode

Reject unsigned or untrusted EXE/MSI updates
before elevation, clean up failed verified copies,
and fix the macOS manual updater logging import.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): clear stale URL on response body errors

Keep body-read failures in the common cleanup path and document the
cache-only Authenticode verification policy.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(update): remove OS signature sanity checks

Remove Windows Authenticode and macOS codesign/Gatekeeper verification.
Keep metadata, artifact integrity, bundle identity, and transaction checks.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(update): move update scripts into res/update

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): secure Windows MSI handoff against junction races

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): secure Windows EXE handoff against junction races

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): preserve legacy custom client app names

- allow names starting with digits or underscores
- preserve leading hyphens on Windows for compatibility
- reject leading hyphens on macOS to avoid pgrep option parsing

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(update): remove some simple wrappers

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(update): simple refactor

* refact(update): comments on update.scpt

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(update): comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): create download destinations exclusively

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): roll back updates when final readiness checks fail

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): propagate MSI installation failures

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): reject stale IPC sockets during updates

Require open IPC sockets in addition to launchd running state before committing the privileged update transaction. Inspect sockets with lsof to avoid triggering IPC authentication.

Add native socket regression coverage to macOS CI. Remove the unused Windows helper, combine offline metadata exports, and restore unrelated formatting changed by the PR.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): allow offline updates without an installed bundle

Validate app-only updates against the running client's bundle identifier and check the destination identity when it exists. Replace source-text assertions with behavioral identity tests and run the macOS update tests in CI.

Signed-off-by: fufesou <linlong1266@gmail.com>

* ci, gate test with PUBLISH_UPDATE_METADATA == 'true'

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-12 10:56:50 +08:00
fufesou c8532719c0 Fix/macos mouse release (#16516)
* fix(macos): preserve mouse-up through cursor protection

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): clear tracked presses released in relative mode

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): preserve desktop mouse releases through cursor protection

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): preserve button state when mouse-up is rejected

Clear tracked buttons only after coordinate validation succeeds.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-10 19:05:45 +08:00
fufesou 001b0e9617 Fix/preserve deleted printer (#16500)
* fix(windows): preserve manually deleted printers

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): distinguish printer detection failures

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): skip printer detection when disabled

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(flutter): load printer status asynchronously

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(flutter): remove printer status mounted checks

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): preserve deleted printers in direct upgrades

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): preserve deleted printers in silent EXE installs

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): skip printer installation when detection fails

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(installer): default printer off when detection fails

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(printer): limit checked enumeration to presence queries

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(installer): allow cancellation during printer detection

Inline MSI update command construction without changing its arguments.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(flutter): time out installer printer detection

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(windows): cover multilingual install app names

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-10 00:21:17 +08:00
fufesou 3f4bfe535e Fix/wayland input keyboard layout (#16462)
* fix(linux): respect Wayland keyboard layouts for uinput

Resolve printable keys from GNOME input sources or the Wayland keymap and KDE active layout group. Refresh layout metadata on input at most once per second, and preserve the keycode and owned modifiers until release.

Preserve the original Legacy character on client key release and handle Caps Lock shortcuts without introducing an unwanted Shift modifier.

Validated with GNOME/KDE full-service input and disconnect checks, plus Windows/Linux native keyboard capture and release checks.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): harden Wayland keyboard layout detection

Keep the last usable map on transient discovery failures and preserve the
legacy character path when no map is available, with explicit warnings.
Retain each injected key representation through release and handle IBus
default layout and variant metadata without compiling a layout named default.

Use native GNOME and Plasma sources, including KWin sessions without the
optional layout service. Read the Xwayland keymap and effective group from
the same keyboard on other desktops. Load XCB/XKB libraries dynamically and
accept the unix/:N local display spelling.

Validation: 24/24 temporary Linux production-module and IPC checks passed,
including an actual read-only Xwayland query. The two latest routing/display
regressions failed before their fixes. Rustfmt and git diff --check passed.
No Cargo/Flutter commands or full remote application tests were rerun.

Xwayland state freshness and synchronous desktop I/O remain known limits.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): bound input waits for layout refresh

Run throttled Wayland layout discovery in a single background worker and give the triggering input a 25 ms wait budget. Later input uses the existing map without waiting or queuing more queries. Preserve source error handling and skip known Legacy clipboard-only text.

Document Xwayland freshness and per-character modifier ownership limits. Desktop calls remain uncancelled; the budget bounds input waiting, subject to OS scheduling.

Validation: Linux module compilation with cached dependencies, 23 existing temporary module/IPC checks, and stalled-XCB timing probes passed. Rechecked cache/error/release cases after the final lock-scope change. No permanent regression tests or Cargo/Flutter commands.
Signed-off-by: fufesou <linlong1266@gmail.com>

* docs(linux): record layout query latency measurements

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): reply to failed uinput key state queries

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): prefer non-keypad character mappings

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): preserve text after unmappable uinput characters

Continue processing a text sequence when one character cannot be resolved
in the host layout. Return the first mapping error after the supported
characters are injected, so the existing throttled log still reports failure.

Keep modifier-query, injection and release errors fail-fast. This fixes
German a^bc losing bc without adding clipboard or fixed-US fallback.
The unsupported caret remains an explicit mapping error.

Validation: three temporary Linux checks (nine inputs) passed using the
production resolver and sequence methods, real framed IPC and native XKB
decoding. The suffix check failed before the fix. Formatting and diff checks
passed; no permanent tests, Cargo/Flutter commands or full app build added.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): preserve Latin shortcuts on non-Latin layouts

Detect Ctrl/Alt/Meta independently of CapsLock. If the active XKB map lacks
an ASCII shortcut letter, reuse the existing physical letter mapping while
preserving explicit Shift and the press-time key for release. Existing
layout mappings and unsupported ordinary text retain their behavior.

Honor disable-uinput-layout-fallback and log this compatibility path with
the existing throttle.

Validation: 12 temporary Linux production-module/IPC checks passed,
including 43 non-Latin shortcut/layout/lock/Shift cases. The new checks
failed before the fix. Formatting and git diff --check passed. No real
mobile/compositor session or Cargo/Flutter build was run.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): preserve missing punctuation shortcuts

Allow missing ASCII graphic shortcuts to reuse the existing physical key
table when Ctrl, Alt or Meta is active. This restores Ctrl+[ and Ctrl+]
with a valid Russian layout instead of discarding their character events.

Retain the table's Shift requirement for punctuation and use existing
modifier ownership and press-time release tracking. Uppercase letters do
not synthesize Shift. Active-layout mappings, ordinary text errors and
disable-uinput-layout-fallback retain their behavior.

Validation: 14 temporary Linux production-module/IPC checks passed after
recompiling the final source with rustc and cached dependencies. Includes
19 punctuation/lock/Shift combinations and 43 existing Latin-shortcut
cases. The two new checks failed before the fix. Formatting and
git diff --check passed. No Cargo/Flutter build or real mobile/native
Wayland session was run.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): respect held Shift when resolving shortcuts

Derive Shift-held candidates from the same XKB keymap and query the
caller's Shift state for character shortcuts. On US layouts, Ctrl+Shift+<
now uses the comma key instead of the extra ISO key that produces >.

Keep caller-held Shift out of synthesized modifier ownership. Preserve
ordinary input and letter-shortcut identity, and do not replace an
ordinary shortcut with a keypad alias to satisfy the held-Shift lookup.

Validation: 16 temporary Linux production-module/IPC checks passed with
native XKB event replay and cached dependencies via rustc. Includes
30 US text/shortcut cases and 40 UK/DE/FR character/lock cases. The new
Ctrl+Shift+< check failed before the fix. Changed sections pass formatting
and git diff --check. No Cargo/Flutter build or real mobile/native Wayland
session was run; device-state and packet-modifier handling use fixtures.

Signed-off-by: fufesou <linlong1266@gmail.com>

* docs(android): document paired-punctuation input limitation

Signed-off-by: fufesou <linlong1266@gmail.com>

* docs(linux): explain Xwayland layout synchronization limits

Document the focus-scoped modifier updates observed on GNOME and KWin. Core and physical X11 keyboards can retain an old layout group while native Wayland windows have focus; reconnecting or waiting cannot request the missing update.

Validation: 16 existing module/IPC probes and the live GNOME mapping probe passed. Live KDE queries reproduced native French versus stale Xwayland US. Formatting and diff checks passed. This is a comment-only change; strict Xwayland-only behavior is preserved.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): query native Sway and Hyprland keyboard layouts

Read the uinput keyboard's active layout through compositor IPC and
validate its group against the native Wayland keymap. Match the IPC
server to the Wayland session, and accept numbered Hyprland device
names while rejecting ambiguous keyboard selections.

Validation: 21 temporary production-module and framed-IPC checks passed.
Live Sway US/FR group and a/q mapping checks passed with Xwayland disabled;
the GNOME French layout probe also passed. Formatting checks passed.
A live Hyprland session and a full application build were not tested.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): defer unverified native compositor layout queries

Remove the Sway/Hyprland adapter that paired a uinput device's layout
group with a seat keymap based only on matching layout names. Different
device options can make a synthesized AltGr key toggle CapsLock instead.

Restore the existing best-effort Xwayland source for other desktops and
document the device-keymap information missing from the examined native
queries. GNOME/KDE discovery and injection/cache behavior are unchanged.

Validation: 16 temporary production-module/IPC checks passed after the
removal, plus read-only GNOME French and Xwayland mapping checks. Rust
formatting and diff checks passed. No full application build or mobile
end-to-end test was run for this follow-up.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): reject unreliable GNOME per-window layout sources

GNOME restores per-window layouts without persisting MRU. Reject MRU
selection when per-window input sources are enabled with multiple sources,
and invalidate the cached map and source identity for this typed error.
Transient discovery failures still retain the last valid mapping.

Reuse the existing logged/disableable compatibility policy. This contains
stale-map acceptance; it does not add effective per-window layout discovery.
Single-source and global-source GNOME configurations retain their path.

Validation: the old code failed the temporary stale-cache regression.
All 3 focused production-module checks pass after the fix, covering cache
invalidation/recovery, compatibility modes, single/default source mapping,
UK punctuation, and last-good retention after an IBus connection failure.
Compiled with rustc and cached Linux dependencies; formatting/diff checks
passed. No permanent tests, Cargo/Flutter build or mobile session added.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): reject inactive IBus layout sources

Read the current GlobalEngine description instead of looking up the
persisted MRU engine by name. GNOME can suppress IBus for password entry
without updating MRU, leaving the old engine's layout valid but inactive.

Report an engine-ID mismatch as UnreliableSource so the existing cache
invalidation path drops that mapping. Keep transient query-error handling
and layout/variant normalization. Do not infer a US compositor layout from
GNOME's xkb:us::eng echo engine.

Validation: the old code failed the inactive-engine cache and default-layout
checks. All 3 temporary production-module/D-Bus checks pass with this fix,
covering invalidation/recovery, compatibility policies, metadata sentinels,
query failures and ordinary French XKB selection. Compiled with rustc and
cached Linux dependencies; formatting/diff checks pass. No permanent tests,
Cargo/Flutter build or live password/mobile session added.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): prewarm uinput layout discovery at startup

Start the existing layout worker when the uinput keyboard client connects,
before the server accepts remote input. Reuse its throttle and single-worker
logic; keep the existing 25 ms input wait and background completion policy.
Prewarming reduces cold-start fallback without introducing input queues or
promising readiness while a desktop query remains outstanding.

Verified with three temporary Linux production-module checks using cached
dependencies: a 125 ms query resolves UK @ before first input, a 200 ms
outstanding query stays nonblocking and publishes its map, and a failed
startup query retries successfully. All three fail their startup assertions
before the change. No permanent tests or Cargo/Flutter builds were added.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): preserve first input wait during layout prewarming

Keep the startup query's completion receiver until the first relevant input
takes it. That input can wait for the already-running query within the
existing 25 ms deadline, without holding the receiver handoff lock. Later
input remains nonblocking and the existing single-worker guard is unchanged.
Prefer a due refresh to an old completed startup notification.

Verified with four temporary Linux production-module checks compiled with
rustc -C opt-level=3 and cached dependencies: joining a pending UK query,
one bounded wait followed by nonblocking input, due refresh after prewarm,
and recovery after query failure. Both lost-wait checks fail before the fix.
Formatting and git diff --check pass. No permanent tests or full build added.

An unoptimized delayed-query probe still exceeded 25 ms and used fallback;
this restores the wait opportunity, not an initial-readiness guarantee.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(uinput): remove unnecessary change

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(linux): simplify uinput character releases

Release the mapping stored for the same character. Linux character input already pairs down/up events, so opposite-case matching is unnecessary.

Validation: git diff --check passed. Static comparisons confirmed that Map-mode dispatch, raw-key injection and keycode offsets match the PR base. No Cargo/Flutter commands or live-session tests were run.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): normalize Unicode CapsLock shortcuts

Normalize single-scalar Unicode lowercase for CapsLock shortcuts without adding Shift. For lowercase expansions, resolve the original symbol with the actual CapsLock state instead of truncating it. Document the existing first-seat discovery limitation.

Validation: two temporary Rust context checks against lookup fixtures failed before the fix and passed afterward. No Cargo/Flutter build or end-to-end input test was run.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): derive CapsLock shortcut keys from XKB

Keep the actual CapsLock state when resolving shortcut characters. Use the existing Caps-off and Caps+Shift mappings to omit generated Shift only when XKB confirms the same physical key and remaining modifiers, preserving the Turkish I/ı and İ/i key identities.

Validation: three temporary production-module checks passed on 192.168.5.32 with native libxkbcommon; two failed before this fix. Covered Turkish, German, Cyrillic and ASCII shortcuts, explicit Shift, ordinary text and UK punctuation. No full application build, IPC or end-to-end input test was run.
Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(linux): remove CapsLock shortcut special cases

Use the existing lock-state synchronization and ordinary XKB mappings for mobile soft-keyboard input. Remove CapsLock-specific shortcut remapping and compatibility-path lowercasing while retaining held-Shift punctuation handling.

Validation: rebuilt the Linux Rust library and reused the unchanged Flutter UI. Actual Android Gboard input matched UK punctuation on GNOME and KDE; French/German switching on KDE passed after the refresh interval. The user confirmed real iPad input on both hosts, including smart quotes. Formatting and whitespace checks passed. The optional Ctrl/Shift toolbar automation was inconclusive and is not counted as passing coverage.
Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): press Level3 before Shift for uinput characters

Preserve mapping candidate preference while storing generated modifiers in Level3-before-Shift order. This avoids activating Compose with lv3:ralt_switch_multikey; releases retain their existing reverse order.

Verified on Linux with temporary production-mapper/libxkbcommon tests: Lithuanian ! fails before this change and passes afterward; all 24 UK punctuation/lock-state cases pass with released modifiers and unchanged lock state. Formatting and diff checks pass. No full application build or mobile end-to-end test was run for this change.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): preserve legacy uinput IPC without a keymap

Send sequences and character clicks through their original IPC messages when the layout cache is unavailable, avoiding unnecessary synchronous key-state queries. Keep throttled fallback warnings and leave layout discovery, retry policy, and key-down/up pairing unchanged.

Verified with three temporary production-method checks on Linux using a recording transport and injected query errors. The unavailable-map routing check fails before the fix and passes afterward; raw-click routing and available-map query-error handling remain unchanged. Changed-code formatting and diff checks pass. No full application build or end-to-end input test was run.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): skip layout queries for legacy uinput fallback

When no keymap is available, single-character input must use the legacy
KeyDown/KeyUp path without querying lock or modifier state first. Retain
the legacy key in the existing press record so discovery completing
before a repeat or release cannot change the selected representation.

Validated with four temporary production-method checks on Linux using a
controlled cache/resolver and recording transport: missing-map fallback
with query failures, press/release across cache transitions, unchanged
raw-key dispatch, and Ctrl+C without extra Shift or queries. The initial
fallback check failed before this change. Valid-map errors still surface.

Changed-code formatting and git diff --check pass. No full Cargo/Flutter
build or live service/mobile test was run for this change.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-09 11:17:04 +08:00
fufesou 25d2c6db55 log key-up and text injection failures (#16455)
* fix(macos): log key-up and text injection failures

Report failed key releases through the rdev input path and
missing sources, mappings, or events in the Enigo keyboard path.
Throttle repeated failures without logging text contents or changing input behavior.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): refine keyboard failure diagnostics

Remove key identities from virtual-input and Enigo failure logs while preserving operation and failure details. Also report key-down event creation failures in key_click with the existing throttle, without changing event posting or error propagation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(logs): update rdev, add diagnostic logs

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-10-08 00:23:14 +08:00
fufesou 0c18a8cbc9 fix(flutter): restore multi-window rendering after reconnect (#16383)
Use the window's current display when lastUserDisplay is unset so
additional monitor windows restore their capture subscriptions.

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-29 10:16:55 +08:00
fufesou 44683d108b fix(macos): clear Finder progress after clipboard transfers (#16346)
* fix(macos): clear Finder progress after clipboard transfers

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(macos): cover single-response clipboard completion

Verify progress completion, handle cleanup, and finalized file contents.
Move function-only Finder imports into progress helpers.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macos): unpublish clipboard progress before renaming

Complete and unpublish NSProgress before the final file rename to avoid
stale Finder and Dock progress indicators.

Signed-off-by: fufesou <linlong1266@gmail.com>

* chore: add ref

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-26 15:03:55 +08:00
fufesou bb924d2939 fix: avoid unnecessary clipboard reads and stale initial sync (#16293)
* refact: simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(clipboard): don't keep the clipboard cache

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent initial clipboard sync from overwriting newer updates

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: invalidate initial clipboard snapshots on remote and file updates

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: handle clipboard changes during initial sync

Track clipboard changes even when they produce no sync message.
Refresh invalidated snapshots to preserve sync after Wayland startup.
Prevent repeated PeerInfo responses from restarting initial sync.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: skip file clipboard reads when file sync is not required

Check is_file_required() before reading clipboard file URLs to avoid
unnecessary clipboard access when no session needs file synchronization.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: refresh clipboard requirements before listener startup

Update Flutter's text and file clipboard requirements before starting the
shared clipboard loop so startup notifications use the current settings.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exclude disconnected sessions from clipboard requirements

Only count connected Flutter sessions when checking clipboard requirements.
Recompute them on disconnect so a remaining session with sync disabled
does not keep unnecessary clipboard reads enabled.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: filter disconnected sessions from clipboard requirements

Exclude disconnected Flutter sessions from text and file clipboard
requirement calculations, even when their tabs remain open.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: block file clipboard traffic before login

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: skip Wayland host startup clipboard broadcasts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: respect initial clipboard sync setting on Wayland

Stop broadcasting startup selections through the normal clipboard sync
path. Keep generation invalidation for snapshots captured before the
listener becomes active.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: wait for clipboard listener readiness before initial sync

Wait for Linux clipboard backends to finish subscribing before the
initial read. Update clipboard-master to use its readiness callback.

Report completion for empty or failed reads so initial sync does not
remain pending. Document snapshot invalidation across listener restarts.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: wait for Linux clipboard readiness asynchronously

Use watch notifications to keep the connection loop responsive during
clipboard startup. Cancel readiness waits when the session channel closes.

Advance generation on readiness and capture it with each initial read
so listener restarts invalidate older snapshots.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: preserve Wayland startup file clipboard sync

Distinguish startup selections from normal clipboard changes.
Process startup files through the existing client clipboard worker,
while keeping text initial sync and host startup suppression unchanged.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: bound clipboard readiness waits on Linux

Add a five-second timeout to the initial-sync readiness wait and
log failures.

Update clipboard-master to support timeout and cancellation while
waiting for the initial Wayland selection.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(clipboard): update clipboard-master

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-25 11:34:01 +08:00
fufesou efec124c52 Fix, online state, validate the data length (#16339)
* fix(client): reject truncated online status responses

Validate the bitmap length before indexing server-provided states. Return an error for short responses so they cannot panic or update cached online status.

Signed-off-by: fufesou <linlong1266@gmail.com>

* docs(client): illustrate online status bitmap sizing

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-25 00:35:47 +08:00
fufesouandMariano Abad 12f61b1fda Fix/drm rotated cursor (#16242)
* fix(drm): turn the cursor by the output's real angle, and guess its hotspot upright

Two things were wrong with a rotated output's cursor, and the maintainer's
physical test found both.

The bare-metal hotspot is a guess - the top-left of the sprite's opaque box,
since HOTSPOT_X/Y only exist on para-virtualised drivers - and it was guessed on
the sprite as scanned out, then mapped as if it were a point on it. A turned
arrow's tip is never in that corner: at 90 the guess is off by the arrow's width
along x, small enough to pass as fine, and at 270 by its height along y, which
is what was noticed. Measured on an i915 box with a 64x64 Adwaita arrow, hotspot
against tip: +11,0 at 90 and 0,+18 at 270 before, 0,0 at every angle after. A
guessed hotspot is now guessed on the upright sprite, in the consumer, and the
wire says whether the driver measured it so a real one still maps as a point.

And 180 was clamped to 0 for the whole session because a hardware-rotated
primary plane scans out upright. That holds for the frame and not for the
cursor: the compositor pre-rotates the sprite in software either way, so it
arrived upside down. The frame keeps the clamp; the cursor gets the real angle.

(cherry picked from commit 6a73100b4783fbf9c8f43e0dc7651b7568db9ea9)

* test(drm): drive the cursor path, and drop the field nothing reads

The two tests called the helpers, so nothing pinned the production
decision: restore master's `t == 90 || t == 270` in deliver_drm_cursor
and both still pass while the 180 sprite goes back to upside down. The
new test drives deliver_drm_cursor and reads back what it published,
and it fails under exactly that mutation.

The other way to reintroduce the bug was to point the receive thread at
`Shared::transform` again. That field had no readers left once both
gates moved to `cursor_transform` - it was stored and never loaded - so
it is gone, and the doc above TRANSFORM_PENDING now names the field
that actually holds a racing cursor back.

Also four comments that said more than the code does: the sprite is
treated as pre-rotated at every angle because wl_output cannot say
otherwise, not because the compositor provably always does it; the old
flow was off at every angle, not just at one of 90/270; the 180
symptom had its own cause, the clamp, rather than all of it coming out
of the hotspot guess; and the test arrow's opaque box is its left three
columns, not the whole bitmap.

* docs(drm): say what the 180 measurement actually showed

The loop asserts the old mapping was wrong at 180 too, and that is true
of the mapping, but master never ran it there: the clamp sent 180 down
the untouched branch, so guess and sprite turned together and the
hotspot landed on the tip. The bench measured exactly that - (0,0) at
180, with only the sprite upside down. The comment said the old flow was
wrong at every angle without that distinction, which is the kind of gap
between a claim and a measurement worth closing before someone else
finds it.

* fix(drm): centre the guessed hotspot of an I-beam lying either way

The bounding-box guess only centred TALL shapes, so a HORIZONTAL I-beam was
handed the top-left corner of its box. Adwaita's `vertical-text` is exactly that
shape: at 24 px its opaque box is 20x9 and the theme's own hotspot is (12, 11),
its centre. The corner guess lands 11.2 px away; the centre lands 1.4 px away.

It matters more since this branch turns the sprite upright before guessing. Such
a cursor used to reach a quarter-turned output as a TALL bitmap, where the
tall-only rule centred it by accident; guessing on the upright bitmap - correct in
itself, and what fixes arrows at every angle - is what exposed the asymmetry.

Found by fufesou at pixel level on rustdesk#16242, and reproduced here against the
installed theme before changing anything: same declared hotspot, same bounding
box, same two errors. Then checked across ALL 35 shapes of that theme at 24 px:
making the aspect test symmetric moves exactly one of them, `vertical-text`, from
11.2 px to 1.4 px, and leaves the other 34 byte-identical.

The old flow is not the fix. It was right for this one shape and wrong for arrows
at every angle, which is what `a_guessed_hotspot_is_guessed_on_the_upright_sprite`
pins; the fix belongs in the guess, not in the order of operations.

The regression test that missed this used only `arrow()`, as fufesou pointed out.
Three things the first version of the replacement got wrong, all of them in what
it CLAIMED rather than in the fix:

- Its expected point was the centre of the BITMAP, which equals the centre of the
  opaque box only because the fixtures filled their bitmaps symmetrically. Both
  I-beam fixtures are now off-centre in their buffers, the way a sprite sits in a
  64x64 hardware cursor, and the test asserts up front that the two centres differ
  so the fixture cannot quietly stop discriminating.
- It called the arrow's box "elongated by less than the factor of two", when 3x6
  IS the factor of two and the arrow keeps its tip only because the comparison is
  strict. That makes the arrow the control for one side of the threshold, and it
  is now described as such.
- It said the case was driven "through the whole delivery path" while chaining the
  helpers itself. It now calls `deliver_drm_cursor` and reads the published
  hotspot back out of DRM_CURSOR, at 0, 90, 180 and 270, feeding it what the
  producer really sends for a guessed hotspot: the producer's own guess, made on
  the sprite as scanned out.

And the threshold was pinned from one side only. A third fixture carries the real
2.22:1 aspect of the shape that was wrong, so a stricter rule cannot pass on 6:1
bars while silently un-centring the actual cursor.

Mutations, each restored byte-for-byte afterwards: back to tall-only, the bitmap
centre instead of the box centre, the threshold loosened to one and tightened to
three. All four fail the test.

* build(drm): point the pin at the synced mirror and drop the helper fallback

The mirror is synced, so the pin moves from 5da68a3a (libdrmtap 0.5.4) to
49b204f (0.5.6), which is what rustdesk-org/libdrmtap now carries.

-Dhelper=disabled, as the maintainer asked on #16242. rustdesk never uses the
privileged helper: every drmtap_open lives in src/ipc/drm.rs, i.e. the root
service, which already holds CAP_SYS_ADMIN, and the unprivileged side opens a
render node instead. What the library carries without the option is a fallback
that walks six hardcoded paths, two of them under /usr/local, and execs the
first one that passes access(X_OK) -- no check of its owner, no check of its
mode -- from inside the ROOT process. The option compiles that path out.

Asserted on the artifact as well as passed as a flag, for the same reason the
EGL check is: a flag cannot notice a stale build-pkg or an object substituted
by hand, and meson accepts an unknown -D silently on versions predating the
option. Measured on the produced .so: socketpair 4 -> 0, the helper search
paths 3 -> 0, and nm -D shows no fork, execl or socketpair import. The check
fails as it should when pointed at a .so built with the helper.

* fix(drm): use the library's hotspot provenance, and keep legacy IPC meaning

Answers the two blockers in the maintainer's review of 9acb37a9d, plus the
three he marked should-fix.

hot_measured no longer guesses. The pinned libdrmtap 0.5.6 exports
drmtap_cursor_hotspot_valid, and the old test -- hot_x != 0 || hot_y != 0 --
cannot separate the two things a (0, 0) hotspot means: no HOTSPOT_X/Y on the
plane, or a driver whose answer really is the corner. The symbol is resolved
OPTIONALLY, the way drmtap_render_node and drmtap_list_devices already are,
with the same version-aware warning when a library that reports 0.5.6 or newer
does not carry it. An older deployed .so keeps the previous behaviour instead
of the ABI floor moving under it and refusing to load.

The IPC default is corrected. Data is JSON over a unix socket between two
processes upgraded separately, so an old root service can be streaming to a
freshly started --server and its DrmCursor has no hot_measured at all. Read as
bool::default() the new consumer discards a hotspot the producer measured and
re-infers one from the upright bitmap, moving the cursor on a rotated display
for the length of the upgrade. The default is now true, which is not a claim
that the value was measured: it means "no provenance available, so behave as
the old protocol did". A new producer that wants re-inference sends false
explicitly, which serializes, so new-to-new is unchanged.

hot_measured is folded into the cursor id. It is not metadata about the cursor,
it selects what the consumer does with it, so a sample whose pixels and (0, 0)
hotspot are unchanged but whose provenance flipped is a different cursor and
must not be deduped away by the producer.

build.py reconfigures an existing meson build dir with -Dhelper=disabled
instead of only configuring a fresh one; a directory from before the option
kept its old configuration and the artifact assertion then failed the build.

And the drm CI job now runs the drm-gated tests. It built the feature without
ever executing them, so every cursor rotation and hotspot test had no
continuous coverage. A cargo test filter that matches nothing exits 0, so the
step also asserts it actually ran some.

Verified by mutation, each caught by its own test and no other: ignoring the
library's answer fails the two provenance tests; the serde default back to
false fails the legacy-deserialization test; dropping hot_measured from the id
fails the identity test.

* feat(drm): say once where the cursor hotspot came from

The provenance decision was unverifiable anywhere but a unit test: nothing on
a running box distinguishes the three states, so a deploy proved nothing. One
info line does, and with it all three are measured -- Some(false) on i915,
None with a 0.5.4 library swapped in behind the soname, and Some(true) via a
C probe on virtio-gpu, where no rustdesk build runs.

Once per state, not once per sample. A cursor is read on every capture
iteration and its provenance changes approximately never; a per-frame log line
here is the same defect libdrmtap already shipped once. Measured on Sigma-26:
one line across a whole session. The should-we-log decision is its own
function so it cannot quietly become per-sample again.

* test(drm): drive a legacy cursor message from the wire, not from a literal

The upgrade window is the one thing in this round that could not be staged
with two live processes: a --server from one build does not run inside the
other's tree, so the old service respawns it and it dies with exit 127. Tried
on two boxes, with two files swapped and with the whole tree.

This is the honest form of that test and it runs in CI rather than once on a
desk. It takes the exact bytes an old producer puts on the socket -- no
hot_measured key at all, adjacently tagged as Data really is -- deserializes
them the way the receive loop does, drives the real delivery path, and reads
back what was published. The transform is non-zero on purpose: at 0 the path
never consults hot_measured, so a test there could not tell the branches
apart, and the fixture is asserted to make mapping and re-inferring disagree
before anything is concluded from them.

Verified by mutation: restoring #[serde(default)] -- the reported bug -- fails
it, and so does a delivery path that ignores hot_measured.

* fix(drm): run the tests that matter in ci, and keep provenance state per stream

Answers the review of 7dc6817 plus the CI failure fufesou reported on 401c2f7.

The drm test step was broken three ways and none of them showed up as a red
test: it reused the production $FEATURES, so the test binary failed to LINK
(undefined reference to fcntl64 from hwcodec) and the tests never ran; it
summed the counts with bc, which the ubuntu18.04 container does not install;
and `cargo test --lib` at the repo root builds the ROOT package only, so the
provenance tests in libs/scrap were never selected -- the "at least one test
ran" guard passed on the root filters and hid it. Now: --features drm rather
than the production set, awk instead of bc, and an explicit `-p scrap` run
with the same assertion. Locally 36, 9 and 5 tests, and a bogus filter still
fails the step.

Provenance logging state was process-global on both sides. DRM runs one
reader per captured display, and two outputs with different but STABLE
provenance -- one publishing HOTSPOT_X/Y and one not, which is an ordinary
multi-GPU host -- would overwrite each other and make every message look like
a change, i.e. exactly the per-frame logging the one-shot was written to
avoid. The producer's state moves into DrmReader, the consumer's is keyed by
display. The hidden-cursor sentinel is no longer recorded: it carries
hot_measured=false because it has no hotspot, so counting it turned every
hide and show into a provenance transition.

The 180 test is renamed to say what it pins -- i915 advertising rotate-180
with mutter -- and its comment says plainly that it is not a general contract,
that Plasma is still wrong there, and that changing it means re-measuring.

Verified by mutation: restoring the global slot fails the new per-display
test, and so does recording the hidden sentinel.

* fix(drm): centre the guessed hotspot on the axes a cursor mirrors about

The guess put the click point at the top-left corner of the opaque box unless
the box was more than twice as long as it is wide. That corner is where an
arrow's tip is, because cursors are drawn pointing up and to the left, but a
shape that mirrors onto itself has no tip to point with: a crosshair, a
two-headed resize arrow, a cell marker. Those got a corner where the theme puts
the middle, and the error is about half the glyph.

So: centre the axis the shape is symmetric about, and keep the corner
everywhere else. Measured against the hotspots the theme declares, which is the
value a compositor programs into HOTSPOT_X/Y where the property exists:

  adwaita-icon-theme 50, 35 shapes at six nominal sizes, 210 cases
    mean error 0.43 -> 0.27 of the cursor size, 17 shapes improve, none worse
  adwaita-icon-theme 46, 34 shapes at five sizes, 170 more cases
    better at every size, none worse

The threshold is not fitted: from 90 to 98 percent agreement nothing regresses,
and below 90 three shapes do.

The corpus is carried as fixtures so the expected value comes from the theme
author rather than from our own heuristic, and two tests read it: the published
hotspot must not move when the output turns, with the old flow's 15 px average
movement as the control, and the guess must stay inside the measured bounds
with the old rule frozen as the baseline.

The elongation test stays ahead of the mirror test so an elongated shape that
is not symmetric still keeps its centre.

* fix(drm): put a directional cursor's hotspot on the edge it resizes

The mirror rule centred the axes a shape is symmetric about and left the
other axis on the top-left corner. That is right for an arrow, whose tip is
that corner, and wrong for the resize cursors: those are a pointer plus the
marker of the edge being resized, the theme puts the click point on the
marker, and the marker is at whichever end the cursor names. Always taking
the corner therefore had to be wrong for one of every mirrored pair, since
e-resize and w-resize are the same picture reflected.

So an axis is read as directional when the perpendicular axis mirrors, which
is the n/e/s/w family, or when the shape mirrors about a diagonal, which is
the corner family. A directional axis takes the heavier end.

A diagonal reflection only lines up inside a square, and these boxes are not
square, so where the box sits in that square decides the answer: ne-resize
scores 0.62 anchored one way and 0.98 the other, and nw-resize is the reverse.
Both anchorings are tried and the better answer wins.

Measured against MASTER now, not against this PR's previous revision, which
is what the maintainer asked for. Over the angles master handles (0, 90, 270),
adwaita-icon-theme 50 at 24 px:

  mean error per shape   12.08 px -> 4.20
  (shape, angle) cases   83 better, 18 the same, 4 worse of 105
  shapes worse than master   help (+4.73) and alias (+0.51)

Both of those are semantic: the theme puts help's click point on the dot of
its question mark. The same holds at 48 px and on adwaita-icon-theme 46,
where ne-resize is also 1.34 px worse because its glyph is drawn differently
there.

The test baseline was wrong and is fixed here: it froze this PR's earlier
symmetric aspect test rather than master's tall-only one, so it could only
show that the new rule does not regress the old rule. It now freezes master's
guess AND master's delivery, and names the two shapes it excuses with the
measured size of each.

The mirror threshold moves from 95 to 88, the middle of the 86 to 91 interval
that regresses nothing on either theme version.

---------

Co-authored-by: Mariano Abad <weimaraner@gmail.com>
2026-09-22 15:18:10 +08:00
fufesou a5d4ef97e8 fix(ws): reject text frames after encryption is enabled (#16295)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-20 18:21:15 +08:00
5278fcab68 fix(cursor): correct native cursor sizing and validate received images (#16213)
* fix(flutter): shrink the unzoomed remote cursor by DPR on macOS and Linux

With "Zoom cursor" off in Adaptive or Custom view, the remote cursor
bitmap was registered at scale 1.0. NSCursor and GdkCursor treat the
bitmap size as logical pixels, so on a HiDPI controller the cursor was
drawn DPR times larger than in Original view (which already passes
1/DPR) and than on Windows (whose cursor path is in physical pixels).
A HiDPI remote such as KDE Wayland sends a 48-64 px bitmap, which then
showed up 3-4x too big on a Retina Mac.

Scale the bitmap by 1/DPR in that case, and scale the Flutter-painted
cursor used while the peer moves the mouse the same way so its size
does not jump. The new branch is an identity at DPR 1 and the Windows
paths are untouched.

Fixes https://github.com/rustdesk/rustdesk/discussions/15363

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8HSHTEHo27mDcJXVyVfSP

* fix(flutter): check the cursor height against the min cursor size

`_checkUpdateScale` computed the scaled height from `width`, so the
min-size clamp never looked at the height.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8HSHTEHo27mDcJXVyVfSP

* fix(flutter): keep the painted cursor hotspot in place when zoom cursor is off

`CursorPaint` subtracted the hotspot in remote pixels and then scaled it
by the canvas scale, but drew the image at scale 1.0, so the hotspot
landed hotx * (1 - scale) logical pixels away from the remote cursor
position. Cursors with a centered hotspot (I-beam, crosshair) were off
by up to half their size in Adaptive view. Subtract the hotspot after
scaling the position instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8HSHTEHo27mDcJXVyVfSP

* fix(flutter): read the live DPR and clamp the painted cursor like the native one

CanvasModel caches devicePixelRatio and only refreshes it when the view
style changes, so after the window moves to a monitor with a different
DPR the unzoomed cursor kept the previous monitor's scale. Read it from
MediaQuery instead, which also rebuilds the cursor when it changes.

The native path clamps the scaled bitmap to kMinCursorSize; apply the
same clamp to the painted cursor so a small cursor does not change size
when the peer moves the mouse.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8HSHTEHo27mDcJXVyVfSP

* build(cursor): declare existing Zstd dependency for bounded decoding

* fix(cursor): validate and bound received cursor images

* fix(cursor): preserve thin cursor sizes when scaling

* fix(cursor): limit view changes to native cursor sizing

* fix(cursor): apply long-edge minimum to Web cursor sizing

* fix: preserve Linux cursor alpha and match Windows Custom scale

* fix(cursor): keep scaled buffers and raster dimensions in sync

* fix(cursor): preserve Windows peer alpha when resizing

* fix(cursor): preserve mixed alpha during downsampling

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(cursor): match desktop cursor size to peer display density

* refactor(cursor): clarify desktop and web scale branches

* fix(cursor): correct adaptive display scaling and fixed cursor size

* fix(cursor): apply all-display adaptive density on every desktop

* fix(cursor): normalize unzoomed all-display cursor density

* fix(cursor): synchronize original view on DPR changes

* fix(cursor): match original zoom to the active renderer

* fix: align Wayland software scrolling with input coordinates

* fix: preserve scroll offsets when switching scrolling modes

* fix: correct cursor size and pointer mapping with custom scale

Apply the hovered Linux display density to Custom cursor zoom in All Displays. Refresh scroll fractions after layout so changing the Custom percentage uses current scrollbar extents and detached controllers.

Validated with macOS and Windows component tests, formatting, and static analysis.

* fix(linux): pad tall native cursors to prevent clipping

* fix(cursor): preserve macOS point size in unzoomed views

* fix(linux): pad rectangular cursors to square canvases

* fix(cursor): divide dpr on Linux -> macOS

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: cursor size test

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(cursor): cursor size of controlled side macOS

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(cursor): limit received cursor allocations

* test(cursor): retain reverse raster transition coverage

* fix(cursor): bound compressed cursor input

* fix(cursor): restrict the scaled size of the cursor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(cursor): align hotspots with resized raster dimensions

Calculate each hotspot axis from the actual raster-to-source ratio.
Update existing boundary tests and run cursor tests in Flutter CI.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(cursor): align Sciter hotspots with raster dimensions

Calculate native and overlay hotspots from the final raster size.
Preserve input coordinate scaling and cursor refresh order.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(cursor): use `1.0` instead of `1.0/dpr` on Linux -> macOS, Zoom off, Scale adaptive

The mouse cursor currently appears somewhat large.
However, this is difficult to adjust because the cursor size
is fixed while the window size varies; its relative size
depends on the specific desktop environment.

We can modify it if users actually provide feedback.
Ideally, we should check the "Zoom cursor" option.

Further adjustments may also be needed later based on cursor density.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: rustdesk <info@rustdesk.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:43:07 +08:00
fufesou 01e3917390 fix(ci): restore global configurations (#16257)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-17 16:59:00 +08:00
fufesou fd0fe592eb fix(rdev): macos, numpad flag, clear on text keys (#16253)
may fix #16227

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-17 12:04:32 +08:00
fufesou 28269af9c7 fix(audio): preserve compatible playback and recover failed Windows outputs (#16150)
* fix(audio): preserve compatible playback on replacement failure

* fix(audio): use Windows events for capture worker wakeups

* fix(audio): recover Windows playback after output device failure

* reduce diffs

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): reset decoder when retaining compatible playback

* fix(audio): integrate playback retention and Windows capture wakeups

* revert(audio): remove Windows capture-event notification changes

* docs(audio): explain Windows 7 capture limitations

* docs(audio): clarify capture changes introduced by #16095

* fix(audio): preserve playback through asynchronous Windows startup

Keep a compatible active output until the replacement callback confirms startup, preserving decoder progress on promotion or rollback. Handle superseding formats and simultaneous output failures without losing recovery state. Pin the scoped CPAL WASAPI event-ownership fix and add deterministic and native regressions.

* fix(audio): retain ready output across superseding formats

* fix(audio): update CPAL teardown recovery

Pin the upstream-aligned WASAPI cleanup with shared event lifetime, self-join prevention and fallible destructor diagnostics. Preserve the existing dependency graph and audio implementation.

* Use upstream-style CPAL stream teardown

* Refact: remove low value test

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): retry playback when startup confirmation times out

Signed-off-by: fufesou <linlong1266@gmail.com>

* update cpal

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-16 11:55:53 +08:00
fufesou a437eb9bc6 fix(windows): recognize Hyper-V Enhanced Sessions by protocol (#16223)
Fixes #16182

Use WTSClientProtocolType to identify RDP sessions with nonstandard
names during session selection and enumeration.

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-15 15:21:19 +08:00
fufesou 9b5f342839 fix(audio): throttled debug logs (#16172)
* fix(audio): throttled debug logs

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): preserve contention counts across throttled reports

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-14 12:26:23 +08:00
fufesouandRustDesk cdcb4d4b4c Fix/android voice call (#16180)
* fix(android): handle missing audio permission in voice calls

- Avoid stop errors when voice capture was never started
- Dispatch voice call error dialogs on the main thread
- Explain how to enable Audio capture on the Screen share page

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore audio after failed voice call switches

* fix: translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: translatin

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(i18n): match UI label capitalization in translated messages

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2026-09-14 12:24:33 +08:00
fufesou bf1ebe5be2 Ci/native arm64 msbuild (#16170)
* ci: use native ARM64 MSBuild for MSI packaging

* ci: clarify MSBuild host and MSI target architectures
2026-09-12 14:31:16 +08:00
fufesou e54f21e10c Fix/ci (#16168)
* fix(ci): allow native x86 Rust toolchain on Windows

* docs(ci): explain the rustup 1.29.1 host check

* docs(ci): explain the unverified x64 cross-compilation alternative
2026-09-12 11:33:49 +08:00
fufesou c4221469d8 Fix/audio stream continuity (#16095)
* fix(audio): add streaming resampler

* fix(audio): preserve stream resampling state

* fix(audio): keep playback callback nonblocking

* fix(audio): decouple capture conversion from dasp

* fix(audio): support stateful samplerate backend

* refactor(audio): isolate stream callback state

* refactor(audio): group capture output options

* fix(audio): clear stale playback state after startup failure

Reset non-Linux playback state when stream startup fails to prevent
new-format audio from using the previous stream or resampler.

Add regression tests for failed format changes and successful playback.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): honor capture resampler selection and reuse buffers

Use the selected resampling backend for fixed-frame capture.
Convert samples directly into the input queue and
reuse the PCM frame buffer.

Add tests for anti-aliasing, thread transfer, and
partial-frame draining.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: reduce diffs

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(audio): check resampler output count and passband energy

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): reset incompatible Linux playback state on
  startup failure

Preserve compatible output streams when replacement
  startup fails.
Clear state when no compatible stream exists and cover
  both paths in tests.

Signed-off-by: fufesou <linlong1266@gmail.com>

* perf(audio): reuse PCM buffers in the capture pipeline

- Reuse capture framing, resampling, and channel conversion buffers
- Deliver borrowed packets and write Sinc output into reusable storage
- Add allocation and output-equivalence regression tests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): smooth buffer discard discontinuities

Signal receiver PCM discards and fade from the current playback output when the callback reaches the new timeline.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): add missing Cargo.toml

Signed-off-by: fufesou <linlong1266@gmail.com>

* perf(audio): move capture encoding off the CPAL callback

Move Opus encoding and service delivery to a dedicated worker.
Use a preallocated bounded PCM queue with explicit loss reporting.
Add tests for callback allocations and queue saturation.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): smooth capture gaps and report losses during backlog

Signed-off-by: fufesou <linlong1266@gmail.com>

* feat(audio): report capture queue high-water mark

Track peak queued PCM packets and log the approximate
queued audio duration alongside capture loss statistics.

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(audio): reduce diffs

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): avoid blocking capture on encoder queue contention

Use preallocated queues with try_lock in the capture callback.
Count and drop the current packet on contention, preserving
drop-oldest behavior on overflow.

Add regressions for paused workers, buffer reuse, and sequence wrap.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: add the missing files

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): isolate zero-gate state per encoder

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: reduce diffs

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(audio): simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(audio): avoid waiting on playback callback locks

Use one PCM try_lock attempt and preserve queued samples during contention. Replace readiness locking with per-stream atomic status and report callback errors from the receiving thread.

Cover callback progress, retained audio, recovery, and poisoned-buffer handling.

* fix(audio): restart capture after processing errors

Stop further processing until the service recreates the stream.
Document the guard as defensive recovery for an unconfirmed failure.
Group capture and resampler submodules under their parent directories.

Signed-off-by: fufesou <linlong1266@gmail.com>

* audio: report capture queue contention drops separately

- Add contention_dropped to loss reports while preserving total drop counts
- Document packet rejection on contention even when buffers are available
- Extend existing contention and saturation test assertions

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact unit tests

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-10 16:00:58 +08:00
fufesou 978e2e28b9 fix(audio): restart capture when the device is unavailable (#16142)
* fix(audio): restart capture when the device is unavailable

* refact: remove low-value test

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-10 15:32:49 +08:00
fufesou 65edf214b9 fix(macos): recover system-stopped audio capture streams (#16123)
* fix(macos): recreate system-stopped audio capture streams

Pin CPAL's ScreenCaptureKit stop notifications and retain interruption
state with each capture stream. Recreate an interrupted stream through
the existing service restart path, outside the backend error callback,
and resend its audio format. Late callbacks cannot restart a replacement.

A natural -3821 stop was observed with the remote connection still open.
Its OS trigger remains unknown and it has no deterministic natural
reproducer. Controlled verification stops the real SCStream and delivers
an explicitly marked -3821 notification; this is not a natural failure.

Dependency: https://github.com/rustdesk-org/cpal/pull/5

Validation: requested macOS Rust and Flutter debug builds; three full-crate
regression tests; build check without ScreenCaptureKit; two controlled
recreations on one connection with independently recorded receiver audio.

* chore(macos): log audio capture startup and resumed samples

* Update deps, cpal

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-09 22:34:25 +08:00
fufesou 22b1ed169a fix(audio): update CPAL for WASAPI thread priority (#16110)
* fix(audio): update CPAL for WASAPI thread priority

* update cpal

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-08 16:19:16 +08:00
fufesou 618bf37deb feat(terminal): add opt-in OSC 52 clipboard writes (#16072)
* feat(terminal): add opt-in OSC 52 clipboard writes

* Remove dup tr

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-05 13:21:49 +08:00
fufesou d5c6d0f6b7 fix(custom client): msi update, preserve exe name (#16057)
Keep the configured app name casing when renaming
the updated executable so legacy MSI custom actions
can terminate custom client processes.

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-04 15:49:50 +08:00
fufesou ba6de7990f fix(ci): ubuntu-22.04-arm, oom (#16056)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-04 15:16:33 +08:00
fufesou 82aa28f129 fix(ci): install CMake 4.3 for ARM64 vcpkg builds (#16044)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-09-03 19:28:54 +08:00
fufesou 03a7fc5992 fix(flutter): align terminal shortcuts with platform conventions (#15970)
* fix(flutter): align terminal shortcuts with platform conventions

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(flutter): handle Linux terminal paste with modifier locks

Detect platform-specific paste shortcuts so Ctrl+Shift+V bypasses
virtual Ctrl/Alt modifiers on Linux. Add regression coverage.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-27 16:33:58 +08:00
fufesou 0b08a83d4b fix(file-transfer): improve large directory loading (#15830)
* fix(file-transfer): improve large directory loading

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file-transfer): avoid failing newer directory reads

Track each remote directory request by its registered completer and only remove
the task when it still matches, preventing stale failures from affecting newer
requests for the same path.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file-transfer): handle slow directory listings safely

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file transfer): correlate directory responses with requests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file transfer): prevent automatic directory responses from matching requests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file-transfer): handle large remote directory listings reliably

- build file rows lazily
- register remote reads before sending requests
- handle Home paths, stale responses, errors, and timeouts
- serialize same-path reads with different hidden-file options

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(file transfer): reduce diffs

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: build

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: invalidate pending dir reads on reconnect

Signed-off-by: fufesou <linlong1266@gmail.com>

* test(file-transfer): cover remote directory read lifecycle

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-27 13:01:11 +08:00
fufesou fd471fcf02 fix: show speed in desktop file transfer status (#15980)
* fix: show speed in desktop file transfer status

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: move file transfer speed beside progress bar

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: move file transfer speed into progress bar

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: refine file transfer speed display

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: adapt file transfer progress text colors

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: reduce file transfer speed text weight

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-27 11:08:58 +08:00
fufesou 0d917c6fa1 fix: remove dup translations (#15967)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-25 18:25:49 +08:00
fufesou a7deef02a2 fix(msi): keep only native ProductCode uninstall entry (#15891)
* fix(msi): keep only native ProductCode uninstall entry

Move installer state outside the Uninstall registry path,
clean up legacy duplicate entries, and use the MSI ProductCode
for updates and uninstalling.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): harden update and uninstall handling

- handle legacy EXE updates without an MSI ProductCode
- propagate MsiExec uninstall failures
- validate and XML-quote custom ARP values

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): validate registry state before update and uninstall

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): pass WindowsInstaller state to elevated sequence

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): block unsupported MSI-to-EXE upgrades

- resolve native MSI state and ProductCode safely
- suppress reboot while preserving MSI uninstall results
- publish the resolved ARP install location
- skip invalid unrelated MSI uninstall entries

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): fail uninstall when ProductCode is missing

Prevent known MSI installations from falling back to
EXE cleanup when the ProductCode cannot be resolved.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(msi): do not abort update on ARP version write failure

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-22 17:49:00 +08:00
fufesou 92eb137178 feat(terminal): use platform-native copy and paste shortcuts (#15931) 2026-08-21 21:20:39 +08:00
fufesou c78bdefc44 fix: dialog, trackpad speed, buttons (close -> ok, cancel) (#15918)
* fix: dialog, trackpad speed, buttons (close -> ok, cancel)

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(flutter): handle trackpad speed dialog submission

- commit typed values from Enter and OK
- validate input before saving
- prevent duplicate submissions
- surface save failures

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(flutter): sync trackpad speed input and slider

- handle trackpad speed submission from IME actions
- update the slider when a valid speed is typed
- cover Enter, OK, IME, and invalid input behavior

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-21 14:15:35 +08:00
fufesou 0a4b431ea2 fix: correct terminal mouse selection and scroll coordinates (#15915)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-20 13:10:45 +08:00
fufesou b0008edcb5 refact: remove linux headless (#15866)
* refact: remove linux headless

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): probe DRM availability asynchronously on login

Signed-off-by: fufesou <linlong1266@gmail.com>

* revert changes in drm_capturer.rs

Signed-off-by: fufesou <linlong1266@gmail.com>

* Update submodule hbb_common

Signed-off-by: fufesou <linlong1266@gmail.com>

* docs(linux): clarify DRM availability comments

Remove stale headless and unauthenticated-request
wording, and document the Available-only login-screen gate.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(linux): remove unreachable session cleanup branch

Remove the obsolete empty-session path and
clarify the intended use of cached DRM availability.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-18 15:02:50 +08:00
fufesou edd0e5fbd4 fix(CI): rust 1.75, linux sciter (#15874)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-17 09:30:19 +08:00
fufesou d1da05c4db refact: remove feature plugin-framework (#15854)
* refact: remove feature plugin-framework

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: remove unused translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: delete settings tab observable with correct type

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-14 14:31:13 +08:00
fufesou c4fd7d692d refact: fuser 0.16.0, cargo 1.75.0 (#15844)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-12 21:36:06 +08:00
fufesou 1d09760ef7 fix(terminal): keep selection aligned after clearing scrollback (#15831)
Remove scrollback lines through the index-aware buffer operation so
deleted anchors are detached and retained lines are reindexed.

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-11 15:54:03 +08:00
fufesou f5ab01f8bd fix(clipboard): win, populate file formats (#15692)
* fix(clipboard): win, populate file formats

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): prevent Windows file clipboard OOB access

* reduce diffs to master

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): win, OOBs and double free

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): win, check deep copy

Signed-off-by: fufesou <linlong1266@gmail.com>

* comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): harden Windows clipboard memory handling

- clear HGLOBAL aliases after ownership transfers
- validate callback inputs and capability sets
- bound file-content responses and close search handles on errors

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): harden Windows cliprdr memory safety

- validate clipboard descriptors and response sizes
- fix allocation ownership and cleanup paths
- synchronize format-map access across callback and STA threads
- prevent clipboard format TOCTOU races

Signed-off-by: fufesou <linlong1266@gmail.com>

* Comments on stale remote file formats

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): check pointers before using

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): harden Windows COM error handling

- roll back FORMATETC enumeration on deep-copy failure
- keep the enumerator constructor internal
- propagate IStream seek and read failures

Signed-off-by: fufesou <linlong1266@gmail.com>

* explicity `WIN32_FIND_DATAW`

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): validate format data size and simplify lock cleanup

Reject clipboard data exceeding UINT32_MAX before allocation and
keep format-map cleanup and lock release within the owning function.
Add boundary tests for response data sizes.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): missing frees

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-06 11:16:01 +08:00
fufesou 402ed07b0c fix: Harden Windows installer temp command scripts (#15634)
* fix: Harden Windows installer temp command scripts

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: restore stop-service after install preparation failure

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(windows): preserve special characters in installer paths

Handle carets and exclamation marks safely across cmd.exe parsing stages.
Add coverage for special-character paths in the elevated installer handoff.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: installer, validate app name

Signed-off-by: fufesou <linlong1266@gmail.com>

* update tests

Signed-off-by: fufesou <linlong1266@gmail.com>

* Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-04 14:29:04 +08:00
fufesou 6f1eb164d6 fix(clipboard): validate files (#15693)
* fix(clipboard): validate files

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): address file validation review feedback

- remove unreachable empty-prefix test assertions
- name the shared COM/LPT prefix length
- document non-atomic path validation behavior

Signed-off-by: fufesou <linlong1266@gmail.com>

* update hbb_common

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): reject traversal in file descriptors

- reuse parser validation for outgoing descriptor names
- propagate descriptor serialization errors
- add regression coverage for parent path components

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: clipboard, validate file name length

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: clipboard, comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): support multi-root file selections

Use each top-level path's parent as its relative root so file
descriptors remain safe and relative across different directories.

Add regression coverage for multi-root selections.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): unix, select multiple items

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-04 14:11:27 +08:00
fufesou a84bad4639 refact(oidc): manually open the browser (#15706)
* refact(oidc): manually open the browser

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): allow copying OIDC authentication links

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unused translation in ko.rs

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): better hint on browser didn't open

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): login handle exception

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): remove unused translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): login handle error

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): login in flight

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(translation): move "Continue" to the end of template.rs

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): var rename

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): remove useless "open sign-in page"

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unecessary translation contents

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): better way to show&expand the url

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact(oidc): better login ui

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): discard stale auth results after cancellation

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): handle auth status query failures safely

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): prevent concurrent login operations

- reuse the active login dialog and block duplicate password submissions
- cancel only active OIDC operations when closing the dialog
- preserve authentication state until failure cancellation succeeds

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(oidc): refine login options error feedback

Preserve typed errors to hide the network tip for
HTTP failures and clarify the login-options API contract.

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-04 12:35:04 +08:00
fufesou 807e05ea9a refact(oidc): login with api domain (#15710)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-30 18:23:48 +08:00
fufesou eefd22b205 fix(macos): prevent remote keyboard focus leaks (#15629)
* fix(macos): prevent remote keyboard focus leaks

Gate keyboard grabbing on window, tab, lifecycle, and primary focus state.
Release grabs on focus loss or minimize and avoid duplicate grab transitions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: macos, keyboard focus, comments known issue

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: macos, keyboard, fullscreen space switch

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: macos, keyboard, focus, relative mouse mode

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(macOS): preserve local overlay focus during input recovery

Prevent fullscreen and relative-mouse focus recovery from reclaiming
remote keyboard input while a local chat or dialog overlay owns focus.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: macos, keyboard, comments trade-off

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-26 11:54:01 +08:00
fufesou ad9dac1001 fix(keyboard): jis, macos, muhenkan henkan (#15669)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-25 09:36:25 +08:00
fufesou bdb38c4730 fix: check valid id (#15535)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-14 15:35:52 +08:00
fufesou 8314335b31 fix: update download, force tls (#15529)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-09 15:05:49 +08:00
fufesou 28930c0463 fix: non-E2EE show dialog (#15514)
* fix: non-E2EE show dialog

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: build web, bridge

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: direct IP access, do not snow non-E2EE dialog

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non E2EE dialog, update contents

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE, show dialog, port forward

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE dialog, port forward, ignore direct IP access

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE is_direct_ip_access()

Signed-off-by: fufesou <linlong1266@gmail.com>

* Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE dialog, port forward, close socket on disconnect

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: non-E2EE dialog, incorrect reuse of Data::Close

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-06 17:05:11 +08:00
fufesou 37141afece refact: remove feature cli (#15524)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-06 16:17:32 +08:00
fufesou 493b14ba78 Fix/session scope permission audit (#15469)
* fix: enforce session-scoped permissions

Restrict non-remote sessions to their allowed message types, filter
out-of-scope login options, and audit rejected or filtered messages.
Hide screenshot controls outside default remote sessions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: typo

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: prevent privacy mode in view-camera sessions

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: switch display, check non-view-camera

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: avoid sending unsupported messages

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, add option to control close/alarm

Signed-off-by: fufesou <linlong1266@gmail.com>

* Fix: scoped session handling for view-camera compatibility

  - Skip view-camera auto-login and display-management side effects
  - Allow harmless render broadcasts without affecting non-video sessions
  - Keep legacy view-camera management messages compatible as no-ops
  - Preserve stricter scope violations for non-video session types

Signed-off-by: fufesou <linlong1266@gmail.com>

* update libs/hbb_common

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: ignore repeated login request

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: view camera, support "Take screenshot"

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scoped messages, check update options

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, check portforward before conn type voolations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: scoped messages, reduce changes.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: keep scoped sessions compatible with render broadcasts

Allow legacy render-broadcast no-op messages for file transfer and terminal
sessions while keeping port forward and mixed options scoped. Also avoid sending
new render updates to non-video Flutter sessions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: scope screenshot requests by video source

Key screenshot requests by video source and display index so camera and
monitor sessions cannot consume each other's requests. Deduplicate the Flutter
render-target predicate while keeping render updates limited to video sessions.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Harden scoped session message handling

Filter option updates by authenticated connection type,
keep legacy no-op messages compatible, and avoid noisy repeated
scope violation alarms.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, comments

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Send close reason for scoped session violations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Enforce scoped session message filtering

  - filter out-of-scope messages for limited session types
  - scope option updates by authenticated connection type
  - keep render-broadcast no-op compatibility for non-video scoped sessions
  - restore view-camera screenshot handling
  - improve session scope violation audit labels
  - avoid cloning option messages on the remote hot path

Signed-off-by: fufesou <linlong1266@gmail.com>

* Fix scoped session clipboard broadcast compatibility

Treat text clipboard broadcasts as no-op compatibility messages for FileTransfer and Terminal sessions, matching existing
handler behavior and preventing optional scope-violation close from disconnecting those sessions. Keep ViewCamera and
PortForward clipboard messages subject to normal scope enforcement.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: log warn

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: restrict Flutter clipboard sync to default sessions

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, comments and tests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, reset sessions in login handle

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: session scope, view camera, allow clipboard noop

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-05 23:28:32 +08:00
fufesou 9fdb8410d3 fix: parse exit code of flutter web (#15501)
* fix: parse exit code of flutter web

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: exit-code, debug print

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-03 12:49:32 +08:00
fufesou dce221be5a fix(clipboard): make CLIPRDR format-map growth checked (#15493)
* fix(clipboard): make CLIPRDR format-map growth checked

The Windows CLIPRDR format-list handler relies on map_ensure_capacity()
while processing peer-provided formats. The previous helper only attempted
growth: if realloc() failed, it returned silently and the caller continued
processing. A later iteration could then index past the allocated
format_mappings array.

Make format-map growth a checked operation. The handler now validates the
peer-provided format count, ensures the mapping array is large enough before
writing entries, and aborts processing if growth fails. Newly allocated slots
are zeroed so existing cleanup can safely run after partial processing.

Also bound remote format names before measuring/converting them. The chosen
limits follow Windows clipboard/atom constraints:
  - registered clipboard format IDs use 0xC000..0xFFFF
  - string atom names are limited to 255 bytes

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): reject invalid remote format-list entries

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-07-02 16:18:07 +08:00
fufesou 4a54029cac fix(update): msi, norestart (#15440)
* fix(update): msi, norestart

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(update): escape path

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-27 16:45:27 +08:00
fufesou 001848bf2f fix(fuse): umount (#15426)
* fix(clipboard): clean up stale Linux FUSE mounts

Recover Linux file clipboard FUSE mount points before remounting and stop treating a cached
context as valid when the mount has already gone away.

This fixes the desktop file manager copy failure that shows dialogs such as
"Error while copying a" and "There was an error copying the file into xxx".

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): fuse, reduce dups

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: clear Linux file clipboard before unmounting FUSE

Ensure Linux client teardown clears RustDesk file clipboard URLs while
the FUSE context is still available. Also prefer fusermount before
umount to avoid noisy unprivileged teardown attempts.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): return and log errors

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-26 16:17:44 +08:00
fufesou b8117c5c34 fix(fuse): fuse path broken, since ipc path changed (#15406)
* fix(fuse): fuse path broken, since ipc path changed

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(fuse): init, handle error

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(fuse): unmount attempt on newly created directory failed

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-24 16:52:27 +08:00
fufesou 58ee593e26 fix(custom-client): show options, incoming-only (#15394)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-24 00:26:09 +08:00
fufesou 09bc9056c9 fix(update): win aarch64 (#15389)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-23 17:59:29 +08:00
fufesou 0c6df924d1 refact: file transfer, do this for all conflicts(tasks) (#15385)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-23 11:23:42 +08:00
fufesou 3c574a4182 fix(wayland): clipboard, support ext-data-control (#15366)
* fix(wayland): clipboard, support ext-data-control

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(clipboard): restart stale listener and log join panics

Signed-off-by: fufesou <linlong1266@gmail.com>

* update clipboard-master

Signed-off-by: fufesou <linlong1266@gmail.com>

* refactor(clipboard): remove redundant stale listener cleanup

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-21 17:09:41 +08:00
fufesou 9ded8d6ab2 fix(keyboard): win, key, Pause (#15351)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-20 21:11:19 +08:00
fufesou 3d478c4935 fix(ios): mouse mismatch (#15339)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-19 20:50:56 +08:00
fufesou a732ebc3e1 fix: win, ci, update RustDeskTempTopMostWindow (#15334)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-19 00:24:48 +08:00
fufesou 30c0867e40 fix(ci): win arm64 (#15333)
* fix(ci): win arm64

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: ci, less changes

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-19 00:10:37 +08:00
fufesou 0797ebb695 Refact/privacy mode 1 multi monitors (#15321)
* refact: privacy mdoe 1, multi-monitors

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: harden privacy mode overlay & capture cleanup

Signed-off-by: fufesou <linlong1266@gmail.com>

* Fix privacy mode edge cases after multi-monitor overlay changes

Signed-off-by: fufesou <linlong1266@gmail.com>

* Add missing changes

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-18 21:27:44 +08:00
fufesou 3cdf1cce54 refact: privacy mdoe 1, multi-monitors (#15318)
* refact: privacy mdoe 1, multi-monitors

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: harden privacy mode overlay & capture cleanup

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-17 21:43:57 +08:00
fufesou 88ae00ba73 refact: restart remote device, autoconnect (#15290)
* refact: restart remote device, autoconnect

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: guard restart reconnect timer after session close

Signed-off-by: fufesou <linlong1266@gmail.com>

* Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(restart): auto connect, comments

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-17 17:50:18 +08:00
fufesou 93d064a9b0 refact(oidc): icon azure to microsoft (#15278)
* refact(oidc): icon azure to microsoft

Signed-off-by: fufesou <linlong1266@gmail.com>

* Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: oidc, remove unused auth-azure.svg

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-17 14:36:53 +08:00
fufesou 6d116cf1c9 fix(clipboard): Windows DIB images, fill missing alpha (#15296)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-16 11:29:53 +08:00
78533e428e feat: theme logo (#15268)
* feat: theme logo

Signed-off-by: fufesou <linlong1266@gmail.com>

* perf(flutter): cache theme logo asset resolution

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-14 13:47:39 +08:00
cc7fe4efdc Fix/generate py target injection (#15248)
* fix: generate.py, target injection

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: logs

Signed-off-by: fufesou <linlong1266@gmail.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Update port_forward.rs

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-11 23:09:34 +08:00
fufesou 6426269d41 Refact/printer driver default unchecked (#15191)
* refact: installation, printer driver, default unchecked

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: silent install, get option from the reg values

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: silent install, arg printer=[0|1]

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-06 08:51:08 +08:00
fufesou 518296f257 fix: bytes codec, reserver(), check max (#15168)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-02 16:39:50 +08:00
fufesou 3217125dd3 fix(keyboard): wayland clipboard input prompt (#14700)
* fix(keyboard): wayland clipboard input prompt

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): clipboard input, remove unused code

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): Simple refactor

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): dialog, better enableAndContinue

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): input dialog consent

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): prompt text

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): text input

1. Use `keysym` for the installed version if possible.
2. Use the clipboard if the string cannot be fully handled by `keysym`.

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): input prompt dialog

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): dialog, title type

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): better decode_utf8_prefix()

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): better process_chr()

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): unit tests

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): input prompt dialog, no icon

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): input dialog, Toast show the result

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): input dialog, showToast() on persist failed

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): input prompt, better dialog

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(wayland): input prompt dialog, translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): better wayland clipboard input prompt

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): wayland clipboard, link external app

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): trivial changes

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): wayland clipboard input, dialog content

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): tranlsations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(input): translations

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-02 16:06:35 +08:00
fufesou 5eed50961d fix(crypt): symmetric crypt, zero nonce (#15144)
* fix(crypt): symmetric crypt, zero nonce

Signed-off-by: fufesou <linlong1266@gmail.com>

* update hbb_common

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2026-06-01 12:01:26 +08:00