Compare commits

...

7 Commits

Author SHA1 Message Date
RustDesk
1fc33218dc Revert "fix(iPad): keep touch gestures with external mouse (#14652)" (#15288)
This reverts commit 5b7ad339b8.
2026-06-15 15:13:54 +08:00
21pages
b73e5bbfa0 opt: release clipboard config lock before updates (#15277)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2026-06-14 17:27:19 +08:00
fufesou
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
fufesou
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
littlestejan
84af60c07e Fix clipboard synchronization not fully disabled in View Only mode (#15224)
* fix: view-only clipboard sync

Signed-off-by: Setani <little_stejan@hotmail.com>

* fix: gate Android MultiClipboards handling with clipboard permissions

Signed-off-by: Setani <little_stejan@hotmail.com>

---------

Signed-off-by: Setani <little_stejan@hotmail.com>
2026-06-10 07:42:58 +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
dependabot[bot]
7c41f993fe Git submodule: Bump libs/hbb_common from df6badc to 387603f (#15189)
Bumps [libs/hbb_common](https://github.com/rustdesk/hbb_common) from `df6badc` to `387603f`.
- [Release notes](https://github.com/rustdesk/hbb_common/releases)
- [Commits](df6badca5b...387603f47c)

---
updated-dependencies:
- dependency-name: libs/hbb_common
  dependency-version: 387603f47cbb15c0d3dc3d67ae3396d3eb707daf
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 14:04:02 +08:00
16 changed files with 160 additions and 67 deletions

View File

@@ -3713,14 +3713,54 @@ Widget loadPowered(BuildContext context) {
).marginOnly(top: 6);
}
// max 300 x 60
Widget loadLogo() {
return FutureBuilder<ByteData>(
future: rootBundle.load('assets/logo.png'),
builder: (BuildContext context, AsyncSnapshot<ByteData> snapshot) {
if (snapshot.hasData) {
const _kDefaultLogoAsset = 'assets/logo.png';
const _kLightLogoAsset = 'assets/logo_light.png';
const _kDarkLogoAsset = 'assets/logo_dark.png';
List<String> _logoAssetCandidatesForBrightness(Brightness brightness) {
return brightness == Brightness.dark
? [_kDarkLogoAsset, _kDefaultLogoAsset]
: [_kLightLogoAsset, _kDefaultLogoAsset];
}
Future<String?> _resolveLogoAsset(Brightness brightness) async {
for (final asset in _logoAssetCandidatesForBrightness(brightness)) {
try {
await rootBundle.load(asset);
return asset;
} on FlutterError {
continue;
}
}
return null;
}
class _Logo extends StatefulWidget {
const _Logo();
@override
State<_Logo> createState() => _LogoState();
}
class _LogoState extends State<_Logo> {
final Map<Brightness, Future<String?>> _logoFutures = {};
Future<String?> _logoFutureFor(Brightness brightness) {
return _logoFutures.putIfAbsent(
brightness,
() => _resolveLogoAsset(brightness),
);
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String?>(
future: _logoFutureFor(Theme.of(context).brightness),
builder: (BuildContext context, AsyncSnapshot<String?> snapshot) {
final asset = snapshot.data;
if (asset != null) {
final image = Image.asset(
'assets/logo.png',
asset,
fit: BoxFit.contain,
errorBuilder: (ctx, error, stackTrace) {
return Container();
@@ -3732,9 +3772,14 @@ Widget loadLogo() {
).marginOnly(left: 12, right: 12, top: 12);
}
return const Offstage();
});
},
);
}
}
// max 300 x 60
Widget loadLogo() => const _Logo();
Widget loadIcon(double size) {
return Image.asset('assets/icon.png',
width: size,

View File

@@ -532,9 +532,7 @@ class _RawTouchGestureDetectorRegionState
// Official
TapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => TapGestureRecognizer(
supportedDevices: kTouchBasedDeviceKinds,
), (instance) {
() => TapGestureRecognizer(), (instance) {
instance
..onTapDown = onTapDown
..onTapUp = onTapUp
@@ -542,18 +540,14 @@ class _RawTouchGestureDetectorRegionState
}),
DoubleTapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
() => DoubleTapGestureRecognizer(
supportedDevices: kTouchBasedDeviceKinds,
), (instance) {
() => DoubleTapGestureRecognizer(), (instance) {
instance
..onDoubleTapDown = onDoubleTapDown
..onDoubleTap = onDoubleTap;
}),
LongPressGestureRecognizer:
GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => LongPressGestureRecognizer(
supportedDevices: kTouchBasedDeviceKinds,
), (instance) {
() => LongPressGestureRecognizer(), (instance) {
instance
..onLongPressDown = onLongPressDown
..onLongPressUp = onLongPressUp
@@ -563,9 +557,7 @@ class _RawTouchGestureDetectorRegionState
// Customized
HoldTapMoveGestureRecognizer:
GestureRecognizerFactoryWithHandlers<HoldTapMoveGestureRecognizer>(
() => HoldTapMoveGestureRecognizer(
supportedDevices: kTouchBasedDeviceKinds,
),
() => HoldTapMoveGestureRecognizer(),
(instance) => instance
..onHoldDragStart = onHoldDragStart
..onHoldDragUpdate = onHoldDragUpdate
@@ -573,18 +565,14 @@ class _RawTouchGestureDetectorRegionState
..onHoldDragEnd = onHoldDragEnd),
DoubleFinerTapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<DoubleFinerTapGestureRecognizer>(
() => DoubleFinerTapGestureRecognizer(
supportedDevices: kTouchBasedDeviceKinds,
), (instance) {
() => DoubleFinerTapGestureRecognizer(), (instance) {
instance
..onDoubleFinerTap = onDoubleFinerTap
..onDoubleFinerTapDown = onDoubleFinerTapDown;
}),
CustomTouchGestureRecognizer:
GestureRecognizerFactoryWithHandlers<CustomTouchGestureRecognizer>(
() => CustomTouchGestureRecognizer(
supportedDevices: kTouchBasedDeviceKinds,
), (instance) {
() => CustomTouchGestureRecognizer(), (instance) {
instance.onOneFingerPanStart =
(DragStartDetails d) => onOneFingerPanStart(context, d);
instance

View File

@@ -65,7 +65,7 @@ class _InstallPageBodyState extends State<_InstallPageBody>
late final TextEditingController controller;
final RxBool startmenu = true.obs;
final RxBool desktopicon = true.obs;
final RxBool printer = true.obs;
final RxBool printer = false.obs;
final RxBool showProgress = false.obs;
final RxBool btnEnabled = true.obs;
@@ -80,7 +80,7 @@ class _InstallPageBodyState extends State<_InstallPageBody>
final installOptions = jsonDecode(bind.installInstallOptions());
startmenu.value = installOptions['STARTMENUSHORTCUTS'] != '0';
desktopicon.value = installOptions['DESKTOPSHORTCUTS'] != '0';
printer.value = installOptions['PRINTER'] != '0';
printer.value = installOptions['PRINTER'] == '1';
}
@override

View File

@@ -517,10 +517,12 @@ class _RemotePageState extends State<RemotePage> with WidgetsBindingObserver {
}
return Container(
color: MyTheme.canvasColor,
child: RawTouchGestureDetectorRegion(
child: getBodyForMobile(),
ffi: gFFI,
),
child: inputModel.isPhysicalMouse.value
? getBodyForMobile()
: RawTouchGestureDetectorRegion(
child: getBodyForMobile(),
ffi: gFFI,
),
);
}),
),

View File

@@ -259,11 +259,13 @@ class _ViewCameraPageState extends State<ViewCameraPage>
}
return Container(
color: MyTheme.canvasColor,
child: RawTouchGestureDetectorRegion(
child: getBodyForMobile(),
ffi: gFFI,
isCamera: true,
),
child: inputModel.isPhysicalMouse.value
? getBodyForMobile()
: RawTouchGestureDetectorRegion(
child: getBodyForMobile(),
ffi: gFFI,
isCamera: true,
),
);
}),
),

View File

@@ -2,6 +2,7 @@
import os
import optparse
import subprocess
from hashlib import md5
import brotli
import datetime
@@ -65,11 +66,15 @@ def write_app_metadata(output_folder: str):
print(f"App metadata has been written to {output_path}")
def build_portable(output_folder: str, target: str):
os.chdir(output_folder)
if target:
os.system("cargo build --locked --release --target " + target)
else:
os.system("cargo build --locked --release")
current_dir = os.getcwd()
try:
os.chdir(output_folder)
cmd = ["cargo", "build", "--locked", "--release"]
if target:
cmd.extend(["--target", target])
subprocess.run(cmd, check=True)
finally:
os.chdir(current_dir)
# Linux: python3 generate.py -f ../rustdesk-portable-packer/test -o . -e ./test/main.py
# Windows: python3 .\generate.py -f ..\rustdesk\flutter\build\windows\runner\Debug\ -o . -e ..\rustdesk\flutter\build\windows\runner\Debug\rustdesk.exe

View File

@@ -67,7 +67,7 @@
Some msi packages reset the `VersionNT` value to 1000 on Windows 10.
https://www.advancedinstaller.com/user-guide/qa-OS-dependent-install.html -->
<!-- Remote printer also works on Win8.1 in my test. -->
<Custom Action="InstallPrinter" Before="InstallFinalize" Condition="VersionNT &gt;= 603 AND PRINTER = 1 OR PRINTER = &quot;Y&quot; OR PRINTER = &quot;y&quot;" />
<Custom Action="InstallPrinter" Before="InstallFinalize" Condition="VersionNT &gt;= 603 AND (PRINTER = 1 OR PRINTER = &quot;Y&quot; OR PRINTER = &quot;y&quot;)" />
<Custom Action="InstallPrinter.SetParam" Before="InstallPrinter" Condition="VersionNT &gt;= 603" />
<!--Workaround of "fire:FirewallException". If Outbound="Yes" or Outbound="true", the following error occurs.-->

View File

@@ -4,17 +4,17 @@
<?include ..\Includes.wxi?>
<!--
Properties and related actions for specifying whether to install start menu/desktop shortcuts.
Properties and related actions for specifying whether to install shortcuts and the printer.
-->
<!-- These are the actual properties that get used in conditions to determine whether to
install start menu shortcuts, they are initialized with a default value to install shortcuts.
They should not be set directly from the command line or registry, instead the CREATE* properties
below should be set, then they will update these properties with their values only if set. -->
install start menu shortcuts or the printer. Shortcut properties default to install;
PRINTER defaults to not install. The CREATE* properties below update shortcut
properties from command line, bundle, or registry values. -->
<Property Id="STARTMENUSHORTCUTS" Value="1" Secure="yes"></Property>
<Property Id="DESKTOPSHORTCUTS" Value="1" Secure="yes"></Property>
<Property Id="STARTUPSHORTCUTS" Value="1" Secure="yes"></Property>
<Property Id="PRINTER" Value="1" Secure="yes"></Property>
<Property Id="PRINTER" Secure="yes"></Property>
<!-- These properties get set from either the command line, bundle or registry value,
if set they update the properties above with their value. -->
@@ -77,7 +77,11 @@
<!-- If a command line value or registry value was set, update the main properties with the value -->
<SetProperty Id="STARTMENUSHORTCUTS" Value="" After="RestoreSavedStartMenuShortcutsValue" Sequence="first" Condition="CREATESTARTMENUSHORTCUTS AND NOT (CREATESTARTMENUSHORTCUTS = 1 OR CREATESTARTMENUSHORTCUTS = &quot;Y&quot; OR CREATESTARTMENUSHORTCUTS = &quot;y&quot;)" />
<SetProperty Id="DESKTOPSHORTCUTS" Value="" After="RestoreSavedDesktopShortcutsValue" Sequence="first" Condition="CREATEDESKTOPSHORTCUTS AND NOT (CREATEDESKTOPSHORTCUTS = 1 OR CREATEDESKTOPSHORTCUTS = &quot;Y&quot; OR CREATEDESKTOPSHORTCUTS = &quot;y&quot;)" />
<SetProperty Id="PRINTER" Value="" After="RestoreSavedPrinterValue" Sequence="first" Condition="INSTALLPRINTER AND NOT (INSTALLPRINTER = 1 OR INSTALLPRINTER = &quot;Y&quot; OR INSTALLPRINTER = &quot;y&quot;)" />
<!-- PRINTER defaults to empty now, so a saved or command-line INSTALLPRINTER=1
must explicitly enable the main PRINTER property. Non-truthy INSTALLPRINTER
values still clear PRINTER so upgrades preserve an explicit disabled choice. -->
<SetProperty Action="SetPrinterValueEnabled" Id="PRINTER" Value="1" After="RestoreSavedPrinterValue" Sequence="first" Condition="INSTALLPRINTER = 1 OR INSTALLPRINTER = &quot;Y&quot; OR INSTALLPRINTER = &quot;y&quot;" />
<SetProperty Action="SetPrinterValueDisabled" Id="PRINTER" Value="" After="SetPrinterValueEnabled" Sequence="first" Condition="INSTALLPRINTER AND NOT (INSTALLPRINTER = 1 OR INSTALLPRINTER = &quot;Y&quot; OR INSTALLPRINTER = &quot;y&quot;)" />
</Fragment>
</Wix>

View File

@@ -1426,7 +1426,11 @@ impl<T: InvokeUiSession> Remote<T> {
self.handler.set_cursor_position(cp);
}
Some(message::Union::Clipboard(cb)) => {
if !self.handler.lc.read().unwrap().disable_clipboard.v {
let clipboard_allowed = {
let lc = self.handler.lc.read().unwrap();
!lc.disable_clipboard.v && !lc.view_only.v
};
if clipboard_allowed {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
update_clipboard(vec![cb], ClipboardSide::Client);
#[cfg(target_os = "ios")]
@@ -1445,7 +1449,11 @@ impl<T: InvokeUiSession> Remote<T> {
}
}
Some(message::Union::MultiClipboards(_mcb)) => {
if !self.handler.lc.read().unwrap().disable_clipboard.v {
let clipboard_allowed = {
let lc = self.handler.lc.read().unwrap();
!lc.disable_clipboard.v && !lc.view_only.v
};
if clipboard_allowed {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
update_clipboard(_mcb.clipboards, ClipboardSide::Client);
#[cfg(target_os = "ios")]

View File

@@ -262,11 +262,9 @@ pub fn core_main() -> Option<Vec<String>> {
if config::is_disable_installation() {
return None;
}
#[cfg(not(windows))]
let options = "desktopicon startmenu";
#[cfg(windows)]
let options = "desktopicon startmenu printer";
let res = platform::install_me(options, "".to_owned(), true, args.len() > 1);
let (printer_override, debug) = parse_silent_install_args(&args);
let options = platform::get_silent_install_options(printer_override);
let res = platform::install_me(options, "".to_owned(), true, debug);
let text = match res {
Ok(_) => translate("Installation Successful!".to_string()),
Err(err) => {
@@ -933,6 +931,23 @@ fn is_cli_setting_change_disabled() -> bool {
config::is_disable_settings() && !allow_command_line_settings
}
#[cfg(windows)]
fn parse_silent_install_args(args: &[String]) -> (Option<bool>, bool) {
let mut printer_override = None;
let mut debug = false;
for arg in args.iter().skip(1) {
match arg.as_str() {
"printer=1" => printer_override = Some(true),
"printer=0" => printer_override = Some(false),
"debug" => debug = true,
_ => {}
}
}
(printer_override, debug)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -326,12 +326,14 @@ pub fn session_toggle_option(session_id: SessionID, value: String) {
try_sync_peer_option(&session, &session_id, &value, None);
}
#[cfg(not(target_os = "ios"))]
if sessions::get_session_by_session_id(&session_id).is_some() && value == "disable-clipboard" {
if sessions::get_session_by_session_id(&session_id).is_some()
&& (value == "disable-clipboard" || value == "view-only")
{
crate::flutter::update_text_clipboard_required();
}
#[cfg(feature = "unix-file-copy-paste")]
if sessions::get_session_by_session_id(&session_id).is_some()
&& value == config::keys::OPTION_ENABLE_FILE_COPY_PASTE
&& (value == config::keys::OPTION_ENABLE_FILE_COPY_PASTE || value == "view-only")
{
crate::flutter::update_file_clipboard_required();
}

View File

@@ -1324,6 +1324,23 @@ pub fn get_install_options() -> String {
serde_json::to_string(&opts).unwrap_or("{}".to_owned())
}
pub fn get_silent_install_options(printer_override: Option<bool>) -> &'static str {
let install_printer = match printer_override {
Some(override_value) => override_value,
None => {
let app_name = crate::get_app_name();
let subkey = format!(".{}", app_name.to_lowercase());
let printer = get_reg_of_hkcr(&subkey, REG_NAME_INSTALL_PRINTER);
printer.as_deref() == Some("1")
}
};
if install_printer && is_win_10_or_greater() {
"desktopicon startmenu printer"
} else {
"desktopicon startmenu"
}
}
// This function return Option<String>, because some registry value may be empty.
fn get_reg_of_hkcr(subkey: &str, name: &str) -> Option<String> {
let hkcr = RegKey::predef(HKEY_CLASSES_ROOT);

View File

@@ -30,7 +30,6 @@ fn run_rdp(port: u16) {
if !password.is_empty() {
args.push(format!("/pass:{}", password));
}
println!("{:?}", args);
std::process::Command::new("cmdkey")
.args(&args)
.output()

View File

@@ -2891,7 +2891,7 @@ impl Connection {
self.update_auto_disconnect_timer();
}
Some(message::Union::Clipboard(cb)) => {
if self.clipboard {
if self.clipboard_enabled() {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
update_clipboard(vec![cb], ClipboardSide::Host);
// ios as the controlled side is actually not supported for now.
@@ -2919,12 +2919,12 @@ impl Connection {
}
}
Some(message::Union::MultiClipboards(_mcb)) => {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.clipboard {
if self.clipboard_enabled() {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
update_clipboard(_mcb.clipboards, ClipboardSide::Host);
#[cfg(target_os = "android")]
crate::clipboard::handle_msg_multi_clipboards(_mcb);
}
#[cfg(target_os = "android")]
crate::clipboard::handle_msg_multi_clipboards(_mcb);
}
#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste"))]
Some(message::Union::Cliprdr(clip)) => {

View File

@@ -175,13 +175,16 @@ impl SessionPermissionConfig {
*self.server_clipboard_enabled.read().unwrap()
&& *self.server_keyboard_enabled.read().unwrap()
&& !self.lc.read().unwrap().disable_clipboard.v
&& !self.lc.read().unwrap().view_only.v
}
#[cfg(feature = "unix-file-copy-paste")]
pub fn is_file_clipboard_required(&self) -> bool {
let lc = self.lc.read().unwrap();
*self.server_keyboard_enabled.read().unwrap()
&& *self.server_file_transfer_enabled.read().unwrap()
&& self.lc.read().unwrap().enable_file_copy_paste.v
&& lc.enable_file_copy_paste.v
&& !lc.view_only.v
}
}
@@ -411,13 +414,16 @@ impl<T: InvokeUiSession> Session<T> {
*self.server_clipboard_enabled.read().unwrap()
&& *self.server_keyboard_enabled.read().unwrap()
&& !self.lc.read().unwrap().disable_clipboard.v
&& !self.lc.read().unwrap().view_only.v
}
#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste"))]
pub fn is_file_clipboard_required(&self) -> bool {
let lc = self.lc.read().unwrap();
*self.server_keyboard_enabled.read().unwrap()
&& *self.server_file_transfer_enabled.read().unwrap()
&& self.lc.read().unwrap().enable_file_copy_paste.v
&& lc.enable_file_copy_paste.v
&& !lc.view_only.v
}
#[cfg(feature = "flutter")]