Commit Graph

11366 Commits

Author SHA1 Message Date
XLion
c312385ffd Update tw.rs (#16031)
* Update tw.rs

* Update tw.rs

* Update tw.rs
2026-09-02 14:36:49 +08:00
palmoni5
f28ac38ccf feat: optionally sync clipboard between connected sessions (#15934)
* feat(clipboard): optionally sync clipboard between connected sessions

Clipboard content received from a remote session is written to the local
clipboard with an owner marker, so the client clipboard loop deliberately
skips re-broadcasting it to avoid echo loops. As a result, text copied in
one remote window could not be pasted in another connected remote window.

Add an opt-in local option (allow-sync-clipboard-between-sessions) that
relays Clipboard/MultiClipboards messages received from one session to
all other connected sessions, excluding the source session. Per-session
clipboard permissions and view-only mode are still respected via the
existing send path, and the owner marker on the receiving peers prevents
any echo back.

Desktop (flutter) only; file clipboard is not affected.

* fix(lang): propagate sync-clipboard-between-sessions-tip to all locale files

Add the new key to template.rs and every locale file per the localization
convention, move the en.rs entry to the end of the list, and drop comments
that only restated the names next to them.

* fix(lang): add the 'Sync clipboard between sessions' label to the localization catalog

The checkbox label goes through translate(), so add it to template.rs
and every locale file so non-English locales can translate it. en.rs is
skipped since the English display text is identical to the key.

* fix(clipboard): check the source session's full clipboard permission before relaying

The relay was gated only by the incoming clipboard_allowed check
(!disable_clipboard && !view_only). Gate it with
is_text_clipboard_required() instead, which additionally respects the
source session's server_clipboard_enabled and server_keyboard_enabled
state, matching the predicate already applied to destination sessions.
A message arriving after the source permission was revoked (or from a
non-conforming peer) is no longer propagated to other sessions. The
existing local update_clipboard behavior is unchanged.

* fix(lang): translate the new clipboard sync entries in all locale files

Fill the 'Sync clipboard between sessions' label and its tooltip in
every locale file instead of leaving them blank, following each file's
existing terminology. template.rs keeps the empty master entries.
2026-09-01 10:47:26 +08:00
rustdesk
28cf1836e6 bump to 1.5.0 2026-09-01 08:57:33 +08:00
Michael Clark
1ec1b9e7e3 fix: android: target API 36 (#15603)
* fix: android: target API 35

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: handle API 35 foreground service types

Integrate the foreground-service and MediaProjection lifecycle changes
from fufesou/rustdesk#68 while leaving storage permission handling to
#15602.

Co-authored-by: fufesou <linlong1266@gmail.com>
Signed-off-by: fufesou <linlong1266@gmail.com>
Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: bump required android sdk version to 36, per recent google requirement change.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix(android): clear microphone FGS type when capture stops

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

* fix(android): harden API 36 capture service lifecycle

- isolate MediaProjection callbacks per session
- keep foreground service types in sync with capture state
- handle audio startup failures and shared frame ownership
- upgrade AGP to 8.10.1 for API 36 support

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

* fix(android): reset capture state on FGS update failure

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

* fix(android): recover capture after projection failure

Propagate virtual display startup failures, clean up partial video
resources, and resume capture after media projection is reauthorized.

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

* fix(android): preserve voice call during projection replacement

Keep the existing capture active until
a new projection is acquired, and restore the
voice-call audio source when capture restarts.

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

* fix(android): use JDK 17 in playground workflow

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

* fix(android): clear pending capture restart on denial

Notify MainService when a recovery projection
request is canceled so a later projection grant
cannot restart stale capture state.

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

* fix(android): handle audio and projection recovery failures

Verify AudioRecord startup, propagate voice-call restoration failures,
and clear stale capture recovery state when projection setup fails.

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

---------

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-08-31 22:29:51 +08:00
rustdesk
2c84c8fb13 change to 3.44.9 flutter for arm 2026-08-31 19:06:32 +08:00
RustDesk
66ab0b87f6 Linux drop shell from service loop (#15979)
* perf(linux): stop the service loop from forking a shell per environment variable

The service loop re-derives the desktop every 500 ms, and every lookup on that
path forks. A healthy GNOME session spends ~104 process spawns a second, 8 full
`ps -u <uid>` scans and 2 full `ps aux` scans, to re-answer a question whose
answer has not changed. `get_env` alone is a `sh -c` pipeline of ~12 processes
per variable.

`get_envs` already reads `/proc` directly and was documented as the intended
replacement, so move the remaining `get_env` callers to it and delete it. The
xwayland probe drops from 4 pipelines (~48 processes) to one `/proc` walk, and
the pathological walk that #15952 was about drops from ~2900 processes to at
most 60 `/proc` walks. `get_cm` and `is_xwayland_running` read `/proc` instead
of forking `ps aux` and `pgrep -a`; `get_cm` also called `current_exe()` once
per line of `ps` output.

Selection semantics are preserved where they were load-bearing:

* `get_envs_of_newest` reproduces the `ps ... | tail -1` the removed pipelines
  used, so a variable the newest matching process does not have means moving on
  to the next pattern, never on to an older process that may belong to a session
  which has since logged out.
* `get_envs` keeps its own order (readdir) and its all-process ranking, so the
  existing `get_display_xauth_wayland` caller is unaffected. Only its handling
  of an exported-but-empty value changes: `DISPLAY=` no longer counts as found,
  where it used to satisfy a single-name query and return the empty value before
  a process holding a real one was examined.
* `get_envs_where` lets the caller state what a complete answer is. Ranking by
  how many of the requested names a process carries cannot know that `DISPLAY`
  is mandatory and the rest interchangeable, so it could rank a process holding
  three optional values above the one holding the pair that matters.

`is_xwayland_running` is scoped to the session's uid. The compositor starts
Xwayland as the session user, so another user's Xwayland -- a switched-away
session, a second seat -- used to route a pure-Wayland session into the Xwayland
probe, which has no display for it to find there.

Not addressed: this discovery path has never had any notion of the active
session, and filters by uid alone. Constraining candidates to the active session
is not possible for the most important one, since `xdg-desktop-portal` and its
backends run under `user@<uid>.service`, which spans sessions and carries no
`XDG_SESSION_ID`, no session cgroup and no audit sessionid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q5egQpH4q4GoXJiuMoTJ5t

* fix(linux): the newest-process walk must not answer with a grep or an older PID

Three findings from review of the commit before this one.

`/proc/<pid>/environ` failing to read left the walk on to the next PID, which in
`newest_first` mode is an older process -- possibly of a session that has since
logged out -- where the `ps ... | tail -1` pipeline this replaces stopped at the
one PID it had already picked. A read that fails is a process carrying none of
the requested names, not a process to skip. The `seen` latch that was meant to
hold the newest process is deleted: `accept` is reached once per matching
process, so returning on the first is what it already did.

The regex is matched against the whole `/proc/<pid>/cmdline`, where the pipeline
had a `grep -v 'grep'`. A user running `grep Xwayland` is otherwise the newest
match for that pattern and answers with whatever environment their shell had --
an X forwarding endpoint over ssh, say. This is the one place the walk still
differs from the `get_envs` it grew out of, which never had that filter and
could take an ssh `grep` over the portal it was looking for.

`get_envs` is left exactly as it was. Its completeness test was every requested
name *present*; stating it through `accept` turned it into every name *non-empty*
and, with the empty-value change that went with it, moved which process the
existing `get_display_xauth_wayland` caller settles on. `accept` is now told the
count and asks the question the loop it replaced asked. This supersedes the
`get_envs` bullet of the previous commit message: an exported-but-empty value
counts as found again, as it always did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QgsYAUYKDei1AM5yHJsMX

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 17:51:27 +08:00
rustdesk
169f74f8d9 fix(ci): check out submodules in update-webpki-roots
The root workspace lists libs/hbb_common as a member, so without the
submodule cargo cannot load the workspace and `cargo update` exits 101.
The job has failed on every scheduled run since it was added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gecc6fgEeSxs6VRiQmAeof
2026-08-31 17:31:57 +08:00
Michael Clark
d4b06a6c5c fix: android: replace all-files access with scoped storage (#15602)
* fix: android: replace all-files access with scoped storage + system picker

Remove MANAGE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE, and
WRITE_EXTERNAL_STORAGE from the Android manifest. Remove
requestLegacyExternalStorage. Replace broad external storage with
app-scoped external storage for the file-transfer workspace.

File import uses the system file_picker. File export uses Android's
SAF ACTION_CREATE_DOCUMENT with path validation that restricts
export sources to app-owned directories.

Remove the external_path dependency.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: refine file import feedback

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: use SAF for file imports

Replace file_picker imports with Android's Storage Access Framework to avoid legacy storage permissions, stale cached files, and duplicate staging of large imports. Stream selected documents into app-scoped storage with failure-safe replacement, keep exports restricted to validated app storage roots, use filesDir for the internal fallback workspace, and remove legacy permissions contributed during manifest merging.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: keep file imports in the selected directory

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: reset projection and constrain file workspace

Release capture resources when media projection is revoked externally. Keep Android local file navigation within the app-scoped workspace.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: handle scoped storage start-up regressions. Allow zero digits in POSIX filenames by rejecting NUL explicitly, and initialise the app-specific home directory before the Android service starts the native server.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: update content resolver mode to use 'wt' instead of 'w' to prevent trailing bytes from old document whilst reporting sucess

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android, enforce file workspace boundary on the server, and unblock the ui thread.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: android: validate rename destinations against the app workspace bound file-operation paths. report rename failures, general import failures, and unregister / reregister projection when its onStop callback fires.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: reconnect was refreshing the directory with net entry instances, while selected items retained the old instances, it was reporting a selected item, but checkbox statue used object identity, and appeared unchecked. Fixed by reconciling by path and entry type before replacing the directory snapshot, rebinding valid selections, and dropping missing ones.

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix: (android) add SAF folder import and multi item export - import directories using ACTION_OPEN_DOCUMENT_TREE. Export multiple files, logs, and screen recordings via export buttons, add localisation keys for new actions

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>

* fix(android): harden scoped storage file handling

- create new SAF documents instead of overwriting export sources
- reject empty peer paths except for home directory reads
- report directory backup restore and cleanup failures
- resolve log export paths from the configured app name

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

* fix(android): harden scoped-storage file operations

- snapshot directory exports before writing to the destination
- query document provider metadata off the main thread
- reject invalid remote directories without read timeouts

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

* fix(android): handle SAF directory name collisions

- reject dot-segment folder names during import
- fail imports with duplicate document display names
- only reuse matching directories during export

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

* fix(android): handle SAF folder import collisions

Reject filesystem-equivalent destination names and
avoid showing a failure when folder overwrite is skipped.

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

---------

Signed-off-by: michaeljclarkk <104532890+michaeljclarkk@users.noreply.github.com>
Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-08-31 16:29:51 +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
RustDesk
1fe451c2e8 chore(flutter): bump desktop_multi_window for show recovery (#15959)
Pick up rustdesk-org/rustdesk_desktop_multi_window#37, which re-arms the existing bounded redraw timer whenever a secondary window is shown, including when its first frame was generated while hidden but not presented.

This may perform one delayed child refresh on each show. It intentionally does not add a presentation-complete flag: Flutter reports frame generation rather than successful presentation, so recording success after a synthetic refresh could suppress later self-recovery without a reliable success signal.
2026-08-27 14:42:09 +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
rustdesk
e9b81e3475 typo 2026-08-27 11:47:36 +08:00
RustDesk
7220f00410 fix(linux): a Wayland session without XAUTHORITY is not incomplete (#15978)
Fixes #15952.

Hyprland runs Xwayland without exporting `XAUTHORITY`, and
`get_display_xauth_xwayland` only returns once it has both `DISPLAY` and
`XAUTHORITY`. On such a session that condition is never met, so every refresh
runs the retry loop to the end: 10 rounds x 6 process patterns x 4 variables =
240 `get_env` calls, each a `sh -c` pipeline of ~12 processes starting with a
full `ps -u <uid> -f`. That is ~2900 fork/exec per refresh, and the service loop
repeats every 500 ms. The reporter measured a full core on a low-end laptop and
~60% of a core on a 13600KF.

The Wayland side answers for such a session, so accept `DISPLAY` together with
either `XAUTHORITY` or `WAYLAND_DISPLAY` + `DBUS_SESSION_BUS_ADDRESS`. The
portal answers on the first pattern, which ends the walk there, as it already
did on desktops that do export an xauth.

The loop also assigned all four variables unconditionally per pattern, so the
patterns that do not run on a given desktop blanked out what an earlier one had
answered with -- the portal's valid `DISPLAY=:1` included. That is why the
`--server` was then started with no `WAYLAND_DISPLAY` and no
`DBUS_SESSION_BUS_ADDRESS`. Candidates are now taken from one pattern as a whole
and ranked, so a later pattern replaces an earlier answer only by being better,
and a session that can only offer a compositor and a bus still keeps them.

A compositor that starts Xwayland on demand shows the same shape from the other
side: the portal came up before Xwayland did, so its environment carries a valid
`WAYLAND_DISPLAY` and `DBUS_SESSION_BUS_ADDRESS` but no `DISPLAY`, and no pattern
here may ever produce one. That pair alone is a session the child server can be
started against -- it is exactly what `get_display_xauth_wayland` returns on --
so it outranks a bare `DISPLAY` and ends the retrying, while the rest of the
round still looks for something that completes the session.

Not specific to the drm build: the function is not feature-gated, and the commit
the report points at does not touch it.


Claude-Session: https://claude.ai/code/session_01Q5egQpH4q4GoXJiuMoTJ5t

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 11:39:59 +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
Jade
7c6e661fcc fix(linux): Set AppIndicator ID for tray-icon (#15981)
* set static AppIndicator ID in tray-icon init

allows DEs, eg. KDE to 'remember' the user's configuration of tray hidden/unhidden. see: https://github.com/rustdesk/rustdesk/discussions/15208

Signed-off-by: Jade <5164609+gnosticJade@users.noreply.github.com>

* Update tray.rs

---------

Signed-off-by: Jade <5164609+gnosticJade@users.noreply.github.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2026-08-27 09:41:24 +08:00
Mariano Abad
3f207e91f6 fix(linux): a session logout should hand the peer to the login screen (#15905)
* fix(linux): a session logout should hand the peer to the login screen

Logging out closes every window in the session, the connection manager's
included, and its close handler kicks every peer with the reason a person
gets when they disconnect one by hand. That reason is the one thing the
client never retries on, so the remote session dies on a frozen frame
instead of reconnecting to the greeter that is already there.

The close carries nothing to tell the two apart: measured on KDE, the CM
receives no signal and logind still reports the session active at that
instant, and the server is killed within a few hundred ms either way, so
neither a state check nor a grace period can decide it. What is
distinguishable is the ACTION: disconnecting a peer is not the same event
as this window going away. So the window-close path now says so, and the
server ends the session without poisoning the retry; the Disconnect
button and the app's own close control keep kicking exactly as before.
Linux only, since that is where a logout closes the window.

Verified on plasma/sddm with a client attached: a logout now reconnects
to the greeter with no dialog, while closing the manager window still
shows Closed manually by the peer.

* fix(linux): close the tunnel too, and keep the web build compiling

Three seams the first pass missed. The web bridge is hand written, not
generated, so the new call needs its stub there or flutter build web
stops compiling - and that job is disabled in CI, so it would have gone
green. try_port_forward_loop is a second consumer of the same channel
and only knew Close, so a forwarded tunnel outlived the window it was
supposed to die with. And the variant had landed inside the DRM section,
whose comment says everything below it is drm-gated.
2026-08-26 18:26:26 +08:00
Kino
cec4085238 Bump aom to v3.14.1 (#15883)
* Bump aom to v3.14.1

* Remove oboe dependency in vcpkg.json
2026-08-25 19:53:56 +08:00
fufesou
0d917c6fa1 fix: remove dup translations (#15967)
Signed-off-by: fufesou <linlong1266@gmail.com>
2026-08-25 18:25:49 +08:00
Rafli Surya Wijaya
893dc27798 docs(readme): fix broken Screenshots section anchor link (#15964) 2026-08-25 11:21:34 +08:00
Abdullah Kaleem
7cc82c1575 Add Urdu language support for UI strings (#15961)
* Add Urdu language support for UI strings till 329 line

Co-authored-by: Copilot <copilot@github.com>

* Add Urdu translations for additional UI strings

* Add Urdu language support in lang.rs

* Fix Urdu translations and remove unused keys in ur.rs

---------

Co-authored-by: Copilot <copilot@github.com>
2026-08-25 09:19:37 +08:00
jhertel
f07b6e2338 Correct Danish spelling, language and translation (#15943)
* Update da.rs

Corrected spelling, language and translation mistakes.

* Update da.rs

Missed one correction.
2026-08-24 17:22:03 +08:00
Robert Markovski
a3bab27a2a fix: Show My Cursor freezes in View Only mode when remote user mo... (#15936) 2026-08-24 17:21:11 +08:00
RustDesk
7423dced37 Update reference from AGENTS.md to @AGENTS.md 2026-08-22 17:50:10 +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
rustdesk
d5a7f67999 fix appimage pixbuf crash 2026-08-22 12:25:35 +08:00
ben-leone
e266380ee9 fix(appimage): keep the XDG default data dirs on XDG_DATA_DIRS (#15938)
AppRun sets XDG_DATA_DIRS to
"$APPDIR/usr/local/share:$APPDIR/usr/share:$XDG_DATA_DIRS". When the host
leaves XDG_DATA_DIRS unset, the result contains no /usr/share, and setting
the variable at all suppresses the XDG default of /usr/local/share:/usr/share.

gdk-pixbuf 2.43+ (Arch, CachyOS, Gentoo, Fedora, openSUSE) no longer ships PNG,
JPEG or WebP as loader modules; libgdk_pixbuf links libglycin and decodes them
through it, and glycin discovers its loaders in
$XDG_DATA_DIRS/glycin-loaders/<ver>/conf.d/*.conf. With /usr/share missing,
glycin finds none and every PNG decode inside the AppImage fails with
"Unrecognized image file format".

RustDesk sends remote cursors to flutter_custom_cursor as PNG, and that plugin
returns nullptr from a std::string function when the decode fails, so the first
non-default cursor of a session aborts the process:

    GdkPixbuf-CRITICAL **: gdk_pixbuf_copy: assertion 'GDK_IS_PIXBUF (pixbuf)' failed
    terminate called after throwing an instance of 'std::logic_error'
      what():  basic_string::_M_construct null not valid

Debian and Ubuntu compile PNG straight into libgdk_pixbuf and never reach
glycin, which is why this only affects non-Debian hosts.

Append the two XDG defaults so they are present when the host does not provide
them. They go last, so a session that sets XDG_DATA_DIRS properly keeps its own
precedence, and appending is a no-op where those paths are already listed.

Verified on CachyOS (gdk-pixbuf 2.44.7) against a stock 1.4.9 AppImage: with
only this variable changed, a full remote session runs without crashing and
renders remote cursors correctly.

Refs #4565 #5457 #7013 #9164 #10563 #11499 #12257 #14305 #14405 #15625

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 11:21:48 +08:00
Saverio Miroddi
cbf9440281 Prefer active X11 session display (#15933)
* Prefer active X11 session display

* Update linux.rs

* fix(linux): keep the logind display only when it is a local one

`get_display_from_session` returns the value pam_systemd was handed at session
creation, and logind never updates it afterwards. That value is not always a
usable local display: it can be qualified with this host (`myhost:0`), name an
X forwarding endpoint (`localhost:10.0`), or be a bare `:`.

Taking it unconditionally is worse than taking nothing, because a non-empty
`self.display` suppresses every fallback below it, `get_display_by_user` and the
`:0` default alike. The stripping at the end of `get_display_x11` does not save
the last two cases either: it leaves `:` as is and turns `localhost:10.0` into a
local looking `:10.0`, either of which is then exported as DISPLAY and leaves the
session unreachable, where before this PR the host got a working `:0`.

Strip this host so `myhost:0` is still accepted as `:0`, leave `localhost` in
place, and require a display number after the colon. Anything else falls through
to the existing chain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TKJxvTT6NQDEcnkWBx5bLA

* docs(agents): prefer a little duplication over a restructure

The "Be minimally invasive" rules already ask for purely additive diffs, but not
in the case where the addition would otherwise reshape an existing function so
the two can share code. Repeating a few lines is the better diff there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TKJxvTT6NQDEcnkWBx5bLA

---------

Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 00:28:12 +08:00
rustdesk
6eaac17ac5 typo 2026-08-22 00:02:34 +08:00
fufesou
92eb137178 feat(terminal): use platform-native copy and paste shortcuts (#15931) 2026-08-21 21:20:39 +08:00
palmoni5
61ddade049 fix(windows): restore keyboard focus when the cursor re-enters the remote image (#15880)
* fix(windows): restore keyboard focus when the cursor re-enters the remote image

On Windows the raw key focus node is unfocused on window blur and nothing
requests it back, so returning to an already connected session left the
keyboard dead until the remote image was clicked.

Request focus from enterView(), gated on the window being active, the tab
being selected and no blocking overlay, so a background window cannot grab
system keys. enterOrLeave(true) is still driven by RawKeyFocusScope's
onFocusChange, so it is not called twice.

* fix(windows): refocus on window focus when the cursor already hovers the image

Alt+Tab or a taskbar click returns focus without a PointerEnter, so
enterView() cannot restore the keyboard. Reuse _cursorOverImage, gated
on the selected tab and no blocking overlay.

* refactor(windows): share one focus predicate for every requestFocus path

The relative-mouse-mode restore on window focus could hand remote input
to this page while a blocking dialog was up or the tab was not selected.
2026-08-21 17:07:25 +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
rustdesk
c45f7d2dd2 refactor is_public 2026-08-21 01:25:51 +08:00
RustDesk
798b73beb1 Update common.rs (#15924) 2026-08-21 00:52:31 +08:00
rustdesk
f1a06f6765 review rules 2026-08-20 19:22:36 +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
21pages
5679670506 fix(flutter): make Adjust Window reliable across desktop platforms (#15853)
* fix(flutter): make Adjust Window reliable across desktop platforms

  - Fix incorrect sizing on scaled displays by calculating the target from the
    rendered canvas scale and platform-specific window coordinate units.
  - Fix adjustments using the wrong monitor by querying the current remote
    window's screen, with the main window as fallback.
  - Fix stale geometry after fullscreen or maximized transitions by refreshing
    metrics before calculating and applying the target frame.
  - Fix fullscreen availability checks on Windows and macOS by predicting the
    restored window borders and caching each macOS window's pre-fullscreen work area.
  - Fix incorrect Linux work areas by handling GNOME Wayland fractional scaling
    and caching compositor/X11 work-area measurements when visibleFrame is wrong.
  - Prevent unsafe adjustments by rejecting invalid, oversized, or implausibly
    small target frames.
  - Avoid failures during window teardown by skipping adjustment when the view,
    screen, or native window frame is unavailable.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): harden Adjust Window handling

  - Use the dynamic Linux resize edge when predicting restored window bounds.
  - Treat GNOME fractional-scaling lookup failures as unknown without repeating
    the lookup for the remote window.
  - Stop adjustment safely when native window calls fail during window teardown.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): correct Linux monitor selection

Update window_size to use monitor height for vertical bounds, preventing incorrect screen selection with vertically stacked displays.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* docs(flutter): simplify Linux screen handling comments

Keep the source rationale concise and move platform measurements and investigation details out of the implementation.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): align Adjust Window resize padding

Use the shared drag-to-resize padding for Linux restored-window predictions so menu validation matches the applied frame dimensions.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): remove Adjust Window screen fallback

Return null when the current window screen is unavailable instead of using the main window's scale factor and work area.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(linux): query Mutter monitor layout mode

  Use DisplayConfig.GetCurrentState instead of inferring scaling from
  experimental features, and handle Ubuntu's UI-scaled logical mode.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): use native maximized state for Wayland cache

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): allow Adjust Window to fill work area

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): avoid racing screen info updates

Signed-off-by: 21pages <sunboeasy@gmail.com>

* refactor(flutter): remove dead Adjust Window web plumbing

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): tolerate near-unity Wayland scale factors

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): harden window screen detection

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(linux): drop deprecated GNOME session detection

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix(flutter): remove GNOME monitor layout mode flutter cache

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-08-19 15:08:58 +08:00
Mariano Abad
630b531108 fix(flutter): initialize the cursor hotspot y from its own origin (#15898)
The CursorData constructor copies hotxOrigin into hoty. Latent today:
both consumers call updateGetKey() before reading, and _checkUpdateScale
recomputes hoty from hotyOrigin - but any future read before that call
inherits the x value silently.
2026-08-19 12:49:28 +08:00
RustDesk
1984678785 Hide printer tab when settings disabled (#15901)
* fix: hide the printer settings tab when settings are disabled

The Security and Network tabs already honour `disable-settings`, but the
Printer tab was gated only on `hide-remote-printer-settings`, so custom
clients built with settings disabled still exposed it.

https://github.com/rustdesk/rustdesk-server-pro/issues/1001

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: add hide-general-settings builtin option

Hides the General tab of the settings page. Unlike the other
hide-*-settings options this one is still useful when settings are
disabled, since `disable-settings` does not cover the General tab.

https://github.com/rustdesk/rustdesk-server-pro/issues/1001

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 10:48:30 +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
rustdesk
0c00d576dd improve comment rules 2026-08-18 12:31:59 +08:00
RustDesk
9b1b810d3a Delete .github/dependabot.yml (#15888) 2026-08-18 10:40:04 +08:00
RustDesk
6a27910f34 fix(wayland): back off the polling display lookups after a failure (drm) (#15865)
* fix(wayland): back off the polling display lookups after a failure (drm)

In drm builds an enumeration that fails with no endpoint named in the
environment falls back to the socket probe, which forks a child bounded by
seconds, and the display service asks again every 300 ms -- at a greeter
with no reachable compositor that is a probe child per turn, forever. Such
a failure now stamps a shared 5 s backoff, and only the polling callers
honor it: the 300 ms displays-changed check skips its turn and the 1.5 s
live layout poll returns no answer for that turn.

Only the failure that would fork stamps. A session server is spawned with
WAYLAND_DISPLAY set, so its failed connect bails in-process before any
fork; stamping there would buy nothing and cost recovery latency, so live
sessions keep master's behavior exactly. The stamp also survives
clear_wayland_displays_cache: it describes the seat, not the cache, and
the ~1/s capturer rebuild loop clears on every teardown -- dropping the
stamp with the cache would let that loop defeat the backoff and would
turn every post-hotplug failure into a "first" one forever.

The displays-changed check weighs the backoff against what is already
published. With nothing synced yet it always populates -- an unaugmented
DRM list beats the empty broadcast the send path would otherwise emit.
With a synced layout, a suppressed turn keeps it, and a fresh first
failure keeps it too; only a failure that persists across a backoff
replaces it with the DRM stack, so a hotplug at a failing seat converges
within one backoff while a transient failure never tears down a good
layout.

One-shot callers -- session init, pipewire stream setup, capturer info --
keep probing fresh through get_displays, whose failure semantics are
unchanged: replaying a transient failure there would latch an empty answer
into session-long state. Non-drm builds compile none of this.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(wayland): log DRM lookup failure once

* fix(wayland): reset lookup warning after recovery

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 09:57:06 +08:00
rustdesk
14a4b197ad translations 2026-08-17 17:22:57 +08:00
Krik JIN
8ffe3117a5 feat(flutter): add mobile canvas lock (#15877)
* feat: add mobile canvas lock

* Update flutter/lib/models/model.dart

Remove redundant canvas-lock comment

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* remove redundant logic

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

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: Krik Jin <isjinhk@outlook.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-08-17 17:04:58 +08:00
RustDesk
5a78be03e3 feat(rdp): title the mstsc window after the peer instead of "localhost" (#15781)
* feat(rdp): title the mstsc window after the peer instead of "localhost"

The RDP tunnel launched `mstsc /v:localhost:<port>`, so with several
sessions open every window is titled "localhost" and servers cannot be
told apart.

mstsc titles the session window after the launched .rdp file's base
name, so write a temp .rdp file (containing only the tunnel address)
named after the peer alias, cached hostname, or id, and launch that
instead. Falls back to the old /v: form when no usable name remains
after filename sanitization or the file cannot be written. Credential
handling is unchanged: cmdkey targets "localhost", which is still the
host mstsc resolves credentials against.

Fixes rustdesk/rustdesk#15775 (discussion)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(rdp): set mstsc title without temporary files

  Keep launching mstsc with /v so Default.rdp settings are preserved
  and unsigned RDP file warnings and policy restrictions are avoided.

  Track the launched mstsc process and reapply the peer name when the
  window title is reset during connection or reconnection.

Signed-off-by: 21pages <sunboeasy@gmail.com>

* docs(rdp): clarify mstsc title limitation

Signed-off-by: 21pages <sunboeasy@gmail.com>

* feat(rdp): show peer identity with hostname in mstsc title

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
2026-08-17 13:30:18 +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
yzxcj797
3871c47855 docs: remove stale flutter/web/js entry and fix dead localized build links (#15869) 2026-08-17 09:18:45 +08:00
yzxcj797
8d52d48b24 docs: fix dead code of conduct links in ID/IT contributing guides (#15868) 2026-08-17 09:18:21 +08:00
RustDesk
7aa98d43cf Refact/plugin removal leftovers (#15864)
* fix(flutter): dispose the settings PageController and order dispose() correctly

`dispose()` began with `super.dispose()`, so the mixin chain marked the State
defunct before the WidgetsBindingObserver registration and the periodic timer
were released. The `PageController` was never disposed at all: `Get.delete`
only runs `onDelete()` for a `GetLifeCycleBase`, and a plain `ChangeNotifier`
is not one, so every open/close of the Settings tab leaked one controller with
its listener still attached.

Also guard `switch2page` on the `Rx<SettingsTabKey>` registration it actually
reads rather than only the `PageController` — now that both are really
deleted, a partial teardown would throw into the catch and silently open the
wrong tab — and re-check `mounted` after the await in the `_videoConnTimer`
tick, which `Timer::cancel` cannot stop once the body has started.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refact: finish the plugin-framework removal sweep

#15854 removed the feature but stopped short of its leftovers:

- `Uninstall`, `Enable`, `Disable`, `Options` and `Please install plugins`
  were consumed only by the deleted `flutter/lib/plugin/**`; drop them from
  template.rs and the 50 locale files (250 dead entries). `Update` and
  `Install` stay, still used by desktop_home_page.dart.
- The server no longer sends `PrvOnFailedPlugin`, and the client no longer
  offers to install plugins when privacy mode fails to turn on.
- Drop the MSI `F_Client_Plugins` / `F_Server_Plugins` localization strings;
  no `.wxs` references them.
- `_DisplayMenu`'s constructor became a pure pass-through once `pluginItem`
  was removed, and the cfg inside `handle_input` repeats the one on the
  function itself.
- Normalize `src/lang/sl.rs` to 0644, the only executable file under src/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(client): handle legacy privacy mode plugin failures

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

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
2026-08-14 18:52:03 +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