* add the base crate and repoint the moved modules at it
`libs/base` (crate `base`) takes the parts of hbb_common that only this app
uses: `fs`, `platform`, `keyboard`, `message.proto`, and 145 of the 177
`config::keys` constants. hbb_common keeps what the server names, and the 32
keys it reads itself are re-exported from `base::config::keys` so call sites
still see the full set through one path.
Sources move verbatim. The only edits inside them are `crate::` prefixes that
now have to say `hbb_common::`; `keyboard.rs` and `platform/windows.rs` are
byte-identical. The crate stays on edition 2018, the edition the moved code was
written under. `log`, `lazy_static` and `anyhow` become direct dependencies so
the bare paths in that code resolve exactly as before, and its winapi features
are spelled out rather than left to feature unification.
Two call sites outside Rust and Cargo had to follow the move: the Android
protobuf source dir, which still pointed at hbb_common/protos for message.proto,
and the three AGENTS.md entries that named hbb_common for options, protos and
file transfer.
`scrap`'s `drm` feature now forwards to `base/wayland_probe`. Left pointing at
hbb_common it would still have compiled, silently dropping the Wayland
socket-probe fallback, so that forward is verified by a build with and without
the feature.
`config::keys` carries a test asserting its names stay disjoint from the ones
hbb_common kept: the glob re-export and the local constants share a namespace,
and Rust prefers the local item silently, so a name added to both sides would
otherwise let client and server disagree with no diagnostic.
Verified: macOS and Linux, debug and release, `--all-targets`; the 177 key
constants diffed name-for-name and value-for-value; the generated protobuf types
compared before and after; every `#[cfg]` gate on a moved import checked against
its original; and every file that was `rustfmt`-clean before this change still
is, compared against master file by file. Windows is checked by inspection only
-- it cannot be compiled here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
* one `use` per crate, and write the rule down
`fs.rs` came out of the move with two ungated `use hbb_common::` statements,
because the original single `use crate::{...}` had to give up `message_proto`
to the new crate and the rest was left in a second block. Fold it back into one.
A scan of the whole tree for the same shape finds nothing else: every other file
with more than one top-level `use base::` or `use hbb_common::` is split by a
`#[cfg]` that does not cover the whole block, or by `pub use` next to `use`.
Those are the cases that cannot merge, so AGENTS.md now states both the rule and
the exemption.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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>
* 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>
* 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.
Fixesrustdesk/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>
* docs(agents): require minimally invasive, additive-first patches
Codify the review feedback from the tray ghost-icon fix: fixes should
add self-contained code around existing lines instead of restructuring
them, keep platform-specific logic in src/platform/ with fn-local
imports, and leave only thin one-line hooks in shared files.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(windows): stop duplicate tray icons from piling up (#15689)
`check_process("--tray", ..)` is used to decide whether a tray process
needs to be spawned, but it can miss one that is already running: it
cannot read the command line of an elevated process from a non-elevated
one (the installer spawns the tray elevated), and wmic, used by 32-bit
builds since #11638, is gone from newer Windows 11. `connection.rs` runs
that check once per incoming connection, so every miss added another tray
icon and they kept piling up, which is the same blind spot behind #6692.
Hold a named mutex in the session namespace as the authoritative single
instance guard, so a redundant tray process exits before creating an
icon. `ERROR_ACCESS_DENIED` also counts as "already running", since it
means the mutex belongs to a tray we may not touch.
Also remove the icon before the tray menu's "Stop service" calls
uninstall_service(): on success it ends the process with
std::process::exit, which skips the destructor that would call
Shell_NotifyIcon(NIM_DELETE), so every click left a ghost icon behind.
The icon is shown again if stopping the service failed or was cancelled.
Ghost icons from the taskkill in the install/update/service flows are
left alone here.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(windows): note that update_me's pid lookup can silently find nothing
The pids are matched by command line, which comes back empty for a 32-bit
build reading 64-bit processes (hence the `wmic` fallback of #11638, and
`wmic` is no longer installed by default since Windows 11 24H2) and for a
non-elevated process reading an elevated one. `taskkill` matches by image
name and still works, but the session lists are then empty, so the restore
guard silently restores nothing and the update leaves the user without a
tray icon and main window.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(windows): record the confirmed cause of the duplicate tray icons
Process Explorer output in #15689 pinned it down: run_after_run_cmds()
spawns the tray in the caller's own context, so installing or toggling
the service from a RustDesk that was itself started elevated leaves a
high integrity tray behind, which a medium integrity main window cannot
inspect afterwards. Record where the detection fails exactly, so the next
reader doesn't have to rediscover that the executable path, not the
command line, is what comes back empty.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Fix Windows session-based logon and lock-screen detection
- scope LogonUI and locked-state checks to the current Windows session
- allow permanent password fallback for logon and lock-screen access
Signed-off-by: 21pages <sunboeasy@gmail.com>
* Log permanent-password fallback on logon screen
Signed-off-by: 21pages <sunboeasy@gmail.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
* feat: Add relative mouse mode
- Add "Relative Mouse Mode" toggle in desktop toolbar and bind to InputModel
- Implement relative mouse movement path: Flutter pointer deltas -> `type: move_relative` -> new `MOUSE_TYPE_MOVE_RELATIVE` in Rust
- In server input service, simulate relative movement via Enigo and keep latest cursor position in sync
- Track pointer-lock center in Flutter (local widget + screen coordinates) and re-center OS cursor after each relative move
- Update pointer-lock center on window move/resize/restore/maximize and when remote display geometry changes
- Hide local cursor when relative mouse mode is active (both Flutter cursor and OS cursor), restore on leave/disable
- On Windows, clip OS cursor to the window rect while in relative mode and release clip when leaving/turning off
- Implement platform helpers: `get_cursor_pos`, `set_cursor_pos`, `show_cursor`, `clip_cursor` (no-op clip/hide on Linux for now)
- Add keyboard shortcut Ctrl+Alt+Shift+M to toggle relative mode (enabled by default, works on all platforms)
- Remove `enable-relative-mouse-shortcut` config option - shortcut is now always available when keyboard permission is granted
- Handle window blur/focus/minimize events to properly release/restore cursor constraints
- Add MOUSE_TYPE_MASK constant and unit tests for mouse event constants
Note: Relative mouse mode state is NOT persisted to config (session-only).
Note: On Linux, show_cursor and clip_cursor are no-ops; cursor hiding is handled by Flutter side.
Signed-off-by: fufesou <linlong1266@gmail.com>
* feat(mouse): relative mouse mode, exit hint
Signed-off-by: fufesou <linlong1266@gmail.com>
* refact(relative mouse): shortcut
Signed-off-by: fufesou <linlong1266@gmail.com>
---------
Signed-off-by: fufesou <linlong1266@gmail.com>
For msi version, the black window is shown when creating desktop
shortcut for connection.
The exe version does not have this issue.
Signed-off-by: fufesou <linlong1266@gmail.com>
* There is no relevant information, but I found that each session has a unique sihost.exe, and the user name of the process is consistent with the user name of the session, and after using the task manager to kill this process, it will automatically restart. Checking sessionUserName=siHost UserName may be unnecessary, but since there is no evidence, check it anyway.
* GetFallbackUserPid is called only when explorer.exe does not exist.
* ProcessHacker shows that the tokens of explorer.exe and sihost.exe are the same, I know little about it.
Signed-off-by: 21pages <pages21@163.com>