mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-02 10:16:28 +02:00
Add an opt-in keyboard-shortcut system that triggers session
actions (Send Ctrl+Alt+Del, Toggle Fullscreen, Switch Display,
Screenshot, Switch Tab, etc.) via three-modifier combinations
during a remote session.
Architecture
- Native: src/keyboard/shortcuts.rs intercepts at the encoder
layer (process_event and process_event_with_session), so the
feature is input-source-independent. Bindings persist as a
single JSON blob in LocalConfig.
- Web: matching + keydown intercept live in the separate hand-
written TS client at flutter/web/js/ (gitignored, not in this
repo). flutter/lib/web/bridge.dart::mainInit registers
window.onShortcutTriggered so the JS matcher can dispatch
back into the active session's ShortcutModel; the bridge's
mainReloadKeyboardShortcuts forwards to a JS reloadShortcuts
on settings writes.
- Three-modifier prefix (Ctrl+Alt+Shift; Cmd+Option+Shift on
macOS/iOS) sidesteps the need for a pass-through toggle.
- Flutter native path threads the explicit per-call SessionID
for tab-precise routing; rdev path uses globally-current
session.
UI
- Settings -> General -> Keyboard Shortcuts opens a dedicated
configuration page; desktop and mobile share a body widget.
- Recording dialog with live capture, prefix validation, and a
conflict-replace flow.
- Toolbar menu items display the bound shortcut inline.
- Default bindings (adapted from AnyDesk):
+Del Send Ctrl+Alt+Del
+Enter Toggle Fullscreen
+Left/Right Switch Display Prev/Next
+P Screenshot
+1..9 Switch Session Tab
Other
- AGENTS.md: documented (a) flutter_rust_bridge_codegen needs
a pinned version + Dart bridge wrappers should be hand-
written, and (b) the Web-target split where flutter/web/js/
is the runtime owner on Web rather than wasm-compiled Rust.
- 38 new i18n strings in src/lang/en.rs with Chinese
translations in src/lang/cn.rs.
Refs discussion #1933.
4.8 KiB
4.8 KiB
RustDesk Guide
Project Layout
Directory Structure
src/Rust appsrc/server/audio / clipboard / input / video / networksrc/platform/platform-specific codesrc/ui/legacy Sciter UI (deprecated)flutter/current UIlibs/hbb_common/config / proto / shared utilslibs/scrap/screen capturelibs/enigo/input controllibs/clipboard/clipboardlibs/hbb_common/src/config.rsall options
Key Components
- Remote Desktop Protocol: Custom protocol implemented in
src/rendezvous_mediator.rsfor communicating with rustdesk-server - Screen Capture: Platform-specific screen capture in
libs/scrap/ - Input Handling: Cross-platform input simulation in
libs/enigo/ - Audio/Video Services: Real-time audio/video streaming in
src/server/ - File Transfer: Secure file transfer implementation in
libs/hbb_common/
UI Architecture
- Legacy UI: Sciter-based (deprecated) - files in
src/ui/ - Modern UI: Flutter-based - files in
flutter/- Desktop:
flutter/lib/desktop/ - Mobile:
flutter/lib/mobile/ - Shared:
flutter/lib/common/andflutter/lib/models/
- Desktop:
Rust Rules
-
Avoid
unwrap()/expect()in production code. -
Exceptions:
- tests;
- lock acquisition where failure means poisoning, not normal control flow.
-
Otherwise prefer
Result+?or explicit handling. -
Do not ignore errors silently.
-
Avoid unnecessary
.clone(). -
Prefer borrowing when practical.
-
Do not add dependencies unless needed.
-
Keep code simple and idiomatic.
Tokio Rules
- Assume a Tokio runtime already exists.
- Never create nested runtimes.
- Never call
Runtime::block_on()inside Tokio / async code. - Do not hide runtime creation inside helpers or libraries.
- Do not hold locks across
.await. - Prefer
.await,tokio::spawn, channels. - Use
spawn_blockingor dedicated threads for blocking work. - Do not use
std::thread::sleep()in async code.
Flutter Rust Bridge
- Do not run
flutter_rust_bridge_codegen— it requires a specific pinned version that is not easy to set up locally. - When adding new FFI functions in
src/flutter_ffi.rs, hand-write the corresponding Dart wrappers instead of regenerating. - Web bridge (committed): edit
flutter/lib/web/bridge.dartdirectly. Follow the existing patterns there forSyncReturn<T>/Future<T>and thedart:jsglue. - Native bridge (
flutter/lib/generated_bridge.dart,src/bridge_generated.rs,src/bridge_generated.io.rs): these are gitignored and regenerated by the project's CI codegen. Manually editing them locally is fine for development testing, but those edits do not persist into commits.
Web (Flutter Web) Architecture
Flutter Web in this repo is not "Dart compiled to JS via Flutter alone". The runtime is split:
- Native targets (Win/Mac/Linux/Android/iOS): Rust drives sessions via
flutter_rust_bridge; Dart only renders UI. - Web target: Rust does not run. There is a separate hand-written TypeScript / JavaScript client at
flutter/web/js/(gitignored — not present in this repo, lives in the maintainer's local tree). It owns connection, codec, keyboard, clipboard, etc. — basically a JS port of the Rust client. The Dart UI talks to it throughflutter/lib/web/bridge.dart, which usesdart:jsto call JS-side functions and to register Dart-side callbacks onwindow.*.
Implications when adding any session-runtime feature (keyboard, clipboard, audio, …):
- The Rust implementation in
src/is for native only. Don't try to compile it to wasm. - The matching Web-side logic must be written in TS/JS under
flutter/web/js/src/. It's a translation of the Rust logic, usually simpler — Web is single-window, so any per-session-id plumbing in Rust collapses to a single global on Web. flutter/lib/web/bridge.dartis the only place where Dart sees JS. Other Dart code stays platform-agnostic and goes throughbind. Don't sprinkleif (isWeb)runtime branches in shared Dart files to call Web-specific logic — put the platform divergence in the bridge.- For JS → Dart events (e.g., a Web matcher firing), the convention is: Dart sets
js.context['onFooBar'] = (...) {...}once at startup (typically inmainInit); the JS side callswindow.onFooBar(...). SeeonLoadAbFinished,onLoadGroupFinishedfor reference. - The maintainer cannot easily run
flutter_rust_bridge_codegen, so when a new FFI function lands insrc/flutter_ffi.rs:- add the Web counterpart to
flutter/lib/web/bridge.dartby hand; - note that on the Web target it may need to be a no-op or a JS bridge call rather than a real Rust invocation.
- add the Web counterpart to
Editing Hygiene
- Change only what is required.
- Prefer the smallest valid diff.
- Do not refactor unrelated code.
- Do not make formatting-only changes.
- Keep naming/style consistent with nearby code.